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…
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.
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(","));
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#
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 }));
dev: false true
hmr: false true
hmr with moduleName: true
hmr on the server target: false
keys: true falseautoReactive#
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).
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"));
PARSE_ERROR 1:13
INVALID_HTML_NESTING 1:10
NAMESPACED_TAG 1:11
PARSE_ERROR 1:13
no errorThe last two lines show the filename rule: JSX in a .ts file is a parse error, in a .js file it compiles.