workstar-compiler 0.1.0 → 0.2.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,10 +10,21 @@ The `workstar-compile` command compiles one component or a directory:
10
10
  workstar-compile --all src .workstar/generated --css .workstar/styles.css
11
11
  ```
12
12
 
13
- For Vite, import `workstar` from `workstar-compiler/vite` and add `workstar()` to `plugins`. Vite compiles imported components in memory; the CLI is for explicit output and type checking.
13
+ For Vite, import `workstar` from `workstar-compiler/vite` and add `workstar()` to `plugins`. Vite compiles explicit `?workstar` component imports in memory. Use `workstar({ foreign: 'automatic' })` to compile ordinary TSX/Vue imports under `src`, including a supported `createRoot(...).render(...)` entry. Use `workstar({ foreign: 'runtime' })` for unchanged React TSX applications that need Workstar-backed state and routing. The runtime mode is experimental and does not yet reproduce every React behavior. The CLI is for explicit output and type checking.
14
+
15
+ During Vite development, the compiler adds state identities for direct local
16
+ `signal()` declarations. Pass a stable `HotContext` from `workstar/dev` to a
17
+ root component when accepting Vite updates, as shown in the static starter.
18
+ Production output does not include this instrumentation. The compiler maps
19
+ script statements and the markup entry point back to authored source in Vite;
20
+ individual markup expressions are not mapped yet.
14
21
 
15
22
  Inside `<script lang="ts">`, imports and an optional exported `Props` type define the component contract. Top-level `const`, `let`, and function declarations create state and behavior for each component instance; import `signal` from `workstar` for reactive local state. Keep module-wide shared state in an imported TypeScript module when sharing is intentional.
16
23
 
24
+ Use `bind:attrs={record}` on a native element to spread a plain record of checked HTML attributes. The record can be reactive. Event handlers, styles, refs, and unsafe URLs are rejected; bind events explicitly with `on:event`.
25
+
17
26
  `<noscript>` accepts static text only. Put links and localized expressions elsewhere in the page; nested markup would be parsed as raw text and dynamic markers cannot hydrate reliably.
18
27
 
19
28
  See the [Workstar repository](https://github.com/wslab-ai/workstar) for starters and current limitations.
29
+
30
+ For experimental React-style TSX and Vue SFC source conversion without their runtimes, see the [compatibility guide](https://github.com/wslab-ai/workstar/blob/main/docs/foreign-components.md). Use `?workstar` imports with the Vite plugin or `workstar-compile --compat` with any other build system.
@@ -2,8 +2,10 @@
2
2
  import { resolve } from 'node:path';
3
3
  import {
4
4
  compileViewFile,
5
+ compileForeignFile,
5
6
  compileViewDirectory,
6
7
  watchViewDirectory,
8
+ auditForeignDirectory,
7
9
  } from '../dist/src/project.js';
8
10
 
9
11
  const args = process.argv.slice(2);
@@ -13,7 +15,10 @@ try {
13
15
  (args.length === 4 && args[2] === '--css')
14
16
  ? { cssOutputPath: resolve(args.at(-1)) }
15
17
  : {};
16
- if (
18
+ if (args.length === 2 && args[0] === '--compat-audit') {
19
+ const report = await auditForeignDirectory(resolve(args[1]));
20
+ process.stdout.write(`${JSON.stringify(report, null, 2)}\n`);
21
+ } else if (
17
22
  (args.length === 3 || (args.length === 5 && args[3] === '--css')) &&
18
23
  args[0] === '--all'
19
24
  ) {
@@ -35,6 +40,13 @@ try {
35
40
  process.stdout.write(
36
41
  `Watching ${resolve(args[1])} for .workstar changes.\n`,
37
42
  );
43
+ } else if (
44
+ (args.length === 3 || (args.length === 5 && args[3] === '--css')) &&
45
+ args[0] === '--compat' &&
46
+ /\.(tsx|vue)$/.test(args[1] ?? '') &&
47
+ args[2]?.endsWith('.ts')
48
+ ) {
49
+ await compileForeignFile(resolve(args[1]), resolve(args[2]), cssOption);
38
50
  } else if (
39
51
  (args.length === 2 || (args.length === 4 && args[2] === '--css')) &&
40
52
  args[0]?.endsWith('.workstar') &&
@@ -45,7 +57,9 @@ try {
45
57
  } else {
46
58
  process.stderr.write(
47
59
  'Usage: workstar-compile input.workstar output.ts [--css public/components.css]\n' +
60
+ ' workstar-compile --compat input.tsx|input.vue output.ts [--css public/components.css]\n' +
48
61
  ' workstar-compile --all source-directory output-directory [--css public/components.css]\n' +
62
+ ' workstar-compile --compat-audit source-directory\n' +
49
63
  ' workstar-compile --watch source-directory output-directory [--css public/components.css]\n',
50
64
  );
51
65
  process.exitCode = 2;
@@ -0,0 +1,2 @@
1
+ export declare const pathExpression: RegExp;
2
+ export declare function reject(filename: string, reason: string): never;
@@ -0,0 +1,4 @@
1
+ export const pathExpression = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*$/;
2
+ export function reject(filename, reason) {
3
+ throw new Error(filename + ': unsupported compatibility syntax: ' + reason);
4
+ }
@@ -0,0 +1,5 @@
1
+ export { convertReactComponent } from './react-compat.js';
2
+ export { convertVueComponent } from './vue-compat.js';
3
+ export declare function convertForeignComponent(source: string, filename: string, options?: {
4
+ resolveReactImport?: (specifier: string) => string | undefined;
5
+ }): string;
@@ -0,0 +1,12 @@
1
+ import { convertReactComponent } from './react-compat.js';
2
+ import { convertVueComponent } from './vue-compat.js';
3
+ import { reject } from './compat-rules.js';
4
+ export { convertReactComponent } from './react-compat.js';
5
+ export { convertVueComponent } from './vue-compat.js';
6
+ export function convertForeignComponent(source, filename, options = {}) {
7
+ if (filename.endsWith('.tsx'))
8
+ return convertReactComponent(source, filename, options);
9
+ if (filename.endsWith('.vue'))
10
+ return convertVueComponent(source, filename);
11
+ return reject(filename, 'file extension');
12
+ }
@@ -1,6 +1,14 @@
1
1
  import type { DefaultTreeAdapterTypes as Html } from 'parse5';
2
- export declare function componentScript(node: Html.Element, filename: string, componentImports: 'generated' | 'source', rewriteRelativeImport?: (specifier: string) => string): {
3
- moduleScript: string;
4
- setupScript: string;
2
+ export interface ScriptStatement {
3
+ readonly code: string;
4
+ readonly sourceOffset?: number | undefined;
5
+ readonly exact?: boolean;
6
+ }
7
+ export declare function componentScript(node: Html.Element, filename: string, componentImports: 'generated' | 'source', sourcePosition: (offset: number) => {
8
+ line: number;
9
+ column: number;
10
+ }, rewriteRelativeImport?: (specifier: string) => string, hotState?: boolean): {
11
+ moduleStatements: ScriptStatement[];
12
+ setupStatements: ScriptStatement[];
5
13
  props: string[];
6
14
  };
@@ -1,15 +1,46 @@
1
1
  import ts from 'typescript';
2
- import { fail } from './errors.js';
3
- export function componentScript(node, filename, componentImports, rewriteRelativeImport) {
2
+ import { ComponentCompileError } from './errors.js';
3
+ function preserveLocalSignals(statement, file, signalBindings) {
4
+ const statementStart = statement.getStart(file);
5
+ let code = statement.getText(file);
6
+ const signals = statement.declarationList.declarations.flatMap((declaration) => {
7
+ const initializer = declaration.initializer;
8
+ if (!ts.isIdentifier(declaration.name) ||
9
+ !initializer ||
10
+ !ts.isCallExpression(initializer) ||
11
+ !ts.isIdentifier(initializer.expression) ||
12
+ !signalBindings.has(initializer.expression.text))
13
+ return [];
14
+ return [{ name: declaration.name.text, initializer }];
15
+ });
16
+ for (const { name, initializer } of signals.reverse()) {
17
+ const start = initializer.getStart(file) - statementStart;
18
+ const end = initializer.getEnd() - statementStart;
19
+ const original = code.slice(start, end);
20
+ const retained = `__context?.state(${JSON.stringify(`${name}:${original}`)}, () => ${original})`;
21
+ code =
22
+ code.slice(0, start) + `(${retained} ?? ${original})` + code.slice(end);
23
+ }
24
+ return signals.length > 0 ? code : undefined;
25
+ }
26
+ export function componentScript(node, filename, componentImports, sourcePosition, rewriteRelativeImport, hotState = false) {
4
27
  if (node.attrs.length !== 1 ||
5
28
  node.attrs[0]?.name !== 'lang' ||
6
29
  node.attrs[0].value !== 'ts') {
7
- fail(filename, 'The component script must be <script lang="ts">.');
30
+ throw new ComponentCompileError('The component script must be <script lang="ts">.', filename, node.sourceCodeLocation
31
+ ? sourcePosition(node.sourceCodeLocation.startOffset)
32
+ : undefined);
8
33
  }
34
+ const scriptStart = node.sourceCodeLocation?.startTag?.endOffset;
35
+ const failAt = (message, offset = 0) => {
36
+ throw new ComponentCompileError(message, filename, scriptStart === undefined
37
+ ? undefined
38
+ : sourcePosition(scriptStart + offset));
39
+ };
9
40
  const script = node.childNodes
10
41
  .map((child) => {
11
42
  if (!('value' in child))
12
- fail(filename, 'Invalid script content.');
43
+ return failAt('Invalid script content.');
13
44
  return child.value;
14
45
  })
15
46
  .join('');
@@ -18,90 +49,128 @@ export function componentScript(node, filename, componentImports, rewriteRelativ
18
49
  fileName: filename.replace(/\.workstar$/, '.ts'),
19
50
  reportDiagnostics: true,
20
51
  });
21
- if (syntax.diagnostics?.some((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error)) {
22
- fail(filename, 'Invalid TypeScript in component script.');
52
+ const syntaxError = syntax.diagnostics?.find((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error);
53
+ if (syntaxError) {
54
+ failAt(`Invalid TypeScript in component script: ${ts.flattenDiagnosticMessageText(syntaxError.messageText, ' ')}`, syntaxError.start ?? 0);
23
55
  }
24
56
  let props;
57
+ const signalBindings = new Set();
25
58
  const moduleStatements = [];
26
59
  const setupStatements = [];
27
60
  for (const statement of file.statements) {
28
61
  if (ts.isEmptyStatement(statement))
29
62
  continue;
63
+ const sourceOffset = scriptStart === undefined
64
+ ? undefined
65
+ : scriptStart + statement.getStart(file);
30
66
  if (ts.isImportDeclaration(statement)) {
31
67
  const specifier = ts.isStringLiteral(statement.moduleSpecifier)
32
68
  ? statement.moduleSpecifier.text
33
69
  : '';
70
+ const named = statement.importClause?.namedBindings;
71
+ if (specifier === 'workstar' && named && ts.isNamedImports(named)) {
72
+ for (const binding of named.elements) {
73
+ if ((binding.propertyName ?? binding.name).text === 'signal') {
74
+ signalBindings.add(binding.name.text);
75
+ }
76
+ }
77
+ }
34
78
  if (specifier.endsWith('.workstar')) {
35
79
  const binding = statement.importClause?.name?.text;
36
80
  if (!/^(?:\.{1,2}\/)+(?:[A-Za-z0-9][\w-]*\/)*[A-Za-z0-9][\w-]*\.workstar$/.test(specifier) ||
37
81
  !binding ||
38
82
  statement.importClause?.isTypeOnly ||
39
83
  statement.importClause?.namedBindings) {
40
- fail(filename, 'Import a relative .workstar view with a default import.');
84
+ failAt('Import a relative .workstar view with a default import.', statement.getStart(file));
41
85
  }
42
- moduleStatements.push(`import { render as ${binding} } from '${componentImports === 'source' ? specifier : specifier.slice(0, -'.workstar'.length)}';`);
86
+ moduleStatements.push({
87
+ code: `import { render as ${binding} } from '${componentImports === 'source' ? specifier : specifier.slice(0, -'.workstar'.length)}';`,
88
+ sourceOffset,
89
+ });
43
90
  }
44
91
  else if (specifier.startsWith('.') && rewriteRelativeImport) {
45
92
  const importText = statement.getText(file);
46
93
  const start = statement.moduleSpecifier.getStart(file) - statement.getStart(file);
47
94
  const end = statement.moduleSpecifier.getEnd() - statement.getStart(file);
48
- moduleStatements.push(importText.slice(0, start) +
49
- JSON.stringify(rewriteRelativeImport(specifier)) +
50
- importText.slice(end));
95
+ moduleStatements.push({
96
+ code: importText.slice(0, start) +
97
+ JSON.stringify(rewriteRelativeImport(specifier)) +
98
+ importText.slice(end),
99
+ sourceOffset,
100
+ });
51
101
  }
52
102
  else {
53
- moduleStatements.push(statement.getText(file));
103
+ moduleStatements.push({
104
+ code: statement.getText(file),
105
+ sourceOffset,
106
+ exact: true,
107
+ });
54
108
  }
55
109
  continue;
56
110
  }
57
111
  if (ts.isInterfaceDeclaration(statement) &&
58
112
  statement.name.text === 'Props') {
59
113
  if (props)
60
- fail(filename, 'The component can declare Props only once.');
114
+ failAt('The component can declare Props only once.', statement.getStart(file));
61
115
  props = statement.members.map((member) => {
62
116
  if (!ts.isPropertySignature(member) ||
63
117
  !member.name ||
64
118
  !ts.isIdentifier(member.name)) {
65
- fail(filename, 'Props must use named properties.');
119
+ return failAt('Props must use named properties.', member.getStart(file));
66
120
  }
67
121
  return member.name.text;
68
122
  });
69
- moduleStatements.push(statement.getText(file));
123
+ moduleStatements.push({
124
+ code: statement.getText(file),
125
+ sourceOffset,
126
+ exact: true,
127
+ });
70
128
  continue;
71
129
  }
72
130
  if (ts.isTypeAliasDeclaration(statement) &&
73
131
  statement.name.text === 'Props' &&
74
132
  ts.isTypeLiteralNode(statement.type)) {
75
133
  if (props)
76
- fail(filename, 'The component can declare Props only once.');
134
+ failAt('The component can declare Props only once.', statement.getStart(file));
77
135
  props = statement.type.members.map((member) => {
78
136
  if (!ts.isPropertySignature(member) ||
79
137
  !member.name ||
80
138
  !ts.isIdentifier(member.name)) {
81
- fail(filename, 'Props must use named properties.');
139
+ return failAt('Props must use named properties.', member.getStart(file));
82
140
  }
83
141
  return member.name.text;
84
142
  });
85
- moduleStatements.push(statement.getText(file));
143
+ moduleStatements.push({
144
+ code: statement.getText(file),
145
+ sourceOffset,
146
+ exact: true,
147
+ });
86
148
  continue;
87
149
  }
88
150
  if (ts.isVariableStatement(statement) ||
89
151
  ts.isFunctionDeclaration(statement)) {
90
152
  if (statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
91
- fail(filename, 'Component-local declarations cannot be exported.');
153
+ failAt('Component-local declarations cannot be exported.', statement.getStart(file));
92
154
  }
93
- setupStatements.push(statement.getText(file));
155
+ const retained = hotState && ts.isVariableStatement(statement)
156
+ ? preserveLocalSignals(statement, file, signalBindings)
157
+ : undefined;
158
+ setupStatements.push({
159
+ code: retained ?? statement.getText(file),
160
+ sourceOffset,
161
+ exact: retained === undefined,
162
+ });
94
163
  continue;
95
164
  }
96
- fail(filename, 'Only imports, Props, and component-local variables/functions are supported in the script.');
165
+ failAt('Only imports, Props, and component-local variables/functions are supported in the script.', statement.getStart(file));
97
166
  }
98
167
  if (!props) {
99
168
  props = [];
100
- moduleStatements.push('type Props = Record<string, never>;');
169
+ moduleStatements.push({ code: 'type Props = Record<string, never>;' });
101
170
  }
102
171
  return {
103
- moduleScript: moduleStatements.join('\n'),
104
- setupScript: setupStatements.join('\n'),
172
+ moduleStatements,
173
+ setupStatements,
105
174
  props,
106
175
  };
107
176
  }
@@ -2,6 +2,11 @@ export declare const controlAttribute = "data-workstar-compiler-control";
2
2
  export interface NormalizedControls {
3
3
  source: string;
4
4
  count: number;
5
+ originalOffset: (normalizedOffset: number) => number;
6
+ }
7
+ export declare class ControlSyntaxError extends Error {
8
+ readonly offset: number;
9
+ constructor(message: string, offset: number);
5
10
  }
6
11
  /** Adapt authoring controls to HTML parser insertion modes, including select and table. */
7
12
  export declare function normalizeControls(source: string): NormalizedControls;
@@ -1,5 +1,13 @@
1
1
  const controlNames = new Set(['Each', 'If', 'Else', 'Use']);
2
2
  export const controlAttribute = 'data-workstar-compiler-control';
3
+ export class ControlSyntaxError extends Error {
4
+ offset;
5
+ constructor(message, offset) {
6
+ super(message);
7
+ this.offset = offset;
8
+ this.name = 'ControlSyntaxError';
9
+ }
10
+ }
3
11
  function tagEnd(source, start) {
4
12
  let quote;
5
13
  for (let index = start + 1; index < source.length; index++) {
@@ -15,19 +23,28 @@ function tagEnd(source, start) {
15
23
  return index;
16
24
  }
17
25
  }
18
- throw new Error('Unclosed HTML tag.');
26
+ throw new ControlSyntaxError('Unclosed HTML tag.', start);
19
27
  }
20
28
  /** Adapt authoring controls to HTML parser insertion modes, including select and table. */
21
29
  export function normalizeControls(source) {
22
30
  if (source.includes(controlAttribute)) {
23
- throw new Error(`${controlAttribute} is reserved for the compiler.`);
31
+ throw new ControlSyntaxError(`${controlAttribute} is reserved for the compiler.`, source.indexOf(controlAttribute));
24
32
  }
25
- const scriptEnd = /<\/script\s*>/i.exec(source);
26
- const start = scriptEnd ? scriptEnd.index + scriptEnd[0].length : 0;
27
- let result = source.slice(0, start);
28
- let cursor = start;
33
+ let result = '';
34
+ let cursor = 0;
29
35
  let count = 0;
30
36
  const stack = [];
37
+ const replacements = [];
38
+ const appendReplacement = (authoredStart, authoredEnd, replacement) => {
39
+ const generatedStart = result.length;
40
+ result += replacement;
41
+ replacements.push({
42
+ generatedStart,
43
+ generatedEnd: result.length,
44
+ authoredStart,
45
+ authoredEnd,
46
+ });
47
+ };
31
48
  while (cursor < source.length) {
32
49
  const opening = source.indexOf('<', cursor);
33
50
  if (opening < 0)
@@ -36,7 +53,7 @@ export function normalizeControls(source) {
36
53
  if (source.startsWith('<!--', opening)) {
37
54
  const end = source.indexOf('-->', opening + 4);
38
55
  if (end < 0)
39
- throw new Error('Unclosed HTML comment.');
56
+ throw new ControlSyntaxError('Unclosed HTML comment.', opening);
40
57
  result += source.slice(opening, end + 3);
41
58
  cursor = end + 3;
42
59
  continue;
@@ -48,8 +65,9 @@ export function normalizeControls(source) {
48
65
  if (!name || !controlNames.has(name)) {
49
66
  result += tag;
50
67
  cursor = end + 1;
51
- if (/^<textarea(?=[\s>])/i.test(tag)) {
52
- const closing = /<\/textarea\s*>/gi;
68
+ const rawTextTag = /^<(script|style|textarea)(?=[\s>])/i.exec(tag)?.[1];
69
+ if (rawTextTag) {
70
+ const closing = new RegExp(`</${rawTextTag}\\s*>`, 'gi');
53
71
  closing.lastIndex = cursor;
54
72
  const close = closing.exec(source);
55
73
  if (close) {
@@ -62,31 +80,48 @@ export function normalizeControls(source) {
62
80
  const closing = match[1] === '/';
63
81
  const selfClosing = /\/\s*>$/.test(tag);
64
82
  if (closing) {
65
- if (stack.pop() !== name) {
66
- throw new Error(`Mismatched </${name}> control element.`);
83
+ if (stack.pop()?.name !== name) {
84
+ throw new ControlSyntaxError(`Mismatched </${name}> control element.`, opening);
67
85
  }
68
- result += '</template>';
86
+ appendReplacement(opening, end + 1, '</template>');
69
87
  }
70
88
  else {
71
89
  if (selfClosing && name !== 'Use') {
72
- throw new Error(`<${name}> cannot be self-closing.`);
90
+ throw new ControlSyntaxError(`<${name}> cannot be self-closing.`, opening);
73
91
  }
74
92
  const authoredAttributes = tag.slice(match[0].length, -1);
75
93
  const attributes = selfClosing
76
94
  ? authoredAttributes.replace(/\/\s*$/, '')
77
95
  : authoredAttributes;
78
- result += `<template ${controlAttribute}="${name}"${attributes}>`;
79
- if (selfClosing)
80
- result += '</template>';
81
- else
82
- stack.push(name);
96
+ appendReplacement(opening, end + 1, `<template ${controlAttribute}="${name}"${attributes}>${selfClosing ? '</template>' : ''}`);
97
+ if (!selfClosing)
98
+ stack.push({ name, offset: opening });
83
99
  count++;
84
100
  }
85
101
  cursor = end + 1;
86
102
  }
87
103
  result += source.slice(cursor);
88
104
  if (stack.length > 0) {
89
- throw new Error(`Unclosed <${stack.at(-1)}> control element.`);
105
+ const unclosed = stack.at(-1);
106
+ throw new ControlSyntaxError(`Unclosed <${unclosed.name}> control element.`, unclosed.offset);
90
107
  }
91
- return { source: result, count };
108
+ return {
109
+ source: result,
110
+ count,
111
+ originalOffset(normalizedOffset) {
112
+ let difference = 0;
113
+ for (const replacement of replacements) {
114
+ if (normalizedOffset < replacement.generatedStart)
115
+ break;
116
+ if (normalizedOffset < replacement.generatedEnd) {
117
+ return replacement.authoredStart;
118
+ }
119
+ difference +=
120
+ replacement.generatedEnd -
121
+ replacement.generatedStart -
122
+ (replacement.authoredEnd - replacement.authoredStart);
123
+ }
124
+ return normalizedOffset - difference;
125
+ },
126
+ };
92
127
  }
@@ -1,5 +1,13 @@
1
1
  export declare class ComponentCompileError extends Error {
2
+ readonly description: string;
2
3
  readonly filename: string;
3
- constructor(message: string, filename: string);
4
+ readonly position?: {
5
+ readonly line: number;
6
+ readonly column: number;
7
+ } | undefined;
8
+ constructor(description: string, filename: string, position?: {
9
+ readonly line: number;
10
+ readonly column: number;
11
+ } | undefined);
4
12
  }
5
13
  export declare function fail(filename: string, message: string): never;
@@ -1,8 +1,12 @@
1
1
  export class ComponentCompileError extends Error {
2
+ description;
2
3
  filename;
3
- constructor(message, filename) {
4
- super(`${filename}: ${message}`);
4
+ position;
5
+ constructor(description, filename, position) {
6
+ super(`${filename}${position ? `:${position.line}:${position.column}` : ''}: ${description}`);
7
+ this.description = description;
5
8
  this.filename = filename;
9
+ this.position = position;
6
10
  this.name = 'ComponentCompileError';
7
11
  }
8
12
  }
@@ -1,16 +1,20 @@
1
+ import { type SourceOrigin } from './source-origin.js';
1
2
  export { ComponentCompileError } from './errors.js';
2
3
  /** Compile a typed component and its optional co-located stylesheet. */
3
4
  export declare function compileComponentParts(source: string, filename?: string, options?: {
4
5
  componentImports?: 'generated' | 'source';
5
6
  rewriteRelativeImport?: (specifier: string) => string;
6
7
  cssImport?: string;
8
+ hotState?: boolean;
7
9
  }): {
8
10
  code: string;
9
11
  css: string;
12
+ origins: SourceOrigin[];
10
13
  };
11
14
  /** Compile a component to a TypeScript module; use parts to emit its CSS. */
12
15
  export declare function compileComponent(source: string, filename?: string, options?: {
13
16
  componentImports?: 'generated' | 'source';
14
17
  rewriteRelativeImport?: (specifier: string) => string;
15
18
  cssImport?: string;
19
+ hotState?: boolean;
16
20
  }): string;