@collie-lang/compiler 1.3.3 → 1.4.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 +3 -3
- package/dist/index.cjs +841 -1200
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -11
- package/dist/index.d.ts +11 -11
- package/dist/index.js +841 -1200
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -50,15 +50,15 @@ __export(index_exports, {
|
|
|
50
50
|
module.exports = __toCommonJS(index_exports);
|
|
51
51
|
|
|
52
52
|
// src/rewrite.ts
|
|
53
|
-
function createTemplateEnv(
|
|
54
|
-
const
|
|
55
|
-
if (
|
|
56
|
-
for (const decl of
|
|
57
|
-
|
|
53
|
+
function createTemplateEnv(inputsDecls) {
|
|
54
|
+
const inputNames = /* @__PURE__ */ new Set();
|
|
55
|
+
if (inputsDecls) {
|
|
56
|
+
for (const decl of inputsDecls) {
|
|
57
|
+
inputNames.add(decl.name);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
return {
|
|
61
|
-
|
|
61
|
+
inputNames,
|
|
62
62
|
localsStack: []
|
|
63
63
|
};
|
|
64
64
|
}
|
|
@@ -77,12 +77,6 @@ function isLocal(env, name) {
|
|
|
77
77
|
}
|
|
78
78
|
return false;
|
|
79
79
|
}
|
|
80
|
-
function isPropAlias(env, name) {
|
|
81
|
-
if (isLocal(env, name)) {
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
return env.propAliases.has(name);
|
|
85
|
-
}
|
|
86
80
|
var IGNORED_IDENTIFIERS = /* @__PURE__ */ new Set([
|
|
87
81
|
"null",
|
|
88
82
|
"undefined",
|
|
@@ -90,10 +84,10 @@ var IGNORED_IDENTIFIERS = /* @__PURE__ */ new Set([
|
|
|
90
84
|
"false",
|
|
91
85
|
"NaN",
|
|
92
86
|
"Infinity",
|
|
93
|
-
"this"
|
|
94
|
-
"props"
|
|
87
|
+
"this"
|
|
95
88
|
]);
|
|
96
89
|
var RESERVED_KEYWORDS = /* @__PURE__ */ new Set([
|
|
90
|
+
"async",
|
|
97
91
|
"await",
|
|
98
92
|
"break",
|
|
99
93
|
"case",
|
|
@@ -135,185 +129,33 @@ var RESERVED_KEYWORDS = /* @__PURE__ */ new Set([
|
|
|
135
129
|
"yield"
|
|
136
130
|
]);
|
|
137
131
|
function rewriteExpression(expression, env) {
|
|
138
|
-
|
|
139
|
-
let state = "code";
|
|
140
|
-
let output = "";
|
|
132
|
+
const tokens = tokenizeExpression(expression);
|
|
141
133
|
const usedBare = /* @__PURE__ */ new Set();
|
|
142
|
-
const usedPropsDot = /* @__PURE__ */ new Set();
|
|
143
134
|
const callSitesBare = /* @__PURE__ */ new Set();
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
const ch = expression[i];
|
|
148
|
-
if (state === "code") {
|
|
149
|
-
if (ch === "'" || ch === '"') {
|
|
150
|
-
state = ch === "'" ? "single" : "double";
|
|
151
|
-
output += ch;
|
|
152
|
-
i++;
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
if (ch === "`") {
|
|
156
|
-
state = "template";
|
|
157
|
-
output += ch;
|
|
158
|
-
i++;
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
if (ch === "/" && expression[i + 1] === "/") {
|
|
162
|
-
state = "line";
|
|
163
|
-
output += ch;
|
|
164
|
-
i++;
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
if (ch === "/" && expression[i + 1] === "*") {
|
|
168
|
-
state = "block";
|
|
169
|
-
output += ch;
|
|
170
|
-
i++;
|
|
171
|
-
continue;
|
|
172
|
-
}
|
|
173
|
-
if (isIdentifierStart(ch)) {
|
|
174
|
-
const start = i;
|
|
175
|
-
i++;
|
|
176
|
-
while (i < expression.length && isIdentifierPart(expression[i])) {
|
|
177
|
-
i++;
|
|
178
|
-
}
|
|
179
|
-
const name = expression.slice(start, i);
|
|
180
|
-
const prevNonSpace = findPreviousNonSpace(expression, start - 1);
|
|
181
|
-
const nextNonSpace = findNextNonSpace(expression, i);
|
|
182
|
-
const isMemberAccess = prevNonSpace === ".";
|
|
183
|
-
const isObjectKey = nextNonSpace === ":" && (prevNonSpace === "{" || prevNonSpace === ",");
|
|
184
|
-
const isCall = nextNonSpace === "(";
|
|
185
|
-
if (prevNonSpace === "." && start >= 2) {
|
|
186
|
-
const propsStart = findPreviousIdentifierStart(expression, start - 2);
|
|
187
|
-
if (propsStart !== null) {
|
|
188
|
-
const possibleProps = expression.slice(propsStart, start - 1).trim();
|
|
189
|
-
if (possibleProps === "props") {
|
|
190
|
-
usedPropsDot.add(name);
|
|
191
|
-
if (isCall) {
|
|
192
|
-
callSitesPropsDot.add(name);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
if (isMemberAccess || isObjectKey || isLocal(env, name) || shouldIgnoreIdentifier(name)) {
|
|
198
|
-
output += name;
|
|
199
|
-
continue;
|
|
200
|
-
}
|
|
201
|
-
if (isPropAlias(env, name)) {
|
|
202
|
-
output += `props.${name}`;
|
|
203
|
-
rewrittenAliases.add(name);
|
|
204
|
-
if (isCall) {
|
|
205
|
-
callSitesBare.add(name);
|
|
206
|
-
}
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
usedBare.add(name);
|
|
210
|
-
if (isCall) {
|
|
211
|
-
callSitesBare.add(name);
|
|
212
|
-
}
|
|
213
|
-
output += name;
|
|
214
|
-
continue;
|
|
215
|
-
}
|
|
216
|
-
output += ch;
|
|
217
|
-
i++;
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
if (state === "line") {
|
|
221
|
-
output += ch;
|
|
222
|
-
if (ch === "\n") {
|
|
223
|
-
state = "code";
|
|
224
|
-
}
|
|
225
|
-
i++;
|
|
226
|
-
continue;
|
|
227
|
-
}
|
|
228
|
-
if (state === "block") {
|
|
229
|
-
output += ch;
|
|
230
|
-
if (ch === "*" && expression[i + 1] === "/") {
|
|
231
|
-
output += "/";
|
|
232
|
-
i += 2;
|
|
233
|
-
state = "code";
|
|
234
|
-
continue;
|
|
235
|
-
}
|
|
236
|
-
i++;
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
if (state === "single") {
|
|
240
|
-
output += ch;
|
|
241
|
-
if (ch === "\\") {
|
|
242
|
-
if (i + 1 < expression.length) {
|
|
243
|
-
output += expression[i + 1];
|
|
244
|
-
i += 2;
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
if (ch === "'") {
|
|
249
|
-
state = "code";
|
|
250
|
-
}
|
|
251
|
-
i++;
|
|
252
|
-
continue;
|
|
253
|
-
}
|
|
254
|
-
if (state === "double") {
|
|
255
|
-
output += ch;
|
|
256
|
-
if (ch === "\\") {
|
|
257
|
-
if (i + 1 < expression.length) {
|
|
258
|
-
output += expression[i + 1];
|
|
259
|
-
i += 2;
|
|
260
|
-
continue;
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
if (ch === '"') {
|
|
264
|
-
state = "code";
|
|
265
|
-
}
|
|
266
|
-
i++;
|
|
267
|
-
continue;
|
|
268
|
-
}
|
|
269
|
-
if (state === "template") {
|
|
270
|
-
output += ch;
|
|
271
|
-
if (ch === "\\") {
|
|
272
|
-
if (i + 1 < expression.length) {
|
|
273
|
-
output += expression[i + 1];
|
|
274
|
-
i += 2;
|
|
275
|
-
continue;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
if (ch === "`") {
|
|
279
|
-
state = "code";
|
|
280
|
-
}
|
|
281
|
-
i++;
|
|
282
|
-
continue;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
return { code: output, usedBare, usedPropsDot, callSitesBare, callSitesPropsDot, rewrittenAliases };
|
|
135
|
+
const localScopes = [/* @__PURE__ */ new Set()];
|
|
136
|
+
analyzeTokens(tokens, 0, tokens.length, env, localScopes, usedBare, callSitesBare, false);
|
|
137
|
+
return { code: expression, usedBare, callSitesBare };
|
|
286
138
|
}
|
|
287
139
|
function rewriteJsxExpression(expression, env) {
|
|
288
|
-
let output = "";
|
|
289
140
|
let i = 0;
|
|
290
141
|
const usedBare = /* @__PURE__ */ new Set();
|
|
291
|
-
const usedPropsDot = /* @__PURE__ */ new Set();
|
|
292
142
|
const callSitesBare = /* @__PURE__ */ new Set();
|
|
293
|
-
const callSitesPropsDot = /* @__PURE__ */ new Set();
|
|
294
|
-
const rewrittenAliases = /* @__PURE__ */ new Set();
|
|
295
143
|
while (i < expression.length) {
|
|
296
144
|
const ch = expression[i];
|
|
297
145
|
if (ch === "{") {
|
|
298
146
|
const braceResult = readBalancedBraces(expression, i + 1);
|
|
299
147
|
if (!braceResult) {
|
|
300
|
-
output += expression.slice(i);
|
|
301
148
|
break;
|
|
302
149
|
}
|
|
303
150
|
const result = rewriteExpression(braceResult.content, env);
|
|
304
|
-
output += `{${result.code}}`;
|
|
305
151
|
for (const name of result.usedBare) usedBare.add(name);
|
|
306
|
-
for (const name of result.usedPropsDot) usedPropsDot.add(name);
|
|
307
152
|
for (const name of result.callSitesBare) callSitesBare.add(name);
|
|
308
|
-
for (const name of result.callSitesPropsDot) callSitesPropsDot.add(name);
|
|
309
|
-
for (const name of result.rewrittenAliases) rewrittenAliases.add(name);
|
|
310
153
|
i = braceResult.endIndex + 1;
|
|
311
154
|
continue;
|
|
312
155
|
}
|
|
313
|
-
output += ch;
|
|
314
156
|
i++;
|
|
315
157
|
}
|
|
316
|
-
return { code:
|
|
158
|
+
return { code: expression, usedBare, callSitesBare };
|
|
317
159
|
}
|
|
318
160
|
function readBalancedBraces(source, startIndex) {
|
|
319
161
|
let i = startIndex;
|
|
@@ -405,38 +247,6 @@ function readBalancedBraces(source, startIndex) {
|
|
|
405
247
|
}
|
|
406
248
|
return null;
|
|
407
249
|
}
|
|
408
|
-
function findPreviousNonSpace(text, index) {
|
|
409
|
-
for (let i = index; i >= 0; i--) {
|
|
410
|
-
const ch = text[i];
|
|
411
|
-
if (!/\s/.test(ch)) {
|
|
412
|
-
return ch;
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
return null;
|
|
416
|
-
}
|
|
417
|
-
function findNextNonSpace(text, index) {
|
|
418
|
-
for (let i = index; i < text.length; i++) {
|
|
419
|
-
const ch = text[i];
|
|
420
|
-
if (!/\s/.test(ch)) {
|
|
421
|
-
return ch;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
return null;
|
|
425
|
-
}
|
|
426
|
-
function findPreviousIdentifierStart(text, index) {
|
|
427
|
-
let i = index;
|
|
428
|
-
while (i >= 0 && /\s/.test(text[i])) {
|
|
429
|
-
i--;
|
|
430
|
-
}
|
|
431
|
-
if (i < 0) return null;
|
|
432
|
-
if (!isIdentifierPart(text[i])) {
|
|
433
|
-
return null;
|
|
434
|
-
}
|
|
435
|
-
while (i > 0 && isIdentifierPart(text[i - 1])) {
|
|
436
|
-
i--;
|
|
437
|
-
}
|
|
438
|
-
return i;
|
|
439
|
-
}
|
|
440
250
|
function isIdentifierStart(ch) {
|
|
441
251
|
return /[A-Za-z_$]/.test(ch);
|
|
442
252
|
}
|
|
@@ -446,96 +256,592 @@ function isIdentifierPart(ch) {
|
|
|
446
256
|
function shouldIgnoreIdentifier(name) {
|
|
447
257
|
return IGNORED_IDENTIFIERS.has(name) || RESERVED_KEYWORDS.has(name);
|
|
448
258
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
259
|
+
function tokenizeExpression(expression) {
|
|
260
|
+
const tokens = [];
|
|
261
|
+
let i = 0;
|
|
262
|
+
while (i < expression.length) {
|
|
263
|
+
const ch = expression[i];
|
|
264
|
+
if (/\s/.test(ch)) {
|
|
265
|
+
i++;
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (ch === "/" && expression[i + 1] === "/") {
|
|
269
|
+
i += 2;
|
|
270
|
+
while (i < expression.length && expression[i] !== "\n") {
|
|
271
|
+
i++;
|
|
272
|
+
}
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (ch === "/" && expression[i + 1] === "*") {
|
|
276
|
+
i += 2;
|
|
277
|
+
while (i < expression.length && !(expression[i] === "*" && expression[i + 1] === "/")) {
|
|
278
|
+
i++;
|
|
279
|
+
}
|
|
280
|
+
i += 2;
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
if (ch === "'" || ch === '"') {
|
|
284
|
+
i = skipStringLiteral(expression, i, ch);
|
|
285
|
+
tokens.push({ type: "literal", value: "string" });
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
if (ch === "`") {
|
|
289
|
+
i = skipTemplateLiteral(expression, i);
|
|
290
|
+
tokens.push({ type: "literal", value: "template" });
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
if (isIdentifierStart(ch)) {
|
|
294
|
+
const start = i;
|
|
295
|
+
i++;
|
|
296
|
+
while (i < expression.length && isIdentifierPart(expression[i])) {
|
|
297
|
+
i++;
|
|
298
|
+
}
|
|
299
|
+
tokens.push({ type: "identifier", value: expression.slice(start, i) });
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
if (/[0-9]/.test(ch)) {
|
|
303
|
+
const start = i;
|
|
304
|
+
i++;
|
|
305
|
+
while (i < expression.length && /[0-9._]/.test(expression[i])) {
|
|
306
|
+
i++;
|
|
307
|
+
}
|
|
308
|
+
tokens.push({ type: "literal", value: expression.slice(start, i) });
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
if (expression.startsWith("...", i)) {
|
|
312
|
+
tokens.push({ type: "punctuator", value: "..." });
|
|
313
|
+
i += 3;
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (expression.startsWith("=>", i)) {
|
|
317
|
+
tokens.push({ type: "punctuator", value: "=>" });
|
|
318
|
+
i += 2;
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
if (expression.startsWith("?.", i)) {
|
|
322
|
+
tokens.push({ type: "punctuator", value: "?." });
|
|
323
|
+
i += 2;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (expression.startsWith("??", i)) {
|
|
327
|
+
tokens.push({ type: "punctuator", value: "??" });
|
|
328
|
+
i += 2;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
tokens.push({ type: "punctuator", value: ch });
|
|
332
|
+
i++;
|
|
462
333
|
}
|
|
463
|
-
|
|
464
|
-
parts.push(functionLines.join("\n"));
|
|
465
|
-
return parts.join("\n\n");
|
|
334
|
+
return tokens;
|
|
466
335
|
}
|
|
467
|
-
function
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
prelude.push(`import React from "react";`);
|
|
336
|
+
function skipStringLiteral(source, start, quote) {
|
|
337
|
+
let i = start + 1;
|
|
338
|
+
while (i < source.length) {
|
|
339
|
+
const ch = source[i];
|
|
340
|
+
if (ch === "\\") {
|
|
341
|
+
i += 2;
|
|
342
|
+
continue;
|
|
343
|
+
}
|
|
344
|
+
if (ch === quote) {
|
|
345
|
+
return i + 1;
|
|
346
|
+
}
|
|
347
|
+
i++;
|
|
480
348
|
}
|
|
481
|
-
|
|
482
|
-
return { prelude, propsType, propsDestructure, jsx, isTsx };
|
|
349
|
+
return source.length;
|
|
483
350
|
}
|
|
484
|
-
function
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
351
|
+
function skipTemplateLiteral(source, start) {
|
|
352
|
+
let i = start + 1;
|
|
353
|
+
while (i < source.length) {
|
|
354
|
+
const ch = source[i];
|
|
355
|
+
if (ch === "\\") {
|
|
356
|
+
i += 2;
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (ch === "`") {
|
|
360
|
+
return i + 1;
|
|
361
|
+
}
|
|
362
|
+
i++;
|
|
488
363
|
}
|
|
489
|
-
|
|
490
|
-
|
|
364
|
+
return source.length;
|
|
365
|
+
}
|
|
366
|
+
function analyzeTokens(tokens, start, end, env, scopes, usedBare, callSitesBare, allowStatements) {
|
|
367
|
+
let i = start;
|
|
368
|
+
while (i < end) {
|
|
369
|
+
const token = tokens[i];
|
|
370
|
+
if (token.type === "identifier") {
|
|
371
|
+
if (token.value === "function") {
|
|
372
|
+
const fnResult = parseFunctionExpression(tokens, i, end, env, scopes, usedBare, callSitesBare);
|
|
373
|
+
if (fnResult > i) {
|
|
374
|
+
i = fnResult;
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
if (allowStatements && (token.value === "const" || token.value === "let" || token.value === "var")) {
|
|
379
|
+
const declResult = parseVariableDeclaration(tokens, i, end, env, scopes, usedBare, callSitesBare);
|
|
380
|
+
if (declResult > i) {
|
|
381
|
+
i = declResult;
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (allowStatements && token.value === "catch") {
|
|
386
|
+
const catchResult = parseCatchClause(tokens, i, end, env, scopes, usedBare, callSitesBare);
|
|
387
|
+
if (catchResult > i) {
|
|
388
|
+
i = catchResult;
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
const arrowResult = parseArrowFunction(tokens, i, end, env, scopes, usedBare, callSitesBare);
|
|
393
|
+
if (arrowResult > i) {
|
|
394
|
+
i = arrowResult;
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
if (shouldIgnoreIdentifier(token.value)) {
|
|
398
|
+
i++;
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
if (isShadowed(env, scopes, token.value)) {
|
|
402
|
+
i++;
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
if (isMemberAccess(tokens, i) || isObjectKey(tokens, i)) {
|
|
406
|
+
i++;
|
|
407
|
+
continue;
|
|
408
|
+
}
|
|
409
|
+
usedBare.add(token.value);
|
|
410
|
+
if (isCallSite(tokens, i)) {
|
|
411
|
+
callSitesBare.add(token.value);
|
|
412
|
+
}
|
|
413
|
+
i++;
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
if (token.type === "punctuator" && token.value === "(") {
|
|
417
|
+
const arrowResult = parseArrowFunction(tokens, i, end, env, scopes, usedBare, callSitesBare);
|
|
418
|
+
if (arrowResult > i) {
|
|
419
|
+
i = arrowResult;
|
|
420
|
+
continue;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
i++;
|
|
491
424
|
}
|
|
492
|
-
return env;
|
|
493
425
|
}
|
|
494
|
-
function
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
if (root.children.length === 0) {
|
|
499
|
-
return false;
|
|
426
|
+
function parseArrowFunction(tokens, index, end, env, scopes, usedBare, callSitesBare) {
|
|
427
|
+
const token = tokens[index];
|
|
428
|
+
if (!token) {
|
|
429
|
+
return index;
|
|
500
430
|
}
|
|
501
|
-
if (
|
|
502
|
-
|
|
431
|
+
if (token.type === "identifier") {
|
|
432
|
+
const next = tokens[index + 1];
|
|
433
|
+
if (next && next.value === "=>") {
|
|
434
|
+
const params = /* @__PURE__ */ new Set([token.value]);
|
|
435
|
+
return analyzeArrowBody(tokens, index + 2, end, params, env, scopes, usedBare, callSitesBare);
|
|
436
|
+
}
|
|
503
437
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
438
|
+
if (token.type === "punctuator" && token.value === "(") {
|
|
439
|
+
const closeIndex = findMatchingToken(tokens, index, "(", ")");
|
|
440
|
+
if (closeIndex !== -1) {
|
|
441
|
+
const afterClose = tokens[closeIndex + 1];
|
|
442
|
+
if (afterClose && afterClose.value === "=>") {
|
|
443
|
+
const params = /* @__PURE__ */ new Set();
|
|
444
|
+
collectBindingNamesFromList(tokens, index + 1, closeIndex, params);
|
|
445
|
+
return analyzeArrowBody(tokens, closeIndex + 2, end, params, env, scopes, usedBare, callSitesBare);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
509
448
|
}
|
|
510
|
-
|
|
511
|
-
|
|
449
|
+
return index;
|
|
450
|
+
}
|
|
451
|
+
function analyzeArrowBody(tokens, start, end, params, env, scopes, usedBare, callSitesBare) {
|
|
452
|
+
const bodyToken = tokens[start];
|
|
453
|
+
if (!bodyToken) {
|
|
454
|
+
return start;
|
|
455
|
+
}
|
|
456
|
+
const scope = new Set(params);
|
|
457
|
+
scopes.push(scope);
|
|
458
|
+
if (bodyToken.type === "punctuator" && bodyToken.value === "{") {
|
|
459
|
+
const closeIndex = findMatchingToken(tokens, start, "{", "}");
|
|
460
|
+
const bodyEnd2 = closeIndex === -1 ? end : closeIndex;
|
|
461
|
+
analyzeTokens(tokens, start + 1, bodyEnd2, env, scopes, usedBare, callSitesBare, true);
|
|
462
|
+
scopes.pop();
|
|
463
|
+
return closeIndex === -1 ? end : closeIndex + 1;
|
|
464
|
+
}
|
|
465
|
+
const bodyEnd = findExpressionEnd(tokens, start, end, EXPRESSION_TERMINATORS);
|
|
466
|
+
analyzeTokens(tokens, start, bodyEnd, env, scopes, usedBare, callSitesBare, false);
|
|
467
|
+
scopes.pop();
|
|
468
|
+
return bodyEnd;
|
|
469
|
+
}
|
|
470
|
+
function parseFunctionExpression(tokens, index, end, env, scopes, usedBare, callSitesBare) {
|
|
471
|
+
let i = index + 1;
|
|
472
|
+
const nameToken = tokens[i];
|
|
473
|
+
let fnName;
|
|
474
|
+
if (nameToken && nameToken.type === "identifier" && tokens[i + 1]?.value === "(") {
|
|
475
|
+
fnName = nameToken.value;
|
|
476
|
+
i++;
|
|
512
477
|
}
|
|
513
|
-
if (
|
|
514
|
-
return
|
|
478
|
+
if (!tokens[i] || tokens[i].value !== "(") {
|
|
479
|
+
return index;
|
|
515
480
|
}
|
|
516
|
-
|
|
517
|
-
|
|
481
|
+
const closeIndex = findMatchingToken(tokens, i, "(", ")");
|
|
482
|
+
if (closeIndex === -1) {
|
|
483
|
+
return index;
|
|
484
|
+
}
|
|
485
|
+
const params = /* @__PURE__ */ new Set();
|
|
486
|
+
collectBindingNamesFromList(tokens, i + 1, closeIndex, params);
|
|
487
|
+
if (fnName) {
|
|
488
|
+
params.add(fnName);
|
|
489
|
+
}
|
|
490
|
+
const bodyStart = closeIndex + 1;
|
|
491
|
+
if (!tokens[bodyStart] || tokens[bodyStart].value !== "{") {
|
|
492
|
+
return index;
|
|
493
|
+
}
|
|
494
|
+
const closeBody = findMatchingToken(tokens, bodyStart, "{", "}");
|
|
495
|
+
const bodyEnd = closeBody === -1 ? end : closeBody;
|
|
496
|
+
scopes.push(params);
|
|
497
|
+
analyzeTokens(tokens, bodyStart + 1, bodyEnd, env, scopes, usedBare, callSitesBare, true);
|
|
498
|
+
scopes.pop();
|
|
499
|
+
return closeBody === -1 ? end : closeBody + 1;
|
|
500
|
+
}
|
|
501
|
+
function parseCatchClause(tokens, index, end, env, scopes, usedBare, callSitesBare) {
|
|
502
|
+
const next = tokens[index + 1];
|
|
503
|
+
if (!next || next.value !== "(") {
|
|
504
|
+
return index;
|
|
505
|
+
}
|
|
506
|
+
const closeIndex = findMatchingToken(tokens, index + 1, "(", ")");
|
|
507
|
+
if (closeIndex === -1) {
|
|
508
|
+
return index;
|
|
509
|
+
}
|
|
510
|
+
const params = /* @__PURE__ */ new Set();
|
|
511
|
+
collectBindingNamesFromList(tokens, index + 2, closeIndex, params);
|
|
512
|
+
const bodyStart = closeIndex + 1;
|
|
513
|
+
if (!tokens[bodyStart] || tokens[bodyStart].value !== "{") {
|
|
514
|
+
return index;
|
|
515
|
+
}
|
|
516
|
+
const closeBody = findMatchingToken(tokens, bodyStart, "{", "}");
|
|
517
|
+
const bodyEnd = closeBody === -1 ? end : closeBody;
|
|
518
|
+
scopes.push(params);
|
|
519
|
+
analyzeTokens(tokens, bodyStart + 1, bodyEnd, env, scopes, usedBare, callSitesBare, true);
|
|
520
|
+
scopes.pop();
|
|
521
|
+
return closeBody === -1 ? end : closeBody + 1;
|
|
522
|
+
}
|
|
523
|
+
function parseVariableDeclaration(tokens, index, end, env, scopes, usedBare, callSitesBare) {
|
|
524
|
+
let i = index + 1;
|
|
525
|
+
const scope = scopes[scopes.length - 1];
|
|
526
|
+
while (i < end) {
|
|
527
|
+
const names = /* @__PURE__ */ new Set();
|
|
528
|
+
const nextIndex = parseBindingPattern(tokens, i, end, names);
|
|
529
|
+
if (nextIndex === i) {
|
|
530
|
+
return index;
|
|
531
|
+
}
|
|
532
|
+
i = nextIndex;
|
|
533
|
+
if (tokens[i] && tokens[i].value === "=") {
|
|
534
|
+
const initStart = i + 1;
|
|
535
|
+
const initEnd = findExpressionEnd(tokens, initStart, end, DECLARATION_TERMINATORS);
|
|
536
|
+
analyzeTokens(tokens, initStart, initEnd, env, scopes, usedBare, callSitesBare, false);
|
|
537
|
+
i = initEnd;
|
|
538
|
+
}
|
|
539
|
+
for (const name of names) {
|
|
540
|
+
scope.add(name);
|
|
541
|
+
}
|
|
542
|
+
if (tokens[i] && tokens[i].value === ",") {
|
|
543
|
+
i++;
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
break;
|
|
518
547
|
}
|
|
519
|
-
return
|
|
548
|
+
return i;
|
|
520
549
|
}
|
|
521
|
-
function
|
|
522
|
-
|
|
523
|
-
|
|
550
|
+
function parseBindingPattern(tokens, start, end, names) {
|
|
551
|
+
const token = tokens[start];
|
|
552
|
+
if (!token) {
|
|
553
|
+
return start;
|
|
524
554
|
}
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
if (node.type === "Text") {
|
|
529
|
-
return emitText(node, env);
|
|
555
|
+
if (token.type === "identifier") {
|
|
556
|
+
names.add(token.value);
|
|
557
|
+
return start + 1;
|
|
530
558
|
}
|
|
531
|
-
if (
|
|
532
|
-
return
|
|
559
|
+
if (token.value === "{") {
|
|
560
|
+
return parseObjectPattern(tokens, start + 1, end, names);
|
|
533
561
|
}
|
|
534
|
-
if (
|
|
535
|
-
return
|
|
562
|
+
if (token.value === "[") {
|
|
563
|
+
return parseArrayPattern(tokens, start + 1, end, names);
|
|
536
564
|
}
|
|
537
|
-
|
|
538
|
-
|
|
565
|
+
return start + 1;
|
|
566
|
+
}
|
|
567
|
+
function parseObjectPattern(tokens, start, end, names) {
|
|
568
|
+
let i = start;
|
|
569
|
+
while (i < end) {
|
|
570
|
+
const token = tokens[i];
|
|
571
|
+
if (!token) {
|
|
572
|
+
return i;
|
|
573
|
+
}
|
|
574
|
+
if (token.value === "}") {
|
|
575
|
+
return i + 1;
|
|
576
|
+
}
|
|
577
|
+
if (token.value === ",") {
|
|
578
|
+
i++;
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
if (token.value === "...") {
|
|
582
|
+
i++;
|
|
583
|
+
i = parseBindingPattern(tokens, i, end, names);
|
|
584
|
+
i = skipDefaultValue(tokens, i, end, OBJECT_PATTERN_TERMINATORS);
|
|
585
|
+
continue;
|
|
586
|
+
}
|
|
587
|
+
if (token.value === "[") {
|
|
588
|
+
const closeIndex = findMatchingToken(tokens, i, "[", "]");
|
|
589
|
+
i = closeIndex === -1 ? end : closeIndex + 1;
|
|
590
|
+
if (tokens[i] && tokens[i].value === ":") {
|
|
591
|
+
i++;
|
|
592
|
+
i = parseBindingPattern(tokens, i, end, names);
|
|
593
|
+
i = skipDefaultValue(tokens, i, end, OBJECT_PATTERN_TERMINATORS);
|
|
594
|
+
}
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
if (token.type === "identifier" || token.type === "literal") {
|
|
598
|
+
const key = token.value;
|
|
599
|
+
i++;
|
|
600
|
+
if (tokens[i] && tokens[i].value === ":") {
|
|
601
|
+
i++;
|
|
602
|
+
i = parseBindingPattern(tokens, i, end, names);
|
|
603
|
+
} else if (token.type === "identifier") {
|
|
604
|
+
names.add(key);
|
|
605
|
+
}
|
|
606
|
+
i = skipDefaultValue(tokens, i, end, OBJECT_PATTERN_TERMINATORS);
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
i++;
|
|
610
|
+
}
|
|
611
|
+
return i;
|
|
612
|
+
}
|
|
613
|
+
function parseArrayPattern(tokens, start, end, names) {
|
|
614
|
+
let i = start;
|
|
615
|
+
while (i < end) {
|
|
616
|
+
const token = tokens[i];
|
|
617
|
+
if (!token) {
|
|
618
|
+
return i;
|
|
619
|
+
}
|
|
620
|
+
if (token.value === "]") {
|
|
621
|
+
return i + 1;
|
|
622
|
+
}
|
|
623
|
+
if (token.value === ",") {
|
|
624
|
+
i++;
|
|
625
|
+
continue;
|
|
626
|
+
}
|
|
627
|
+
if (token.value === "...") {
|
|
628
|
+
i++;
|
|
629
|
+
i = parseBindingPattern(tokens, i, end, names);
|
|
630
|
+
i = skipDefaultValue(tokens, i, end, ARRAY_PATTERN_TERMINATORS);
|
|
631
|
+
continue;
|
|
632
|
+
}
|
|
633
|
+
i = parseBindingPattern(tokens, i, end, names);
|
|
634
|
+
i = skipDefaultValue(tokens, i, end, ARRAY_PATTERN_TERMINATORS);
|
|
635
|
+
if (tokens[i] && tokens[i].value === ",") {
|
|
636
|
+
i++;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return i;
|
|
640
|
+
}
|
|
641
|
+
function collectBindingNamesFromList(tokens, start, end, names) {
|
|
642
|
+
let i = start;
|
|
643
|
+
while (i < end) {
|
|
644
|
+
if (tokens[i] && tokens[i].value === ",") {
|
|
645
|
+
i++;
|
|
646
|
+
continue;
|
|
647
|
+
}
|
|
648
|
+
const nextIndex = parseBindingPattern(tokens, i, end, names);
|
|
649
|
+
if (nextIndex === i) {
|
|
650
|
+
i++;
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
i = skipParameterSuffix(tokens, nextIndex, end);
|
|
654
|
+
if (tokens[i] && tokens[i].value === ",") {
|
|
655
|
+
i++;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
function skipParameterSuffix(tokens, start, end) {
|
|
660
|
+
if (!tokens[start]) {
|
|
661
|
+
return start;
|
|
662
|
+
}
|
|
663
|
+
if (tokens[start].value === "=" || tokens[start].value === ":") {
|
|
664
|
+
return findExpressionEnd(tokens, start + 1, end, PARAMETER_TERMINATORS);
|
|
665
|
+
}
|
|
666
|
+
return start;
|
|
667
|
+
}
|
|
668
|
+
function skipDefaultValue(tokens, start, end, terminators) {
|
|
669
|
+
if (!tokens[start] || tokens[start].value !== "=") {
|
|
670
|
+
return start;
|
|
671
|
+
}
|
|
672
|
+
return findExpressionEnd(tokens, start + 1, end, terminators);
|
|
673
|
+
}
|
|
674
|
+
function findMatchingToken(tokens, start, open, close) {
|
|
675
|
+
let depth = 0;
|
|
676
|
+
for (let i = start; i < tokens.length; i++) {
|
|
677
|
+
const value = tokens[i].value;
|
|
678
|
+
if (value === open) {
|
|
679
|
+
depth++;
|
|
680
|
+
} else if (value === close) {
|
|
681
|
+
depth--;
|
|
682
|
+
if (depth === 0) {
|
|
683
|
+
return i;
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
return -1;
|
|
688
|
+
}
|
|
689
|
+
function findExpressionEnd(tokens, start, end, terminators) {
|
|
690
|
+
let depthParen = 0;
|
|
691
|
+
let depthBracket = 0;
|
|
692
|
+
let depthBrace = 0;
|
|
693
|
+
for (let i = start; i < end; i++) {
|
|
694
|
+
const value = tokens[i].value;
|
|
695
|
+
if (value === "(") {
|
|
696
|
+
depthParen++;
|
|
697
|
+
} else if (value === ")") {
|
|
698
|
+
if (depthParen === 0 && terminators.has(value)) {
|
|
699
|
+
return i;
|
|
700
|
+
}
|
|
701
|
+
depthParen = Math.max(0, depthParen - 1);
|
|
702
|
+
} else if (value === "[") {
|
|
703
|
+
depthBracket++;
|
|
704
|
+
} else if (value === "]") {
|
|
705
|
+
if (depthBracket === 0 && terminators.has(value)) {
|
|
706
|
+
return i;
|
|
707
|
+
}
|
|
708
|
+
depthBracket = Math.max(0, depthBracket - 1);
|
|
709
|
+
} else if (value === "{") {
|
|
710
|
+
depthBrace++;
|
|
711
|
+
} else if (value === "}") {
|
|
712
|
+
if (depthBrace === 0 && terminators.has(value)) {
|
|
713
|
+
return i;
|
|
714
|
+
}
|
|
715
|
+
depthBrace = Math.max(0, depthBrace - 1);
|
|
716
|
+
}
|
|
717
|
+
if (depthParen === 0 && depthBracket === 0 && depthBrace === 0 && terminators.has(value)) {
|
|
718
|
+
return i;
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
return end;
|
|
722
|
+
}
|
|
723
|
+
function isMemberAccess(tokens, index) {
|
|
724
|
+
const prev = tokens[index - 1];
|
|
725
|
+
return prev?.value === "." || prev?.value === "?.";
|
|
726
|
+
}
|
|
727
|
+
function isObjectKey(tokens, index) {
|
|
728
|
+
const next = tokens[index + 1];
|
|
729
|
+
if (!next || next.value !== ":") {
|
|
730
|
+
return false;
|
|
731
|
+
}
|
|
732
|
+
const prev = tokens[index - 1];
|
|
733
|
+
return prev?.value === "{" || prev?.value === ",";
|
|
734
|
+
}
|
|
735
|
+
function isCallSite(tokens, index) {
|
|
736
|
+
const next = tokens[index + 1];
|
|
737
|
+
return next?.value === "(";
|
|
738
|
+
}
|
|
739
|
+
function isShadowed(env, scopes, name) {
|
|
740
|
+
if (isLocal(env, name)) {
|
|
741
|
+
return true;
|
|
742
|
+
}
|
|
743
|
+
for (let i = scopes.length - 1; i >= 0; i--) {
|
|
744
|
+
if (scopes[i].has(name)) {
|
|
745
|
+
return true;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
return false;
|
|
749
|
+
}
|
|
750
|
+
var PARAMETER_TERMINATORS = /* @__PURE__ */ new Set([","]);
|
|
751
|
+
var OBJECT_PATTERN_TERMINATORS = /* @__PURE__ */ new Set([",", "}"]);
|
|
752
|
+
var ARRAY_PATTERN_TERMINATORS = /* @__PURE__ */ new Set([",", "]"]);
|
|
753
|
+
var EXPRESSION_TERMINATORS = /* @__PURE__ */ new Set([",", ")", "]", "}", ";"]);
|
|
754
|
+
var DECLARATION_TERMINATORS = /* @__PURE__ */ new Set([",", ")", "]", "}", ";"]);
|
|
755
|
+
|
|
756
|
+
// src/codegen.ts
|
|
757
|
+
function generateRenderModule(root, options) {
|
|
758
|
+
const { prelude, inputsType, inputsPrelude, jsx, isTsx } = buildModuleParts(root, options);
|
|
759
|
+
const parts = [...prelude, inputsType];
|
|
760
|
+
if (!isTsx) {
|
|
761
|
+
parts.push(`/** @param {any} __inputs */`);
|
|
762
|
+
}
|
|
763
|
+
const functionLines = [
|
|
764
|
+
isTsx ? "export function render(__inputs: any) {" : "export function render(__inputs) {"
|
|
765
|
+
];
|
|
766
|
+
if (inputsPrelude) {
|
|
767
|
+
functionLines.push(` ${inputsPrelude}`);
|
|
768
|
+
}
|
|
769
|
+
functionLines.push(` return ${jsx};`, `}`);
|
|
770
|
+
parts.push(functionLines.join("\n"));
|
|
771
|
+
return parts.join("\n\n");
|
|
772
|
+
}
|
|
773
|
+
function buildModuleParts(root, options) {
|
|
774
|
+
const { jsxRuntime, flavor } = options;
|
|
775
|
+
const isTsx = flavor === "tsx";
|
|
776
|
+
const aliasEnv = buildClassAliasEnvironment(root.classAliases);
|
|
777
|
+
const env = createTemplateEnv(root.inputsDecls);
|
|
778
|
+
const jsx = renderRootChildren(root.children, aliasEnv, env);
|
|
779
|
+
const inputsPrelude = emitInputsPrelude(root.inputsDecls);
|
|
780
|
+
const prelude = [];
|
|
781
|
+
if (root.clientComponent) {
|
|
782
|
+
prelude.push(`"use client";`);
|
|
783
|
+
}
|
|
784
|
+
if (jsxRuntime === "classic" && templateUsesJsx(root)) {
|
|
785
|
+
prelude.push(`import React from "react";`);
|
|
786
|
+
}
|
|
787
|
+
const inputsType = emitInputsType(root.inputs, flavor);
|
|
788
|
+
return { prelude, inputsType, inputsPrelude, jsx, isTsx };
|
|
789
|
+
}
|
|
790
|
+
function buildClassAliasEnvironment(decl) {
|
|
791
|
+
const env = /* @__PURE__ */ new Map();
|
|
792
|
+
if (!decl) {
|
|
793
|
+
return env;
|
|
794
|
+
}
|
|
795
|
+
for (const alias of decl.aliases) {
|
|
796
|
+
env.set(alias.name, alias.classes);
|
|
797
|
+
}
|
|
798
|
+
return env;
|
|
799
|
+
}
|
|
800
|
+
function renderRootChildren(children, aliasEnv, env) {
|
|
801
|
+
return emitNodesExpression(children, aliasEnv, env);
|
|
802
|
+
}
|
|
803
|
+
function templateUsesJsx(root) {
|
|
804
|
+
if (root.children.length === 0) {
|
|
805
|
+
return false;
|
|
806
|
+
}
|
|
807
|
+
if (root.children.length > 1) {
|
|
808
|
+
return true;
|
|
809
|
+
}
|
|
810
|
+
return nodeUsesJsx(root.children[0]);
|
|
811
|
+
}
|
|
812
|
+
function nodeUsesJsx(node) {
|
|
813
|
+
if (node.type === "Element" || node.type === "Text" || node.type === "Component") {
|
|
814
|
+
return true;
|
|
815
|
+
}
|
|
816
|
+
if (node.type === "Expression" || node.type === "JSXPassthrough") {
|
|
817
|
+
return false;
|
|
818
|
+
}
|
|
819
|
+
if (node.type === "Conditional") {
|
|
820
|
+
return node.branches.some((branch) => branchUsesJsx(branch));
|
|
821
|
+
}
|
|
822
|
+
if (node.type === "For") {
|
|
823
|
+
return node.body.some((child) => nodeUsesJsx(child));
|
|
824
|
+
}
|
|
825
|
+
return false;
|
|
826
|
+
}
|
|
827
|
+
function branchUsesJsx(branch) {
|
|
828
|
+
if (!branch.body.length) {
|
|
829
|
+
return false;
|
|
830
|
+
}
|
|
831
|
+
return branch.body.some((child) => nodeUsesJsx(child));
|
|
832
|
+
}
|
|
833
|
+
function emitNodeInJsx(node, aliasEnv, env) {
|
|
834
|
+
if (node.type === "Text") {
|
|
835
|
+
return emitText(node, env);
|
|
836
|
+
}
|
|
837
|
+
if (node.type === "Expression") {
|
|
838
|
+
return `{${emitExpressionValue(node.value, env)}}`;
|
|
839
|
+
}
|
|
840
|
+
if (node.type === "JSXPassthrough") {
|
|
841
|
+
return `{${emitJsxExpression(node.expression, env)}}`;
|
|
842
|
+
}
|
|
843
|
+
if (node.type === "Conditional") {
|
|
844
|
+
return `{${emitConditionalExpression(node, aliasEnv, env)}}`;
|
|
539
845
|
}
|
|
540
846
|
if (node.type === "For") {
|
|
541
847
|
return `{${emitForExpression(node, aliasEnv, env)}}`;
|
|
@@ -559,8 +865,8 @@ function emitElement(node, aliasEnv, env) {
|
|
|
559
865
|
}
|
|
560
866
|
function emitComponent(node, aliasEnv, env) {
|
|
561
867
|
const attrs = emitAttributes(node.attributes, aliasEnv, env);
|
|
562
|
-
const
|
|
563
|
-
const allAttrs = `${attrs}${
|
|
868
|
+
const slotBindings = emitSlotBindings(node, aliasEnv, env);
|
|
869
|
+
const allAttrs = `${attrs}${slotBindings}`;
|
|
564
870
|
const children = emitChildrenWithSpacing(node.children, aliasEnv, env);
|
|
565
871
|
if (children.length > 0) {
|
|
566
872
|
return `<${node.name}${allAttrs}>${children}</${node.name}>`;
|
|
@@ -598,7 +904,7 @@ function emitAttributes(attributes, aliasEnv, env) {
|
|
|
598
904
|
return ` ${attr.name}=${emitAttributeValue(attr.value, env)}`;
|
|
599
905
|
}).join("");
|
|
600
906
|
}
|
|
601
|
-
function
|
|
907
|
+
function emitSlotBindings(node, aliasEnv, env) {
|
|
602
908
|
if (!node.slots || node.slots.length === 0) {
|
|
603
909
|
return "";
|
|
604
910
|
}
|
|
@@ -729,41 +1035,41 @@ function emitSingleNodeExpression(node, aliasEnv, env) {
|
|
|
729
1035
|
}
|
|
730
1036
|
return emitNodeInJsx(node, aliasEnv, env);
|
|
731
1037
|
}
|
|
732
|
-
function
|
|
1038
|
+
function emitInputsType(inputs, flavor) {
|
|
733
1039
|
if (flavor === "tsx") {
|
|
734
|
-
return
|
|
1040
|
+
return emitTsInputsType(inputs);
|
|
735
1041
|
}
|
|
736
|
-
return
|
|
1042
|
+
return emitJsDocInputsType(inputs);
|
|
737
1043
|
}
|
|
738
|
-
function
|
|
739
|
-
if (!
|
|
740
|
-
return "/** @typedef {any}
|
|
1044
|
+
function emitJsDocInputsType(inputs) {
|
|
1045
|
+
if (!inputs) {
|
|
1046
|
+
return "/** @typedef {any} Inputs */";
|
|
741
1047
|
}
|
|
742
|
-
if (!
|
|
743
|
-
return "/** @typedef {{}}
|
|
1048
|
+
if (!inputs.fields.length) {
|
|
1049
|
+
return "/** @typedef {{}} Inputs */";
|
|
744
1050
|
}
|
|
745
|
-
const fields =
|
|
1051
|
+
const fields = inputs.fields.map((field) => {
|
|
746
1052
|
const optional = field.optional ? "?" : "";
|
|
747
1053
|
return `${field.name}${optional}: ${field.typeText}`;
|
|
748
1054
|
}).join("; ");
|
|
749
|
-
return `/** @typedef {{ ${fields} }}
|
|
1055
|
+
return `/** @typedef {{ ${fields} }} Inputs */`;
|
|
750
1056
|
}
|
|
751
|
-
function
|
|
752
|
-
if (!
|
|
753
|
-
return "export type
|
|
1057
|
+
function emitTsInputsType(inputs) {
|
|
1058
|
+
if (!inputs || inputs.fields.length === 0) {
|
|
1059
|
+
return "export type Inputs = Record<string, never>;";
|
|
754
1060
|
}
|
|
755
|
-
const lines =
|
|
1061
|
+
const lines = inputs.fields.map((field) => {
|
|
756
1062
|
const optional = field.optional ? "?" : "";
|
|
757
1063
|
return ` ${field.name}${optional}: ${field.typeText};`;
|
|
758
1064
|
});
|
|
759
|
-
return ["export interface
|
|
1065
|
+
return ["export interface Inputs {", ...lines, "}"].join("\n");
|
|
760
1066
|
}
|
|
761
|
-
function
|
|
762
|
-
if (!
|
|
1067
|
+
function emitInputsPrelude(inputsDecls) {
|
|
1068
|
+
if (!inputsDecls || inputsDecls.length === 0) {
|
|
763
1069
|
return null;
|
|
764
1070
|
}
|
|
765
|
-
const names =
|
|
766
|
-
return `const { ${names.join(", ")} } =
|
|
1071
|
+
const names = inputsDecls.map((decl) => decl.name);
|
|
1072
|
+
return `const { ${names.join(", ")} } = __inputs ?? {};`;
|
|
767
1073
|
}
|
|
768
1074
|
function escapeText(value) {
|
|
769
1075
|
return value.replace(/[&<>{}]/g, (char) => {
|
|
@@ -924,837 +1230,197 @@ function unescapeChar(char, quote) {
|
|
|
924
1230
|
return "'";
|
|
925
1231
|
default:
|
|
926
1232
|
if (char === quote) {
|
|
927
|
-
return quote;
|
|
928
|
-
}
|
|
929
|
-
return char;
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
function buildClassAliasEnvironment2(decl) {
|
|
933
|
-
const env = /* @__PURE__ */ new Map();
|
|
934
|
-
if (!decl) {
|
|
935
|
-
return env;
|
|
936
|
-
}
|
|
937
|
-
for (const alias of decl.aliases) {
|
|
938
|
-
env.set(alias.name, alias.classes);
|
|
939
|
-
}
|
|
940
|
-
return env;
|
|
941
|
-
}
|
|
942
|
-
function expandClasses2(classes, aliasEnv) {
|
|
943
|
-
const result = [];
|
|
944
|
-
for (const cls of classes) {
|
|
945
|
-
const match = cls.match(/^\$([A-Za-z_][A-Za-z0-9_]*)$/);
|
|
946
|
-
if (!match) {
|
|
947
|
-
result.push(cls);
|
|
948
|
-
continue;
|
|
949
|
-
}
|
|
950
|
-
const aliasClasses = aliasEnv.get(match[1]);
|
|
951
|
-
if (!aliasClasses) {
|
|
952
|
-
continue;
|
|
953
|
-
}
|
|
954
|
-
result.push(...aliasClasses);
|
|
955
|
-
}
|
|
956
|
-
return result;
|
|
957
|
-
}
|
|
958
|
-
function escapeStaticText(value) {
|
|
959
|
-
return value.replace(/[&<>{}]/g, (char) => {
|
|
960
|
-
switch (char) {
|
|
961
|
-
case "&":
|
|
962
|
-
return "&";
|
|
963
|
-
case "<":
|
|
964
|
-
return "<";
|
|
965
|
-
case ">":
|
|
966
|
-
return ">";
|
|
967
|
-
case "{":
|
|
968
|
-
return "{";
|
|
969
|
-
case "}":
|
|
970
|
-
return "}";
|
|
971
|
-
default:
|
|
972
|
-
return char;
|
|
973
|
-
}
|
|
974
|
-
});
|
|
975
|
-
}
|
|
976
|
-
function escapeAttributeValue(value) {
|
|
977
|
-
return value.replace(/["&<>]/g, (char) => {
|
|
978
|
-
switch (char) {
|
|
979
|
-
case "&":
|
|
980
|
-
return "&";
|
|
981
|
-
case "<":
|
|
982
|
-
return "<";
|
|
983
|
-
case ">":
|
|
984
|
-
return ">";
|
|
985
|
-
case '"':
|
|
986
|
-
return """;
|
|
987
|
-
default:
|
|
988
|
-
return char;
|
|
989
|
-
}
|
|
990
|
-
});
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
// src/diagnostics.ts
|
|
994
|
-
function createSpan(line, col, length, lineOffset) {
|
|
995
|
-
const startOffset = lineOffset + col - 1;
|
|
996
|
-
return {
|
|
997
|
-
start: { line, col, offset: startOffset },
|
|
998
|
-
end: { line, col: col + length, offset: startOffset + length }
|
|
999
|
-
};
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
// src/dialect.ts
|
|
1003
|
-
function enforceDialect(root, config) {
|
|
1004
|
-
const diagnostics = [];
|
|
1005
|
-
if (root.idToken) {
|
|
1006
|
-
diagnostics.push(
|
|
1007
|
-
...evaluateToken(
|
|
1008
|
-
{ kind: "id", token: root.idToken, span: root.idTokenSpan },
|
|
1009
|
-
config.tokens.id
|
|
1010
|
-
)
|
|
1011
|
-
);
|
|
1012
|
-
}
|
|
1013
|
-
walkNodes(root.children, (occurrence) => {
|
|
1014
|
-
const rule = config.tokens[occurrence.kind];
|
|
1015
|
-
diagnostics.push(...evaluateToken(occurrence, rule));
|
|
1016
|
-
});
|
|
1017
|
-
return diagnostics;
|
|
1018
|
-
}
|
|
1019
|
-
function walkNodes(nodes, onToken) {
|
|
1020
|
-
for (const node of nodes) {
|
|
1021
|
-
if (node.type === "For") {
|
|
1022
|
-
onFor(node, onToken);
|
|
1023
|
-
walkNodes(node.body, onToken);
|
|
1024
|
-
continue;
|
|
1025
|
-
}
|
|
1026
|
-
if (node.type === "Conditional") {
|
|
1027
|
-
onConditional(node, onToken);
|
|
1028
|
-
continue;
|
|
1029
|
-
}
|
|
1030
|
-
if (node.type === "Element" || node.type === "Component") {
|
|
1031
|
-
walkNodes(node.children, onToken);
|
|
1032
|
-
if (node.type === "Component" && node.slots) {
|
|
1033
|
-
for (const slot of node.slots) {
|
|
1034
|
-
walkNodes(slot.children, onToken);
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
continue;
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
function onFor(node, onToken) {
|
|
1042
|
-
if (!node.token) {
|
|
1043
|
-
return;
|
|
1044
|
-
}
|
|
1045
|
-
onToken({ kind: "for", token: node.token, span: node.tokenSpan });
|
|
1046
|
-
}
|
|
1047
|
-
function onConditional(node, onToken) {
|
|
1048
|
-
for (const branch of node.branches) {
|
|
1049
|
-
onBranch(branch, onToken);
|
|
1050
|
-
walkNodes(branch.body, onToken);
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
function onBranch(branch, onToken) {
|
|
1054
|
-
if (!branch.token || !branch.kind) {
|
|
1055
|
-
return;
|
|
1056
|
-
}
|
|
1057
|
-
onToken({ kind: branch.kind, token: branch.token, span: branch.tokenSpan });
|
|
1058
|
-
}
|
|
1059
|
-
function evaluateToken(occurrence, rule) {
|
|
1060
|
-
const diagnostics = [];
|
|
1061
|
-
const used = occurrence.token;
|
|
1062
|
-
const preferred = rule.preferred;
|
|
1063
|
-
const isAllowed = rule.allow.includes(used);
|
|
1064
|
-
if (!isAllowed) {
|
|
1065
|
-
const severity = levelToSeverity(rule.onDisallowed);
|
|
1066
|
-
if (severity) {
|
|
1067
|
-
diagnostics.push(
|
|
1068
|
-
createDialectDiagnostic(
|
|
1069
|
-
"dialect.token.disallowed",
|
|
1070
|
-
severity,
|
|
1071
|
-
used,
|
|
1072
|
-
preferred,
|
|
1073
|
-
occurrence.span,
|
|
1074
|
-
`Token "${used}" is not allowed for ${occurrence.kind}. Preferred: "${preferred}".`
|
|
1075
|
-
)
|
|
1076
|
-
);
|
|
1077
|
-
}
|
|
1078
|
-
return diagnostics;
|
|
1079
|
-
}
|
|
1080
|
-
if (used !== preferred) {
|
|
1081
|
-
const severity = levelToSeverity(rule.onDisallowed);
|
|
1082
|
-
if (severity) {
|
|
1083
|
-
diagnostics.push(
|
|
1084
|
-
createDialectDiagnostic(
|
|
1085
|
-
"dialect.token.nonPreferred",
|
|
1086
|
-
severity,
|
|
1087
|
-
used,
|
|
1088
|
-
preferred,
|
|
1089
|
-
occurrence.span,
|
|
1090
|
-
`Token "${used}" is allowed but not preferred for ${occurrence.kind}. Preferred: "${preferred}".`
|
|
1091
|
-
)
|
|
1092
|
-
);
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
return diagnostics;
|
|
1096
|
-
}
|
|
1097
|
-
function createDialectDiagnostic(code, severity, used, preferred, span, message) {
|
|
1098
|
-
const fix = span ? {
|
|
1099
|
-
range: span,
|
|
1100
|
-
replacementText: preferred
|
|
1101
|
-
} : void 0;
|
|
1102
|
-
return {
|
|
1103
|
-
severity,
|
|
1104
|
-
code,
|
|
1105
|
-
message: message.replace(/\\s+/g, " "),
|
|
1106
|
-
span,
|
|
1107
|
-
fix
|
|
1108
|
-
};
|
|
1109
|
-
}
|
|
1110
|
-
function levelToSeverity(level) {
|
|
1111
|
-
if (level === "off") {
|
|
1112
|
-
return null;
|
|
1113
|
-
}
|
|
1114
|
-
if (level === "error") {
|
|
1115
|
-
return "error";
|
|
1116
|
-
}
|
|
1117
|
-
return "warning";
|
|
1118
|
-
}
|
|
1119
|
-
|
|
1120
|
-
// src/props.ts
|
|
1121
|
-
var IGNORED_IDENTIFIERS2 = /* @__PURE__ */ new Set([
|
|
1122
|
-
"null",
|
|
1123
|
-
"undefined",
|
|
1124
|
-
"true",
|
|
1125
|
-
"false",
|
|
1126
|
-
"NaN",
|
|
1127
|
-
"Infinity",
|
|
1128
|
-
"this",
|
|
1129
|
-
"props"
|
|
1130
|
-
]);
|
|
1131
|
-
var RESERVED_KEYWORDS2 = /* @__PURE__ */ new Set([
|
|
1132
|
-
"await",
|
|
1133
|
-
"break",
|
|
1134
|
-
"case",
|
|
1135
|
-
"catch",
|
|
1136
|
-
"class",
|
|
1137
|
-
"const",
|
|
1138
|
-
"continue",
|
|
1139
|
-
"debugger",
|
|
1140
|
-
"default",
|
|
1141
|
-
"delete",
|
|
1142
|
-
"do",
|
|
1143
|
-
"else",
|
|
1144
|
-
"enum",
|
|
1145
|
-
"export",
|
|
1146
|
-
"extends",
|
|
1147
|
-
"false",
|
|
1148
|
-
"finally",
|
|
1149
|
-
"for",
|
|
1150
|
-
"function",
|
|
1151
|
-
"if",
|
|
1152
|
-
"import",
|
|
1153
|
-
"in",
|
|
1154
|
-
"instanceof",
|
|
1155
|
-
"let",
|
|
1156
|
-
"new",
|
|
1157
|
-
"null",
|
|
1158
|
-
"return",
|
|
1159
|
-
"super",
|
|
1160
|
-
"switch",
|
|
1161
|
-
"this",
|
|
1162
|
-
"throw",
|
|
1163
|
-
"true",
|
|
1164
|
-
"try",
|
|
1165
|
-
"typeof",
|
|
1166
|
-
"var",
|
|
1167
|
-
"void",
|
|
1168
|
-
"while",
|
|
1169
|
-
"with",
|
|
1170
|
-
"yield"
|
|
1171
|
-
]);
|
|
1172
|
-
function enforceProps(root, propsConfig) {
|
|
1173
|
-
const diagnostics = [];
|
|
1174
|
-
const declaredProps = /* @__PURE__ */ new Map();
|
|
1175
|
-
const usedLocal = /* @__PURE__ */ new Map();
|
|
1176
|
-
const usedNamespace = /* @__PURE__ */ new Map();
|
|
1177
|
-
const usedAny = /* @__PURE__ */ new Set();
|
|
1178
|
-
const missingReported = /* @__PURE__ */ new Set();
|
|
1179
|
-
const localStyleReported = /* @__PURE__ */ new Set();
|
|
1180
|
-
const namespaceStyleReported = /* @__PURE__ */ new Set();
|
|
1181
|
-
if (root.props?.fields) {
|
|
1182
|
-
for (const field of root.props.fields) {
|
|
1183
|
-
declaredProps.set(field.name, field);
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
const preferStyle = propsConfig.preferAccessStyle;
|
|
1187
|
-
const flagLocalStyle = !propsConfig.allowDeclaredLocals || preferStyle === "namespace";
|
|
1188
|
-
const flagNamespaceStyle = !propsConfig.allowPropsNamespace || preferStyle === "locals";
|
|
1189
|
-
const walkNodes2 = (nodes, locals) => {
|
|
1190
|
-
for (const node of nodes) {
|
|
1191
|
-
if (node.type === "Conditional") {
|
|
1192
|
-
handleConditional(node, locals);
|
|
1193
|
-
continue;
|
|
1194
|
-
}
|
|
1195
|
-
if (node.type === "For") {
|
|
1196
|
-
handleFor(node, locals);
|
|
1197
|
-
continue;
|
|
1198
|
-
}
|
|
1199
|
-
if (node.type === "Expression") {
|
|
1200
|
-
handleExpression(node.value, node.span, locals);
|
|
1201
|
-
continue;
|
|
1202
|
-
}
|
|
1203
|
-
if (node.type === "JSXPassthrough") {
|
|
1204
|
-
handleExpression(node.expression, node.span, locals);
|
|
1205
|
-
continue;
|
|
1206
|
-
}
|
|
1207
|
-
if (node.type === "Text") {
|
|
1208
|
-
handleText(node.parts, locals);
|
|
1209
|
-
continue;
|
|
1210
|
-
}
|
|
1211
|
-
if (node.type === "Element") {
|
|
1212
|
-
handleElement(node, locals);
|
|
1213
|
-
continue;
|
|
1214
|
-
}
|
|
1215
|
-
if (node.type === "Component") {
|
|
1216
|
-
handleComponent(node, locals);
|
|
1217
|
-
continue;
|
|
1218
|
-
}
|
|
1219
|
-
}
|
|
1220
|
-
};
|
|
1221
|
-
const handleConditional = (node, locals) => {
|
|
1222
|
-
for (const branch of node.branches) {
|
|
1223
|
-
if (branch.test) {
|
|
1224
|
-
handleExpression(branch.test, branch.testSpan, locals);
|
|
1225
|
-
}
|
|
1226
|
-
walkNodes2(branch.body, locals);
|
|
1227
|
-
}
|
|
1228
|
-
};
|
|
1229
|
-
const handleFor = (node, locals) => {
|
|
1230
|
-
handleExpression(node.arrayExpr, node.arrayExprSpan, locals);
|
|
1231
|
-
const nextLocals = new Set(locals);
|
|
1232
|
-
nextLocals.add(node.itemName);
|
|
1233
|
-
walkNodes2(node.body, nextLocals);
|
|
1234
|
-
};
|
|
1235
|
-
const handleElement = (node, locals) => {
|
|
1236
|
-
if (node.guard) {
|
|
1237
|
-
handleExpression(node.guard, node.guardSpan, locals);
|
|
1238
|
-
}
|
|
1239
|
-
handleAttributes(node.attributes, locals);
|
|
1240
|
-
walkNodes2(node.children, locals);
|
|
1241
|
-
};
|
|
1242
|
-
const handleComponent = (node, locals) => {
|
|
1243
|
-
if (node.guard) {
|
|
1244
|
-
handleExpression(node.guard, node.guardSpan, locals);
|
|
1245
|
-
}
|
|
1246
|
-
handleAttributes(node.attributes, locals);
|
|
1247
|
-
if (node.slots) {
|
|
1248
|
-
for (const slot of node.slots) {
|
|
1249
|
-
walkNodes2(slot.children, locals);
|
|
1250
|
-
}
|
|
1251
|
-
}
|
|
1252
|
-
walkNodes2(node.children, locals);
|
|
1253
|
-
};
|
|
1254
|
-
const handleText = (parts, locals) => {
|
|
1255
|
-
for (const part of parts) {
|
|
1256
|
-
if (part.type === "expr") {
|
|
1257
|
-
handleExpression(part.value, part.span, locals);
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
};
|
|
1261
|
-
const handleAttributes = (attributes, locals) => {
|
|
1262
|
-
for (const attr of attributes) {
|
|
1263
|
-
if (!attr.value) continue;
|
|
1264
|
-
const trimmed = attr.value.trim();
|
|
1265
|
-
if (!trimmed || trimmed.startsWith("'") || trimmed.startsWith('"')) {
|
|
1266
|
-
continue;
|
|
1267
|
-
}
|
|
1268
|
-
handleExpression(trimmed, void 0, locals);
|
|
1269
|
-
}
|
|
1270
|
-
};
|
|
1271
|
-
const handleExpression = (expression, span, locals) => {
|
|
1272
|
-
const occurrences = scanExpression(expression);
|
|
1273
|
-
for (const occurrence of occurrences) {
|
|
1274
|
-
const name = occurrence.name;
|
|
1275
|
-
if (occurrence.kind === "local" && locals.has(name)) {
|
|
1276
|
-
continue;
|
|
1277
|
-
}
|
|
1278
|
-
if (shouldIgnoreIdentifier2(name)) {
|
|
1279
|
-
continue;
|
|
1280
|
-
}
|
|
1281
|
-
const usageSpan = span ? offsetSpan(span, occurrence.index, occurrence.length) : void 0;
|
|
1282
|
-
if (occurrence.kind === "namespace") {
|
|
1283
|
-
recordUsage(usedNamespace, name, usageSpan);
|
|
1284
|
-
usedAny.add(name);
|
|
1285
|
-
if (propsConfig.requireDeclarationForLocals && !declaredProps.has(name) && !missingReported.has(name)) {
|
|
1286
|
-
const severity = levelToSeverity2(propsConfig.diagnostics.missingDeclaration);
|
|
1287
|
-
if (severity) {
|
|
1288
|
-
diagnostics.push(createMissingDeclarationDiagnostic(name, severity, usageSpan));
|
|
1289
|
-
missingReported.add(name);
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1292
|
-
if (flagNamespaceStyle && !namespaceStyleReported.has(name)) {
|
|
1293
|
-
const severity = levelToSeverity2(propsConfig.diagnostics.style);
|
|
1294
|
-
if (severity) {
|
|
1295
|
-
diagnostics.push(
|
|
1296
|
-
createStyleDiagnostic(
|
|
1297
|
-
name,
|
|
1298
|
-
"namespace",
|
|
1299
|
-
severity,
|
|
1300
|
-
usageSpan,
|
|
1301
|
-
propsConfig.allowPropsNamespace
|
|
1302
|
-
)
|
|
1303
|
-
);
|
|
1304
|
-
namespaceStyleReported.add(name);
|
|
1305
|
-
}
|
|
1306
|
-
}
|
|
1307
|
-
continue;
|
|
1308
|
-
}
|
|
1309
|
-
recordUsage(usedLocal, name, usageSpan);
|
|
1310
|
-
usedAny.add(name);
|
|
1311
|
-
if (propsConfig.requireDeclarationForLocals && !declaredProps.has(name) && !missingReported.has(name)) {
|
|
1312
|
-
const severity = levelToSeverity2(propsConfig.diagnostics.missingDeclaration);
|
|
1313
|
-
if (severity) {
|
|
1314
|
-
diagnostics.push(createMissingDeclarationDiagnostic(name, severity, usageSpan));
|
|
1315
|
-
missingReported.add(name);
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
if (flagLocalStyle && !localStyleReported.has(name)) {
|
|
1319
|
-
const severity = levelToSeverity2(propsConfig.diagnostics.style);
|
|
1320
|
-
if (severity) {
|
|
1321
|
-
diagnostics.push(
|
|
1322
|
-
createStyleDiagnostic(name, "local", severity, usageSpan, propsConfig.allowDeclaredLocals)
|
|
1323
|
-
);
|
|
1324
|
-
localStyleReported.add(name);
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
};
|
|
1329
|
-
walkNodes2(root.children, /* @__PURE__ */ new Set());
|
|
1330
|
-
if (root.props?.fields) {
|
|
1331
|
-
for (const field of root.props.fields) {
|
|
1332
|
-
if (!usedAny.has(field.name)) {
|
|
1333
|
-
const severity = levelToSeverity2(propsConfig.diagnostics.unusedDeclaration);
|
|
1334
|
-
if (severity) {
|
|
1335
|
-
diagnostics.push({
|
|
1336
|
-
severity,
|
|
1337
|
-
code: "props.unusedDeclaration",
|
|
1338
|
-
message: `Prop "${field.name}" is declared but never used.`,
|
|
1339
|
-
span: field.span
|
|
1340
|
-
});
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
if (propsConfig.requirePropsBlockWhen.enabled && !root.props && usedAny.size >= propsConfig.requirePropsBlockWhen.minUniquePropsUsed) {
|
|
1346
|
-
const severity = levelToSeverity2(propsConfig.requirePropsBlockWhen.severity);
|
|
1347
|
-
if (severity) {
|
|
1348
|
-
diagnostics.push({
|
|
1349
|
-
severity,
|
|
1350
|
-
code: "props.block.recommendedOrRequired",
|
|
1351
|
-
message: `Props block recommended: ${usedAny.size} unique prop${usedAny.size === 1 ? "" : "s"} used.`
|
|
1352
|
-
});
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
return diagnostics;
|
|
1356
|
-
}
|
|
1357
|
-
function createMissingDeclarationDiagnostic(name, severity, span) {
|
|
1358
|
-
return {
|
|
1359
|
-
severity,
|
|
1360
|
-
code: "props.missingDeclaration",
|
|
1361
|
-
message: `Prop \`${name}\` is used but not declared in \`#props\`.`,
|
|
1362
|
-
span,
|
|
1363
|
-
data: {
|
|
1364
|
-
kind: "addPropDeclaration",
|
|
1365
|
-
propName: name
|
|
1366
|
-
}
|
|
1367
|
-
};
|
|
1368
|
-
}
|
|
1369
|
-
function createStyleDiagnostic(name, kind, severity, span, allowed) {
|
|
1370
|
-
if (kind === "namespace") {
|
|
1371
|
-
const message2 = allowed ? `props.${name} is allowed but not preferred; use "${name}" instead.` : `props.${name} is disabled; use "${name}" instead.`;
|
|
1372
|
-
return {
|
|
1373
|
-
severity,
|
|
1374
|
-
code: "props.style.nonPreferred",
|
|
1375
|
-
message: message2,
|
|
1376
|
-
span
|
|
1377
|
-
};
|
|
1378
|
-
}
|
|
1379
|
-
const message = allowed ? `"${name}" is allowed but not preferred; use props.${name} instead.` : `"${name}" is disabled; use props.${name} instead.`;
|
|
1380
|
-
return {
|
|
1381
|
-
severity,
|
|
1382
|
-
code: "props.style.nonPreferred",
|
|
1383
|
-
message,
|
|
1384
|
-
span
|
|
1385
|
-
};
|
|
1386
|
-
}
|
|
1387
|
-
function recordUsage(map, name, span) {
|
|
1388
|
-
const existing = map.get(name);
|
|
1389
|
-
if (existing) {
|
|
1390
|
-
existing.count += 1;
|
|
1391
|
-
return;
|
|
1392
|
-
}
|
|
1393
|
-
map.set(name, { count: 1, span });
|
|
1394
|
-
}
|
|
1395
|
-
function scanExpression(expression) {
|
|
1396
|
-
const occurrences = [];
|
|
1397
|
-
let i = 0;
|
|
1398
|
-
let state = "code";
|
|
1399
|
-
while (i < expression.length) {
|
|
1400
|
-
const ch = expression[i];
|
|
1401
|
-
if (state === "code") {
|
|
1402
|
-
if (ch === "'" || ch === '"') {
|
|
1403
|
-
state = ch === "'" ? "single" : "double";
|
|
1404
|
-
i++;
|
|
1405
|
-
continue;
|
|
1406
|
-
}
|
|
1407
|
-
if (ch === "`") {
|
|
1408
|
-
state = "template";
|
|
1409
|
-
i++;
|
|
1410
|
-
continue;
|
|
1411
|
-
}
|
|
1412
|
-
if (ch === "/" && expression[i + 1] === "/") {
|
|
1413
|
-
state = "line";
|
|
1414
|
-
i += 2;
|
|
1415
|
-
continue;
|
|
1416
|
-
}
|
|
1417
|
-
if (ch === "/" && expression[i + 1] === "*") {
|
|
1418
|
-
state = "block";
|
|
1419
|
-
i += 2;
|
|
1420
|
-
continue;
|
|
1421
|
-
}
|
|
1422
|
-
if (isIdentifierStart2(ch)) {
|
|
1423
|
-
const start = i;
|
|
1424
|
-
i++;
|
|
1425
|
-
while (i < expression.length && isIdentifierPart2(expression[i])) {
|
|
1426
|
-
i++;
|
|
1427
|
-
}
|
|
1428
|
-
const name = expression.slice(start, i);
|
|
1429
|
-
const prevNonSpace = findPreviousNonSpace2(expression, start - 1);
|
|
1430
|
-
if (name === "props" && prevNonSpace !== ".") {
|
|
1431
|
-
const namespace = readNamespaceAccess(expression, i);
|
|
1432
|
-
if (namespace) {
|
|
1433
|
-
occurrences.push({
|
|
1434
|
-
name: namespace.name,
|
|
1435
|
-
kind: "namespace",
|
|
1436
|
-
index: namespace.index,
|
|
1437
|
-
length: namespace.name.length
|
|
1438
|
-
});
|
|
1439
|
-
i = namespace.endIndex;
|
|
1440
|
-
continue;
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
if (prevNonSpace !== ".") {
|
|
1444
|
-
occurrences.push({ name, kind: "local", index: start, length: name.length });
|
|
1445
|
-
}
|
|
1446
|
-
continue;
|
|
1447
|
-
}
|
|
1448
|
-
i++;
|
|
1449
|
-
continue;
|
|
1450
|
-
}
|
|
1451
|
-
if (state === "line") {
|
|
1452
|
-
if (ch === "\n") {
|
|
1453
|
-
state = "code";
|
|
1454
|
-
}
|
|
1455
|
-
i++;
|
|
1456
|
-
continue;
|
|
1457
|
-
}
|
|
1458
|
-
if (state === "block") {
|
|
1459
|
-
if (ch === "*" && expression[i + 1] === "/") {
|
|
1460
|
-
state = "code";
|
|
1461
|
-
i += 2;
|
|
1462
|
-
continue;
|
|
1463
|
-
}
|
|
1464
|
-
i++;
|
|
1465
|
-
continue;
|
|
1466
|
-
}
|
|
1467
|
-
if (state === "single") {
|
|
1468
|
-
if (ch === "\\") {
|
|
1469
|
-
i += 2;
|
|
1470
|
-
continue;
|
|
1471
|
-
}
|
|
1472
|
-
if (ch === "'") {
|
|
1473
|
-
state = "code";
|
|
1474
|
-
}
|
|
1475
|
-
i++;
|
|
1476
|
-
continue;
|
|
1477
|
-
}
|
|
1478
|
-
if (state === "double") {
|
|
1479
|
-
if (ch === "\\") {
|
|
1480
|
-
i += 2;
|
|
1481
|
-
continue;
|
|
1482
|
-
}
|
|
1483
|
-
if (ch === '"') {
|
|
1484
|
-
state = "code";
|
|
1485
|
-
}
|
|
1486
|
-
i++;
|
|
1487
|
-
continue;
|
|
1488
|
-
}
|
|
1489
|
-
if (state === "template") {
|
|
1490
|
-
if (ch === "\\") {
|
|
1491
|
-
i += 2;
|
|
1492
|
-
continue;
|
|
1493
|
-
}
|
|
1494
|
-
if (ch === "`") {
|
|
1495
|
-
state = "code";
|
|
1496
|
-
i++;
|
|
1497
|
-
continue;
|
|
1498
|
-
}
|
|
1499
|
-
i++;
|
|
1500
|
-
continue;
|
|
1501
|
-
}
|
|
1233
|
+
return quote;
|
|
1234
|
+
}
|
|
1235
|
+
return char;
|
|
1502
1236
|
}
|
|
1503
|
-
return occurrences;
|
|
1504
1237
|
}
|
|
1505
|
-
function
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
}
|
|
1510
|
-
if (expression[i] === "?") {
|
|
1511
|
-
if (expression[i + 1] !== ".") {
|
|
1512
|
-
return null;
|
|
1513
|
-
}
|
|
1514
|
-
i += 2;
|
|
1515
|
-
} else if (expression[i] === ".") {
|
|
1516
|
-
i++;
|
|
1517
|
-
} else {
|
|
1518
|
-
return null;
|
|
1519
|
-
}
|
|
1520
|
-
while (i < expression.length && /\s/.test(expression[i])) {
|
|
1521
|
-
i++;
|
|
1522
|
-
}
|
|
1523
|
-
if (!isIdentifierStart2(expression[i])) {
|
|
1524
|
-
return null;
|
|
1238
|
+
function buildClassAliasEnvironment2(decl) {
|
|
1239
|
+
const env = /* @__PURE__ */ new Map();
|
|
1240
|
+
if (!decl) {
|
|
1241
|
+
return env;
|
|
1525
1242
|
}
|
|
1526
|
-
const
|
|
1527
|
-
|
|
1528
|
-
while (i < expression.length && isIdentifierPart2(expression[i])) {
|
|
1529
|
-
i++;
|
|
1243
|
+
for (const alias of decl.aliases) {
|
|
1244
|
+
env.set(alias.name, alias.classes);
|
|
1530
1245
|
}
|
|
1531
|
-
return
|
|
1532
|
-
name: expression.slice(propStart, i),
|
|
1533
|
-
index: propStart,
|
|
1534
|
-
endIndex: i
|
|
1535
|
-
};
|
|
1246
|
+
return env;
|
|
1536
1247
|
}
|
|
1537
|
-
function
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1248
|
+
function expandClasses2(classes, aliasEnv) {
|
|
1249
|
+
const result = [];
|
|
1250
|
+
for (const cls of classes) {
|
|
1251
|
+
const match = cls.match(/^\$([A-Za-z_][A-Za-z0-9_]*)$/);
|
|
1252
|
+
if (!match) {
|
|
1253
|
+
result.push(cls);
|
|
1254
|
+
continue;
|
|
1542
1255
|
}
|
|
1256
|
+
const aliasClasses = aliasEnv.get(match[1]);
|
|
1257
|
+
if (!aliasClasses) {
|
|
1258
|
+
continue;
|
|
1259
|
+
}
|
|
1260
|
+
result.push(...aliasClasses);
|
|
1543
1261
|
}
|
|
1544
|
-
return
|
|
1545
|
-
}
|
|
1546
|
-
function isIdentifierStart2(ch) {
|
|
1547
|
-
return /[A-Za-z_$]/.test(ch);
|
|
1548
|
-
}
|
|
1549
|
-
function isIdentifierPart2(ch) {
|
|
1550
|
-
return /[A-Za-z0-9_$]/.test(ch);
|
|
1262
|
+
return result;
|
|
1551
1263
|
}
|
|
1552
|
-
function
|
|
1553
|
-
return
|
|
1264
|
+
function escapeStaticText(value) {
|
|
1265
|
+
return value.replace(/[&<>{}]/g, (char) => {
|
|
1266
|
+
switch (char) {
|
|
1267
|
+
case "&":
|
|
1268
|
+
return "&";
|
|
1269
|
+
case "<":
|
|
1270
|
+
return "<";
|
|
1271
|
+
case ">":
|
|
1272
|
+
return ">";
|
|
1273
|
+
case "{":
|
|
1274
|
+
return "{";
|
|
1275
|
+
case "}":
|
|
1276
|
+
return "}";
|
|
1277
|
+
default:
|
|
1278
|
+
return char;
|
|
1279
|
+
}
|
|
1280
|
+
});
|
|
1554
1281
|
}
|
|
1555
|
-
function
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1282
|
+
function escapeAttributeValue(value) {
|
|
1283
|
+
return value.replace(/["&<>]/g, (char) => {
|
|
1284
|
+
switch (char) {
|
|
1285
|
+
case "&":
|
|
1286
|
+
return "&";
|
|
1287
|
+
case "<":
|
|
1288
|
+
return "<";
|
|
1289
|
+
case ">":
|
|
1290
|
+
return ">";
|
|
1291
|
+
case '"':
|
|
1292
|
+
return """;
|
|
1293
|
+
default:
|
|
1294
|
+
return char;
|
|
1295
|
+
}
|
|
1296
|
+
});
|
|
1563
1297
|
}
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1298
|
+
|
|
1299
|
+
// src/diagnostics.ts
|
|
1300
|
+
function createSpan(line, col, length, lineOffset) {
|
|
1301
|
+
const startOffset = lineOffset + col - 1;
|
|
1567
1302
|
return {
|
|
1568
|
-
start: {
|
|
1569
|
-
|
|
1570
|
-
col: startCol,
|
|
1571
|
-
offset: startOffset
|
|
1572
|
-
},
|
|
1573
|
-
end: {
|
|
1574
|
-
line: base.start.line,
|
|
1575
|
-
col: startCol + length,
|
|
1576
|
-
offset: startOffset + length
|
|
1577
|
-
}
|
|
1303
|
+
start: { line, col, offset: startOffset },
|
|
1304
|
+
end: { line, col: col + length, offset: startOffset + length }
|
|
1578
1305
|
};
|
|
1579
1306
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
}
|
|
1307
|
+
|
|
1308
|
+
// src/dialect.ts
|
|
1309
|
+
function enforceDialect(root, config) {
|
|
1584
1310
|
const diagnostics = [];
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
message: `Identifier "${name}" is used without "props." but is not declared in #props. Declare "${name}" in #props or use "props.${name}".`
|
|
1593
|
-
});
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1596
|
-
for (const [name, decl] of declaredProps) {
|
|
1597
|
-
const usedAsBare = allUsage.usedBareAliases.has(name);
|
|
1598
|
-
const usedAsProps = allUsage.usedPropsDot.has(name);
|
|
1599
|
-
if (!usedAsBare && !usedAsProps) {
|
|
1600
|
-
diagnostics.push({
|
|
1601
|
-
severity: "warning",
|
|
1602
|
-
code: "props.unusedDeclaration",
|
|
1603
|
-
message: `Prop "${name}" is declared in #props but never used in this template.`,
|
|
1604
|
-
span: decl.span
|
|
1605
|
-
});
|
|
1606
|
-
}
|
|
1607
|
-
}
|
|
1608
|
-
for (const name of allUsage.usedPropsDot) {
|
|
1609
|
-
if (declaredProps.has(name)) {
|
|
1610
|
-
diagnostics.push({
|
|
1611
|
-
severity: "warning",
|
|
1612
|
-
code: "props.style.nonPreferred",
|
|
1613
|
-
message: `"props.${name}" is unnecessary because "${name}" is declared in #props. Use "{${name}}" instead.`
|
|
1614
|
-
});
|
|
1615
|
-
}
|
|
1616
|
-
}
|
|
1617
|
-
for (const [name, decl] of declaredProps) {
|
|
1618
|
-
const isCallable = decl.kind === "callable";
|
|
1619
|
-
const usedAsCall = allUsage.callSitesBare.has(name);
|
|
1620
|
-
const usedAsValue = allUsage.usedBareAliases.has(name) && !usedAsCall;
|
|
1621
|
-
if (isCallable && usedAsValue) {
|
|
1622
|
-
diagnostics.push({
|
|
1623
|
-
severity: "warning",
|
|
1624
|
-
code: "props.style.nonPreferred",
|
|
1625
|
-
message: `"${name}" is declared as callable in #props (${name}()) but used as a value.`
|
|
1626
|
-
});
|
|
1627
|
-
} else if (!isCallable && usedAsCall) {
|
|
1628
|
-
diagnostics.push({
|
|
1629
|
-
severity: "warning",
|
|
1630
|
-
code: "props.style.nonPreferred",
|
|
1631
|
-
message: `"${name}" is declared as a value in #props but used as a function call.`
|
|
1632
|
-
});
|
|
1633
|
-
}
|
|
1311
|
+
if (root.idToken) {
|
|
1312
|
+
diagnostics.push(
|
|
1313
|
+
...evaluateToken(
|
|
1314
|
+
{ kind: "id", token: root.idToken, span: root.idTokenSpan },
|
|
1315
|
+
config.tokens.id
|
|
1316
|
+
)
|
|
1317
|
+
);
|
|
1634
1318
|
}
|
|
1319
|
+
walkNodes(root.children, (occurrence) => {
|
|
1320
|
+
const rule = config.tokens[occurrence.kind];
|
|
1321
|
+
diagnostics.push(...evaluateToken(occurrence, rule));
|
|
1322
|
+
});
|
|
1635
1323
|
return diagnostics;
|
|
1636
1324
|
}
|
|
1637
|
-
function
|
|
1638
|
-
const
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
callSitesPropsDot: /* @__PURE__ */ new Set()
|
|
1644
|
-
};
|
|
1645
|
-
const env = createTemplateEnv(root.propsDecls);
|
|
1646
|
-
function mergeResult(result) {
|
|
1647
|
-
for (const name of result.usedBare) {
|
|
1648
|
-
usage.usedBare.add(name);
|
|
1649
|
-
}
|
|
1650
|
-
for (const name of result.rewrittenAliases) {
|
|
1651
|
-
usage.usedBareAliases.add(name);
|
|
1652
|
-
}
|
|
1653
|
-
for (const name of result.usedPropsDot) {
|
|
1654
|
-
usage.usedPropsDot.add(name);
|
|
1325
|
+
function walkNodes(nodes, onToken) {
|
|
1326
|
+
for (const node of nodes) {
|
|
1327
|
+
if (node.type === "For") {
|
|
1328
|
+
onFor(node, onToken);
|
|
1329
|
+
walkNodes(node.body, onToken);
|
|
1330
|
+
continue;
|
|
1655
1331
|
}
|
|
1656
|
-
|
|
1657
|
-
|
|
1332
|
+
if (node.type === "Conditional") {
|
|
1333
|
+
onConditional(node, onToken);
|
|
1334
|
+
continue;
|
|
1658
1335
|
}
|
|
1659
|
-
|
|
1660
|
-
|
|
1336
|
+
if (node.type === "Element" || node.type === "Component") {
|
|
1337
|
+
walkNodes(node.children, onToken);
|
|
1338
|
+
if (node.type === "Component" && node.slots) {
|
|
1339
|
+
for (const slot of node.slots) {
|
|
1340
|
+
walkNodes(slot.children, onToken);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
continue;
|
|
1661
1344
|
}
|
|
1662
1345
|
}
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1346
|
+
}
|
|
1347
|
+
function onFor(node, onToken) {
|
|
1348
|
+
if (!node.token) {
|
|
1349
|
+
return;
|
|
1667
1350
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1351
|
+
onToken({ kind: "for", token: node.token, span: node.tokenSpan });
|
|
1352
|
+
}
|
|
1353
|
+
function onConditional(node, onToken) {
|
|
1354
|
+
for (const branch of node.branches) {
|
|
1355
|
+
onBranch(branch, onToken);
|
|
1356
|
+
walkNodes(branch.body, onToken);
|
|
1672
1357
|
}
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
if (part.type === "expr") {
|
|
1678
|
-
analyzeExpression(part.value);
|
|
1679
|
-
}
|
|
1680
|
-
}
|
|
1681
|
-
break;
|
|
1682
|
-
case "Expression":
|
|
1683
|
-
analyzeExpression(node.value);
|
|
1684
|
-
break;
|
|
1685
|
-
case "JSXPassthrough":
|
|
1686
|
-
analyzeJsxExpression(node.expression);
|
|
1687
|
-
break;
|
|
1688
|
-
case "Element":
|
|
1689
|
-
if (node.guard) {
|
|
1690
|
-
analyzeExpression(node.guard);
|
|
1691
|
-
}
|
|
1692
|
-
for (const attr of node.attributes) {
|
|
1693
|
-
if (attr.value) {
|
|
1694
|
-
analyzeAttributeValue(attr.value);
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
for (const child of node.children) {
|
|
1698
|
-
walkNode(child);
|
|
1699
|
-
}
|
|
1700
|
-
break;
|
|
1701
|
-
case "Component":
|
|
1702
|
-
if (node.guard) {
|
|
1703
|
-
analyzeExpression(node.guard);
|
|
1704
|
-
}
|
|
1705
|
-
for (const attr of node.attributes) {
|
|
1706
|
-
if (attr.value) {
|
|
1707
|
-
analyzeAttributeValue(attr.value);
|
|
1708
|
-
}
|
|
1709
|
-
}
|
|
1710
|
-
if (node.slots) {
|
|
1711
|
-
for (const slot of node.slots) {
|
|
1712
|
-
for (const child of slot.children) {
|
|
1713
|
-
walkNode(child);
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
}
|
|
1717
|
-
for (const child of node.children) {
|
|
1718
|
-
walkNode(child);
|
|
1719
|
-
}
|
|
1720
|
-
break;
|
|
1721
|
-
case "Conditional":
|
|
1722
|
-
for (const branch of node.branches) {
|
|
1723
|
-
if (branch.test) {
|
|
1724
|
-
analyzeExpression(branch.test);
|
|
1725
|
-
}
|
|
1726
|
-
for (const child of branch.body) {
|
|
1727
|
-
walkNode(child);
|
|
1728
|
-
}
|
|
1729
|
-
}
|
|
1730
|
-
break;
|
|
1731
|
-
case "For":
|
|
1732
|
-
analyzeExpression(node.arrayExpr);
|
|
1733
|
-
for (const child of node.body) {
|
|
1734
|
-
walkNode(child);
|
|
1735
|
-
}
|
|
1736
|
-
break;
|
|
1737
|
-
}
|
|
1358
|
+
}
|
|
1359
|
+
function onBranch(branch, onToken) {
|
|
1360
|
+
if (!branch.token || !branch.kind) {
|
|
1361
|
+
return;
|
|
1738
1362
|
}
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1363
|
+
onToken({ kind: branch.kind, token: branch.token, span: branch.tokenSpan });
|
|
1364
|
+
}
|
|
1365
|
+
function evaluateToken(occurrence, rule) {
|
|
1366
|
+
const diagnostics = [];
|
|
1367
|
+
const used = occurrence.token;
|
|
1368
|
+
const preferred = rule.preferred;
|
|
1369
|
+
const isAllowed = rule.allow.includes(used);
|
|
1370
|
+
if (!isAllowed) {
|
|
1371
|
+
const severity = levelToSeverity(rule.onDisallowed);
|
|
1372
|
+
if (severity) {
|
|
1373
|
+
diagnostics.push(
|
|
1374
|
+
createDialectDiagnostic(
|
|
1375
|
+
"dialect.token.disallowed",
|
|
1376
|
+
severity,
|
|
1377
|
+
used,
|
|
1378
|
+
preferred,
|
|
1379
|
+
occurrence.span,
|
|
1380
|
+
`Token "${used}" is not allowed for ${occurrence.kind}. Preferred: "${preferred}".`
|
|
1381
|
+
)
|
|
1382
|
+
);
|
|
1749
1383
|
}
|
|
1384
|
+
return diagnostics;
|
|
1750
1385
|
}
|
|
1751
|
-
|
|
1752
|
-
|
|
1386
|
+
if (used !== preferred) {
|
|
1387
|
+
const severity = levelToSeverity(rule.onDisallowed);
|
|
1388
|
+
if (severity) {
|
|
1389
|
+
diagnostics.push(
|
|
1390
|
+
createDialectDiagnostic(
|
|
1391
|
+
"dialect.token.nonPreferred",
|
|
1392
|
+
severity,
|
|
1393
|
+
used,
|
|
1394
|
+
preferred,
|
|
1395
|
+
occurrence.span,
|
|
1396
|
+
`Token "${used}" is allowed but not preferred for ${occurrence.kind}. Preferred: "${preferred}".`
|
|
1397
|
+
)
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1753
1400
|
}
|
|
1754
|
-
return
|
|
1401
|
+
return diagnostics;
|
|
1402
|
+
}
|
|
1403
|
+
function createDialectDiagnostic(code, severity, used, preferred, span, message) {
|
|
1404
|
+
const fix = span ? {
|
|
1405
|
+
range: span,
|
|
1406
|
+
replacementText: preferred
|
|
1407
|
+
} : void 0;
|
|
1408
|
+
return {
|
|
1409
|
+
severity,
|
|
1410
|
+
code,
|
|
1411
|
+
message: message.replace(/\\s+/g, " "),
|
|
1412
|
+
span,
|
|
1413
|
+
fix
|
|
1414
|
+
};
|
|
1755
1415
|
}
|
|
1756
|
-
function
|
|
1757
|
-
|
|
1416
|
+
function levelToSeverity(level) {
|
|
1417
|
+
if (level === "off") {
|
|
1418
|
+
return null;
|
|
1419
|
+
}
|
|
1420
|
+
if (level === "error") {
|
|
1421
|
+
return "error";
|
|
1422
|
+
}
|
|
1423
|
+
return "warning";
|
|
1758
1424
|
}
|
|
1759
1425
|
|
|
1760
1426
|
// src/parser.ts
|
|
@@ -1934,7 +1600,7 @@ function parseTemplateBlock(lines, lineOffsets, startIndex, endIndex, options) {
|
|
|
1934
1600
|
const diagnostics = [];
|
|
1935
1601
|
const root = { type: "Root", children: [] };
|
|
1936
1602
|
const stack = [{ node: root, level: -1 }];
|
|
1937
|
-
let
|
|
1603
|
+
let inputsBlockLevel = null;
|
|
1938
1604
|
let classesBlockLevel = null;
|
|
1939
1605
|
let sawTopLevelTemplateNode = false;
|
|
1940
1606
|
const conditionalChains = /* @__PURE__ */ new Map();
|
|
@@ -1976,19 +1642,19 @@ function parseTemplateBlock(lines, lineOffsets, startIndex, endIndex, options) {
|
|
|
1976
1642
|
continue;
|
|
1977
1643
|
}
|
|
1978
1644
|
let level = indent / 2;
|
|
1979
|
-
if (
|
|
1980
|
-
|
|
1645
|
+
if (inputsBlockLevel !== null && level <= inputsBlockLevel) {
|
|
1646
|
+
inputsBlockLevel = null;
|
|
1981
1647
|
}
|
|
1982
1648
|
if (classesBlockLevel !== null && level <= classesBlockLevel) {
|
|
1983
1649
|
classesBlockLevel = null;
|
|
1984
1650
|
}
|
|
1985
|
-
const
|
|
1651
|
+
const isInInputsBlock = inputsBlockLevel !== null && level > inputsBlockLevel;
|
|
1986
1652
|
const isInClassesBlock = classesBlockLevel !== null && level > classesBlockLevel;
|
|
1987
1653
|
while (stack.length > 1 && stack[stack.length - 1].level >= level) {
|
|
1988
1654
|
stack.pop();
|
|
1989
1655
|
}
|
|
1990
1656
|
const parentLevel = stack[stack.length - 1].level;
|
|
1991
|
-
if (level > parentLevel + 1 && !
|
|
1657
|
+
if (level > parentLevel + 1 && !isInInputsBlock && !isInClassesBlock) {
|
|
1992
1658
|
pushDiag(
|
|
1993
1659
|
diagnostics,
|
|
1994
1660
|
"COLLIE003",
|
|
@@ -2034,48 +1700,35 @@ function parseTemplateBlock(lines, lineOffsets, startIndex, endIndex, options) {
|
|
|
2034
1700
|
}
|
|
2035
1701
|
continue;
|
|
2036
1702
|
}
|
|
2037
|
-
if (trimmed === "
|
|
2038
|
-
pushDiag(
|
|
2039
|
-
diagnostics,
|
|
2040
|
-
"COLLIE103",
|
|
2041
|
-
"`props` must be declared using `#props`.",
|
|
2042
|
-
lineNumber,
|
|
2043
|
-
indent + 1,
|
|
2044
|
-
lineOffset,
|
|
2045
|
-
trimmed.length
|
|
2046
|
-
);
|
|
2047
|
-
if (level === 0) {
|
|
2048
|
-
propsBlockLevel = level;
|
|
2049
|
-
}
|
|
2050
|
-
continue;
|
|
2051
|
-
}
|
|
2052
|
-
if (trimmed === "#props") {
|
|
1703
|
+
if (trimmed === "#inputs") {
|
|
2053
1704
|
if (level !== 0) {
|
|
2054
1705
|
pushDiag(
|
|
2055
1706
|
diagnostics,
|
|
2056
1707
|
"COLLIE102",
|
|
2057
|
-
"#
|
|
1708
|
+
"#inputs block must be at the top level.",
|
|
2058
1709
|
lineNumber,
|
|
2059
1710
|
indent + 1,
|
|
2060
1711
|
lineOffset,
|
|
2061
1712
|
trimmed.length
|
|
2062
1713
|
);
|
|
2063
|
-
} else if (root.
|
|
1714
|
+
} else if (root.inputs) {
|
|
2064
1715
|
pushDiag(
|
|
2065
1716
|
diagnostics,
|
|
2066
1717
|
"COLLIE101",
|
|
2067
|
-
"Only one #
|
|
1718
|
+
"Only one #inputs block is allowed per #id.",
|
|
2068
1719
|
lineNumber,
|
|
2069
1720
|
indent + 1,
|
|
2070
1721
|
lineOffset,
|
|
2071
1722
|
trimmed.length
|
|
2072
1723
|
);
|
|
2073
1724
|
} else {
|
|
2074
|
-
root.
|
|
2075
|
-
root.propsDecls = [];
|
|
1725
|
+
root.inputs = { fields: [] };
|
|
2076
1726
|
}
|
|
2077
1727
|
if (level === 0) {
|
|
2078
|
-
|
|
1728
|
+
if (!root.inputsDecls) {
|
|
1729
|
+
root.inputsDecls = [];
|
|
1730
|
+
}
|
|
1731
|
+
inputsBlockLevel = level;
|
|
2079
1732
|
}
|
|
2080
1733
|
continue;
|
|
2081
1734
|
}
|
|
@@ -2115,33 +1768,33 @@ function parseTemplateBlock(lines, lineOffsets, startIndex, endIndex, options) {
|
|
|
2115
1768
|
}
|
|
2116
1769
|
continue;
|
|
2117
1770
|
}
|
|
2118
|
-
if (
|
|
2119
|
-
if (level !==
|
|
1771
|
+
if (inputsBlockLevel !== null && level > inputsBlockLevel) {
|
|
1772
|
+
if (level !== inputsBlockLevel + 1) {
|
|
2120
1773
|
pushDiag(
|
|
2121
1774
|
diagnostics,
|
|
2122
1775
|
"COLLIE102",
|
|
2123
|
-
"#
|
|
1776
|
+
"#inputs lines must be indented two spaces under the #inputs header.",
|
|
2124
1777
|
lineNumber,
|
|
2125
1778
|
indent + 1,
|
|
2126
1779
|
lineOffset
|
|
2127
1780
|
);
|
|
2128
1781
|
continue;
|
|
2129
1782
|
}
|
|
2130
|
-
const decl =
|
|
2131
|
-
if (decl && root.
|
|
2132
|
-
const existing = root.
|
|
1783
|
+
const decl = parseInputDecl(lineContent, lineNumber, indent + 1, lineOffset, diagnostics);
|
|
1784
|
+
if (decl && root.inputsDecls) {
|
|
1785
|
+
const existing = root.inputsDecls.find((d) => d.name === decl.name);
|
|
2133
1786
|
if (existing) {
|
|
2134
1787
|
pushDiag(
|
|
2135
1788
|
diagnostics,
|
|
2136
1789
|
"COLLIE106",
|
|
2137
|
-
`Duplicate
|
|
1790
|
+
`Duplicate input declaration "${decl.name}".`,
|
|
2138
1791
|
lineNumber,
|
|
2139
1792
|
indent + 1,
|
|
2140
1793
|
lineOffset,
|
|
2141
1794
|
trimmed.length
|
|
2142
1795
|
);
|
|
2143
1796
|
} else {
|
|
2144
|
-
root.
|
|
1797
|
+
root.inputsDecls.push(decl);
|
|
2145
1798
|
}
|
|
2146
1799
|
}
|
|
2147
1800
|
continue;
|
|
@@ -2554,9 +2207,7 @@ function parseTemplateBlock(lines, lineOffsets, startIndex, endIndex, options) {
|
|
|
2554
2207
|
}
|
|
2555
2208
|
if (options.dialect) {
|
|
2556
2209
|
diagnostics.push(...enforceDialect(root, options.dialect));
|
|
2557
|
-
diagnostics.push(...enforceProps(root, options.dialect.props));
|
|
2558
2210
|
}
|
|
2559
|
-
diagnostics.push(...enforcePropAliases(root));
|
|
2560
2211
|
return { root, diagnostics };
|
|
2561
2212
|
}
|
|
2562
2213
|
function buildLineOffsets(lines) {
|
|
@@ -3716,13 +3367,13 @@ function parseAndAddAttribute(attrStr, attributes, diagnostics, lineNumber, colu
|
|
|
3716
3367
|
}
|
|
3717
3368
|
}
|
|
3718
3369
|
}
|
|
3719
|
-
function
|
|
3370
|
+
function parseInputDecl(line, lineNumber, column, lineOffset, diagnostics) {
|
|
3720
3371
|
const trimmed = line.trim();
|
|
3721
3372
|
if (trimmed.includes(":") || trimmed.includes("<") || trimmed.includes("?")) {
|
|
3722
3373
|
pushDiag(
|
|
3723
3374
|
diagnostics,
|
|
3724
3375
|
"COLLIE104",
|
|
3725
|
-
'Types are not supported in #
|
|
3376
|
+
'Types are not supported in #inputs yet. Use "name".',
|
|
3726
3377
|
lineNumber,
|
|
3727
3378
|
column,
|
|
3728
3379
|
lineOffset,
|
|
@@ -3730,17 +3381,6 @@ function parsePropDecl(line, lineNumber, column, lineOffset, diagnostics) {
|
|
|
3730
3381
|
);
|
|
3731
3382
|
return null;
|
|
3732
3383
|
}
|
|
3733
|
-
const callableMatch = trimmed.match(/^([A-Za-z_$][A-Za-z0-9_$]*)\(\)$/);
|
|
3734
|
-
if (callableMatch) {
|
|
3735
|
-
const name = callableMatch[1];
|
|
3736
|
-
const nameStart = line.indexOf(name);
|
|
3737
|
-
const nameColumn = column + nameStart;
|
|
3738
|
-
return {
|
|
3739
|
-
name,
|
|
3740
|
-
kind: "callable",
|
|
3741
|
-
span: createSpan(lineNumber, nameColumn, name.length, lineOffset)
|
|
3742
|
-
};
|
|
3743
|
-
}
|
|
3744
3384
|
const valueMatch = trimmed.match(/^([A-Za-z_$][A-Za-z0-9_$]*)$/);
|
|
3745
3385
|
if (valueMatch) {
|
|
3746
3386
|
const name = valueMatch[1];
|
|
@@ -3755,7 +3395,7 @@ function parsePropDecl(line, lineNumber, column, lineOffset, diagnostics) {
|
|
|
3755
3395
|
pushDiag(
|
|
3756
3396
|
diagnostics,
|
|
3757
3397
|
"COLLIE105",
|
|
3758
|
-
'Invalid #
|
|
3398
|
+
'Invalid #inputs declaration. Use "name".',
|
|
3759
3399
|
lineNumber,
|
|
3760
3400
|
column,
|
|
3761
3401
|
lineOffset,
|
|
@@ -3890,8 +3530,8 @@ function serializeRoot(root, indentSize) {
|
|
|
3890
3530
|
if (root.classAliases && root.classAliases.aliases.length > 0) {
|
|
3891
3531
|
sections.push(formatClassAliases(root.classAliases, indentSize));
|
|
3892
3532
|
}
|
|
3893
|
-
if (root.
|
|
3894
|
-
sections.push(
|
|
3533
|
+
if (root.inputs && root.inputs.fields.length > 0) {
|
|
3534
|
+
sections.push(formatInputs(root.inputs, indentSize));
|
|
3895
3535
|
}
|
|
3896
3536
|
if (root.children.length > 0) {
|
|
3897
3537
|
sections.push(formatNodes(root.children, 0, indentSize));
|
|
@@ -3920,10 +3560,10 @@ function formatClassAliases(decl, indentSize) {
|
|
|
3920
3560
|
}
|
|
3921
3561
|
return lines;
|
|
3922
3562
|
}
|
|
3923
|
-
function
|
|
3563
|
+
function formatInputs(inputs, indentSize) {
|
|
3924
3564
|
const indent = indentString(1, indentSize);
|
|
3925
|
-
const lines = ["
|
|
3926
|
-
for (const field of
|
|
3565
|
+
const lines = ["#inputs"];
|
|
3566
|
+
for (const field of inputs.fields) {
|
|
3927
3567
|
const optionalFlag = field.optional ? "?" : "";
|
|
3928
3568
|
lines.push(cleanLine(`${indent}${field.name}${optionalFlag}: ${field.typeText.trim()}`));
|
|
3929
3569
|
}
|
|
@@ -4113,19 +3753,19 @@ function convertTsxToCollie(source, options = {}) {
|
|
|
4113
3753
|
);
|
|
4114
3754
|
const warnings = [];
|
|
4115
3755
|
const ctx = { sourceFile, warnings };
|
|
4116
|
-
const
|
|
4117
|
-
const component = findComponentInfo(sourceFile,
|
|
3756
|
+
const inputDeclarations = collectInputDeclarations(sourceFile);
|
|
3757
|
+
const component = findComponentInfo(sourceFile, inputDeclarations, ctx);
|
|
4118
3758
|
if (!component) {
|
|
4119
3759
|
throw new Error("Could not find a component that returns JSX in this file.");
|
|
4120
3760
|
}
|
|
4121
|
-
const
|
|
3761
|
+
const inputsLines = buildInputsBlock(component, inputDeclarations, ctx);
|
|
4122
3762
|
const templateLines = convertJsxNode(component.jsxRoot, ctx, 0);
|
|
4123
3763
|
if (!templateLines.length) {
|
|
4124
3764
|
throw new Error("Unable to convert JSX tree to Collie template.");
|
|
4125
3765
|
}
|
|
4126
3766
|
const sections = [];
|
|
4127
|
-
if (
|
|
4128
|
-
sections.push(
|
|
3767
|
+
if (inputsLines.length) {
|
|
3768
|
+
sections.push(inputsLines.join("\n"));
|
|
4129
3769
|
}
|
|
4130
3770
|
sections.push(templateLines.join("\n"));
|
|
4131
3771
|
const collie = `${sections.join("\n\n").trimEnd()}
|
|
@@ -4140,18 +3780,18 @@ function inferScriptKind(filename) {
|
|
|
4140
3780
|
if (ext === ".ts") return import_typescript.default.ScriptKind.TS;
|
|
4141
3781
|
return import_typescript.default.ScriptKind.JS;
|
|
4142
3782
|
}
|
|
4143
|
-
function
|
|
3783
|
+
function collectInputDeclarations(sourceFile) {
|
|
4144
3784
|
const map = /* @__PURE__ */ new Map();
|
|
4145
3785
|
for (const statement of sourceFile.statements) {
|
|
4146
3786
|
if (import_typescript.default.isInterfaceDeclaration(statement) && statement.name) {
|
|
4147
|
-
map.set(statement.name.text,
|
|
3787
|
+
map.set(statement.name.text, extractInputsFromMembers(statement.members, sourceFile));
|
|
4148
3788
|
} else if (import_typescript.default.isTypeAliasDeclaration(statement) && import_typescript.default.isTypeLiteralNode(statement.type)) {
|
|
4149
|
-
map.set(statement.name.text,
|
|
3789
|
+
map.set(statement.name.text, extractInputsFromMembers(statement.type.members, sourceFile));
|
|
4150
3790
|
}
|
|
4151
3791
|
}
|
|
4152
3792
|
return map;
|
|
4153
3793
|
}
|
|
4154
|
-
function
|
|
3794
|
+
function extractInputsFromMembers(members, sourceFile) {
|
|
4155
3795
|
const fields = [];
|
|
4156
3796
|
for (const member of members) {
|
|
4157
3797
|
if (!import_typescript.default.isPropertySignature(member) || member.name === void 0) {
|
|
@@ -4176,11 +3816,11 @@ function findComponentInfo(sourceFile, declarations, ctx) {
|
|
|
4176
3816
|
const jsx = findJsxReturn(statement.body);
|
|
4177
3817
|
if (jsx) {
|
|
4178
3818
|
const defaults = extractDefaultsFromParameters(statement.parameters, ctx);
|
|
4179
|
-
const
|
|
3819
|
+
const inputsInfo = resolveInputsFromParameters(statement.parameters, declarations, ctx);
|
|
4180
3820
|
return {
|
|
4181
3821
|
jsxRoot: jsx,
|
|
4182
|
-
|
|
4183
|
-
|
|
3822
|
+
inputsTypeName: inputsInfo.typeName,
|
|
3823
|
+
inlineInputs: inputsInfo.inline,
|
|
4184
3824
|
defaults
|
|
4185
3825
|
};
|
|
4186
3826
|
}
|
|
@@ -4194,20 +3834,20 @@ function findComponentInfo(sourceFile, declarations, ctx) {
|
|
|
4194
3834
|
continue;
|
|
4195
3835
|
}
|
|
4196
3836
|
const defaults = extractDefaultsFromParameters(init.parameters, ctx);
|
|
4197
|
-
const
|
|
4198
|
-
if (!
|
|
4199
|
-
const inferred =
|
|
4200
|
-
if (inferred.typeName && !
|
|
4201
|
-
|
|
3837
|
+
const inputsInfo = resolveInputsFromParameters(init.parameters, declarations, ctx);
|
|
3838
|
+
if (!inputsInfo.typeName && !inputsInfo.inline && decl.type) {
|
|
3839
|
+
const inferred = resolveInputsFromTypeAnnotation(decl.type, sourceFile, declarations);
|
|
3840
|
+
if (inferred.typeName && !inputsInfo.typeName) {
|
|
3841
|
+
inputsInfo.typeName = inferred.typeName;
|
|
4202
3842
|
}
|
|
4203
|
-
if (inferred.inline && !
|
|
4204
|
-
|
|
3843
|
+
if (inferred.inline && !inputsInfo.inline) {
|
|
3844
|
+
inputsInfo.inline = inferred.inline;
|
|
4205
3845
|
}
|
|
4206
3846
|
}
|
|
4207
3847
|
return {
|
|
4208
3848
|
jsxRoot: jsx,
|
|
4209
|
-
|
|
4210
|
-
|
|
3849
|
+
inputsTypeName: inputsInfo.typeName,
|
|
3850
|
+
inlineInputs: inputsInfo.inline,
|
|
4211
3851
|
defaults
|
|
4212
3852
|
};
|
|
4213
3853
|
}
|
|
@@ -4216,13 +3856,13 @@ function findComponentInfo(sourceFile, declarations, ctx) {
|
|
|
4216
3856
|
}
|
|
4217
3857
|
return null;
|
|
4218
3858
|
}
|
|
4219
|
-
function
|
|
3859
|
+
function resolveInputsFromParameters(parameters, declarations, ctx) {
|
|
4220
3860
|
if (!parameters.length) {
|
|
4221
3861
|
return {};
|
|
4222
3862
|
}
|
|
4223
3863
|
const param = parameters[0];
|
|
4224
3864
|
if (param.type) {
|
|
4225
|
-
const inferred =
|
|
3865
|
+
const inferred = resolveInputsFromTypeAnnotation(param.type, ctx.sourceFile, declarations);
|
|
4226
3866
|
if (inferred.inline) {
|
|
4227
3867
|
return inferred;
|
|
4228
3868
|
}
|
|
@@ -4232,7 +3872,7 @@ function resolvePropsFromParameters(parameters, declarations, ctx) {
|
|
|
4232
3872
|
}
|
|
4233
3873
|
return {};
|
|
4234
3874
|
}
|
|
4235
|
-
function
|
|
3875
|
+
function resolveInputsFromTypeAnnotation(typeNode, sourceFile, declarations) {
|
|
4236
3876
|
if (import_typescript.default.isTypeReferenceNode(typeNode)) {
|
|
4237
3877
|
const referenced = getTypeReferenceName(typeNode.typeName);
|
|
4238
3878
|
if (referenced && declarations.has(referenced)) {
|
|
@@ -4246,12 +3886,12 @@ function resolvePropsFromTypeAnnotation(typeNode, sourceFile, declarations) {
|
|
|
4246
3886
|
return { typeName: nested };
|
|
4247
3887
|
}
|
|
4248
3888
|
} else if (import_typescript.default.isTypeLiteralNode(typeArg)) {
|
|
4249
|
-
return { inline:
|
|
3889
|
+
return { inline: extractInputsFromMembers(typeArg.members, sourceFile) };
|
|
4250
3890
|
}
|
|
4251
3891
|
}
|
|
4252
3892
|
}
|
|
4253
3893
|
if (import_typescript.default.isTypeLiteralNode(typeNode)) {
|
|
4254
|
-
return { inline:
|
|
3894
|
+
return { inline: extractInputsFromMembers(typeNode.members, sourceFile) };
|
|
4255
3895
|
}
|
|
4256
3896
|
return {};
|
|
4257
3897
|
}
|
|
@@ -4307,16 +3947,16 @@ function extractDefaultsFromParameters(parameters, ctx) {
|
|
|
4307
3947
|
if (!element.initializer) {
|
|
4308
3948
|
continue;
|
|
4309
3949
|
}
|
|
4310
|
-
const
|
|
4311
|
-
if (!
|
|
3950
|
+
const inputName = getBindingElementInputName(element, ctx.sourceFile);
|
|
3951
|
+
if (!inputName) {
|
|
4312
3952
|
ctx.warnings.push("Skipping complex destructured default value.");
|
|
4313
3953
|
continue;
|
|
4314
3954
|
}
|
|
4315
|
-
defaults.set(
|
|
3955
|
+
defaults.set(inputName, element.initializer.getText(ctx.sourceFile).trim());
|
|
4316
3956
|
}
|
|
4317
3957
|
return defaults;
|
|
4318
3958
|
}
|
|
4319
|
-
function
|
|
3959
|
+
function getBindingElementInputName(element, sourceFile) {
|
|
4320
3960
|
const prop = element.propertyName;
|
|
4321
3961
|
if (prop) {
|
|
4322
3962
|
if (import_typescript.default.isIdentifier(prop) || import_typescript.default.isStringLiteral(prop) || import_typescript.default.isNumericLiteral(prop)) {
|
|
@@ -4338,24 +3978,25 @@ function getPropertyName(name, sourceFile) {
|
|
|
4338
3978
|
}
|
|
4339
3979
|
return name.getText(sourceFile);
|
|
4340
3980
|
}
|
|
4341
|
-
function
|
|
4342
|
-
const fields = info.
|
|
3981
|
+
function buildInputsBlock(info, inputDeclarations, ctx) {
|
|
3982
|
+
const fields = info.inlineInputs ?? (info.inputsTypeName ? inputDeclarations.get(info.inputsTypeName) ?? [] : void 0) ?? [];
|
|
4343
3983
|
if (!fields.length && !info.defaults.size) {
|
|
4344
3984
|
return [];
|
|
4345
3985
|
}
|
|
4346
|
-
const lines = ["
|
|
3986
|
+
const lines = ["#inputs"];
|
|
4347
3987
|
if (fields.length) {
|
|
4348
3988
|
for (const field of fields) {
|
|
4349
3989
|
const def = info.defaults.get(field.name);
|
|
4350
|
-
let line = ` ${field.name}
|
|
3990
|
+
let line = ` ${field.name}`;
|
|
4351
3991
|
if (def) {
|
|
4352
|
-
|
|
3992
|
+
ctx.warnings.push(`Default value for "${field.name}" cannot be preserved in Collie #inputs.`);
|
|
4353
3993
|
}
|
|
4354
3994
|
lines.push(line);
|
|
4355
3995
|
}
|
|
4356
3996
|
} else {
|
|
4357
3997
|
for (const [name, defValue] of info.defaults.entries()) {
|
|
4358
|
-
|
|
3998
|
+
ctx.warnings.push(`Default value for "${name}" cannot be preserved in Collie #inputs.`);
|
|
3999
|
+
lines.push(` ${name}`);
|
|
4359
4000
|
}
|
|
4360
4001
|
}
|
|
4361
4002
|
return lines;
|
|
@@ -4636,28 +4277,28 @@ function hasErrors(diagnostics) {
|
|
|
4636
4277
|
function createStubComponent(name, flavor) {
|
|
4637
4278
|
if (flavor === "tsx") {
|
|
4638
4279
|
return [
|
|
4639
|
-
"export type
|
|
4640
|
-
`export default function ${name}(
|
|
4280
|
+
"export type Inputs = Record<string, never>;",
|
|
4281
|
+
`export default function ${name}(__inputs: Inputs) {`,
|
|
4641
4282
|
" return null;",
|
|
4642
4283
|
"}"
|
|
4643
4284
|
].join("\n");
|
|
4644
4285
|
}
|
|
4645
|
-
return [`export default function ${name}(
|
|
4286
|
+
return [`export default function ${name}(__inputs) {`, " return null;", "}"].join("\n");
|
|
4646
4287
|
}
|
|
4647
4288
|
function createStubRender(flavor) {
|
|
4648
4289
|
if (flavor === "tsx") {
|
|
4649
4290
|
return [
|
|
4650
|
-
"export type
|
|
4651
|
-
"export function render(
|
|
4291
|
+
"export type Inputs = Record<string, never>;",
|
|
4292
|
+
"export function render(__inputs: any) {",
|
|
4652
4293
|
" return null;",
|
|
4653
4294
|
"}"
|
|
4654
4295
|
].join("\n");
|
|
4655
4296
|
}
|
|
4656
|
-
return ["export function render(
|
|
4297
|
+
return ["export function render(__inputs) {", " return null;", "}"].join("\n");
|
|
4657
4298
|
}
|
|
4658
4299
|
function wrapRenderModuleAsComponent(renderModule, name, flavor) {
|
|
4659
|
-
const signature = flavor === "tsx" ? `export default function ${name}(
|
|
4660
|
-
const wrapper = [signature, " return render(
|
|
4300
|
+
const signature = flavor === "tsx" ? `export default function ${name}(__inputs: Inputs) {` : `export default function ${name}(__inputs) {`;
|
|
4301
|
+
const wrapper = [signature, " return render(__inputs);", "}"].join("\n");
|
|
4661
4302
|
return `${renderModule}
|
|
4662
4303
|
|
|
4663
4304
|
${wrapper}`;
|