@gi-tcg/gts-transpiler 0.3.3 → 0.3.4
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/dist/index.js +151 -88
- package/package.json +2 -2
- package/src/transform/gts.ts +11 -70
- package/src/transform/volar/content_start.ts +69 -0
- package/src/transform/volar/index.ts +27 -2
- package/src/transform/volar/printer.ts +40 -2
- package/src/transform/volar/walker.ts +56 -33
package/dist/index.js
CHANGED
|
@@ -814,12 +814,17 @@ const commonGtsVisitor = {
|
|
|
814
814
|
};
|
|
815
815
|
},
|
|
816
816
|
GTSQueryExpression(node, { state, visit }) {
|
|
817
|
-
state.hasQueryExpressions = true;
|
|
818
817
|
return {
|
|
819
818
|
...node,
|
|
820
819
|
type: "CallExpression",
|
|
821
820
|
optional: false,
|
|
822
|
-
callee:
|
|
821
|
+
callee: {
|
|
822
|
+
type: "MemberExpression",
|
|
823
|
+
object: state.fnArgId,
|
|
824
|
+
property: node.star ? state.QueryAllLit : state.QueryLit,
|
|
825
|
+
computed: true,
|
|
826
|
+
optional: false
|
|
827
|
+
},
|
|
823
828
|
arguments: [{
|
|
824
829
|
type: "ArrowFunctionExpression",
|
|
825
830
|
body: visit(node.argument),
|
|
@@ -827,34 +832,6 @@ const commonGtsVisitor = {
|
|
|
827
832
|
expression: true,
|
|
828
833
|
loc: node.argument.loc,
|
|
829
834
|
range: node.argument.range
|
|
830
|
-
}, {
|
|
831
|
-
type: "ObjectExpression",
|
|
832
|
-
properties: [{
|
|
833
|
-
type: "Property",
|
|
834
|
-
key: {
|
|
835
|
-
type: "Identifier",
|
|
836
|
-
name: "star"
|
|
837
|
-
},
|
|
838
|
-
computed: false,
|
|
839
|
-
kind: "init",
|
|
840
|
-
method: false,
|
|
841
|
-
shorthand: false,
|
|
842
|
-
value: {
|
|
843
|
-
type: "Literal",
|
|
844
|
-
value: !!node.star
|
|
845
|
-
}
|
|
846
|
-
}, {
|
|
847
|
-
type: "Property",
|
|
848
|
-
key: {
|
|
849
|
-
type: "Identifier",
|
|
850
|
-
name: "context"
|
|
851
|
-
},
|
|
852
|
-
computed: false,
|
|
853
|
-
kind: "init",
|
|
854
|
-
method: false,
|
|
855
|
-
shorthand: false,
|
|
856
|
-
value: state.fnArgId
|
|
857
|
-
}]
|
|
858
835
|
}]
|
|
859
836
|
};
|
|
860
837
|
}
|
|
@@ -868,18 +845,6 @@ const gtsVisitor = {
|
|
|
868
845
|
}
|
|
869
846
|
body.unshift(...state.bindingStatements);
|
|
870
847
|
state.bindingStatements = [];
|
|
871
|
-
if (state.hasQueryExpressions) body.unshift({
|
|
872
|
-
type: "ImportDeclaration",
|
|
873
|
-
specifiers: [{
|
|
874
|
-
type: "ImportDefaultSpecifier",
|
|
875
|
-
local: state.queryFnId
|
|
876
|
-
}],
|
|
877
|
-
source: {
|
|
878
|
-
type: "Literal",
|
|
879
|
-
value: `${state.providerImportSource}/query`
|
|
880
|
-
},
|
|
881
|
-
attributes: []
|
|
882
|
-
});
|
|
883
848
|
body.unshift({
|
|
884
849
|
type: "ImportDeclaration",
|
|
885
850
|
specifiers: [{
|
|
@@ -1195,10 +1160,6 @@ const initialTranspileState = (option = {}) => {
|
|
|
1195
1160
|
type: "Identifier",
|
|
1196
1161
|
name: "__gts_rootVm"
|
|
1197
1162
|
},
|
|
1198
|
-
queryFnId: {
|
|
1199
|
-
type: "Identifier",
|
|
1200
|
-
name: "__gts_query"
|
|
1201
|
-
},
|
|
1202
1163
|
queryParameters: [{
|
|
1203
1164
|
type: "ObjectPattern",
|
|
1204
1165
|
properties: queryBindings.map((name) => ({
|
|
@@ -1217,28 +1178,17 @@ const initialTranspileState = (option = {}) => {
|
|
|
1217
1178
|
shorthand: true
|
|
1218
1179
|
}))
|
|
1219
1180
|
}],
|
|
1181
|
+
QueryLit: {
|
|
1182
|
+
type: "Literal",
|
|
1183
|
+
value: "~query"
|
|
1184
|
+
},
|
|
1185
|
+
QueryAllLit: {
|
|
1186
|
+
type: "Literal",
|
|
1187
|
+
value: "~queryAll"
|
|
1188
|
+
},
|
|
1220
1189
|
runtimeImportSource: option.runtimeImportSource ?? "@gi-tcg/gts-runtime",
|
|
1221
1190
|
providerImportSource: option.providerImportSource ?? "@gi-tcg/core/gts",
|
|
1222
|
-
queryArg: {
|
|
1223
|
-
type: "ObjectPattern",
|
|
1224
|
-
properties: (option.queryBindings ?? []).map((name) => ({
|
|
1225
|
-
type: "Property",
|
|
1226
|
-
key: {
|
|
1227
|
-
type: "Identifier",
|
|
1228
|
-
name
|
|
1229
|
-
},
|
|
1230
|
-
computed: false,
|
|
1231
|
-
kind: "init",
|
|
1232
|
-
method: false,
|
|
1233
|
-
shorthand: true,
|
|
1234
|
-
value: {
|
|
1235
|
-
type: "Identifier",
|
|
1236
|
-
name
|
|
1237
|
-
}
|
|
1238
|
-
}))
|
|
1239
|
-
},
|
|
1240
1191
|
externalizedBindings: [],
|
|
1241
|
-
hasQueryExpressions: false,
|
|
1242
1192
|
defineIdCounter: 0,
|
|
1243
1193
|
bindingStatements: []
|
|
1244
1194
|
};
|
|
@@ -1375,14 +1325,6 @@ function applyReplacements(state, code, mappings) {
|
|
|
1375
1325
|
//#endregion
|
|
1376
1326
|
//#region src/transform/volar/walker.ts
|
|
1377
1327
|
const EMPTY = { type: "EmptyStatement" };
|
|
1378
|
-
const ANY_INIT = {
|
|
1379
|
-
type: "TSAsExpression",
|
|
1380
|
-
expression: {
|
|
1381
|
-
type: "Literal",
|
|
1382
|
-
value: 0
|
|
1383
|
-
},
|
|
1384
|
-
typeAnnotation: { type: "TSAnyKeyword" }
|
|
1385
|
-
};
|
|
1386
1328
|
const enterVMFromRoot = (state) => {
|
|
1387
1329
|
let defTypeId = {
|
|
1388
1330
|
type: "Identifier",
|
|
@@ -1510,13 +1452,19 @@ const gtsToTypingsWalker = {
|
|
|
1510
1452
|
declarations: [{
|
|
1511
1453
|
type: "VariableDeclarator",
|
|
1512
1454
|
id: extBinding.bindingName,
|
|
1513
|
-
init: ANY_INIT,
|
|
1514
1455
|
typeAnnotation: {
|
|
1515
1456
|
type: "TSTypeAnnotation",
|
|
1516
1457
|
typeAnnotation: {
|
|
1517
1458
|
type: "TSTypeReference",
|
|
1518
1459
|
typeName: extBinding.typingId
|
|
1519
1460
|
}
|
|
1461
|
+
},
|
|
1462
|
+
init: {
|
|
1463
|
+
type: "TSNonNullExpression",
|
|
1464
|
+
expression: {
|
|
1465
|
+
type: "Literal",
|
|
1466
|
+
value: null
|
|
1467
|
+
}
|
|
1520
1468
|
}
|
|
1521
1469
|
}]
|
|
1522
1470
|
};
|
|
@@ -1534,27 +1482,37 @@ const gtsToTypingsWalker = {
|
|
|
1534
1482
|
}
|
|
1535
1483
|
}
|
|
1536
1484
|
const importDecls = [];
|
|
1537
|
-
|
|
1485
|
+
importDecls.push({
|
|
1538
1486
|
type: "ImportDeclaration",
|
|
1539
1487
|
specifiers: [{
|
|
1540
1488
|
type: "ImportDefaultSpecifier",
|
|
1541
|
-
local: state.
|
|
1489
|
+
local: state.rootVmId
|
|
1542
1490
|
}],
|
|
1543
1491
|
source: {
|
|
1544
1492
|
type: "Literal",
|
|
1545
|
-
value: `${state.providerImportSource}/
|
|
1493
|
+
value: `${state.providerImportSource}/vm`
|
|
1546
1494
|
},
|
|
1547
1495
|
attributes: []
|
|
1548
|
-
}
|
|
1549
|
-
importDecls.push({
|
|
1496
|
+
}, {
|
|
1550
1497
|
type: "ImportDeclaration",
|
|
1551
1498
|
specifiers: [{
|
|
1552
|
-
type: "
|
|
1553
|
-
|
|
1499
|
+
type: "ImportSpecifier",
|
|
1500
|
+
imported: {
|
|
1501
|
+
type: "Identifier",
|
|
1502
|
+
name: "createDefine"
|
|
1503
|
+
},
|
|
1504
|
+
local: state.createDefineFnId
|
|
1505
|
+
}, {
|
|
1506
|
+
type: "ImportSpecifier",
|
|
1507
|
+
imported: {
|
|
1508
|
+
type: "Identifier",
|
|
1509
|
+
name: "createBinding"
|
|
1510
|
+
},
|
|
1511
|
+
local: state.createBindingFnId
|
|
1554
1512
|
}],
|
|
1555
1513
|
source: {
|
|
1556
1514
|
type: "Literal",
|
|
1557
|
-
value:
|
|
1515
|
+
value: state.runtimeImportSource
|
|
1558
1516
|
},
|
|
1559
1517
|
attributes: []
|
|
1560
1518
|
});
|
|
@@ -1562,7 +1520,22 @@ const gtsToTypingsWalker = {
|
|
|
1562
1520
|
state.diagnosticsOnTopNodes.add(importDecl.source);
|
|
1563
1521
|
for (const specifier of importDecl.specifiers) state.diagnosticsOnTopNodes.add(specifier);
|
|
1564
1522
|
}
|
|
1565
|
-
|
|
1523
|
+
const lastImportDecl = importDecls.pop() ?? {
|
|
1524
|
+
type: "ImportDeclaration",
|
|
1525
|
+
specifiers: [],
|
|
1526
|
+
source: {
|
|
1527
|
+
type: "Literal",
|
|
1528
|
+
value: ""
|
|
1529
|
+
},
|
|
1530
|
+
attributes: []
|
|
1531
|
+
};
|
|
1532
|
+
body.unshift(...importDecls, {
|
|
1533
|
+
type: "ExpressionStatement",
|
|
1534
|
+
expression: {
|
|
1535
|
+
type: "Literal",
|
|
1536
|
+
value: 0
|
|
1537
|
+
}
|
|
1538
|
+
}, lastImportDecl, createReplacementHolder(state, { type: "preface" }));
|
|
1566
1539
|
return {
|
|
1567
1540
|
...node,
|
|
1568
1541
|
body
|
|
@@ -1770,6 +1743,27 @@ function getPrintOptions(source, state) {
|
|
|
1770
1743
|
end: 1
|
|
1771
1744
|
}, generatedStart, generatedEnd, VERIFICATION_ONLY_MAPPING_DATA);
|
|
1772
1745
|
}
|
|
1746
|
+
},
|
|
1747
|
+
ImportSpecifier(node, context) {
|
|
1748
|
+
const generatedStart = context.generatedOffset;
|
|
1749
|
+
defaultPrinters.ImportSpecifier(node, context);
|
|
1750
|
+
if (state.diagnosticsOnTopNodes.has(node)) {
|
|
1751
|
+
const generatedEnd = context.generatedOffset;
|
|
1752
|
+
context.createExtraMapping({
|
|
1753
|
+
start: 0,
|
|
1754
|
+
end: 1
|
|
1755
|
+
}, generatedStart, generatedEnd, VERIFICATION_ONLY_MAPPING_DATA);
|
|
1756
|
+
}
|
|
1757
|
+
},
|
|
1758
|
+
ImportDeclaration(node, context) {
|
|
1759
|
+
defaultPrinters.ImportDeclaration(node, context);
|
|
1760
|
+
if (state.lastImportDeclarationIfGen === node) {
|
|
1761
|
+
context.write("\n");
|
|
1762
|
+
context.createExtraMapping({
|
|
1763
|
+
start: state.contentStartOffset,
|
|
1764
|
+
end: state.contentStartOffset + 1
|
|
1765
|
+
}, context.generatedOffset, context.generatedOffset + 1, DEFAULT_VOLAR_MAPPING_DATA);
|
|
1766
|
+
}
|
|
1773
1767
|
}
|
|
1774
1768
|
},
|
|
1775
1769
|
experimentalGetLeftParenSourceRange: (node) => {
|
|
@@ -1783,6 +1777,62 @@ function getPrintOptions(source, state) {
|
|
|
1783
1777
|
};
|
|
1784
1778
|
}
|
|
1785
1779
|
//#endregion
|
|
1780
|
+
//#region src/transform/volar/content_start.ts
|
|
1781
|
+
/**
|
|
1782
|
+
* Get the character offset after hashbang and file-scope leading comments.
|
|
1783
|
+
* This is for simulating the behavior of TSServer insert auto-imports if no
|
|
1784
|
+
* imports are present.
|
|
1785
|
+
* @param source
|
|
1786
|
+
* @returns
|
|
1787
|
+
*/
|
|
1788
|
+
function getContentStartOffset(source) {
|
|
1789
|
+
let result = 0;
|
|
1790
|
+
/** Current visiting character */
|
|
1791
|
+
let pos = 0;
|
|
1792
|
+
if (source.startsWith("#!")) {
|
|
1793
|
+
const nl = source.indexOf("\n", pos);
|
|
1794
|
+
if (nl === -1) return source.length;
|
|
1795
|
+
pos = nl + 1;
|
|
1796
|
+
}
|
|
1797
|
+
const skipWhitespaces = () => {
|
|
1798
|
+
let newlineCount = 0;
|
|
1799
|
+
for (; pos < source.length; pos++) {
|
|
1800
|
+
const ch = source[pos];
|
|
1801
|
+
if (ch === " " || ch === " " || ch === "\r") continue;
|
|
1802
|
+
if (ch === "\n") {
|
|
1803
|
+
newlineCount++;
|
|
1804
|
+
continue;
|
|
1805
|
+
}
|
|
1806
|
+
break;
|
|
1807
|
+
}
|
|
1808
|
+
return newlineCount;
|
|
1809
|
+
};
|
|
1810
|
+
skipWhitespaces();
|
|
1811
|
+
while (pos < source.length) {
|
|
1812
|
+
let newlineCount = 0;
|
|
1813
|
+
if (source[pos] === "/" && pos + 1 < source.length) {
|
|
1814
|
+
if (source[pos + 1] === "/") {
|
|
1815
|
+
const nl = source.indexOf("\n", pos);
|
|
1816
|
+
pos = nl === -1 ? source.length : nl + 1;
|
|
1817
|
+
newlineCount++;
|
|
1818
|
+
} else if (source[pos + 1] === "*") {
|
|
1819
|
+
pos += 2;
|
|
1820
|
+
while (pos < source.length) {
|
|
1821
|
+
if (source[pos] === "*" && pos + 1 < source.length && source[pos + 1] === "/") {
|
|
1822
|
+
pos += 2;
|
|
1823
|
+
break;
|
|
1824
|
+
}
|
|
1825
|
+
pos++;
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1828
|
+
result = pos;
|
|
1829
|
+
newlineCount += skipWhitespaces();
|
|
1830
|
+
if (newlineCount >= 2) break;
|
|
1831
|
+
} else break;
|
|
1832
|
+
}
|
|
1833
|
+
return result;
|
|
1834
|
+
}
|
|
1835
|
+
//#endregion
|
|
1786
1836
|
//#region src/transform/volar/index.ts
|
|
1787
1837
|
function transformForVolar(ast, option, sourceInfo) {
|
|
1788
1838
|
const state = {
|
|
@@ -1818,18 +1868,25 @@ function transformForVolar(ast, option, sourceInfo) {
|
|
|
1818
1868
|
namedAttributeCalleeLParenRange: /* @__PURE__ */ new WeakMap(),
|
|
1819
1869
|
literalFromIdentifier: /* @__PURE__ */ new WeakSet(),
|
|
1820
1870
|
lastArgNodes: /* @__PURE__ */ new WeakSet(),
|
|
1871
|
+
lastImportDeclarationIfGen: null,
|
|
1821
1872
|
diagnosticsOnTopNodes: /* @__PURE__ */ new WeakSet(),
|
|
1822
|
-
extraMappings: []
|
|
1873
|
+
extraMappings: [],
|
|
1874
|
+
contentStartOffset: getContentStartOffset(sourceInfo.content)
|
|
1823
1875
|
};
|
|
1824
1876
|
walk(ast, state, { _(node, { state, next }) {
|
|
1825
1877
|
if (node.range) state.sourceNodes.add(node);
|
|
1826
1878
|
next();
|
|
1827
1879
|
} });
|
|
1828
1880
|
const newAst = walk(ast, state, gtsToTypingsWalker);
|
|
1829
|
-
walk(newAst, state, {
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1881
|
+
walk(newAst, state, {
|
|
1882
|
+
CallExpression(node, { state }) {
|
|
1883
|
+
const lastArg = node.arguments.at(-1);
|
|
1884
|
+
if (lastArg) state.lastArgNodes.add(lastArg);
|
|
1885
|
+
},
|
|
1886
|
+
ImportDeclaration(node, { state }) {
|
|
1887
|
+
if (!state.sourceNodes.has(node)) state.lastImportDeclarationIfGen = node;
|
|
1888
|
+
}
|
|
1889
|
+
});
|
|
1833
1890
|
let { code, mappings } = print$1(newAst, getPrintOptions(sourceInfo.content, state));
|
|
1834
1891
|
code = applyReplacements(state, code, mappings);
|
|
1835
1892
|
for (const extraMapping of state.extraMappings) {
|
|
@@ -1842,6 +1899,12 @@ function transformForVolar(ast, option, sourceInfo) {
|
|
|
1842
1899
|
data: VERIFICATION_ONLY_MAPPING_DATA
|
|
1843
1900
|
});
|
|
1844
1901
|
}
|
|
1902
|
+
walk(newAst, null, { ImportDeclaration(node) {
|
|
1903
|
+
if (!node.range) return;
|
|
1904
|
+
const endOffset = node.range[1];
|
|
1905
|
+
const mappingEndsWithThisImport = mappings.find((m) => m.sourceOffsets[0] + m.lengths[0] === endOffset);
|
|
1906
|
+
if (mappingEndsWithThisImport?.lengths[0]) mappingEndsWithThisImport.lengths[0] += 1;
|
|
1907
|
+
} });
|
|
1845
1908
|
return {
|
|
1846
1909
|
code,
|
|
1847
1910
|
mappings
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gi-tcg/gts-transpiler",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"repository": "https://github.com/piovium/gts.git",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"@sveltejs/acorn-typescript": "^1.0.8",
|
|
20
20
|
"acorn": "^8.15.0",
|
|
21
21
|
"dedent": "^1.7.2",
|
|
22
|
-
"espolar": "^0.
|
|
22
|
+
"espolar": "^0.5.1",
|
|
23
23
|
"esrap": "2.2.1",
|
|
24
24
|
"magic-string": "^0.30.21",
|
|
25
25
|
"path-browserify-esm": "^1.0.6",
|
package/src/transform/gts.ts
CHANGED
|
@@ -10,7 +10,6 @@ import type {
|
|
|
10
10
|
ModuleDeclaration,
|
|
11
11
|
Node,
|
|
12
12
|
ObjectExpression,
|
|
13
|
-
ObjectPattern,
|
|
14
13
|
Pattern,
|
|
15
14
|
Program,
|
|
16
15
|
Statement,
|
|
@@ -35,14 +34,12 @@ export interface TranspileState {
|
|
|
35
34
|
readonly fnArgId: Identifier;
|
|
36
35
|
readonly shortcutFunctionParameters: Pattern[];
|
|
37
36
|
readonly rootVmId: Identifier;
|
|
38
|
-
readonly queryFnId: Identifier;
|
|
39
37
|
readonly queryParameters: Pattern[];
|
|
38
|
+
readonly QueryLit: Literal;
|
|
39
|
+
readonly QueryAllLit: Literal;
|
|
40
40
|
|
|
41
41
|
readonly runtimeImportSource: string;
|
|
42
42
|
readonly providerImportSource: string;
|
|
43
|
-
readonly queryArg: ObjectPattern;
|
|
44
|
-
|
|
45
|
-
hasQueryExpressions: boolean;
|
|
46
43
|
|
|
47
44
|
externalizedBindings: ExternalizedBinding[];
|
|
48
45
|
/** Internal counters / state for emitting per-define nodes & bindings */
|
|
@@ -137,12 +134,17 @@ export const commonGtsVisitor: Visitors<Node, TranspileState> = {
|
|
|
137
134
|
};
|
|
138
135
|
},
|
|
139
136
|
GTSQueryExpression(node, { state, visit }) {
|
|
140
|
-
state.hasQueryExpressions = true;
|
|
141
137
|
return {
|
|
142
138
|
...node,
|
|
143
139
|
type: "CallExpression",
|
|
144
140
|
optional: false,
|
|
145
|
-
callee:
|
|
141
|
+
callee: {
|
|
142
|
+
type: "MemberExpression",
|
|
143
|
+
object: state.fnArgId,
|
|
144
|
+
property: node.star ? state.QueryAllLit : state.QueryLit,
|
|
145
|
+
computed: true,
|
|
146
|
+
optional: false,
|
|
147
|
+
},
|
|
146
148
|
arguments: [
|
|
147
149
|
{
|
|
148
150
|
type: "ArrowFunctionExpression",
|
|
@@ -152,45 +154,12 @@ export const commonGtsVisitor: Visitors<Node, TranspileState> = {
|
|
|
152
154
|
loc: node.argument.loc,
|
|
153
155
|
range: node.argument.range,
|
|
154
156
|
},
|
|
155
|
-
{
|
|
156
|
-
type: "ObjectExpression",
|
|
157
|
-
properties: [
|
|
158
|
-
{
|
|
159
|
-
type: "Property",
|
|
160
|
-
key: { type: "Identifier", name: "star" },
|
|
161
|
-
computed: false,
|
|
162
|
-
kind: "init",
|
|
163
|
-
method: false,
|
|
164
|
-
shorthand: false,
|
|
165
|
-
value: {
|
|
166
|
-
type: "Literal",
|
|
167
|
-
value: !!node.star,
|
|
168
|
-
},
|
|
169
|
-
},
|
|
170
|
-
{
|
|
171
|
-
type: "Property",
|
|
172
|
-
key: { type: "Identifier", name: "context" },
|
|
173
|
-
computed: false,
|
|
174
|
-
kind: "init",
|
|
175
|
-
method: false,
|
|
176
|
-
shorthand: false,
|
|
177
|
-
value: state.fnArgId,
|
|
178
|
-
}
|
|
179
|
-
],
|
|
180
|
-
},
|
|
181
157
|
],
|
|
182
158
|
};
|
|
183
159
|
},
|
|
184
160
|
};
|
|
185
161
|
|
|
186
162
|
const gtsVisitor: Visitors<Node, TranspileState> = {
|
|
187
|
-
// _(node, { next }) {
|
|
188
|
-
// console.log(node.type, !!node.leadingComments)
|
|
189
|
-
// if (node.leadingComments) {
|
|
190
|
-
// console.log(node.leadingComments);
|
|
191
|
-
// }
|
|
192
|
-
// return next();
|
|
193
|
-
// },
|
|
194
163
|
Program(node, { state, visit }) {
|
|
195
164
|
const body: Program["body"] = [];
|
|
196
165
|
for (const stmt of node.body) {
|
|
@@ -201,22 +170,6 @@ const gtsVisitor: Visitors<Node, TranspileState> = {
|
|
|
201
170
|
body.unshift(...state.bindingStatements);
|
|
202
171
|
state.bindingStatements = [];
|
|
203
172
|
|
|
204
|
-
if (state.hasQueryExpressions) {
|
|
205
|
-
body.unshift({
|
|
206
|
-
type: "ImportDeclaration",
|
|
207
|
-
specifiers: [
|
|
208
|
-
{
|
|
209
|
-
type: "ImportDefaultSpecifier",
|
|
210
|
-
local: state.queryFnId,
|
|
211
|
-
},
|
|
212
|
-
],
|
|
213
|
-
source: {
|
|
214
|
-
type: "Literal",
|
|
215
|
-
value: `${state.providerImportSource}/query`,
|
|
216
|
-
},
|
|
217
|
-
attributes: [],
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
173
|
body.unshift(
|
|
221
174
|
{
|
|
222
175
|
type: "ImportDeclaration",
|
|
@@ -555,26 +508,14 @@ export const initialTranspileState = (
|
|
|
555
508
|
fnArgId,
|
|
556
509
|
shortcutFunctionParameters,
|
|
557
510
|
rootVmId: { type: "Identifier", name: "__gts_rootVm" },
|
|
558
|
-
queryFnId: { type: "Identifier", name: "__gts_query" },
|
|
559
511
|
queryParameters,
|
|
512
|
+
QueryLit: { type: "Literal", value: "~query" },
|
|
513
|
+
QueryAllLit: { type: "Literal", value: "~queryAll" },
|
|
560
514
|
|
|
561
515
|
runtimeImportSource: option.runtimeImportSource ?? "@gi-tcg/gts-runtime",
|
|
562
516
|
providerImportSource: option.providerImportSource ?? "@gi-tcg/core/gts",
|
|
563
|
-
queryArg: {
|
|
564
|
-
type: "ObjectPattern",
|
|
565
|
-
properties: (option.queryBindings ?? []).map((name) => ({
|
|
566
|
-
type: "Property",
|
|
567
|
-
key: { type: "Identifier", name },
|
|
568
|
-
computed: false,
|
|
569
|
-
kind: "init",
|
|
570
|
-
method: false,
|
|
571
|
-
shorthand: true,
|
|
572
|
-
value: { type: "Identifier", name },
|
|
573
|
-
})),
|
|
574
|
-
},
|
|
575
517
|
|
|
576
518
|
externalizedBindings: [],
|
|
577
|
-
hasQueryExpressions: false,
|
|
578
519
|
defineIdCounter: 0,
|
|
579
520
|
|
|
580
521
|
bindingStatements: [],
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the character offset after hashbang and file-scope leading comments.
|
|
3
|
+
* This is for simulating the behavior of TSServer insert auto-imports if no
|
|
4
|
+
* imports are present.
|
|
5
|
+
* @param source
|
|
6
|
+
* @returns
|
|
7
|
+
*/
|
|
8
|
+
export function getContentStartOffset(source: string): number {
|
|
9
|
+
let result = 0;
|
|
10
|
+
/** Current visiting character */
|
|
11
|
+
let pos = 0;
|
|
12
|
+
if (source.startsWith("#!")) {
|
|
13
|
+
const nl = source.indexOf("\n", pos);
|
|
14
|
+
if (nl === -1) {
|
|
15
|
+
return source.length;
|
|
16
|
+
}
|
|
17
|
+
pos = nl + 1;
|
|
18
|
+
}
|
|
19
|
+
const skipWhitespaces = () => {
|
|
20
|
+
let newlineCount = 0;
|
|
21
|
+
for (; pos < source.length; pos++) {
|
|
22
|
+
const ch = source[pos];
|
|
23
|
+
if (ch === " " || ch === "\t" || ch === "\r") {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (ch === "\n") {
|
|
27
|
+
newlineCount++;
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
return newlineCount;
|
|
33
|
+
}
|
|
34
|
+
skipWhitespaces();
|
|
35
|
+
while (pos < source.length) {
|
|
36
|
+
let newlineCount = 0;
|
|
37
|
+
// Eat a comment
|
|
38
|
+
if (source[pos] === "/" && pos + 1 < source.length) {
|
|
39
|
+
if (source[pos + 1] === "/") {
|
|
40
|
+
const nl = source.indexOf("\n", pos);
|
|
41
|
+
pos = nl === -1 ? source.length : nl + 1;
|
|
42
|
+
newlineCount++;
|
|
43
|
+
} else if (source[pos + 1] === "*") {
|
|
44
|
+
pos += 2;
|
|
45
|
+
while (pos < source.length) {
|
|
46
|
+
if (
|
|
47
|
+
source[pos] === "*" &&
|
|
48
|
+
pos + 1 < source.length &&
|
|
49
|
+
source[pos + 1] === "/"
|
|
50
|
+
) {
|
|
51
|
+
pos += 2;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
pos++;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
result = pos;
|
|
58
|
+
newlineCount += skipWhitespaces();
|
|
59
|
+
// If there are already 2 newlines in whitespace, the leading comments are ended
|
|
60
|
+
if (newlineCount >= 2) {
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
// Not a comment, stop here
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
@@ -10,8 +10,12 @@ import {
|
|
|
10
10
|
import { gtsToTypingsWalker, type TypingTranspileState } from "./walker.ts";
|
|
11
11
|
import { applyReplacements } from "./replacements.ts";
|
|
12
12
|
import type { Program } from "estree";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
VERIFICATION_ONLY_MAPPING_DATA,
|
|
15
|
+
type VolarMappingResult,
|
|
16
|
+
} from "./mappings.ts";
|
|
14
17
|
import { getPrintOptions } from "./printer.ts";
|
|
18
|
+
import { getContentStartOffset } from "./content_start.ts";
|
|
15
19
|
|
|
16
20
|
export function transformForVolar(
|
|
17
21
|
ast: Program,
|
|
@@ -40,8 +44,10 @@ export function transformForVolar(
|
|
|
40
44
|
namedAttributeCalleeLParenRange: new WeakMap(),
|
|
41
45
|
literalFromIdentifier: new WeakSet(),
|
|
42
46
|
lastArgNodes: new WeakSet(),
|
|
47
|
+
lastImportDeclarationIfGen: null,
|
|
43
48
|
diagnosticsOnTopNodes: new WeakSet(),
|
|
44
49
|
extraMappings: [],
|
|
50
|
+
contentStartOffset: getContentStartOffset(sourceInfo.content),
|
|
45
51
|
};
|
|
46
52
|
// mark sourceNodes before the transformation
|
|
47
53
|
walk(ast as AST.Node, state, {
|
|
@@ -60,7 +66,12 @@ export function transformForVolar(
|
|
|
60
66
|
if (lastArg) {
|
|
61
67
|
state.lastArgNodes.add(lastArg);
|
|
62
68
|
}
|
|
63
|
-
}
|
|
69
|
+
},
|
|
70
|
+
ImportDeclaration(node, { state }) {
|
|
71
|
+
if (!state.sourceNodes.has(node)) {
|
|
72
|
+
state.lastImportDeclarationIfGen = node;
|
|
73
|
+
}
|
|
74
|
+
},
|
|
64
75
|
});
|
|
65
76
|
const printOptions = getPrintOptions(sourceInfo.content, state);
|
|
66
77
|
let { code, mappings } = print(newAst, printOptions);
|
|
@@ -75,6 +86,20 @@ export function transformForVolar(
|
|
|
75
86
|
data: VERIFICATION_ONLY_MAPPING_DATA,
|
|
76
87
|
});
|
|
77
88
|
}
|
|
89
|
+
walk(newAst, null, {
|
|
90
|
+
ImportDeclaration(node) {
|
|
91
|
+
if (!node.range) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const endOffset = node.range[1];
|
|
95
|
+
const mappingEndsWithThisImport = mappings.find(
|
|
96
|
+
(m) => m.sourceOffsets[0] + m.lengths[0] === endOffset,
|
|
97
|
+
);
|
|
98
|
+
if (mappingEndsWithThisImport?.lengths[0]) {
|
|
99
|
+
mappingEndsWithThisImport.lengths[0] += 1; // include the newline after the import
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
});
|
|
78
103
|
return {
|
|
79
104
|
code,
|
|
80
105
|
mappings,
|
|
@@ -67,7 +67,7 @@ export function getPrintOptions(
|
|
|
67
67
|
defaultPrinters.Literal(node, context);
|
|
68
68
|
}
|
|
69
69
|
// For generated `import xxx from "yyy"`, add mappings from xxx and yyy
|
|
70
|
-
// to the top-of-file for diagnostics around missing / wrong imports.
|
|
70
|
+
// to the top-of-file for diagnostics around missing / wrong imports. [[1]]
|
|
71
71
|
if (state.diagnosticsOnTopNodes.has(node)) {
|
|
72
72
|
const generatedEnd = context.generatedOffset;
|
|
73
73
|
context.createExtraMapping(
|
|
@@ -78,10 +78,25 @@ export function getPrintOptions(
|
|
|
78
78
|
);
|
|
79
79
|
}
|
|
80
80
|
},
|
|
81
|
+
// Same as [[1]]
|
|
81
82
|
ImportDefaultSpecifier(node, context) {
|
|
82
83
|
const generatedStart = context.generatedOffset;
|
|
83
84
|
defaultPrinters.ImportDefaultSpecifier(node, context);
|
|
84
|
-
if (state.diagnosticsOnTopNodes.has(node
|
|
85
|
+
if (state.diagnosticsOnTopNodes.has(node)) {
|
|
86
|
+
const generatedEnd = context.generatedOffset;
|
|
87
|
+
context.createExtraMapping(
|
|
88
|
+
{ start: 0, end: 1 },
|
|
89
|
+
generatedStart,
|
|
90
|
+
generatedEnd,
|
|
91
|
+
VERIFICATION_ONLY_MAPPING_DATA,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
// Same as [[1]]
|
|
96
|
+
ImportSpecifier(node, context) {
|
|
97
|
+
const generatedStart = context.generatedOffset;
|
|
98
|
+
defaultPrinters.ImportSpecifier(node, context);
|
|
99
|
+
if (state.diagnosticsOnTopNodes.has(node)) {
|
|
85
100
|
const generatedEnd = context.generatedOffset;
|
|
86
101
|
context.createExtraMapping(
|
|
87
102
|
{ start: 0, end: 1 },
|
|
@@ -91,6 +106,29 @@ export function getPrintOptions(
|
|
|
91
106
|
);
|
|
92
107
|
}
|
|
93
108
|
},
|
|
109
|
+
// If the last import declaration is a generated one, because of we ensured that
|
|
110
|
+
// the generated import declarations are always unsorted, so TSServer will set the
|
|
111
|
+
// auto-insertion point next to this last import declaration.
|
|
112
|
+
// Add a mapping to that position (a written newline character) to the top-of-file,
|
|
113
|
+
// after skipping hashbang and leading comments.
|
|
114
|
+
// NOTE: since the insertion derived from here won't add additional newline *before*
|
|
115
|
+
// the inserted text, so here is a difference behavior from standard TSServer and our
|
|
116
|
+
// language server. We'd try our best.
|
|
117
|
+
ImportDeclaration(node, context) {
|
|
118
|
+
defaultPrinters.ImportDeclaration(node, context);
|
|
119
|
+
if (state.lastImportDeclarationIfGen === node) {
|
|
120
|
+
context.write("\n");
|
|
121
|
+
context.createExtraMapping(
|
|
122
|
+
{
|
|
123
|
+
start: state.contentStartOffset,
|
|
124
|
+
end: state.contentStartOffset + 1,
|
|
125
|
+
},
|
|
126
|
+
context.generatedOffset,
|
|
127
|
+
context.generatedOffset + 1,
|
|
128
|
+
DEFAULT_VOLAR_MAPPING_DATA,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
},
|
|
94
132
|
},
|
|
95
133
|
// Enable triggering signature completion
|
|
96
134
|
experimentalGetLeftParenSourceRange: (node) => {
|
|
@@ -67,6 +67,11 @@ export interface TypingTranspileState extends TranspileState {
|
|
|
67
67
|
literalFromIdentifier: WeakSet<Literal>;
|
|
68
68
|
/** Nodes that are last arguments of a CallExpression */
|
|
69
69
|
lastArgNodes: WeakSet<Node>;
|
|
70
|
+
/**
|
|
71
|
+
* The last import declaration, record if it is a generated one.
|
|
72
|
+
* This declaration marks the TS auto-import insertion point.
|
|
73
|
+
*/
|
|
74
|
+
lastImportDeclarationIfGen: ImportDeclaration | null;
|
|
70
75
|
/** Nodes that have a diagnostic mappings to the top of the file */
|
|
71
76
|
diagnosticsOnTopNodes: WeakSet<Node>;
|
|
72
77
|
/** Extra mappings, the generated range will be found by the needle after replacement */
|
|
@@ -75,22 +80,12 @@ export interface TypingTranspileState extends TranspileState {
|
|
|
75
80
|
length: number;
|
|
76
81
|
generatedNeedle: string;
|
|
77
82
|
}[];
|
|
83
|
+
/** Character offset after hashbang and file-scope leading comments */
|
|
84
|
+
contentStartOffset: number;
|
|
78
85
|
}
|
|
79
86
|
|
|
80
87
|
const EMPTY: EmptyStatement = { type: "EmptyStatement" };
|
|
81
88
|
|
|
82
|
-
const ANY = {
|
|
83
|
-
type: "TSAnyKeyword",
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
// definite not supported by esrap yet, so we init the binding with an `as any` cast
|
|
87
|
-
// https://github.com/sveltejs/esrap/issues/95
|
|
88
|
-
const ANY_INIT = {
|
|
89
|
-
type: "TSAsExpression",
|
|
90
|
-
expression: { type: "Literal", value: 0 },
|
|
91
|
-
typeAnnotation: ANY,
|
|
92
|
-
} as {} as Expression;
|
|
93
|
-
|
|
94
89
|
const enterVMFromRoot = (state: TypingTranspileState) => {
|
|
95
90
|
let defTypeId: Identifier = {
|
|
96
91
|
type: "Identifier",
|
|
@@ -258,7 +253,7 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
258
253
|
{
|
|
259
254
|
type: "VariableDeclarator",
|
|
260
255
|
id: extBinding.bindingName,
|
|
261
|
-
|
|
256
|
+
// @ts-expect-error TS property not provided in ESTree
|
|
262
257
|
typeAnnotation: {
|
|
263
258
|
type: "TSTypeAnnotation",
|
|
264
259
|
typeAnnotation: {
|
|
@@ -266,7 +261,14 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
266
261
|
typeName: extBinding.typingId,
|
|
267
262
|
},
|
|
268
263
|
},
|
|
269
|
-
|
|
264
|
+
init: {
|
|
265
|
+
type: "TSNonNullExpression",
|
|
266
|
+
expression: {
|
|
267
|
+
type: "Literal",
|
|
268
|
+
value: null,
|
|
269
|
+
} satisfies Expression,
|
|
270
|
+
} as any,
|
|
271
|
+
},
|
|
270
272
|
],
|
|
271
273
|
};
|
|
272
274
|
if (extBinding.export) {
|
|
@@ -284,44 +286,65 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
284
286
|
}
|
|
285
287
|
}
|
|
286
288
|
const importDecls: ImportDeclaration[] = [];
|
|
287
|
-
|
|
288
|
-
|
|
289
|
+
importDecls.push(
|
|
290
|
+
{
|
|
289
291
|
type: "ImportDeclaration",
|
|
290
292
|
specifiers: [
|
|
291
293
|
{
|
|
292
294
|
type: "ImportDefaultSpecifier",
|
|
293
|
-
local: state.
|
|
295
|
+
local: state.rootVmId,
|
|
294
296
|
},
|
|
295
297
|
],
|
|
296
298
|
source: {
|
|
297
299
|
type: "Literal",
|
|
298
|
-
value: `${state.providerImportSource}/
|
|
300
|
+
value: `${state.providerImportSource}/vm`,
|
|
299
301
|
},
|
|
300
302
|
attributes: [],
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
importDecls.push({
|
|
304
|
-
type: "ImportDeclaration",
|
|
305
|
-
specifiers: [
|
|
306
|
-
{
|
|
307
|
-
type: "ImportDefaultSpecifier",
|
|
308
|
-
local: state.rootVmId,
|
|
309
|
-
},
|
|
310
|
-
],
|
|
311
|
-
source: {
|
|
312
|
-
type: "Literal",
|
|
313
|
-
value: `${state.providerImportSource}/vm`,
|
|
314
303
|
},
|
|
315
|
-
|
|
316
|
-
|
|
304
|
+
{
|
|
305
|
+
type: "ImportDeclaration",
|
|
306
|
+
specifiers: [
|
|
307
|
+
{
|
|
308
|
+
type: "ImportSpecifier",
|
|
309
|
+
imported: { type: "Identifier", name: "createDefine" },
|
|
310
|
+
local: state.createDefineFnId,
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
type: "ImportSpecifier",
|
|
314
|
+
imported: { type: "Identifier", name: "createBinding" },
|
|
315
|
+
local: state.createBindingFnId,
|
|
316
|
+
},
|
|
317
|
+
],
|
|
318
|
+
source: { type: "Literal", value: state.runtimeImportSource },
|
|
319
|
+
attributes: [],
|
|
320
|
+
},
|
|
321
|
+
);
|
|
317
322
|
for (const importDecl of importDecls) {
|
|
318
323
|
state.diagnosticsOnTopNodes.add(importDecl.source);
|
|
319
324
|
for (const specifier of importDecl.specifiers) {
|
|
320
325
|
state.diagnosticsOnTopNodes.add(specifier);
|
|
321
326
|
}
|
|
322
327
|
}
|
|
328
|
+
const lastImportDecl = importDecls.pop() ?? {
|
|
329
|
+
type: "ImportDeclaration",
|
|
330
|
+
specifiers: [],
|
|
331
|
+
source: { type: "Literal", value: "" },
|
|
332
|
+
attributes: [],
|
|
333
|
+
} satisfies ImportDeclaration;
|
|
323
334
|
body.unshift(
|
|
324
335
|
...importDecls,
|
|
336
|
+
// Add an unrelated statement between system generated imports to make them unsorted,
|
|
337
|
+
// so that TSServer will always insert auto-imports after the last ImportDeclaration.
|
|
338
|
+
// `lastImportDecl` will be marked as insertion point in printer if no user's
|
|
339
|
+
// ImportDeclaration is provided
|
|
340
|
+
{
|
|
341
|
+
type: "ExpressionStatement",
|
|
342
|
+
expression: {
|
|
343
|
+
type: "Literal",
|
|
344
|
+
value: 0,
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
lastImportDecl,
|
|
325
348
|
createReplacementHolder(state, {
|
|
326
349
|
type: "preface",
|
|
327
350
|
}),
|