tachyon-dom 0.1.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.
Files changed (120) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +339 -0
  3. package/dist/adapters/lambda.d.ts +65 -0
  4. package/dist/adapters/lambda.js +211 -0
  5. package/dist/adapters/node.d.ts +25 -0
  6. package/dist/adapters/node.js +360 -0
  7. package/dist/adapters/workers.d.ts +149 -0
  8. package/dist/adapters/workers.js +272 -0
  9. package/dist/adapters.d.ts +3 -0
  10. package/dist/adapters.js +3 -0
  11. package/dist/app.d.ts +98 -0
  12. package/dist/app.js +306 -0
  13. package/dist/cli.d.ts +55 -0
  14. package/dist/cli.js +711 -0
  15. package/dist/compiler/expression.d.ts +55 -0
  16. package/dist/compiler/expression.js +858 -0
  17. package/dist/compiler/index.d.ts +8 -0
  18. package/dist/compiler/index.js +50 -0
  19. package/dist/compiler/ir.d.ts +4 -0
  20. package/dist/compiler/ir.js +255 -0
  21. package/dist/compiler/parser.d.ts +3 -0
  22. package/dist/compiler/parser.js +236 -0
  23. package/dist/compiler/sfc.d.ts +33 -0
  24. package/dist/compiler/sfc.js +439 -0
  25. package/dist/compiler/targets/client.d.ts +3 -0
  26. package/dist/compiler/targets/client.js +568 -0
  27. package/dist/compiler/targets/server.d.ts +7 -0
  28. package/dist/compiler/targets/server.js +396 -0
  29. package/dist/compiler/targets/stream.d.ts +2 -0
  30. package/dist/compiler/targets/stream.js +204 -0
  31. package/dist/compiler/types.d.ts +185 -0
  32. package/dist/compiler/types.js +1 -0
  33. package/dist/compiler/utils.d.ts +40 -0
  34. package/dist/compiler/utils.js +185 -0
  35. package/dist/compiler/whitespace.d.ts +2 -0
  36. package/dist/compiler/whitespace.js +66 -0
  37. package/dist/compiler.d.ts +1 -0
  38. package/dist/compiler.js +1 -0
  39. package/dist/constant-time.d.ts +1 -0
  40. package/dist/constant-time.js +26 -0
  41. package/dist/cookies.d.ts +44 -0
  42. package/dist/cookies.js +199 -0
  43. package/dist/diagnostics.d.ts +22 -0
  44. package/dist/diagnostics.js +45 -0
  45. package/dist/env.d.ts +27 -0
  46. package/dist/env.js +37 -0
  47. package/dist/html-escape.d.ts +2 -0
  48. package/dist/html-escape.js +20 -0
  49. package/dist/html-whitespace.d.ts +6 -0
  50. package/dist/html-whitespace.js +83 -0
  51. package/dist/i18n.d.ts +19 -0
  52. package/dist/i18n.js +93 -0
  53. package/dist/index.d.ts +20 -0
  54. package/dist/index.js +18 -0
  55. package/dist/language-server.d.ts +17 -0
  56. package/dist/language-server.js +79 -0
  57. package/dist/package-integrity.d.ts +9 -0
  58. package/dist/package-integrity.js +64 -0
  59. package/dist/result.d.ts +13 -0
  60. package/dist/result.js +4 -0
  61. package/dist/router-node.d.ts +2 -0
  62. package/dist/router-node.js +12 -0
  63. package/dist/router.d.ts +324 -0
  64. package/dist/router.js +1024 -0
  65. package/dist/runtime/attr.d.ts +3 -0
  66. package/dist/runtime/attr.js +66 -0
  67. package/dist/runtime/class.d.ts +2 -0
  68. package/dist/runtime/class.js +10 -0
  69. package/dist/runtime/conditional.d.ts +78 -0
  70. package/dist/runtime/conditional.js +140 -0
  71. package/dist/runtime/enhancement.d.ts +11 -0
  72. package/dist/runtime/enhancement.js +70 -0
  73. package/dist/runtime/error-boundary.d.ts +8 -0
  74. package/dist/runtime/error-boundary.js +27 -0
  75. package/dist/runtime/event.d.ts +1 -0
  76. package/dist/runtime/event.js +11 -0
  77. package/dist/runtime/form.d.ts +41 -0
  78. package/dist/runtime/form.js +167 -0
  79. package/dist/runtime/fragment.d.ts +6 -0
  80. package/dist/runtime/fragment.js +18 -0
  81. package/dist/runtime/html.d.ts +8 -0
  82. package/dist/runtime/html.js +6 -0
  83. package/dist/runtime/hydrate.d.ts +68 -0
  84. package/dist/runtime/hydrate.js +205 -0
  85. package/dist/runtime/keyed-rows.d.ts +61 -0
  86. package/dist/runtime/keyed-rows.js +192 -0
  87. package/dist/runtime/list.d.ts +84 -0
  88. package/dist/runtime/list.js +424 -0
  89. package/dist/runtime/portal.d.ts +6 -0
  90. package/dist/runtime/portal.js +12 -0
  91. package/dist/runtime/router.d.ts +102 -0
  92. package/dist/runtime/router.js +649 -0
  93. package/dist/runtime/signal.d.ts +30 -0
  94. package/dist/runtime/signal.js +275 -0
  95. package/dist/runtime/store.d.ts +1 -0
  96. package/dist/runtime/store.js +1 -0
  97. package/dist/runtime/stream-client.d.ts +8 -0
  98. package/dist/runtime/stream-client.js +33 -0
  99. package/dist/runtime/text.d.ts +2 -0
  100. package/dist/runtime/text.js +10 -0
  101. package/dist/runtime/virtual-list.d.ts +15 -0
  102. package/dist/runtime/virtual-list.js +89 -0
  103. package/dist/security.d.ts +27 -0
  104. package/dist/security.js +173 -0
  105. package/dist/server/form-action.d.ts +52 -0
  106. package/dist/server/form-action.js +102 -0
  107. package/dist/server/html.d.ts +19 -0
  108. package/dist/server/html.js +101 -0
  109. package/dist/server/stream.d.ts +4 -0
  110. package/dist/server/stream.js +72 -0
  111. package/dist/source-map.d.ts +17 -0
  112. package/dist/source-map.js +18 -0
  113. package/dist/tachyon-html.d.ts +110 -0
  114. package/dist/testing.d.ts +14 -0
  115. package/dist/testing.js +36 -0
  116. package/dist/typed.d.ts +9 -0
  117. package/dist/typed.js +4 -0
  118. package/dist/vite.d.ts +84 -0
  119. package/dist/vite.js +426 -0
  120. package/package.json +604 -0
