@anireact/d
Version:
Dedent templates, autoindent interpolations, and more.
1 lines • 11 kB
Source Map (JSON)
{"version":3,"file":"dedent.mjs","sourceRoot":"","sources":["../impl/dedent.mts"],"names":[],"mappings":"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","sourcesContent":["import { getRaw, scan } from './private.mjs';\n\n/**\n * Just an identity function. Can be reused to save few bytes of your bundle, huh.\n */\nexport const id = <t extends unknown>(x: t): t => x;\n\n/**\n * Template tag args type check. Does its best to detect actual template-tag\n * calls and to filter out other similar signatures:\n *\n * - The {@linkcode args} array must be non-empty.\n * - The head `args[0]`:\n * - Must be `Array.isArray`.\n * - Must be frozen.\n * - Must have own `raw` prop with non-enumerable `value` descriptor.\n * - The raws `args[0].raw`:\n * - Must be `Array.isArray`.\n * - Must be frozen.\n * - Must have the same length as head.\n * - The tail `args.slice(1)`:\n * - Must be one item shorter than head.\n *\n * ```typescript\n * function f(...args: BDD.Args<any> | [x: string[]]) {\n * if (isTemplate(args)) {\n * // Called as a tag\n * } else {\n * let [x] = args;\n * // Called as a regular function\n * }\n * }\n * ```\n */\nexport const isTemplate = <t extends d.Args<any>>(args: any[]): args is t => {\n if (!args.length) return false;\n\n let head = args[0];\n\n if (!Array.isArray(head)) return false;\n if (!('raw' in head)) return false;\n\n let prop = Object.getOwnPropertyDescriptor(head, 'raw');\n let raws = head.raw;\n\n if (!prop) return false;\n if (!('value' in prop)) return false;\n if (prop.enumerable) return false;\n\n if (!Array.isArray(raws)) return false;\n\n if (!Object.isFrozen(head)) return false;\n if (!Object.isFrozen(raws)) return false;\n\n if (head.length !== raws.length) return false;\n if (head.length !== args.length) return false;\n\n return true;\n};\n\n/**\n * The dedent tag you want:\n *\n * - Raw mode.\n * - If the first line is blank, it’s trimmed.\n * - If the last line is blank, it’s trimmed.\n * - Other lines are dedented by the least common indent.\n * - Blank lines don’t affect the dedent width.\n * - Non-blank first line doesn’t affect the dedent width and isn’t dedented.\n * - Tab interpreted as a _single_ space.\n * - Interpolation values are converted to strings.\n * - If the interpolation value is multiline, the lines are autoindented to\n * match the indent of the line the interpolation is placed at. Blank lines of\n * interpolation are not autoindented.\n * - Completebly blank templates with no interpolations are kept untouched.\n *\n * _See README or tests for additional examples._\n */\nexport function d(...args: d.Args<any>): string;\n\n/**\n * Custom dedent tag constructor.\n *\n * _See the types {@linkcode d.Tag|Tag} and {@linkcode d.Params|Params}._\n *\n * ```typescript\n * // Similar to `d`, but uses cooked literals:\n * d({\n * raw: false,\n * impl: d,\n * });\n *\n * // Similar to `d`, but doesn’t autoindent interpolations:\n * d({\n * impl: v => v.map(d.stringify).join(''),\n * });\n *\n * // Similar to `d`, but doesn’t concatenate substrings:\n * d({\n * impl: v => v.map(d),\n * });\n *\n * // Similar to `d.tokenize`, but stringifies tokens:\n * d({\n * impl: v => v.map(d.stringify),\n * });\n * ```\n *\n * @param params Tag constructor params.\n * @returns Constructed dedent tag.\n */\nexport function d<q, z>(params: d.Params<q, z>): d.Tag<q, z>;\n\n/**\n * The default {@linkcode d} tag {@linkcode d.Params.impl|impl} function:\n *\n * 1. Stringifies tokens and autoindents interpolations.\n * 2. Then concatentates everything into a single string.\n */\nexport function d(v: d.Token<any>[]): string;\n\n/**\n * Converts {@linkcode d.Token|Token} to string, autoindents interpolations:\n *\n * ```typescript\n * let arr = [\n * { lit: true, value: 'lit-1' },\n * { lit: false, value: 'line-1\\nline-2', pad: ' ' },\n * { lit: true, value: 'lit-2' },\n * ];\n *\n * deepEqual(arr.map(d), [\n * 'lit-1',\n * 'line-1\\n line-2',\n * 'lit-2',\n * ]);\n * ```\n *\n * @param t A token to autoindent.\n * @returns Stringified and autoindented token value.\n */\nexport function d(t: d.Token<any>): string;\n\nexport function d(...args: d.Args<any> | [d.Params<any, any> | d.Token<any>[] | d.Token<any>]) {\n // Tag call:\n if (isTemplate(args)) {\n return scan(args[0].raw, args.slice(1)).map(d).join('');\n }\n\n let head = args[0];\n\n // function Dedent(v: Dedent.Token<any>[]): string;\n if (Array.isArray(head)) {\n return head.map(d).join('');\n }\n\n // function Dedent<q, z>(params: Dedent.Params<q, z>): Dedent.Tag<q, z>;\n else if ('impl' in head) {\n let { raw = true, impl } = head;\n let getLit = raw ? getRaw : id;\n return (...args: d.Args<any>) => impl(scan(getLit(args[0]), args.slice(1)));\n }\n\n // function Dedent(t: Dedent.Token<any>): string;\n else {\n let str = String(head.value);\n\n if (head.lit) return str;\n\n let buf = str.split('\\n');\n let pad = head.pad;\n\n for (let i = 1; i < buf.length; i++) {\n let line = buf[i]!;\n if (line) buf[i] = pad + line;\n }\n\n return buf.join('\\n');\n }\n}\n\nexport namespace d {\n /**\n * Template tag args array type.\n *\n * @template q Quasi type.\n */\n export type Args<q> = [head: TemplateStringsArray, ...tail: q[]];\n\n /**\n * Template tag type.\n *\n * @template q Quasi type.\n * @template z Result type.\n */\n export type Tag<q, z> = (...args: Args<q>) => z;\n\n /**\n * Better Dedent token type, either literal {@linkcode Lit} or interpolation\n * {@linkcode Quasi}.\n */\n export type Token<q> = Lit | Quasi<q>;\n\n /**\n * Literal token.\n */\n export interface Lit {\n /** Type tag. */ readonly lit: true;\n /** Literal value. */ value: string;\n }\n\n /**\n * Quasi token.\n *\n * @template q Quasi type.\n */\n export interface Quasi<q> {\n /** Type tag. */ readonly lit: false;\n /** Quasi value. */ value: q;\n /** Autoindent prefix. */ pad: string;\n }\n\n /**\n * Custom Better Dedent tag {@link d|constructor} params.\n *\n * @template q Quasi type.\n * @template z Result type.\n */\n export interface Params<q, z> {\n /** Raw mode flag; defaults to `true`. */ raw?: boolean | undefined;\n /** Tag implementation function. */ impl(v: Token<q>[]): z;\n }\n\n /**\n * Converts {@linkcode Token} value using `String()`.\n *\n * ```typescript\n * d.stringify({\n * lit: true,\n * value: 'Literal',\n * }) === 'Literal';\n *\n * d.stringify({\n * lit: false,\n * value: 2434,\n * pad: '',\n * }) === '2434';\n *\n * d.stringify({\n * lit: false,\n * value: {\n * toString: () => 'x.toString',\n * },\n * pad: '',\n * }) === 'x.toString';\n *\n * d.stringify({\n * lit: false,\n * value: {\n * [Symbol.toPrimitive]: () => 'x[Symbol.toPrimitive]',\n * },\n * pad: '',\n * }) === 'x[Symbol.toPrimitive]';\n * ```\n */\n export const stringify = (t: d.Token<any>): string => String(t.value);\n\n /**\n * Similar to the {@linkcode d} tag, but returns an array of\n * {@linkcode Token} objects, with no autoindent applied to interpolation\n * tokens:\n *\n * ```typescript\n * deepEqual(\n * d.tokenize`¶\n * ␣␣␣␣␣␣␣␣Literal¶\n * ␣␣␣␣␣␣␣␣␣␣␣␣${'Interpolation'}¶\n * ␣␣␣␣␣␣␣␣Another literal¶\n * ␣␣␣␣`,\n * [\n * { lit: true, value: 'Literal\\n ' },\n * { lit: false, value: 'Interpolation', pad: ' ' },\n * { lit: true, value: '\\nAnother literal' },\n * ],\n * );\n * ```\n */\n export const tokenize = <q extends unknown>(head: TemplateStringsArray, ...tail: q[]): Token<q>[] => {\n return scan(head.raw, tail);\n };\n}\n"]}