Skip to content

hudlow/taglet

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

taglet

taglet is a zero-dependency utility for generating code, written in TypeScript. It mostly exists to improve the ergonomics of native ECMAScript tagged templates. It can also be used to generate partially abstract code which is useful when the same code has multiple target formats (such as TypeScript and JavaScript).

Usage

taglet exports a single tagged template function called taglet() which returns has special accommodations for multiline templates and returns a taglet.Taglet — an object whose toString() function renders the template, but which also exposes the ability to transform the arguments before rendering.

Inline templates

Inline templates provide lazy evaluation and transformation, but do not modify whitespace:

const line = taglet`items[${1}] = ${2};`;

// render as-is:
console.log(line.toString());
// items[1] = 2;

// transform numbers:
const newLine = line.transform((arg) => (typeof arg === "number" ? arg * 2 : arg));
console.log(newLine.toString());
// items[2] = 4;

// supply substitute arguments at render-time:
console.log(line.toString(8, '"hippo"'));
// items[8] = "hippo";

With some narrow exceptions, all characters are preserved literally, including backslashes. This means you don't have to double-backslash in most circumstances:

const line = taglet`const regexp = /\//;`;
console.log(line.toString());
// const regexp = /\//;

Block templates

In addition to minimal escaping, lazy evaluation, and argument transformation, block templates add several conveniences for multiline templates. First and foremost, block templates trim excess indentation, allowing templates to flow with code:

function f() {
  return taglet`|
    const x = 1;
    const y = 2;
  `;
}

console.log(f().toString());
const x = 1;
const y = 2;

Block templates also maintain indention for arguments that evaluate to multiple lines:

block = taglet`|
  function f(x) {
    ${["const c = 1;", "return x + c;"].join("\n")}
  }
`;

console.log(block.toString());
function f(x) {
  const c = 1;
  return x + c;
}

Block templates are formatted very similarly to block scalars in YAML. They begin with a header line consisting of:

  1. A style indicator: | for "literal" or > for "folded."
  2. An optional indention indicator: the number of spaces of non-semantic indention for the content. If omitted, the indention amount is inferred to be the leading whitespace of the first non-blank line.
  3. An optional chomping indicator: - to strip trailing empty lines and + to keep trailing empty lines. If omitted, a single trailing empty line is preserved or appended.

Like YAML, when both optional indicators are present, they may be in either order. Unlike YAML, any whitespace in the header line is illegal.

Indention

Like YAML:

  • When an indention indicator is present, the non-semantic indention prefix is the number of spaces indicated.
  • Blank lines that consist of the indention prefix or any truncation of the indention prefix are treated equivalently: as empty lines.
  • Any other line which does not begin with the indention prefix is illegal.
  • Non-empty blank lines preceding the first non-blank line are illegal.

Unlike YAML, when indention is inferred, the prefix may consist of tabs or even a mixture of spaces and tabs.

const block = taglet`|
  const x =
    y >= 0 ? 1 : -1;
`;

console.log(block.toString());
const x = y >= 0 ? 1 : -1;
const block = taglet`|0
  const x =
    y >= 0 ? 1 : -1;
`;

console.log(block.toString());
const x = y >= 0 ? 1 : -1;

Chomping

Like YAML:

  • The strip indicator (-) truncates all trailing empty lines
  • The keep indicator (+) preserves all trailing empty lines
  • The default behavior is to preserve or, if necessary, append a single trailing empty line.

Also like YAML, all non-empty lines are treated the same even if they are blank.

const block = taglet`|
  const x = 1;
  const y = 2;


`;

console.log(block.toString());
const x = 1;
const y = 2;
const block2 = taglet`|
  const x = 1;
  const y = 2;`;

console.log(block.toString());
const x = 1;
const y = 2;
const block = taglet`|+
  const x = 1;
  const y = 2;


`;

console.log(block.toString());
const x = 1;
const y = 2;
const block = taglet`|-
  const x = 1;
  const y = 2;


`;

console.log(block.toString());
const x = 1;
const y = 2;

Folding

Like YAML, for two adjacent lines to be folded, neither can have any semantic leading whitespace. Unlike YAML, both lines must be non-empty.

const block = taglet`>
  first
  second
  third
`;

console.log(block.toString());
first second third

const block = taglet`>
  first
  second
  
  third
  fourth
`;

console.log(block.toString());
first second

third fourth

Folding behavior may be controlled directly with backslash-escaped line endings:

  • For literal block templates, this folds a line with the subsequent line, without inserting a space.
  • For folded block templates, this ensures a line will not be folded.

Backslash-escaping is illegal on empty lines.

const block = taglet`>
  first
  second\
  third
`;

console.log(block.toString());
first second
third

const block = taglet`|
  one\
  -thousand
`;

console.log(block.toString());
one-thousand

Glossary

  • blank line: A line containing only whitespace
  • empty line: A line containing no semantic characters
  • semantic Preserved in a processed template
  • folding Concatenating two lines together, separated by a space
  • chomping Truncating trailing empty lines

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors