UNPKG

@anireact/d

Version:

Dedent templates, autoindent interpolations, and more.

120 lines 4.28 kB
/** * Match the first non-whitespace and the rest of line. */ const PAT = /\S.*\s*$/gm; /** * Getter for the `raw` property. */ export const getRaw = (s) => s.raw; /** * Template arguments tokenizer. * * @param s Literals array; must be non-empty. * @param q Quasis array; must be one item shorted than {@linkcode s}. * @returns Interleaved sequence of literal and quasi tokens, with literals * properly dedented and trimmed, and quasis with inferred autoindent * strings. */ export const scan = (s, q) => { // ================================================================== // // Reduce the string: // // 1. Replace quasis with `!`. // // 2. In each line, drop everything after the first non-WS char. // // 3. Transform non-WS substrings to `!`. // // 4. Drop trailing WS. // // 5. Collapse blank lines. // // ================================================================== // /** Shape string. */ let shape = s.join('!'); // 1. Don’t process completely blank literals: if (shape.trim() === '') { return [ { lit: true, value: shape, }, ]; } // 2. Use a single regexp to: // 1. In each line, replace the first non-WS and the rest // with the `!` char. // 2. In each line, trim trailing WS. // 3. Collapse blank lines. // 4. Trim trailing WS and blanks. shape = shape.replace(PAT, '!'); /** Line vector. */ let lines = shape.split('\n'); /** Least padding. */ let shift = Infinity; // ======================== // // Detect the dedent width: // // ======================== // // 1. For each line except the first one: for (let line of lines.slice(1)) { // Get the minimum of: // a) The current least padding. // b) The current line padding. shift = Math.min(shift, line.length - 1); } // 2. Use zero padding for a single-line template: if (shift === Infinity) shift = 0; // ============================== // // Actually process the template: // // ============================== // // 1. Clear temporaries: shape = lines = null; /** Output buffer. */ let v = []; /** Current literal index. */ let i = 0; /** Last literal index. */ let z = q.length; /** Quasi autoindent string. */ let pad = ''; // 2. For each quasi (and preceding literal): for (let value of q) { // 1. Consume and push the preceding literal // and infer the padding for the current quasi: lit(); // 2. Push the token with the value and inferred padding: v.push({ lit: false, value, pad, }); } // 3. Consume and push the final literal: lit(); // 4. Return the result: return v; /** * Consume a literal chunk and push a corresponding token. * * @returns The chunk’s line vector. */ function lit() { /** Current chunk’s line vector. */ let lv = s[i].split('\n'); /** Current chunk’s line count. */ let lc = lv.length; // 1. Dedent the tail lines of the chunk: for (let i = 1; i < lc; i++) { lv[i] = lv[i].slice(shift); } // 2. Drop the first line of the first chunk unless it’s // a) non-blank, OR // b) the only line of the chunk. if (i === 0 && lv[0].trim() === '' && lc > 1) { lv.shift(); } // 3. Trim the very final newline of the final chunk: if (i === z && lv.at(-1) === '') { lv.pop(); } // 4. Push the token: v.push({ lit: true, value: lv.join('\n'), }); // 5. Update the quasi padding: if (lc > 1) { pad = lv.at(-1).replace(PAT, ''); } // 6. Increment the literal index: i++; } }; //# sourceMappingURL=private.mjs.map