[]previous and next page

Compiler options

compile(source, options) is a pure function: it parses the module with a real AST, replaces each JSX tree by generated code and returns the module text with…

For agentsUpdated

compile(source, options) is a pure function: it parses the module with a real AST, replaces each JSX tree by generated code and returns the module text with everything else untouched. See compile and CompileOptions.

Option Type Default Effect
filename string "module.tsx" Used in diagnostics and source maps, and to choose the parser language: .tsx is TSX; .ts, .mts, .cts are TypeScript without JSX; .jsx and every other extension are JavaScript with JSX.
generate "dom" | "ssr" "dom" dom emits template clones and bindings for the browser. ssr emits string builders for the server.
moduleName string "@shadow-js/web" for dom, "@shadow-js/web/server" for ssr The module the runtime helpers (template, insert, effect, createComponent, derived, ssr, ...) are imported from. The HMR helpers come from <moduleName>/hmr.
dev boolean false Development output. In the dom target every createComponent(...) call gets the component's name as a third argument, which the runtime records for devtools.
autoReactive boolean true The forgiving reactivity rewrite. false leaves constants, destructured props and returned conditionals exactly as written.
hmr boolean false dom target only. Wraps top-level components so an edit swaps them in place, using registry, component and accept from <moduleName>/hmr and import.meta.hot. Ignored for ssr.
hydratable boolean true ssr target only. Stamps a data-hk key on every template root so hydrate can claim the element. false omits the keys; that markup cannot be hydrated. The dom output is the same either way.
sourceMap boolean true Produce a source map (hires: "boundary", with the source content). false returns map: null.

The result has four fields: code, map, diagnostics (always empty today; problems are thrown) and transformed, which is false when the module contains no JSX. A module without JSX is returned untouched and map is null.

src/compile-basics.tsts
import { compile } from "@shadow-js/compiler";

const plain = compile("export const answer = 42;\n");
console.log(plain.transformed, plain.map === null);

const source = `export const Hello = () => <p class="a">hi</p>;\n`;
const result = compile(source);
console.log(result.transformed, result.map === null, result.diagnostics.length);

const firstLine = (code: string): string => code.split("\n")[0] ?? "";
console.log(firstLine(result.code));
console.log(firstLine(compile(source, { generate: "ssr" }).code));
console.log(firstLine(compile(source, { moduleName: "my-runtime" }).code));
console.log(firstLine(compile(source, { generate: "ssr", moduleName: "my-runtime" }).code));

console.log(compile(source, { sourceMap: false }).map === null);
console.log(compile(source, { filename: "src/App.tsx" }).map?.sources.join(","));
Expected output
false true
true false 0
import { template as _$template } from 

The first block shows transformed, the default module names and moduleName; the last two lines show sourceMap and that filename becomes the source of the map.

dev, hmr and hydratable#

src/compile-flags.tsts
import { compile } from "@shadow-js/compiler";

const app = `function Hello() {
  return <p>hi</p>;
}
export const View = () => <Hello />;
`;

const names = (options: object): boolean => compile(app, options).code.includes('}, "Hello")');
console.log("dev:", names({}), names({ dev: true }));

const hot = (options: object): boolean => compile(app, options).code.includes('"@shadow-js/web/hmr"');
console.log("hmr:", hot({}), hot({ hmr: true }));

const custom = compile(app, { hmr: true, moduleName: "my-runtime" }).code;
console.log("hmr with moduleName:", custom.includes('"my-runtime/hmr"'));

console.log("hmr on the server target:", compile(app, { hmr: true, generate: "ssr" }).code.includes("hmr"));

const keyed = (options: object): boolean => compile(app, options).code.includes("ssrHydrationKey");
console.log("keys:", keyed({ generate: "ssr" }), keyed({ generate: "ssr", hydratable: false }));
Expected output
dev: false true
hmr: false true
hmr with moduleName: true
hmr on the server target: false
keys: true false

autoReactive#

The default is true; the compiler output for false is shown in Forgiving reactivity.

Errors#

Source the compiler cannot or will not transform throws a CompileError. Its diagnostic has code, message, severity, start, end (UTF-16 offsets), line (1-based) and column (0-based); frame is a code frame and id the filename. Parse errors have the code PARSE_ERROR. The compiler also refuses invalid HTML nesting (INVALID_HTML_NESTING) and namespaced tag names (NAMESPACED_TAG).

src/compile-errors.tsts
import { CompileError, compile } from "@shadow-js/compiler";

function report(source: string, filename?: string): string {
  try {
    compile(source, filename === undefined ? {} : { filename });
    return "no error";
  } catch (error) {
    if (error instanceof CompileError) {
      return `${error.diagnostic.code} ${error.diagnostic.line}:${error.diagnostic.column}`;
    }
    throw error;
  }
}

console.log(report("const x = <p>;"));
console.log(report("const x = <p><div /></p>;"));
console.log(report("const x = <svg:rect />;"));
console.log(report("const x = <p />;", "a.ts"));
console.log(report("const x = <p />;", "a.js"));
Expected output
PARSE_ERROR 1:13
INVALID_HTML_NESTING 1:10
NAMESPACED_TAG 1:11
PARSE_ERROR 1:13
no error

The last two lines show the filename rule: JSX in a .ts file is a parse error, in a .js file it compiles.