@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 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: state.queryFnId,
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
- if (state.hasQueryExpressions) importDecls.push({
1485
+ importDecls.push({
1538
1486
  type: "ImportDeclaration",
1539
1487
  specifiers: [{
1540
1488
  type: "ImportDefaultSpecifier",
1541
- local: state.queryFnId
1489
+ local: state.rootVmId
1542
1490
  }],
1543
1491
  source: {
1544
1492
  type: "Literal",
1545
- value: `${state.providerImportSource}/query`
1493
+ value: `${state.providerImportSource}/vm`
1546
1494
  },
1547
1495
  attributes: []
1548
- });
1549
- importDecls.push({
1496
+ }, {
1550
1497
  type: "ImportDeclaration",
1551
1498
  specifiers: [{
1552
- type: "ImportDefaultSpecifier",
1553
- local: state.rootVmId
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: `${state.providerImportSource}/vm`
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
- body.unshift(...importDecls, createReplacementHolder(state, { type: "preface" }));
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, { CallExpression(node, { state }) {
1830
- const lastArg = node.arguments.at(-1);
1831
- if (lastArg) state.lastArgNodes.add(lastArg);
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",
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.2.1",
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",
@@ -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: state.queryFnId,
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 { VERIFICATION_ONLY_MAPPING_DATA, type VolarMappingResult } from "./mappings.ts";
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 as 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
- init: ANY_INIT,
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
- } as VariableDeclarator,
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
- if (state.hasQueryExpressions) {
288
- importDecls.push({
289
+ importDecls.push(
290
+ {
289
291
  type: "ImportDeclaration",
290
292
  specifiers: [
291
293
  {
292
294
  type: "ImportDefaultSpecifier",
293
- local: state.queryFnId,
295
+ local: state.rootVmId,
294
296
  },
295
297
  ],
296
298
  source: {
297
299
  type: "Literal",
298
- value: `${state.providerImportSource}/query`,
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
- attributes: [],
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
  }),