JavaScript SDK

Author a pattern as a small JavaScript file that calls a fixed set of builder functions. The file runs once, with a chosen set of parameter values, and produces a fully literal pattern — every row's text, every count, all baked in. The result is tracked exactly the same way as a Builder or DSL pattern.

When to reach for the JS SDK

The Builder and the DSL describe a pattern as a fixed shape. The JS SDK lets you describe how to generate a pattern. That's a meaningful upgrade when:

  • Stitch counts depend on math the DSL doesn't naturally express.
  • Rows are repeated with small variations you'd rather express with a loop.
  • You want functions you can reuse across patterns (your own helpers).
  • You'd rather use a real editor with refactoring and types.

For straightforward patterns the Builder is faster. Reach for JS once you find yourself fighting either of the other paths.

The model

The script gets a small set of global functions. The shape of a script is:

Pattern("Pattern name", () => {
  // Declare parameters first.
  const cast_on = Parameter("cast_on", { type: "int", default: 64 });

  // Then segments. Each segment has a name and a body.
  Segment("Cuff", () => {
    // Inside a segment, emit Rows, Notes, Setup, Repeats, etc.
    Row(`Cast on ${cast_on} stitches.`);
    Repeat(20, () => {
      Row("K1, P1 ribbing");
    });
  });
});

Parameter() returns a concrete value (a number, string, or boolean) — not a symbolic reference. If you re-run the script with a different parameter set, you get a different IR. The compiled IR is flat: rows have literal text, repeats have literal counts, no expressions, no counters.

Builder reference

The functions available inside your script:

Pattern(name, fn)

Top-level container. Called exactly once.

Parameter(name, opts)

Declares a parameter. opts is { type: "int" | "text" | "bool", default: value, label?: string }. Returns the concrete value (the override if one was passed at run time, otherwise the default). Must be called inside Pattern, before any Segment.

Segment(name, fn)

Declares a segment. The body emits the steps of that segment in order. Segments may not nest.

Setup(fn)

Optional. Runs inside a segment, before the segment body. Steps emitted inside Setup become the segment's setup block — shown to the knitter once at the start of the segment but not part of the looped body. Allowed once per segment.

Row(text)

Emits one row. text must be a string — typically a template literal that interpolates parameter values or local JS variables.

Note(text)

Emits one note. Same string contract as Row.

Repeat(count, fn)

Repeats the body count times. count must be a positive integer. Use a JS for-loop if you need access to the iteration index — this builder doesn't expose one.

RepeatUntilText(text, fn)

The one runtime escape hatch. Emits a repeat block that runs until the knitter judges a milestone is met ("until 4 inches", "until sleeve reaches your wrist"). Useful for measurement-driven sections that can't be unrolled.

Common patterns

A computed cast-on

const sts = Parameter("sts", { type: "int", default: 64 });
const repeat_unit = 4;
const repeats = Math.floor(sts / repeat_unit);

Segment("Cuff", () => {
  Row(`Cast on ${repeats * repeat_unit} stitches.`);
});

Alternating rows via JS conditional

Repeat(20, () => {
  // Use a for-loop if you need the index.
});

// Or, just emit the alternation explicitly:
for (let r = 1; r <= 20; r++) {
  Row(r % 2 === 1 ? "RS: Knit all" : "WS: Purl all");
}

A reusable helper

function ribbing(rows, sts) {
  for (let r = 0; r < rows; r++) {
    Row(`K1, P1 to end (${sts} sts)`);
  }
}

Pattern("Sock", () => {
  const cast_on = Parameter("cast_on", { type: "int", default: 64 });
  Segment("Cuff", () => {
    ribbing(15, cast_on);
  });
});

A measurement-driven section

Segment("Foot", () => {
  RepeatUntilText("until foot reaches base of toes", () => {
    Row("Knit all");
  });
});

A complete sock cuff

Pattern("Simple Sock Cuff", () => {
  const cast_on = Parameter("cast_on", { type: "int", default: 64 });
  const cuff_length = Parameter("cuff_length", { type: "int", default: 15 });

  Segment("Cuff", () => {
    Note(`Cast on ${cast_on} sts and join for working in the round.`);

    for (let r = 1; r <= cuff_length; r++) {
      Row(r < 5 ? "K1, P1 ribbing (tight)" : "K1, P1 ribbing (standard)");
    }

    Note("Cuff complete! Prepare for the leg section.");
  });
});

Importing a JS pattern

The pattern library has an Import Pattern JS action: paste the script, set parameter values, and the SDK runs the script and stores both the JS source and the compiled DSL alongside it. The tracker uses the compiled output. To re-render with new parameter values, edit the source and re-import.

Patterns authored in JS coexist with Builder- and DSL-authored ones — see the Builder and DSL paths for those.