@anireact/d
Version:
Dedent templates, autoindent interpolations, and more.
150 lines • 4.21 kB
JavaScript
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