@swiftuijs/twill 0.3.0 → 0.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 +8 -3
- package/bin/run.mjs +2 -2
- package/dist/branches.d.ts +7 -1
- package/dist/cli.js +7 -8
- package/dist/{compilation-cache-7SugqJ4n.js → compilation-cache-BrV71YKt.js} +2 -3
- package/dist/{compilation-cache-7SugqJ4n.js.map → compilation-cache-BrV71YKt.js.map} +1 -1
- package/dist/{compiler-CbNSBUI2.js → compiler-DI0H4d1R.js} +178 -66
- package/dist/compiler-DI0H4d1R.js.map +1 -0
- package/dist/compiler.d.ts +2 -0
- package/dist/config-la_KkjCS.js +1 -0
- package/dist/config.js +2 -2
- package/dist/{configuration-BVMf6Q_j.js → configuration-ppqhmuy4.js} +7 -5
- package/dist/configuration-ppqhmuy4.js.map +1 -0
- package/dist/configuration.d.ts +1 -1
- package/dist/declarations-DXtb1AHP.js +131 -0
- package/dist/declarations-DXtb1AHP.js.map +1 -0
- package/dist/declarations.js +1 -132
- package/dist/doctor-BHwU_YBw.js +200 -0
- package/dist/doctor-BHwU_YBw.js.map +1 -0
- package/dist/doctor.js +1 -202
- package/dist/editor.d.ts +1 -0
- package/dist/editor.js +31 -5
- package/dist/editor.js.map +1 -1
- package/dist/esbuild.js +2 -3
- package/dist/{extensions-B1xTkoYP.js → extensions-7ixttcoK.js} +2 -3
- package/dist/{extensions-B1xTkoYP.js.map → extensions-7ixttcoK.js.map} +1 -1
- package/dist/{files-BW9geU0_.js → files-CgwJJ992.js} +3 -4
- package/dist/{files-BW9geU0_.js.map → files-CgwJJ992.js.map} +1 -1
- package/dist/index.js +2 -2
- package/dist/{loader-compiler-D1teTrXw.js → loader-compiler.js} +4 -5
- package/dist/loader-compiler.js.map +1 -0
- package/dist/loader-registration-BO43rvVU.js +14 -0
- package/dist/loader-registration-BO43rvVU.js.map +1 -0
- package/dist/loader-registration.d.ts +4 -0
- package/dist/loader-registration.js +1 -0
- package/dist/loader-state-DB7sONR_.js +60 -0
- package/dist/loader-state-DB7sONR_.js.map +1 -0
- package/dist/loader-state.d.ts +34 -0
- package/dist/loader-sync.d.ts +3 -0
- package/dist/loader-sync.js +27 -0
- package/dist/loader-sync.js.map +1 -0
- package/dist/loader.d.ts +2 -4
- package/dist/loader.js +10 -48
- package/dist/loader.js.map +1 -1
- package/dist/{plugin-BHfiQ2L-.js → plugin-DDOHhv7Z.js} +6 -7
- package/dist/{plugin-BHfiQ2L-.js.map → plugin-DDOHhv7Z.js.map} +1 -1
- package/dist/project-DYXf2Kgt.js +442 -0
- package/dist/project-DYXf2Kgt.js.map +1 -0
- package/dist/project.js +2 -443
- package/dist/register.js +3 -4
- package/dist/register.js.map +1 -1
- package/dist/rollup.js +2 -3
- package/dist/rspack.js +2 -3
- package/dist/syntax.js +2 -3
- package/dist/{transpile-D8P8Lsbx.js → transpile--6CFGUm0.js} +3 -4
- package/dist/{transpile-D8P8Lsbx.js.map → transpile--6CFGUm0.js.map} +1 -1
- package/dist/typescript-plugin.cjs +252 -128
- package/dist/typescript-plugin.cjs.map +1 -1
- package/dist/vite-D0Ct4uy4.js +6 -0
- package/dist/vite-D0Ct4uy4.js.map +1 -0
- package/dist/vite-react.js +2 -3
- package/dist/vite.js +1 -7
- package/dist/webpack.js +2 -3
- package/package.json +3 -3
- package/schemas/twill.schema.json +6 -0
- package/dist/compiler-CbNSBUI2.js.map +0 -1
- package/dist/configuration-BVMf6Q_j.js.map +0 -1
- package/dist/declarations.js.map +0 -1
- package/dist/doctor.js.map +0 -1
- package/dist/loader-compiler-D1teTrXw.js.map +0 -1
- package/dist/project.js.map +0 -1
- package/dist/vite.js.map +0 -1
|
@@ -3,7 +3,7 @@ import { Parser, tokTypes } from "acorn";
|
|
|
3
3
|
import { tsPlugin } from "acorn-typescript";
|
|
4
4
|
import jsx from "acorn-jsx";
|
|
5
5
|
import { GREATEST_LOWER_BOUND, TraceMap, decodedMappings, encodedMappings, generatedPositionFor, originalPositionFor } from "@jridgewell/trace-mapping";
|
|
6
|
-
|
|
6
|
+
// src/parser.js
|
|
7
7
|
var options = {
|
|
8
8
|
ecmaVersion: "latest",
|
|
9
9
|
sourceType: "module",
|
|
@@ -19,9 +19,12 @@ function guardAt(input, offset, implicitMember = false) {
|
|
|
19
19
|
const lexer = Parser.tokenizer(input.slice(offset), { ecmaVersion: "latest" });
|
|
20
20
|
const stack = [];
|
|
21
21
|
let first = true;
|
|
22
|
+
let missingElse = false;
|
|
23
|
+
let afterNonNull = false;
|
|
22
24
|
try {
|
|
23
25
|
for (;;) {
|
|
24
|
-
const
|
|
26
|
+
const token = lexer.getToken();
|
|
27
|
+
const label = token.type.label;
|
|
25
28
|
if (first && [
|
|
26
29
|
"=",
|
|
27
30
|
".",
|
|
@@ -35,6 +38,22 @@ function guardAt(input, offset, implicitMember = false) {
|
|
|
35
38
|
"in",
|
|
36
39
|
"instanceof"
|
|
37
40
|
].includes(label) && !(implicitMember && label === ".")) return false;
|
|
41
|
+
if (first || afterNonNull) {
|
|
42
|
+
missingElse = !/[\r\n\u2028\u2029]/.test(input.slice(offset, offset + token.start)) && (token.type === tokTypes.name && !["as", "satisfies"].includes(token.value) || [
|
|
43
|
+
"const",
|
|
44
|
+
"num",
|
|
45
|
+
"string",
|
|
46
|
+
"true",
|
|
47
|
+
"false",
|
|
48
|
+
"null",
|
|
49
|
+
"this",
|
|
50
|
+
"new",
|
|
51
|
+
"typeof",
|
|
52
|
+
"void",
|
|
53
|
+
"delete"
|
|
54
|
+
].includes(label) || token.type === tokTypes.prefix && token.value === "~" || implicitMember && first && label === ".");
|
|
55
|
+
afterNonNull = token.type === tokTypes.prefix && token.value === "!";
|
|
56
|
+
}
|
|
38
57
|
if (!stack.length && [
|
|
39
58
|
"if",
|
|
40
59
|
"for",
|
|
@@ -45,15 +64,12 @@ function guardAt(input, offset, implicitMember = false) {
|
|
|
45
64
|
"throw",
|
|
46
65
|
"var",
|
|
47
66
|
"export"
|
|
48
|
-
].includes(label)) return false;
|
|
49
|
-
if (!first && !stack.length && label === "const") return false;
|
|
67
|
+
].includes(label)) return missingElse ? "missingElse" : false;
|
|
68
|
+
if (!first && !stack.length && label === "const") return missingElse ? "missingElse" : false;
|
|
50
69
|
first = false;
|
|
51
70
|
if (!stack.length && label === "else") return true;
|
|
52
|
-
if (!stack.length &&
|
|
53
|
-
|
|
54
|
-
";",
|
|
55
|
-
"}"
|
|
56
|
-
].includes(label)) return false;
|
|
71
|
+
if (label === "eof") return !stack.length && missingElse ? "missingElse" : false;
|
|
72
|
+
if (!stack.length && [";", "}"].includes(label)) return missingElse ? "missingElse" : false;
|
|
57
73
|
if ([
|
|
58
74
|
"(",
|
|
59
75
|
"[",
|
|
@@ -290,7 +306,9 @@ function parserFor(language) {
|
|
|
290
306
|
node.cleanup = cleanup;
|
|
291
307
|
return this.finishNode(node, "DeferStatement");
|
|
292
308
|
}
|
|
293
|
-
|
|
309
|
+
const guard = this.isContextual("guard") && guardAt(this.input, this.end, !!this.implicitClosure);
|
|
310
|
+
if (guard === "missingElse") this.raise(this.start, "Missing 'else' in guard statement. Put 'else' before the failure block: 'guard condition else { return ...; }'.");
|
|
311
|
+
if (!guard) {
|
|
294
312
|
const statement = super.parseStatement(context, ...args);
|
|
295
313
|
if (context && statement.type === "TwillEnumDeclaration") this.raise(statement.start, "An associated-value enum requires a block; add braces around this body.");
|
|
296
314
|
return statement;
|
|
@@ -299,6 +317,7 @@ function parserFor(language) {
|
|
|
299
317
|
node.singleStatement = !!context;
|
|
300
318
|
this.next();
|
|
301
319
|
node.binding = null;
|
|
320
|
+
if (this.type === tokTypes._else) this.raise(this.start, "Guard needs a condition or 'const' binding before 'else'.");
|
|
302
321
|
if (this.type === tokTypes._const) {
|
|
303
322
|
if (context) this.raise(node.start, "A guard binding requires a block; add braces around this body.");
|
|
304
323
|
const declaration = this.startNode();
|
|
@@ -308,8 +327,9 @@ function parserFor(language) {
|
|
|
308
327
|
if (node.binding.declarations.length !== 1) this.raise(node.start, "guard const requires one binding declaration.");
|
|
309
328
|
} else node.test = this.parseExpression();
|
|
310
329
|
node.elseStart = this.start;
|
|
311
|
-
this.
|
|
312
|
-
|
|
330
|
+
if (this.type !== tokTypes._else) this.raise(this.start, "Expected 'else' after the guard condition or binding.");
|
|
331
|
+
this.next();
|
|
332
|
+
if (this.type !== tokTypes.braceL) this.raise(this.start, "Guard 'else' must be followed by a block, for example 'else { return ...; }'.");
|
|
313
333
|
node.failure = this.parseBlock();
|
|
314
334
|
if (!exits(node.failure)) this.raise(node.failure.start, "Every guard else path must exit with return, throw, break or continue.");
|
|
315
335
|
return this.finishNode(node, "GuardStatement");
|
|
@@ -694,13 +714,11 @@ function parse(source, language = "ts", sourceType = "module") {
|
|
|
694
714
|
}))
|
|
695
715
|
};
|
|
696
716
|
}
|
|
697
|
-
|
|
698
|
-
//#region src/defer-helper.ts
|
|
717
|
+
// src/defer-helper.ts
|
|
699
718
|
function syncDeferBody(names) {
|
|
700
719
|
return "\n let " + names.failed + " = false,\n " + names.failure + ";\n while (" + names.stack + "?.length) {\n const " + names.cleanup + " = " + names.stack + ".pop();\n if (" + names.cleanup + ") {\n try {\n " + names.cleanup + "();\n } catch (" + names.error + ") {\n " + names.failed + " = true;\n " + names.failure + " = " + names.error + ";\n }\n }\n }\n if (" + names.failed + ") throw " + names.failure + ";\n";
|
|
701
720
|
}
|
|
702
|
-
|
|
703
|
-
//#region src/defer.ts
|
|
721
|
+
// src/defer.ts
|
|
704
722
|
var functions = /* @__PURE__ */ new Set([
|
|
705
723
|
"FunctionDeclaration",
|
|
706
724
|
"FunctionExpression",
|
|
@@ -876,8 +894,7 @@ function lowerDefers(source, ast, code, language, usedNames, contentStarts, comm
|
|
|
876
894
|
code.prependLeft(start, `\nimport { runDefers as ${sharedHelper} } from '@swiftuijs/twill-runtime/helpers/v1';\n`);
|
|
877
895
|
}
|
|
878
896
|
}
|
|
879
|
-
|
|
880
|
-
//#region src/branches.ts
|
|
897
|
+
// src/branches.ts
|
|
881
898
|
function suspension(node) {
|
|
882
899
|
if (["AwaitExpression", "YieldExpression"].includes(node.type)) return node;
|
|
883
900
|
if ([
|
|
@@ -897,10 +914,53 @@ function suspension(node) {
|
|
|
897
914
|
}
|
|
898
915
|
}
|
|
899
916
|
}
|
|
917
|
+
function directEval(node) {
|
|
918
|
+
if ([
|
|
919
|
+
"FunctionDeclaration",
|
|
920
|
+
"FunctionExpression",
|
|
921
|
+
"ArrowFunctionExpression"
|
|
922
|
+
].includes(node.type)) return false;
|
|
923
|
+
if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "eval" && !node.optional) return true;
|
|
924
|
+
return Object.entries(node).some(([key, value]) => ![
|
|
925
|
+
"loc",
|
|
926
|
+
"trailing",
|
|
927
|
+
"tag"
|
|
928
|
+
].includes(key) && (Array.isArray(value) ? value : [value]).some((child) => child?.type && directEval(child)));
|
|
929
|
+
}
|
|
930
|
+
/** Preserve the existing expression boundary for direct eval and suspension. */
|
|
931
|
+
function canInlineSwitchExpression(node) {
|
|
932
|
+
return !directEval(node) && !suspension(node);
|
|
933
|
+
}
|
|
934
|
+
/** Reuse authored blocks only when no direct eval (including a captured one)
|
|
935
|
+
* can observe the added temporary's broader lifetime/temporal dead zone.
|
|
936
|
+
* Memoize shared parser nodes so this analysis stays linear in the AST size. */
|
|
937
|
+
function reusableStatements(ast, usedNames) {
|
|
938
|
+
const reusable = /* @__PURE__ */ new Set();
|
|
939
|
+
const observed = /* @__PURE__ */ new Map();
|
|
940
|
+
const visit = (node) => {
|
|
941
|
+
const known = observed.get(node);
|
|
942
|
+
if (known !== void 0) return known;
|
|
943
|
+
if (node.type === "Identifier" || node.type === "JSXIdentifier") usedNames.add(node.name);
|
|
944
|
+
let observable = node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "eval";
|
|
945
|
+
for (const [key, value] of Object.entries(node)) {
|
|
946
|
+
if (["loc", "trailing"].includes(key)) continue;
|
|
947
|
+
for (const child of Array.isArray(value) ? value : [value]) if (child?.type && visit(child)) observable = true;
|
|
948
|
+
}
|
|
949
|
+
if (!observable && node.type === "BlockStatement") {
|
|
950
|
+
for (const statement of node.body) if (statement.type === "TwillIfBinding" || (statement.argument ?? statement.expression)?.type === "TwillSwitchExpression") reusable.add(statement);
|
|
951
|
+
}
|
|
952
|
+
observed.set(node, observable);
|
|
953
|
+
return observable;
|
|
954
|
+
};
|
|
955
|
+
visit(ast);
|
|
956
|
+
return reusable;
|
|
957
|
+
}
|
|
900
958
|
/** Native switches with per-arm scopes. Direct returns and standalone variable
|
|
901
959
|
* initializers need no extra function; other expressions keep a lexical IIFE. */
|
|
902
|
-
function lowerSwitchExpressions(source, ast, switches, code, language, usedNames, comments, fail) {
|
|
960
|
+
function lowerSwitchExpressions(source, ast, switches, code, language, usedNames, comments, fail, implicitReturns = [], reusable = /* @__PURE__ */ new Set()) {
|
|
903
961
|
const returns = /* @__PURE__ */ new Set();
|
|
962
|
+
const implicit = new Set(implicitReturns);
|
|
963
|
+
const sharedScopes = /* @__PURE__ */ new Set();
|
|
904
964
|
const initializers = /* @__PURE__ */ new Map();
|
|
905
965
|
const leadingDoc = (start) => {
|
|
906
966
|
let low = 0, high = comments.length;
|
|
@@ -912,19 +972,6 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
|
|
|
912
972
|
const comment = comments[low - 1];
|
|
913
973
|
return comment && /^\s*$/.test(source.slice(comment.end, start)) ? comment : void 0;
|
|
914
974
|
};
|
|
915
|
-
const directEval = (node) => {
|
|
916
|
-
if ([
|
|
917
|
-
"FunctionDeclaration",
|
|
918
|
-
"FunctionExpression",
|
|
919
|
-
"ArrowFunctionExpression"
|
|
920
|
-
].includes(node.type)) return false;
|
|
921
|
-
if (node.type === "CallExpression" && node.callee.type === "Identifier" && node.callee.name === "eval" && !node.optional) return true;
|
|
922
|
-
return Object.entries(node).some(([key, value]) => ![
|
|
923
|
-
"loc",
|
|
924
|
-
"trailing",
|
|
925
|
-
"tag"
|
|
926
|
-
].includes(key) && (Array.isArray(value) ? value : [value]).some((child) => child?.type && directEval(child)));
|
|
927
|
-
};
|
|
928
975
|
const grouping = (start, end, initializer = false) => {
|
|
929
976
|
const parentheses = [];
|
|
930
977
|
const tokens = Parser.tokenizer(source.slice(start, end), { ecmaVersion: "latest" });
|
|
@@ -937,6 +984,15 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
|
|
|
937
984
|
for (const token of parentheses) code.remove(start + token.start, start + token.end);
|
|
938
985
|
};
|
|
939
986
|
const visit = (node, parent) => {
|
|
987
|
+
if (node.type === "BlockStatement") for (const statement of node.body) {
|
|
988
|
+
const value = statement.type === "ReturnStatement" ? statement.argument : statement.expression;
|
|
989
|
+
if (value?.type === "TwillSwitchExpression" && (node.body.length === 1 || reusable.has(statement)) && (statement.type === "ReturnStatement" || implicit.has(value))) sharedScopes.add(value);
|
|
990
|
+
}
|
|
991
|
+
if (node.type === "ExpressionStatement" && implicit.has(node.expression)) {
|
|
992
|
+
grouping(node.start, node.expression.start);
|
|
993
|
+
grouping(node.expression.end, node.end);
|
|
994
|
+
if (source[node.end - 1] === ";") code.remove(node.end - 1, node.end);
|
|
995
|
+
}
|
|
940
996
|
if (node.type === "VariableDeclaration" && node.declarations.length === 1) {
|
|
941
997
|
const declaration = node.declarations[0];
|
|
942
998
|
if (declaration.id.type === "Identifier" && declaration.init?.type === "TwillSwitchExpression" && [
|
|
@@ -958,6 +1014,7 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
|
|
|
958
1014
|
if (node.type === "ReturnStatement" && node.argument?.type === "TwillSwitchExpression") {
|
|
959
1015
|
returns.add(node.argument);
|
|
960
1016
|
code.remove(node.start, node.start + 6);
|
|
1017
|
+
if (source[node.end - 1] === ";") code.remove(node.end - 1, node.end);
|
|
961
1018
|
for (const [start, end] of [[node.start + 6, node.argument.start], [node.argument.end, node.end]]) grouping(start, end);
|
|
962
1019
|
}
|
|
963
1020
|
for (const [key, value] of Object.entries(node)) {
|
|
@@ -980,8 +1037,8 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
|
|
|
980
1037
|
return name;
|
|
981
1038
|
};
|
|
982
1039
|
for (const node of switches) {
|
|
983
|
-
const direct = returns.has(node);
|
|
984
|
-
const suspended = !
|
|
1040
|
+
const direct = returns.has(node) || implicit.has(node);
|
|
1041
|
+
const suspended = !returns.has(node) && suspension(node);
|
|
985
1042
|
if (suspended) fail(suspended, `await/yield inside a ${node.keyword} expression requires a direct return. Bind the subject first, or await the whole result explicitly.`);
|
|
986
1043
|
const initializer = initializers.get(node);
|
|
987
1044
|
const result = initializer ? fresh("Result") : void 0;
|
|
@@ -1000,7 +1057,10 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
|
|
|
1000
1057
|
const valuePrefix = result ? `${result} = (` : "return ";
|
|
1001
1058
|
const valueSuffix = result ? `); break ${exit};` : ";";
|
|
1002
1059
|
const kind = node.cases.some((branch) => branch.enumPattern) ? fresh("Kind") : void 0;
|
|
1003
|
-
if (!initializer)
|
|
1060
|
+
if (!initializer) {
|
|
1061
|
+
const opening = direct ? sharedScopes.has(node) ? "" : "{" : "(() => {";
|
|
1062
|
+
code.appendLeft(node.start, `${opening}const ${subject} = `);
|
|
1063
|
+
}
|
|
1004
1064
|
code.remove(node.parenStart, node.parenStart + 1);
|
|
1005
1065
|
if (node.keyword === "match") {
|
|
1006
1066
|
const tokens = Parser.tokenizer(source.slice(node.discriminant.end, node.parenEnd), { ecmaVersion: "latest" });
|
|
@@ -1057,13 +1117,13 @@ function lowerSwitchExpressions(source, ast, switches, code, language, usedNames
|
|
|
1057
1117
|
}
|
|
1058
1118
|
}
|
|
1059
1119
|
const hasDefault = node.cases.some((branch) => !branch.pattern && !branch.enumPattern && branch.test === null);
|
|
1060
|
-
const check = !hasDefault && language.startsWith("ts") ?
|
|
1061
|
-
const failure = hasDefault ? "" : `${check} throw new TypeError('Non-exhaustive switch expression');`;
|
|
1062
|
-
|
|
1120
|
+
const check = !hasDefault && language.startsWith("ts") ? fresh("Check") : void 0;
|
|
1121
|
+
const failure = hasDefault ? "" : `${check ? ` type ${check}<T extends never = typeof ${kind ?? subject}> = T & never;` : ""} throw new TypeError('Non-exhaustive switch expression'${check ? ` as ${check}` : ""});`;
|
|
1122
|
+
const closing = initializer || direct && !sharedScopes.has(node) ? "}" : direct ? "" : "})()";
|
|
1123
|
+
code.overwrite(node.end - 1, node.end, `}${failure}${closing}`, { contentOnly: true });
|
|
1063
1124
|
}
|
|
1064
1125
|
}
|
|
1065
|
-
|
|
1066
|
-
//#region src/enums.ts
|
|
1126
|
+
// src/enums.ts
|
|
1067
1127
|
/** Find free type references, respecting generic function/mapped/infer binders. */
|
|
1068
1128
|
function references(node, names, found = /* @__PURE__ */ new Set(), bound = /* @__PURE__ */ new Set()) {
|
|
1069
1129
|
const local = new Set(bound);
|
|
@@ -1242,8 +1302,7 @@ function mapEnumCopies(source, generated, map, insertions) {
|
|
|
1242
1302
|
}));
|
|
1243
1303
|
return copies;
|
|
1244
1304
|
}
|
|
1245
|
-
|
|
1246
|
-
//#region src/compiler.ts
|
|
1305
|
+
// src/compiler.ts
|
|
1247
1306
|
function inferLanguage(filename) {
|
|
1248
1307
|
if (/\.(?:twillx|tsx)$/.test(filename)) return "tsx";
|
|
1249
1308
|
if (/\.jsx$/.test(filename)) return "jsx";
|
|
@@ -1268,6 +1327,25 @@ var TwillSyntaxError = class extends SyntaxError {
|
|
|
1268
1327
|
this.frame = `${source.split(/\r\n|[\r\n\u2028\u2029]/)[line - 1] ?? ""}\n${" ".repeat(column)}^`;
|
|
1269
1328
|
}
|
|
1270
1329
|
};
|
|
1330
|
+
function closureExpression(body) {
|
|
1331
|
+
if (body.length !== 1) return;
|
|
1332
|
+
return body[0].type === "ReturnStatement" ? body[0].argument : body[0].expression;
|
|
1333
|
+
}
|
|
1334
|
+
function conciseExpression(body) {
|
|
1335
|
+
const expression = closureExpression(body);
|
|
1336
|
+
return !!expression && (expression.type !== "TwillSwitchExpression" || !canInlineSwitchExpression(expression));
|
|
1337
|
+
}
|
|
1338
|
+
function expressionGrouping(expression, source, start) {
|
|
1339
|
+
while ([
|
|
1340
|
+
"TSAsExpression",
|
|
1341
|
+
"TSSatisfiesExpression",
|
|
1342
|
+
"TSNonNullExpression"
|
|
1343
|
+
].includes(expression.type)) expression = expression.expression;
|
|
1344
|
+
if (!["ObjectExpression", "SequenceExpression"].includes(expression.type)) return "";
|
|
1345
|
+
const tokens = Parser.tokenizer(source.slice(start, expression.start), { ecmaVersion: "latest" });
|
|
1346
|
+
for (let token = tokens.getToken(); token.type !== tokTypes.eof; token = tokens.getToken()) if (token.type === tokTypes.parenL) return "";
|
|
1347
|
+
return "(";
|
|
1348
|
+
}
|
|
1271
1349
|
function walk(node, visit) {
|
|
1272
1350
|
visit(node);
|
|
1273
1351
|
for (const key of Object.keys(node)) {
|
|
@@ -1297,7 +1375,9 @@ function transform(source, options = {}) {
|
|
|
1297
1375
|
const code = new MagicString(source);
|
|
1298
1376
|
const enumInsertions = parsed.enums.length ? lowerEnums(source, parsed.ast, parsed.enums, code) : [];
|
|
1299
1377
|
const usedNames = /* @__PURE__ */ new Set();
|
|
1300
|
-
|
|
1378
|
+
const reusable = parsed.ifBindings.length || parsed.switches.length ? reusableStatements(parsed.ast, usedNames) : /* @__PURE__ */ new Set();
|
|
1379
|
+
const patternGuards = parsed.guards.filter((guard) => guard.binding && guard.binding.declarations[0].id.type !== "Identifier");
|
|
1380
|
+
if (!parsed.ifBindings.length && !parsed.switches.length && (patternGuards.length || parsed.defers.length || parsed.closures.some((closure) => closure.implicitMembers.length) || /\.twillx$/.test(filename.split(/[?#]/, 1)[0]))) walk(parsed.ast, (node) => {
|
|
1301
1381
|
if (node.type === "Identifier" || node.type === "JSXIdentifier") usedNames.add(node.name);
|
|
1302
1382
|
});
|
|
1303
1383
|
let guardCounter = 0;
|
|
@@ -1310,12 +1390,13 @@ function transform(source, options = {}) {
|
|
|
1310
1390
|
while (usedNames.has(temporary));
|
|
1311
1391
|
usedNames.add(temporary);
|
|
1312
1392
|
const patternEnd = binding.typeAnnotation?.start ?? binding.end;
|
|
1313
|
-
|
|
1393
|
+
const reuseScope = reusable.has(statement);
|
|
1394
|
+
code.overwrite(statement.start, binding.start, `${reuseScope ? "" : "{"}const ${temporary}`);
|
|
1314
1395
|
code.move(binding.start, patternEnd, statement.consequent.start + 1);
|
|
1315
1396
|
code.prependRight(binding.start, "const ");
|
|
1316
1397
|
code.appendLeft(patternEnd, ` = ${temporary};`);
|
|
1317
1398
|
code.prependLeft(statement.consequent.start, `; if (${temporary} !== null && ${temporary} !== void 0) `);
|
|
1318
|
-
code.appendLeft(statement.end, "}");
|
|
1399
|
+
if (!reuseScope) code.appendLeft(statement.end, "}");
|
|
1319
1400
|
}
|
|
1320
1401
|
for (const guard of parsed.guards) {
|
|
1321
1402
|
if (guard.singleStatement) {
|
|
@@ -1324,22 +1405,24 @@ function transform(source, options = {}) {
|
|
|
1324
1405
|
}
|
|
1325
1406
|
if (guard.binding) {
|
|
1326
1407
|
const binding = guard.binding.declarations[0].id;
|
|
1327
|
-
|
|
1328
|
-
|
|
1408
|
+
let name;
|
|
1409
|
+
if (binding.type === "Identifier") {
|
|
1410
|
+
name = binding.name;
|
|
1411
|
+
code.remove(guard.start, guard.start + 5);
|
|
1412
|
+
} else {
|
|
1329
1413
|
let temporary;
|
|
1330
1414
|
do
|
|
1331
1415
|
temporary = `__twillGuard${guardCounter++}`;
|
|
1332
1416
|
while (usedNames.has(temporary));
|
|
1333
1417
|
usedNames.add(temporary);
|
|
1418
|
+
name = temporary;
|
|
1334
1419
|
const patternEnd = binding.typeAnnotation?.start ?? binding.end;
|
|
1335
1420
|
code.overwrite(guard.start, binding.start, `const ${temporary}`);
|
|
1336
1421
|
code.move(binding.start, patternEnd, guard.end);
|
|
1337
1422
|
code.prependRight(binding.start, "; const ");
|
|
1338
1423
|
code.appendLeft(patternEnd, ` = ${temporary};`);
|
|
1339
|
-
code.overwrite(guard.elseStart, guard.elseStart + 4, `; if (${temporary} == null)`);
|
|
1340
|
-
continue;
|
|
1341
1424
|
}
|
|
1342
|
-
code.overwrite(guard.elseStart, guard.elseStart + 4, `; if (${
|
|
1425
|
+
code.overwrite(guard.elseStart, guard.elseStart + 4, `; if (${name} === null || ${name} === void 0)`);
|
|
1343
1426
|
} else {
|
|
1344
1427
|
code.overwrite(guard.start, guard.start + 5, "if (!(");
|
|
1345
1428
|
code.overwrite(guard.elseStart, guard.elseStart + 4, "))");
|
|
@@ -1370,7 +1453,9 @@ function transform(source, options = {}) {
|
|
|
1370
1453
|
name = `__twillArg${implicitCounter++}`;
|
|
1371
1454
|
while (usedNames.has(name));
|
|
1372
1455
|
usedNames.add(name);
|
|
1373
|
-
|
|
1456
|
+
const body = metadata.node.body.body;
|
|
1457
|
+
const opening = (options.implicitReturn !== false || body[0]?.type === "ReturnStatement") && conciseExpression(body) ? expressionGrouping(closureExpression(body), source, metadata.header?.end ?? metadata.node.start + 1) : "{";
|
|
1458
|
+
code.overwrite(metadata.node.start, metadata.node.start + 1, `(${name}) => ${opening}`);
|
|
1374
1459
|
for (const offset of metadata.implicitMembers) code.appendLeft(offset, name);
|
|
1375
1460
|
}
|
|
1376
1461
|
const pragma = [...parsed.comments.filter((comment) => comment.start < (parsed.ast.body[0]?.start ?? source.length)).map((comment) => comment.value).join("\n").matchAll(/@jsxImportSource\s+(\S+)/g)].at(-1)?.[1];
|
|
@@ -1398,9 +1483,13 @@ function transform(source, options = {}) {
|
|
|
1398
1483
|
return edits.toString();
|
|
1399
1484
|
};
|
|
1400
1485
|
let counter = 0;
|
|
1486
|
+
const directReturns = [];
|
|
1401
1487
|
for (const node of parsed.calls) {
|
|
1402
1488
|
const { callEnd, hadParens, originalArgs, closures } = node.trailing;
|
|
1403
1489
|
const component = componentCalls.has(node);
|
|
1490
|
+
const slotObject = vue && (options.vueDefaultSlot !== "function" || closures.length > 1);
|
|
1491
|
+
let nativeChild = false;
|
|
1492
|
+
let bareChild = false;
|
|
1404
1493
|
if (component) {
|
|
1405
1494
|
if (originalArgs.length > 1 || originalArgs[0]?.type === "SpreadElement" || node.optional || node.callee.optional) throw new TwillSyntaxError(source, filename, {
|
|
1406
1495
|
message: "Component syntax accepts one props object and children; optional calls use ordinary callbacks.",
|
|
@@ -1425,8 +1514,13 @@ function transform(source, options = {}) {
|
|
|
1425
1514
|
labels.add(name);
|
|
1426
1515
|
}
|
|
1427
1516
|
}
|
|
1428
|
-
const opening = vue ? ">{({default: " : ">{(";
|
|
1429
1517
|
const tagEnd = node.typeParameters?.end ?? node.callee.end;
|
|
1518
|
+
const child = closures[0];
|
|
1519
|
+
const expression = child.body.body[0]?.expression;
|
|
1520
|
+
nativeChild = !vue && !metadataByNode.get(child).header && child.body.body.length === 1 && (expression?.type === "JSXElement" || expression?.type === "JSXFragment") && source.slice(child.start + 1, expression.start).trim() === "" && /^\s*;?\s*$/.test(source.slice(expression.end, child.end - 1)) && source.slice(callEnd, child.start).trim() === "" && (!hadParens || originalArgs.length > 0 || /^\s*\(\s*\)$/.test(source.slice(tagEnd, callEnd)));
|
|
1521
|
+
bareChild = !vue && (!!metadataByNode.get(child).header || child.body.body.length === 1 && expression !== void 0 && expression.type !== "SequenceExpression");
|
|
1522
|
+
let opening = nativeChild ? ">" : bareChild ? ">{" : ">{(";
|
|
1523
|
+
if (vue) opening = slotObject ? ">{{default: " : ">{";
|
|
1430
1524
|
code.appendLeft(node.callee.start, "(<");
|
|
1431
1525
|
const props = originalArgs[0];
|
|
1432
1526
|
if (!vue && props?.type === "ObjectExpression" && props.properties.every((property) => property.type === "SpreadElement" || property.kind === "init" && !property.method && !property.computed && (property.key.name ?? property.key.value) !== "__proto__" && /^[A-Za-z_$][\w$.:-]*$/.test(String(property.key.name ?? property.key.value)))) {
|
|
@@ -1473,7 +1567,7 @@ function transform(source, options = {}) {
|
|
|
1473
1567
|
const tokens = Parser.tokenizer(gap, { ecmaVersion: "latest" });
|
|
1474
1568
|
const first = tokens.getToken();
|
|
1475
1569
|
const last = tokens.getToken();
|
|
1476
|
-
code.overwrite(tagEnd, callEnd, opening + gap.slice(first.end, last.start));
|
|
1570
|
+
code.overwrite(tagEnd, callEnd, opening + (nativeChild ? "" : gap.slice(first.end, last.start)));
|
|
1477
1571
|
} else code.appendLeft(callEnd, opening);
|
|
1478
1572
|
} else if (hadParens) {
|
|
1479
1573
|
const tailStart = originalArgs.at(-1)?.end ?? callEnd - 1;
|
|
@@ -1496,24 +1590,40 @@ function transform(source, options = {}) {
|
|
|
1496
1590
|
"DeferStatement",
|
|
1497
1591
|
"EmptyStatement"
|
|
1498
1592
|
].includes(statement.type)) && body.filter((statement) => statement.type !== "EmptyStatement").at(-1) === expressions[0];
|
|
1593
|
+
const explicitReturn = !content && body.length === 1 && body[0].type === "ReturnStatement" && body[0].argument;
|
|
1594
|
+
const returnsValue = explicitReturn || singleContent || options.implicitReturn !== false && body.length === 1 && body[0].type === "ExpressionStatement";
|
|
1595
|
+
const conciseBody = returnsValue && conciseExpression(body);
|
|
1596
|
+
const grouping = conciseBody ? expressionGrouping(closureExpression(body), source, metadata.header?.end ?? closure.start + 1) : "{";
|
|
1499
1597
|
if (singleContent && body.length === 1 && !vue) {
|
|
1500
1598
|
directChild = true;
|
|
1501
|
-
code.remove(closure.start, closure.start + 1);
|
|
1502
|
-
code.remove(closure.end - 1, closure.end);
|
|
1503
1599
|
const statement = expressions[0];
|
|
1504
|
-
if (
|
|
1600
|
+
if (nativeChild) {
|
|
1601
|
+
code.remove(callEnd, statement.expression.start);
|
|
1602
|
+
code.overwrite(statement.expression.end, closure.end, "", { contentOnly: true });
|
|
1603
|
+
} else {
|
|
1604
|
+
code.remove(closure.start, closure.start + 1);
|
|
1605
|
+
code.overwrite(closure.end - 1, closure.end, "", { contentOnly: true });
|
|
1606
|
+
if (source[statement.end - 1] === ";") code.remove(statement.end - 1, statement.end);
|
|
1607
|
+
}
|
|
1505
1608
|
continue;
|
|
1506
1609
|
}
|
|
1507
1610
|
if (metadata.label) {
|
|
1508
1611
|
const label = source.slice(metadata.label.start, metadata.label.end).replace(/\s*:\s*$/, "");
|
|
1509
|
-
code.overwrite(metadata.label.start, closure.start, component && vue ? `, [${JSON.stringify(label)}]: ` : ", ");
|
|
1612
|
+
code.overwrite(metadata.label.start, closure.start, component && vue ? `, ${label === "__proto__" ? `[${JSON.stringify(label)}]` : JSON.stringify(label)}: ` : ", ");
|
|
1510
1613
|
}
|
|
1511
1614
|
if (metadata.header) {
|
|
1512
1615
|
const header = metadata.header;
|
|
1513
1616
|
code.overwrite(closure.start, closure.start + 1, header.parenthesized || metadata.async ? "" : "(");
|
|
1514
1617
|
if (metadata.async && !header.parenthesized) code.appendLeft(header.asyncEnd, " (");
|
|
1515
|
-
code.overwrite(header.inStart, header.end, `${header.parenthesized ? "" : ") "}=> {`);
|
|
1516
|
-
} else if (!metadata.implicitMembers.length) code.overwrite(closure.start, closure.start + 1,
|
|
1618
|
+
code.overwrite(header.inStart, header.end, `${header.parenthesized ? "" : ") "}=> ${grouping}`);
|
|
1619
|
+
} else if (!metadata.implicitMembers.length) code.overwrite(closure.start, closure.start + 1, `() => ${grouping}`);
|
|
1620
|
+
if (conciseBody) {
|
|
1621
|
+
const statement = body[0];
|
|
1622
|
+
if (explicitReturn) code.remove(statement.start, statement.start + 6);
|
|
1623
|
+
if (source[statement.end - 1] === ";") code.remove(statement.end - 1, statement.end);
|
|
1624
|
+
code.overwrite(closure.end - 1, closure.end, grouping ? ")" : "", { contentOnly: true });
|
|
1625
|
+
continue;
|
|
1626
|
+
}
|
|
1517
1627
|
if (content && !singleContent) {
|
|
1518
1628
|
let collector;
|
|
1519
1629
|
do
|
|
@@ -1566,13 +1676,16 @@ function transform(source, options = {}) {
|
|
|
1566
1676
|
body.forEach(collect);
|
|
1567
1677
|
code.prependLeft(bodyStart, `const ${collector} = [];`);
|
|
1568
1678
|
code.prependLeft(closure.end - 1, vue ? `;return ${collector};` : `;return ${collector}.length === 1 ? ${collector}[0] : ${collector};`);
|
|
1569
|
-
} else if (
|
|
1679
|
+
} else if (returnsValue && !explicitReturn) {
|
|
1570
1680
|
const statement = singleContent ? expressions[0] : body[0];
|
|
1571
|
-
|
|
1572
|
-
|
|
1681
|
+
if (statement.expression.type === "TwillSwitchExpression" && canInlineSwitchExpression(statement.expression)) directReturns.push(statement.expression);
|
|
1682
|
+
else {
|
|
1683
|
+
code.prependLeft(statement.start, "return (");
|
|
1684
|
+
code.appendLeft(source[statement.end - 1] === ";" ? statement.end - 1 : statement.end, ")");
|
|
1685
|
+
}
|
|
1573
1686
|
}
|
|
1574
1687
|
}
|
|
1575
|
-
if (component) code.prependLeft(closures.at(-1).end, (vue ? "}
|
|
1688
|
+
if (component) code.prependLeft(closures.at(-1).end, (vue ? slotObject ? "}}" : "}" : nativeChild ? "" : bareChild ? "}" : directChild ? ")}" : ")()}") + `</${calleeName(node.callee)}>)`);
|
|
1576
1689
|
else code.appendLeft(closures.at(-1).end, ")");
|
|
1577
1690
|
}
|
|
1578
1691
|
if (parsed.switches.length) lowerSwitchExpressions(source, parsed.ast, parsed.switches, code, options.language ?? inferLanguage(filename), usedNames, parsed.comments, (node, message) => {
|
|
@@ -1581,7 +1694,7 @@ function transform(source, options = {}) {
|
|
|
1581
1694
|
pos: node.start,
|
|
1582
1695
|
loc: node.loc.start
|
|
1583
1696
|
});
|
|
1584
|
-
});
|
|
1697
|
+
}, directReturns, reusable);
|
|
1585
1698
|
for (const end of attributeClosings) code.appendLeft(end, "}");
|
|
1586
1699
|
if (parsed.defers.length) {
|
|
1587
1700
|
const starts = /* @__PURE__ */ new Map();
|
|
@@ -1640,7 +1753,6 @@ function generatedPosition(result, line, column) {
|
|
|
1640
1753
|
bias: GREATEST_LOWER_BOUND
|
|
1641
1754
|
});
|
|
1642
1755
|
}
|
|
1643
|
-
//#endregion
|
|
1644
1756
|
export { transform as a, originalPosition as i, generatedPosition as n, parse as o, inferLanguage as r, TwillSyntaxError as t };
|
|
1645
1757
|
|
|
1646
|
-
//# sourceMappingURL=compiler-
|
|
1758
|
+
//# sourceMappingURL=compiler-DI0H4d1R.js.map
|