@@ -0,0 +1,439 @@
1
+ import * as ts from "typescript";
2
+ import { err, ok } from "../result.js";
3
+ import { compileTemplate } from "./index.js";
4
+ export const sfcDefaultScopeName = "__tachyonSfcDefaultScope";
5
+ export const sfcNamedScopeName = "__tachyonSfcScope";
6
+ export const sfcSetupScopeName = "__tachyonSfcSetupScope";
7
+ const scriptOpenPattern = /<script\b([^>]*)>/gi;
8
+ const autoImports = {
9
+ batch: "tachyon-dom",
10
+ compileTachyonSfc: "tachyon-dom",
11
+ createClientRouter: "tachyon-dom",
12
+ createMemo: "tachyon-dom",
13
+ createSignal: "tachyon-dom",
14
+ createStore: "tachyon-dom",
15
+ effect: "tachyon-dom",
16
+ enhanceForm: "tachyon-dom",
17
+ err: "tachyon-dom",
18
+ generateClientModule: "tachyon-dom",
19
+ generateServerStreamModule: "tachyon-dom",
20
+ ok: "tachyon-dom",
21
+ readTextStreamChunks: "tachyon-dom",
22
+ renderServerTemplate: "tachyon-dom",
23
+ renderToReadableStream: "tachyon-dom",
24
+ validateFormData: "tachyon-dom",
25
+ };
26
+ const autoImportPattern = new RegExp(`\\b(?:${Object.keys(autoImports)
27
+ .map((name) => name.replaceAll("$", "\\$"))
28
+ .join("|")})\\b`);
29
+ const mapGeneratedOffset = (ranges, sourceLength, offset) => {
30
+ for (const range of ranges) {
31
+ const rangeEnd = range.generatedStart + range.length;
32
+ if (offset >= range.generatedStart && offset <= rangeEnd) {
33
+ return range.originalStart + (offset - range.generatedStart);
34
+ }
35
+ }
36
+ return sourceLength;
37
+ };
38
+ const emptyTemplate = (source) => ({
39
+ source,
40
+ ir: {
41
+ kind: "template",
42
+ root: { type: "element", tagName: "template", attrs: [], children: [] },
43
+ directives: [],
44
+ },
45
+ root: { type: "element", tagName: "template", attrs: [], children: [] },
46
+ client: {
47
+ bindings: [],
48
+ components: [],
49
+ hydrationBoundaries: [],
50
+ stores: [],
51
+ templateHtml: "",
52
+ },
53
+ });
54
+ const attrValue = (attrs, name) => {
55
+ const pattern = new RegExp(`(?:^|\\s)${name}\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s"'>]+))`, "i");
56
+ const match = pattern.exec(attrs);
57
+ return match?.[1] ?? match?.[2] ?? match?.[3];
58
+ };
59
+ const hasBooleanAttr = (attrs, name) => new RegExp(`(?:^|\\s)${name}(?:\\s|=|$)`, "i").test(attrs);
60
+ export const sfcScriptLanguage = (script) => {
61
+ const lang = attrValue(script?.attrs ?? "", "lang")?.toLowerCase();
62
+ return lang === "ts" || lang === "typescript" ? "ts" : "js";
63
+ };
64
+ export const isSfcSetupScript = (script) => hasBooleanAttr(script?.attrs ?? "", "setup");
65
+ const unwrapStaticExpression = (node) => {
66
+ if (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isSatisfiesExpression(node)) {
67
+ return unwrapStaticExpression(node.expression);
68
+ }
69
+ return node;
70
+ };
71
+ const staticPropertyName = (name) => {
72
+ if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNumericLiteral(name)) {
73
+ return name.text;
74
+ }
75
+ return undefined;
76
+ };
77
+ const staticExpressionValue = (input) => {
78
+ const node = unwrapStaticExpression(input);
79
+ if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node))
80
+ return ok(node.text);
81
+ if (ts.isNumericLiteral(node))
82
+ return ok(Number(node.text));
83
+ if (node.kind === ts.SyntaxKind.TrueKeyword)
84
+ return ok(true);
85
+ if (node.kind === ts.SyntaxKind.FalseKeyword)
86
+ return ok(false);
87
+ if (node.kind === ts.SyntaxKind.NullKeyword)
88
+ return ok(null);
89
+ if (ts.isPrefixUnaryExpression(node) && node.operator === ts.SyntaxKind.MinusToken) {
90
+ const value = staticExpressionValue(node.operand);
91
+ return value.ok && typeof value.value === "number" ? ok(-value.value) : err("Static scope has an invalid number.");
92
+ }
93
+ if (ts.isArrayLiteralExpression(node)) {
94
+ const values = [];
95
+ for (const element of node.elements) {
96
+ if (ts.isSpreadElement(element))
97
+ return err("Static scope does not support spread elements.");
98
+ const value = staticExpressionValue(element);
99
+ if (!value.ok)
100
+ return value;
101
+ values.push(value.value);
102
+ }
103
+ return ok(values);
104
+ }
105
+ if (ts.isObjectLiteralExpression(node)) {
106
+ const value = {};
107
+ for (const property of node.properties) {
108
+ if (!ts.isPropertyAssignment(property))
109
+ return err("Static scope supports property assignments only.");
110
+ const name = staticPropertyName(property.name);
111
+ if (name === undefined)
112
+ return err("Static scope has an unsupported property name.");
113
+ const propertyValue = staticExpressionValue(property.initializer);
114
+ if (!propertyValue.ok)
115
+ return propertyValue;
116
+ value[name] = propertyValue.value;
117
+ }
118
+ return ok(value);
119
+ }
120
+ return err("Static scope values must be literals, arrays, or object literals.");
121
+ };
122
+ export const extractStaticSfcScope = (source) => {
123
+ const descriptor = parseTachyonSfc(source);
124
+ if (!descriptor.ok)
125
+ return err(descriptor.error.message);
126
+ const script = descriptor.value.script;
127
+ if (!script)
128
+ return ok(undefined);
129
+ const sourceFile = sourceFileFor(script.content, script);
130
+ for (const statement of sourceFile.statements) {
131
+ if (!ts.isVariableStatement(statement) || !statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) {
132
+ continue;
133
+ }
134
+ for (const declaration of statement.declarationList.declarations) {
135
+ if (!ts.isIdentifier(declaration.name) || declaration.name.text !== "scope" || !declaration.initializer)
136
+ continue;
137
+ const initializer = unwrapStaticExpression(declaration.initializer);
138
+ if (!ts.isArrowFunction(initializer) && !ts.isFunctionExpression(initializer)) {
139
+ return err("SFC scope must be a function returning a static object or be supplied explicitly by defineApp().");
140
+ }
141
+ let returned;
142
+ if (ts.isBlock(initializer.body)) {
143
+ const returnStatement = initializer.body.statements.find(ts.isReturnStatement);
144
+ returned = returnStatement?.expression;
145
+ }
146
+ else {
147
+ returned = initializer.body;
148
+ }
149
+ if (!returned)
150
+ return err("SFC scope must return a static object.");
151
+ const value = staticExpressionValue(returned);
152
+ if (!value.ok)
153
+ return err(`${value.error} Supply page.scope explicitly for dynamic values.`);
154
+ if (!value.value || typeof value.value !== "object" || Array.isArray(value.value)) {
155
+ return err("SFC scope must return an object.");
156
+ }
157
+ return ok(value.value);
158
+ }
159
+ }
160
+ return ok(undefined);
161
+ };
162
+ const sourceFileFor = (source, script) => ts.createSourceFile(sfcScriptLanguage(script) === "ts" ? "component.td.ts" : "component.td.js", source, ts.ScriptTarget.ES2022, true, sfcScriptLanguage(script) === "ts" ? ts.ScriptKind.TS : ts.ScriptKind.JS);
163
+ const compilerErrorFromDiagnostic = (diagnostic, script) => {
164
+ const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
165
+ return {
166
+ message,
167
+ offset: script.offset + (typeof diagnostic.start === "number" ? diagnostic.start : 0),
168
+ };
169
+ };
170
+ const transpileScriptContent = (script) => {
171
+ const language = sfcScriptLanguage(script);
172
+ if (language === "js") {
173
+ return ok(script.content.trim());
174
+ }
175
+ const result = ts.transpileModule(script.content, {
176
+ compilerOptions: {
177
+ module: ts.ModuleKind.ESNext,
178
+ target: ts.ScriptTarget.ES2022,
179
+ useDefineForClassFields: true,
180
+ verbatimModuleSyntax: true,
181
+ },
182
+ fileName: "component.td.ts",
183
+ reportDiagnostics: true,
184
+ });
185
+ const diagnostic = result.diagnostics?.find((item) => item.category === ts.DiagnosticCategory.Error);
186
+ if (diagnostic) {
187
+ return err(compilerErrorFromDiagnostic(diagnostic, script));
188
+ }
189
+ return ok(result.outputText.trim());
190
+ };
191
+ const addBindingNames = (name, names) => {
192
+ if (ts.isIdentifier(name)) {
193
+ names.add(name.text);
194
+ return;
195
+ }
196
+ for (const element of name.elements) {
197
+ if (ts.isBindingElement(element)) {
198
+ addBindingNames(element.name, names);
199
+ }
200
+ }
201
+ };
202
+ const topLevelBindings = (script) => {
203
+ if (!script) {
204
+ return [];
205
+ }
206
+ const names = new Set();
207
+ const sourceFile = sourceFileFor(script.content, script);
208
+ for (const statement of sourceFile.statements) {
209
+ if (ts.isVariableStatement(statement)) {
210
+ for (const declaration of statement.declarationList.declarations) {
211
+ addBindingNames(declaration.name, names);
212
+ }
213
+ }
214
+ else if (ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) {
215
+ if (statement.name) {
216
+ names.add(statement.name.text);
217
+ }
218
+ }
219
+ }
220
+ return Array.from(names).sort();
221
+ };
222
+ const isDeclarationName = (node) => {
223
+ const parent = node.parent;
224
+ return ((ts.isVariableDeclaration(parent) && parent.name === node) ||
225
+ (ts.isFunctionDeclaration(parent) && parent.name === node) ||
226
+ (ts.isClassDeclaration(parent) && parent.name === node) ||
227
+ (ts.isParameter(parent) && parent.name === node) ||
228
+ (ts.isImportSpecifier(parent) && parent.name === node) ||
229
+ (ts.isImportClause(parent) && parent.name === node) ||
230
+ (ts.isNamespaceImport(parent) && parent.name === node) ||
231
+ (ts.isPropertyAccessExpression(parent) && parent.name === node) ||
232
+ (ts.isPropertyAssignment(parent) && parent.name === node && !parent.name.getText().startsWith("{")) ||
233
+ (ts.isShorthandPropertyAssignment(parent) && parent.name === node));
234
+ };
235
+ const collectScriptIdentifiers = (source, script) => {
236
+ const declared = new Set();
237
+ const referenced = new Set();
238
+ const sourceFile = sourceFileFor(source, script);
239
+ const visit = (node) => {
240
+ if (ts.isImportDeclaration(node)) {
241
+ const clause = node.importClause;
242
+ if (clause?.name) {
243
+ declared.add(clause.name.text);
244
+ }
245
+ if (clause?.namedBindings) {
246
+ if (ts.isNamespaceImport(clause.namedBindings)) {
247
+ declared.add(clause.namedBindings.name.text);
248
+ }
249
+ else {
250
+ for (const specifier of clause.namedBindings.elements) {
251
+ declared.add(specifier.name.text);
252
+ }
253
+ }
254
+ }
255
+ return;
256
+ }
257
+ if (ts.isVariableDeclaration(node)) {
258
+ addBindingNames(node.name, declared);
259
+ }
260
+ else if (ts.isParameter(node)) {
261
+ addBindingNames(node.name, declared);
262
+ }
263
+ else if ((ts.isFunctionDeclaration(node) || ts.isClassDeclaration(node)) && node.name) {
264
+ declared.add(node.name.text);
265
+ }
266
+ else if (ts.isIdentifier(node) && !isDeclarationName(node)) {
267
+ referenced.add(node.text);
268
+ }
269
+ ts.forEachChild(node, visit);
270
+ };
271
+ visit(sourceFile);
272
+ return { declared, referenced };
273
+ };
274
+ const autoImportScriptHelpers = (code, script) => {
275
+ if (!autoImportPattern.test(code)) {
276
+ return code;
277
+ }
278
+ const identifiers = collectScriptIdentifiers(code, script);
279
+ const importsByModule = new Map();
280
+ for (const [name, module] of Object.entries(autoImports)) {
281
+ if (!identifiers.referenced.has(name) || identifiers.declared.has(name)) {
282
+ continue;
283
+ }
284
+ importsByModule.set(module, [...(importsByModule.get(module) ?? []), name]);
285
+ }
286
+ if (importsByModule.size === 0) {
287
+ return code;
288
+ }
289
+ const imports = Array.from(importsByModule)
290
+ .map(([module, names]) => `import { ${names.sort().join(", ")} } from ${JSON.stringify(module)};`)
291
+ .join("\n");
292
+ return `${imports}\n${code}`;
293
+ };
294
+ export const parseTachyonSfc = (source) => {
295
+ const matches = Array.from(source.matchAll(scriptOpenPattern));
296
+ if (matches.length === 0) {
297
+ return ok({
298
+ template: source,
299
+ mapTemplateOffset: (offset) => offset,
300
+ });
301
+ }
302
+ if (matches.length > 1) {
303
+ return err({ message: "Only one <script> block is currently supported.", offset: matches[1]?.index ?? 0 });
304
+ }
305
+ const match = matches[0];
306
+ const openStart = match?.index ?? 0;
307
+ const openEnd = openStart + (match?.[0].length ?? 0);
308
+ const closeStart = source.indexOf("</script>", openEnd);
309
+ if (closeStart < 0) {
310
+ return err({ message: "Missing closing </script> tag.", offset: openStart });
311
+ }
312
+ const closeEnd = closeStart + "</script>".length;
313
+ const before = source.slice(0, openStart);
314
+ const after = source.slice(closeEnd);
315
+ const ranges = [
316
+ { generatedStart: 0, originalStart: 0, length: before.length },
317
+ { generatedStart: before.length, originalStart: closeEnd, length: after.length },
318
+ ];
319
+ return ok({
320
+ script: {
321
+ attrs: match?.[1]?.trim() ?? "",
322
+ content: source.slice(openEnd, closeStart),
323
+ offset: openEnd,
324
+ },
325
+ template: `${before}${after}`,
326
+ mapTemplateOffset: (offset) => mapGeneratedOffset(ranges, source.length, offset),
327
+ });
328
+ };
329
+ export const compileTachyonSfc = (source, options = {}) => {
330
+ const descriptor = parseTachyonSfc(source);
331
+ if (!descriptor.ok) {
332
+ return err(descriptor.error);
333
+ }
334
+ const scriptOnly = Boolean(descriptor.value.script) && descriptor.value.template.trim().length === 0;
335
+ if (scriptOnly) {
336
+ return ok({
337
+ descriptor: descriptor.value,
338
+ scriptOnly: true,
339
+ template: emptyTemplate(descriptor.value.template),
340
+ });
341
+ }
342
+ const template = compileTemplate(descriptor.value.template, options);
343
+ if (!template.ok) {
344
+ return err({
345
+ message: template.error.message,
346
+ offset: descriptor.value.mapTemplateOffset(template.error.offset),
347
+ ...(template.error.endOffset === undefined
348
+ ? {}
349
+ : { endOffset: descriptor.value.mapTemplateOffset(template.error.endOffset) }),
350
+ });
351
+ }
352
+ return ok({
353
+ descriptor: descriptor.value,
354
+ scriptOnly: false,
355
+ template: template.value,
356
+ });
357
+ };
358
+ export const generateScriptOnlyModule = (target) => {
359
+ if (target === "server") {
360
+ return [
361
+ `const escapeScriptJson = (value) => value.replaceAll("<", "\\\\u003c").replaceAll(">", "\\\\u003e");`,
362
+ `const escapeAttribute = (value) => String(value).replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;");`,
363
+ `export const hydrationBoundaries = [];`,
364
+ `export const renderHydrationState = (id, state) => '<script type="application/json" data-tachyon-state="' + escapeAttribute(id) + '">' + escapeScriptJson(JSON.stringify(state) ?? "null") + '</script>';`,
365
+ `export const render = () => "";`,
366
+ ].join("\n");
367
+ }
368
+ if (target === "stream") {
369
+ return `export const stream = async function* () {};\n`;
370
+ }
371
+ return [
372
+ `export const templateHtml = "";`,
373
+ `export const hydrationBoundaries = [];`,
374
+ `export const componentBoundaries = [];`,
375
+ `export const bind = () => undefined;`,
376
+ ].join("\n");
377
+ };
378
+ export const transformSfcScript = (script) => {
379
+ if (!script || script.content.trim().length === 0) {
380
+ return ok({ code: "", setupBindings: [] });
381
+ }
382
+ const transpiled = transpileScriptContent(script);
383
+ if (!transpiled.ok) {
384
+ return err(transpiled.error);
385
+ }
386
+ const setupBindings = isSfcSetupScript(script) ? topLevelBindings(script) : [];
387
+ const content = autoImportScriptHelpers(transpiled.value, script);
388
+ if (isSfcSetupScript(script)) {
389
+ const scopeEntries = setupBindings.map((name) => `${name}: ${name}`).join(", ");
390
+ return ok({
391
+ code: `${content}\nconst ${sfcSetupScopeName} = { ${scopeEntries} };\n`,
392
+ defaultScopeName: sfcSetupScopeName,
393
+ setupBindings,
394
+ });
395
+ }
396
+ const namedScopeExport = /\bexport\s+const\s+scope\s*=/;
397
+ const defaultExport = /\bexport\s+default\b/;
398
+ if (namedScopeExport.test(content) && defaultExport.test(content)) {
399
+ return err({ message: "Use either export const scope or export default, not both.", offset: script.offset });
400
+ }
401
+ if (namedScopeExport.test(content)) {
402
+ const code = content.replace(namedScopeExport, `const ${sfcNamedScopeName} =`).trim();
403
+ return ok({
404
+ code: `${code}\nexport { ${sfcNamedScopeName} as scope };\n`,
405
+ defaultScopeName: sfcNamedScopeName,
406
+ setupBindings,
407
+ });
408
+ }
409
+ if (!defaultExport.test(content)) {
410
+ return ok({ code: `${content.trim()}\n`, setupBindings });
411
+ }
412
+ const code = content.replace(defaultExport, `const ${sfcDefaultScopeName} =`).trim();
413
+ return ok({
414
+ code: `${code}\nexport { ${sfcDefaultScopeName} as default };\n`,
415
+ defaultScopeName: sfcDefaultScopeName,
416
+ setupBindings,
417
+ });
418
+ };
419
+ export const generateSfcScriptDeclarations = (script) => {
420
+ if (!script || script.content.trim().length === 0) {
421
+ return ok("");
422
+ }
423
+ const result = ts.transpileDeclaration(script.content, {
424
+ compilerOptions: {
425
+ allowJs: true,
426
+ declaration: true,
427
+ module: ts.ModuleKind.ESNext,
428
+ target: ts.ScriptTarget.ES2022,
429
+ },
430
+ fileName: sfcScriptLanguage(script) === "ts" ? "component.td.ts" : "component.td.js",
431
+ });
432
+ if (result.outputText.trim().length === 0) {
433
+ const diagnostic = result.diagnostics?.find((item) => item.category === ts.DiagnosticCategory.Error);
434
+ return err(diagnostic
435
+ ? ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")
436
+ : "Unable to emit script declarations.");
437
+ }
438
+ return ok(result.outputText.trim());
439
+ };
@@ -0,0 +1,3 @@
1
+ import type { CompiledTemplate, ElementNode, GenerateClientModuleOptions } from "../types.js";
2
+ export declare const lowerClientTemplate: (root: ElementNode) => CompiledTemplate["client"];
3
+ export declare const generateClientModule: (template: CompiledTemplate, options?: GenerateClientModuleOptions) => string;