solid-tag
v0.0.4
Published
Browser-friendly JSX to @solidjs/html tagged-template source transformer
Maintainers
Readme
Monorepo: See the workspace root
README.md,ARCHITECTURE.mdanddocs/CONFORMANCE.mdfor shared playwright test orchestration. This package remains independently publishable.
solid-tag
solid-tag lowers ordinary JSX source into the tagged-template syntax consumed by Solid 2's @solidjs/html runtime.
It is intentionally not the normal Solid JSX DOM compiler and it does not depend on Solid.
ordinary JSX source
↓
solid-tag
↓
JavaScript + html`...`
↓
@solidjs/html at runtimeInstall
npm install solid-tagOptional Solid 1.x entry point (since 0.0.4)
The default solid-tag import continues to target Solid 2 (@solidjs/html).
Solid 1.x has a completely separate transformer package, [solid-tag-1],
which can be accessed directly or through this small convenience re-export:
# Solid 2 only: no Solid 1 dependency installed
npm install solid-tag
# Only Solid 1: no need for solid-tag
npm install solid-tag-1
# Optional convenience path through solid-tag
npm install solid-tag solid-tag-1// Solid 2 (unchanged)
import { transformJSX as transformSolid2 } from "solid-tag";
// Solid 1 (independent implementation)
import { transform } from "solid-tag-1";
// Or, with both packages installed:
import { transform as transformSolid1 } from "solid-tag/solid1";
const { code } = transformSolid1('export default () => <div>Hello</div>', {
filename: 'App.jsx'
});
// Solid 1 output uses `solid-js/html`, not `@solidjs/html`.The /solid1 path contains only an ES module and TypeScript re-export of
solid-tag-1. solid-tag declares solid-tag-1 as an optional peer; it does
not bundle or automatically install the Solid 1 compiler. Importing the subpath
without installing solid-tag-1 produces a missing-module error.
The two transformers have distinct APIs: for example, Solid 1's
transformJSX() returns a string, whereas Solid 2's transformJSX() returns a
result object. Do not interchange their API assumptions. Use the published
solid-tag-1 README for Solid 1 options, playground, and conformance tests.
solid-tag/solid1 is a convenience surface only. The canonical package for
BMR's future Solid 1 framework adapter is solid-tag-1 itself; this avoids
accidentally coupling its provider to Solid 2.
Basic use
import { transformJSX } from "solid-tag";
const result = transformJSX(`
function App(props) {
return <button onClick={props.onClick}>{props.label}</button>;
}
`);
console.log(result.code);The result contains tagged syntax such as:
function App(props) {
return html`<button onClick=${props.onClick}>${() => (props.label)}</button>`;
}transformJSX() only rewrites JSX. It does not add an @solidjs/html import.
Module helper
transformModule() can optionally add the @solidjs/html runtime import to transformed module source:
import { transformModule } from "solid-tag";
const result = transformModule(source);
// result.code begins with:
// import html from "@solidjs/html";You can disable or customize that behavior:
transformModule(source, {
injectHtmlImport: false,
tag: "tpl",
htmlImportSource: "@solidjs/html"
});API
transformJSX(source, options?)
Returns:
{
code: string;
diagnostics: Array<{
severity: string;
message: string;
start: number | null;
end: number | null;
}>;
changed: boolean;
}Options currently include:
{
tag?: string; // default "html"
parseOptions?: Record<string, unknown>;
wrapDynamicDomExpressions?: boolean;
wrapComponentProps?: boolean;
rewriteIdentifierRefs?: boolean;
}transformModule(source, options?)
Same result as transformJSX, with optional @solidjs/html import injection.
parseJSX(source, options?)
Exposes the built-in Acorn + acorn-jsx parser.
transformAst(source, ast, options?)
Parser-independent lower-level transform. The AST must be ESTree-compatible and provide start/end offsets on JSX/expression nodes.
createTransformer(parse, options?)
Build a transformer around another parser.
Component callback props
Event-looking props on components remain component callback values rather than DOM events.
<Editor onChange={value => save(value)} />Because @solidjs/html uses function arity to distinguish callback values from getter holes, a zero-argument inline component callback is rewritten with one ignored argument. Callbacks that already accept one or more arguments are preserved directly.
Refs
@solidjs/html accepts callback-form refs, while Solid JSX allows both writable variable refs and callback refs. solid-tag adapts identifier refs at runtime so both forms work:
let element;
<div ref={element} />assigns the rendered element to element, while:
const mount = element => console.log(element);
<div ref={mount} />invokes mount(element) without attempting to assign to the const binding. Inline and forwarded callback expressions are preserved directly.
Current scope
The initial lowering handles:
- native JSX elements
- components and member components
- dynamic DOM attributes and children
- component props
- DOM and component events
- variable and callback refs lowered to callback-form refs
- spreads
- fragments
- nested JSX in expressions
For-style render callbacks- JSX text whitespace normalization
Current limitations:
- the built-in parser accepts JavaScript + JSX, not TypeScript/TSX source strings
- source maps are not generated yet
- directives and special DOM namespaces still need a larger runtime-conformance matrix
- this is an early compatibility layer, not yet a complete replacement for all
babel-preset-solidJSX semantics
Component event callback arity
solid-tag distinguishes event handlers from ordinary reactive attribute expressions so they are not accidentally turned into a function that returns another function.
For component on* props, @solidjs/html uses function arity to distinguish callbacks from getter-shaped values. Inline callbacks that already accept an argument are preserved. Zero-argument callbacks are adapted to accept an ignored event argument. Identifier/member/dynamic event expressions are evaluated once; non-functions and functions with existing arity are preserved exactly, while only zero-argument functions receive a forwarding arity adapter.
