UNPKG

@anireact/d

Version:

Dedent templates, autoindent interpolations, and more.

150 lines 4.21 kB
import { getRaw, scan } from './private.mjs'; /** * Just an identity function. Can be reused to save few bytes of your bundle, huh. */ export const id = (x) => x; /** * Template tag args type check. Does its best to detect actual template-tag * calls and to filter out other similar signatures: * * - The {@linkcode args} array must be non-empty. * - The head `args[0]`: * - Must be `Array.isArray`. * - Must be frozen. * - Must have own `raw` prop with non-enumerable `value` descriptor. * - The raws `args[0].raw`: * - Must be `Array.isArray`. * - Must be frozen. * - Must have the same length as head. * - The tail `args.slice(1)`: * - Must be one item shorter than head. * * ```typescript * function f(...args: BDD.Args<any> | [x: string[]]) { * if (isTemplate(args)) { * // Called as a tag * } else { * let [x] = args; * // Called as a regular function * } * } * ``` */ export const isTemplate = (args) => { if (!args.length) return false; let head = args[0]; if (!Array.isArray(head)) return false; if (!('raw' in head)) return false; let prop = Object.getOwnPropertyDescriptor(head, 'raw'); let raws = head.raw; if (!prop) return false; if (!('value' in prop)) return false; if (prop.enumerable) return false; if (!Array.isArray(raws)) return false; if (!Object.isFrozen(head)) return false; if (!Object.isFrozen(raws)) return false; if (head.length !== raws.length) return false; if (head.length !== args.length) return false; return true; }; export function d(...args) { // Tag call: if (isTemplate(args)) { return scan(args[0].raw, args.slice(1)).map(d).join(''); } let head = args[0]; // function Dedent(v: Dedent.Token<any>[]): string; if (Array.isArray(head)) { return head.map(d).join(''); } // function Dedent<q, z>(params: Dedent.Params<q, z>): Dedent.Tag<q, z>; else if ('impl' in head) { let { raw = true, impl } = head; let getLit = raw ? getRaw : id; return (...args) => impl(scan(getLit(args[0]), args.slice(1))); } // function Dedent(t: Dedent.Token<any>): string; else { let str = String(head.value); if (head.lit) return str; let buf = str.split('\n'); let pad = head.pad; for (let i = 1; i < buf.length; i++) { let line = buf[i]; if (line) buf[i] = pad + line; } return buf.join('\n'); } } (function (d) { /** * Converts {@linkcode Token} value using `String()`. * * ```typescript * d.stringify({ * lit: true, * value: 'Literal', * }) === 'Literal'; * * d.stringify({ * lit: false, * value: 2434, * pad: '', * }) === '2434'; * * d.stringify({ * lit: false, * value: { * toString: () => 'x.toString', * }, * pad: '', * }) === 'x.toString'; * * d.stringify({ * lit: false, * value: { * [Symbol.toPrimitive]: () => 'x[Symbol.toPrimitive]', * }, * pad: '', * }) === 'x[Symbol.toPrimitive]'; * ``` */ d.stringify = (t) => String(t.value); /** * Similar to the {@linkcode d} tag, but returns an array of * {@linkcode Token} objects, with no autoindent applied to interpolation * tokens: * * ```typescript * deepEqual( * d.tokenize`¶ * ␣␣␣␣␣␣␣␣Literal¶ * ␣␣␣␣␣␣␣␣␣␣␣␣${'Interpolation'}¶ * ␣␣␣␣␣␣␣␣Another literal¶ * ␣␣␣␣`, * [ * { lit: true, value: 'Literal\n ' }, * { lit: false, value: 'Interpolation', pad: ' ' }, * { lit: true, value: '\nAnother literal' }, * ], * ); * ``` */ d.tokenize = (head, ...tail) => { return scan(head.raw, tail); }; })(d || (d = {})); //# sourceMappingURL=dedent.mjs.map