@gi-tcg/gts-transpiler 0.2.0 → 0.3.1

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.
@@ -192,19 +192,12 @@ export function getGeneratedPosition(
192
192
  src_line: number,
193
193
  src_column: number,
194
194
  srcToGenMap: CodeToGeneratedMap,
195
- ): CodePosition | undefined {
195
+ ): CodePosition[] {
196
196
  const key = `${src_line}:${src_column}`;
197
197
  const positions = srcToGenMap.get(key);
198
198
 
199
- if (!positions || positions.length === 0) {
200
- // No mapping found in source map - this shouldn't happen since all tokens should have mappings
201
- // throw new Error(
202
- // `No source map entry for position "${src_line}:${src_column}"`
203
- // );
204
- }
205
-
206
199
  // If multiple generated positions map to same source, return the first
207
- return positions?.[0];
200
+ return positions || [];
208
201
  }
209
202
  // Helper to create a line-to-offset lookup table
210
203
  function createLineOffsets(content: string): number[] {
@@ -245,7 +238,7 @@ export function convertToVolarMappings(
245
238
  source: string,
246
239
  sourceMap: SourceMap,
247
240
  tokens: LeafToken[],
248
- additionalMappings: Map<string, string>,
241
+ extraMappings: Map<string, string>,
249
242
  ): CodeMapping[] {
250
243
  const sourceLineOffsets = createLineOffsets(source);
251
244
  const generatedLineOffsets = createLineOffsets(generated);
@@ -263,13 +256,18 @@ export function convertToVolarMappings(
263
256
  token.loc.start.column,
264
257
  sourceLineOffsets,
265
258
  );
259
+ sourceStart += token.sourceStartOffset ?? 0;
260
+
266
261
  const sourceEnd = locToOffset(
267
262
  token.loc.end.line,
268
263
  token.loc.end.column,
269
264
  sourceLineOffsets,
270
265
  );
266
+
271
267
  let sourceLength = token.sourceLength ?? sourceEnd - sourceStart;
272
- const genLineCol = getGeneratedPosition(
268
+
269
+ sourceLength += token.sourceLengthOffset ?? 0;
270
+ const [genLineCol] = getGeneratedPosition(
273
271
  token.loc.start.line,
274
272
  token.loc.start.column,
275
273
  srcToGenMap,
@@ -283,19 +281,6 @@ export function convertToVolarMappings(
283
281
  genLineCol.column,
284
282
  generatedLineOffsets,
285
283
  );
286
- if (token.locationAdjustment) {
287
- // maps verification back to the start of source
288
- mappings.push({
289
- sourceOffsets: [sourceStart],
290
- generatedOffsets: [genStart],
291
- lengths: [0],
292
- generatedLengths: [token.locationAdjustment.generatedLength],
293
- data: {
294
- verification: true,
295
- },
296
- });
297
- genStart += token.locationAdjustment.startOffset;
298
- }
299
284
  if (token.isDummy) {
300
285
  // A dummy token might be generated for a missing property / argument.
301
286
  // Notice that when facing this scenario, the parser tries to 'defer' and step through
@@ -308,9 +293,25 @@ export function convertToVolarMappings(
308
293
  sourceLength++;
309
294
  }
310
295
  }
311
-
312
296
  const generatedLength = token.generatedLength ?? sourceLength;
313
297
 
298
+ if (
299
+ typeof token.generatedStartOffset === "number" &&
300
+ token.generatedStartOffset > 0
301
+ ) {
302
+ // maps verification back to the start of source
303
+ mappings.push({
304
+ sourceOffsets: [sourceStart],
305
+ generatedOffsets: [genStart],
306
+ lengths: [0],
307
+ generatedLengths: [generatedLength],
308
+ data: {
309
+ verification: true,
310
+ },
311
+ });
312
+ genStart += token.generatedStartOffset;
313
+ }
314
+
314
315
  mappings.push({
315
316
  sourceOffsets: [sourceStart],
316
317
  generatedOffsets: [genStart],
@@ -320,7 +321,25 @@ export function convertToVolarMappings(
320
321
  });
321
322
  }
322
323
 
323
- for (const [loc, codeSnippet] of additionalMappings) {
324
+ // Handle extra mappings that purely generated and wants a diagnostic
325
+ // that appears on the top of file.
326
+ // We'd add these mappings at walker that they will have a `loc` to `1:0`
327
+ // so find them by looking up a source position of `1:0` will work
328
+ const locMapsToTop = getGeneratedPosition(1, 0, srcToGenMap);
329
+ for (const loc of locMapsToTop) {
330
+ const offset = locToOffset(loc.line, loc.column, generatedLineOffsets);
331
+ mappings.push({
332
+ sourceOffsets: [0],
333
+ generatedOffsets: [offset],
334
+ lengths: [0],
335
+ generatedLengths: [0],
336
+ data: {
337
+ verification: true,
338
+ },
339
+ });
340
+ }
341
+
342
+ for (const [loc, codeSnippet] of extraMappings) {
324
343
  const generatedStart = generated.indexOf(codeSnippet);
325
344
  if (generatedStart === -1) {
326
345
  continue;
@@ -0,0 +1,123 @@
1
+ import tsPrinter from "esrap/languages/ts";
2
+ import type { AST } from "../../types";
3
+ import type { Context, Visitors } from "esrap";
4
+ import type { Node, SimpleCallExpression } from "estree";
5
+
6
+ const printer = tsPrinter({
7
+ getLeadingComments: (node) => (node as AST.Node).leadingComments,
8
+ getTrailingComments: (node) => (node as AST.Node).trailingComments,
9
+ });
10
+
11
+ // Make the print of dummy identifier print nothing.
12
+ // Exception: if GTS attribute list's last argument is dummy, e.g.
13
+ // foo bar, ;
14
+ // ^~ here
15
+ // Then the printed JS will be `foo(bar, )` which WILL NOT be syntax error in ES6.
16
+ // So we mark the lastArg manually and print an additional comma
17
+ // for this dummy identifier, i.e. `foo(bar,,)` and TypeScript will recognize the error.
18
+
19
+ const prevIdentifier = printer.Identifier!;
20
+ printer.Identifier = function (node, context) {
21
+ if (node.isDummy) {
22
+ const text = Reflect.get(node, "lastArg") ? "," : "";
23
+ context.write(text, node);
24
+ } else {
25
+ prevIdentifier(node, context);
26
+ }
27
+ };
28
+
29
+ const prevCallNewExpression = printer.CallExpression!;
30
+ const newCallNewExpression: typeof prevCallNewExpression = function (
31
+ node: SimpleCallExpression,
32
+ context,
33
+ ) {
34
+ const lastArg = node.arguments.at(-1);
35
+ if (lastArg) {
36
+ Reflect.set(lastArg, "lastArg", true);
37
+ }
38
+ if (!node.lParenLoc) {
39
+ return prevCallNewExpression(node, context);
40
+ }
41
+ let hasDeferredWrite = false;
42
+ let interceptionDone = false;
43
+ const lParenFakeNode = {
44
+ loc: node.lParenLoc!,
45
+ } as AST.Node;
46
+
47
+ // Map the print of `(` with the fake lParen node.
48
+ // The print of `(` can be happened in two area:
49
+ // 1. The wrapped parenthesis towards callee, which follows a `context.visit` to the callee;
50
+ // 2. The argument list (what we should map), which follows a `context.append` call
51
+ // so we defer the write of `(` to the next call of `context.visit|append`.
52
+ // If it is `context.append`, then make `context.write("(")` happens with our fake node
53
+ // and mapping will be added to the final code-mapping. Otherwise, keep original write.
54
+
55
+ const patchedWrite = (text: string, node?: AST.Node) => {
56
+ if (text === "(") {
57
+ hasDeferredWrite = true;
58
+ } else {
59
+ context.write(text, node);
60
+ }
61
+ };
62
+ const patchedVisit = (node: AST.Node) => {
63
+ if (hasDeferredWrite) {
64
+ context.write("(");
65
+ }
66
+ return context.visit(node);
67
+ };
68
+ const patchedAppend = (subcontext: Context) => {
69
+ if (hasDeferredWrite) {
70
+ context.write("(", lParenFakeNode);
71
+ // console.log("Inserted fake [LPAREN] for node at ", lParenFakeNode.loc);
72
+ interceptionDone = true;
73
+ }
74
+ return context.append(subcontext);
75
+ };
76
+ const proxiedContext = new Proxy(context, {
77
+ get(target, prop) {
78
+ if (!interceptionDone) {
79
+ if (prop === "write") {
80
+ return patchedWrite;
81
+ }
82
+ if (prop === "visit") {
83
+ return patchedVisit;
84
+ }
85
+ if (prop === "append") {
86
+ return patchedAppend;
87
+ }
88
+ }
89
+ const value = Reflect.get(target, prop);
90
+ if (typeof value === "function") {
91
+ return value.bind(target);
92
+ }
93
+ return value;
94
+ },
95
+ });
96
+ return prevCallNewExpression(node, proxiedContext);
97
+ };
98
+ printer.CallExpression = newCallNewExpression;
99
+ printer.NewExpression = newCallNewExpression;
100
+
101
+ // Handle node with `diagnosticsOnTop`. These nodes and their children will be
102
+ // printed with an extra `loc` that point to the beginning of the source file,
103
+ // so the source-mapping will include them with a 0:1 -> generated position entry.
104
+
105
+ const prevWildcard = printer._!;
106
+ printer._ = (node: Node, context, visit) => {
107
+ const contextProto = Object.getPrototypeOf(context);
108
+ const prevContextWrite: typeof context.write = contextProto.write;
109
+ if ("diagnosticsOnTop" in node && node.diagnosticsOnTop) {
110
+ contextProto.write = function (text: string, node?: AST.Node) {
111
+ node ??= {} as AST.Node;
112
+ node.loc ??= {
113
+ start: { line: 1, column: 0 },
114
+ end: { line: 1, column: 0 },
115
+ };
116
+ return prevContextWrite.call(this, text, node);
117
+ };
118
+ }
119
+ prevWildcard(node, context, visit);
120
+ contextProto.write = prevContextWrite;
121
+ };
122
+
123
+ export const patchedPrinter: Visitors<AST.Node> = printer;
@@ -2,6 +2,9 @@ import type { ExpressionStatement } from "estree";
2
2
  import type { TypingTranspileState } from "./walker";
3
3
 
4
4
  type ReplacementPayload =
5
+ | {
6
+ type: "preface";
7
+ }
5
8
  | {
6
9
  type: "enterVMFromRoot";
7
10
  vm: string;
@@ -27,6 +30,7 @@ type ReplacementPayload =
27
30
  defType: string;
28
31
  metaType: string;
29
32
  lhs: string;
33
+ attrName: string;
30
34
  }
31
35
  | {
32
36
  type: "createBindingTyping";
@@ -76,32 +80,106 @@ export function applyReplacements(
76
80
  "\\b" + state.replacementTag.name + "`(.*?)`",
77
81
  "gm",
78
82
  );
79
- const {
80
- symbolsId: { NamedDefinition, Meta },
81
- } = state;
83
+ const { NamedDefinitionLit, MetaLit } = state;
84
+ const NamedDefinition = JSON.stringify(NamedDefinitionLit.value);
85
+ const Meta = JSON.stringify(MetaLit.value);
86
+ const oneLine = (
87
+ strings: TemplateStringsArray,
88
+ ...values: Array<string | number | boolean>
89
+ ): string =>
90
+ strings
91
+ .reduce(
92
+ (result, chunk, index) =>
93
+ result + chunk + (index < values.length ? values[index] : ""),
94
+ "",
95
+ )
96
+ .replace(/\n[ \t]*/g, " ")
97
+ .trim();
82
98
  // All replacement should be written in one line to avoid messing up source map
83
99
  return code.replace(replacementRegex, (_, rawPayload) => {
84
100
  const payload: ReplacementPayload = JSON.parse(rawPayload);
85
- if (payload.type === "enterVMFromRoot") {
86
- return `type ${payload.defType} = (typeof ${payload.vm})[${NamedDefinition.name}]; type ${payload.metaType} = ${payload.defType}[${Meta.name}];`;
101
+ if (payload.type === "preface") {
102
+ return oneLine`
103
+ namespace ${state.utilNsId.name} {
104
+ export type UniqueKeyProbSegment = "__gts_unique_prob_seg__";
105
+ export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
106
+ }
107
+ `;
108
+ } else if (payload.type === "enterVMFromRoot") {
109
+ return oneLine`
110
+ type ${payload.defType} = (typeof ${payload.vm})[${NamedDefinition}];
111
+ type ${payload.metaType} = ${payload.defType}[${Meta}];
112
+ `;
87
113
  } else if (payload.type === "enterVMFromAttr") {
88
- return `type ${payload.defType} = ${payload.returnType} extends { namedDefinition: infer Def } ? Def : { [${Meta.name}]: unknown }; type ${payload.metaType} = ${payload.defType}[${Meta.name}];`;
114
+ return oneLine`
115
+ type ${payload.defType} = ${payload.returnType} extends { namedDefinition: infer Def } ? Def : { ${Meta}: unknown };
116
+ type ${payload.metaType} = ${payload.defType}[${Meta}];
117
+ `;
89
118
  } else if (payload.type === "exitVM") {
90
119
  const lhs = `${payload.finalMetaType}_lhs`;
91
120
  const requiredAttrsNs = `${payload.finalMetaType}_rans`;
92
121
  const collectedAttrsExpr = `${payload.collectedAttrs.join(" | ")}`;
93
122
  const needleString = `"${requiredAttrsNs}_NeedleString" as any as "required attributes are missing"`;
94
123
  if (payload.errorLoc) {
95
- state.additionalMappings.set(payload.errorLoc, needleString);
124
+ state.extraMappings.set(payload.errorLoc, needleString);
96
125
  }
97
- return `type ${payload.finalMetaType} = ${payload.metaType}; const ${lhs}: { [${Meta.name}]: ${payload.metaType} } & Omit<${payload.defType}, ${Meta.name}> = 0 as any; type ${lhs} = typeof ${lhs}; namespace ${requiredAttrsNs} { export type Collected = ${collectedAttrsExpr}; export type Expected = { [K in keyof ${payload.defType}]: ${lhs}[K] extends { required(this: ${lhs}): true } ? K : never }[keyof ${payload.defType}]; }; ((_: ${requiredAttrsNs}.Expected extends ${requiredAttrsNs}.Collected ? string : ${requiredAttrsNs}.Expected) => 0)(${needleString});`;
126
+ return oneLine`
127
+ type ${payload.finalMetaType} = ${payload.metaType};
128
+ let ${lhs}!: { ${Meta}: ${payload.metaType} } & Omit<${payload.defType}, ${Meta}>;
129
+ type ${lhs} = typeof ${lhs};
130
+ namespace ${requiredAttrsNs} {
131
+ export type Collected = ${collectedAttrsExpr};
132
+ export type Expected = { [K in keyof ${payload.defType}]: ${lhs}[K] extends { required(this: ${lhs}): true } ? K : never }[keyof ${payload.defType}];
133
+ };
134
+ ((_: ${requiredAttrsNs}.Expected extends ${requiredAttrsNs}.Collected ? string : ${requiredAttrsNs}.Expected) => 0)(${needleString});
135
+ `;
98
136
  } else if (payload.type === "enterAttr") {
99
- return `const ${payload.lhs}: { [${Meta.name}]: ${payload.metaType} } & Omit<${payload.defType}, ${Meta.name}> = 0 as any;`;
137
+ const uniqueKeyLhs = `${payload.lhs}_uniqueKey_lhs`;
138
+ const uniqueKey = `${payload.lhs}_uniqueKey`;
139
+ const uniqueKeyForThis = `${payload.lhs}_uniqueKeyFor_${payload.lhs}`;
140
+ const uniqueKeyHelperIntf = `${payload.defType}_uniqueKeyProbeHelper`;
141
+ const omittedKeys = `${payload.lhs}_omittedKeys`;
142
+ return oneLine`
143
+ type ${uniqueKeyLhs} = {
144
+ ${Meta}: ${payload.metaType};
145
+ uniqueKey: ${payload.defType} extends { [${payload.attrName}]: { uniqueKey: infer UniqueKey } } ? UniqueKey : () => 0;
146
+
147
+ let ${uniqueKeyLhs}!: ${uniqueKeyLhs};
148
+ let ${uniqueKey} = ${uniqueKeyLhs}.uniqueKey();
149
+ type ${uniqueKey} = typeof ${uniqueKey};
150
+ let ${uniqueKeyForThis}!: \`\${${uniqueKey}}\${${state.utilNsId.name}.UniqueKeyProbSegment}${payload.lhs}\`;
151
+ interface ${uniqueKeyHelperIntf} {
152
+ [${uniqueKeyForThis}]: 1;
153
+ }
154
+ type ${omittedKeys} = ${Meta} | (
155
+ ${uniqueKey} extends 0
156
+ ? never /* no unique requirement */
157
+ : string extends keyof ${uniqueKeyHelperIntf}
158
+ ? keyof ${payload.defType} /* too loose, disable all */
159
+ : ${state.utilNsId.name}.UnionToIntersection<
160
+ keyof ${uniqueKeyHelperIntf} & \`\${${uniqueKey}}\${${state.utilNsId.name}.UniqueKeyProbSegment}\${string}\`
161
+ > extends never
162
+ ? ${payload.attrName} /* have duplicate, disable this */
163
+ : never
164
+ );
165
+ let ${payload.lhs}!: { ${Meta}: ${payload.metaType} } & Omit<${payload.defType}, ${omittedKeys}>;
166
+ `;
100
167
  } else if (payload.type === "createBindingTyping") {
101
168
  const typingIdLhs = `${payload.typingId}_lhs`;
102
- return `type ${typingIdLhs} = { [${Meta.name}]: ${payload.finalMetaType}; as: ${payload.defType}[${payload.attrName}] extends { as: infer As } ? As : unknown }; let ${typingIdLhs}!: ${typingIdLhs}; let ${payload.typingId} = ${typingIdLhs}.as(); type ${payload.typingId} = typeof ${payload.typingId};`;
169
+ return oneLine`
170
+ type ${typingIdLhs} = {
171
+ ${Meta}: ${payload.finalMetaType};
172
+ as: ${payload.defType} extends { [${payload.attrName}]: { as: infer As } } ? As : unknown;
173
+ };
174
+ let ${typingIdLhs}!: ${typingIdLhs};
175
+ let ${payload.typingId} = ${typingIdLhs}.as();
176
+ type ${payload.typingId} = typeof ${payload.typingId};
177
+ `;
103
178
  } else if (payload.type === "exitAttr") {
104
- return `type ${payload.returnType} = typeof ${payload.returnType}; type ${payload.newMetaType} = ${payload.returnType} extends { rewriteMeta: infer NewMeta extends {} } ? NewMeta : ${payload.oldMetaType}`;
179
+ return oneLine`
180
+ type ${payload.returnType} = typeof ${payload.returnType};
181
+ type ${payload.newMetaType} = ${payload.returnType} extends { rewriteMeta: infer NewMeta extends {} } ? NewMeta : ${payload.oldMetaType}
182
+ `;
105
183
  } else {
106
184
  return "";
107
185
  }
@@ -38,10 +38,9 @@ export interface TypingTranspileState extends TranspileState {
38
38
  externalizedBindings: ExternalizedTypedBinding[];
39
39
  idCounter: number;
40
40
  rootVmId: Identifier;
41
- symbolsId: {
42
- Meta: Identifier;
43
- NamedDefinition: Identifier;
44
- };
41
+ utilNsId: Identifier;
42
+ MetaLit: Literal;
43
+ NamedDefinitionLit: Literal;
45
44
  defineLeadingComments: Comment[] | undefined;
46
45
  // type of current VM's definition
47
46
  vmDefTypeIdStack: Identifier[];
@@ -53,11 +52,10 @@ export interface TypingTranspileState extends TranspileState {
53
52
  finalMetaTypeIdStack: Identifier[];
54
53
  // `obj` of `obj.attr(...)`
55
54
  // attrLhsIdStack: Identifier[];
56
- prefaceInserted: boolean;
57
55
  /** Pending statements to be inserted to the top-level */
58
56
  typingPendingStatements: Statement[];
59
57
  replacementTag: Identifier;
60
- additionalMappings: Map<string, string>;
58
+ extraMappings: Map<string, string>;
61
59
  }
62
60
 
63
61
  const EMPTY: EmptyStatement = { type: "EmptyStatement" };
@@ -74,68 +72,7 @@ const ANY_INIT = {
74
72
  typeAnnotation: ANY,
75
73
  } as {} as Expression;
76
74
 
77
- const emitPreface = (state: TypingTranspileState) => {
78
- if (state.prefaceInserted) {
79
- return;
80
- }
81
- const symbolsLhs = {
82
- type: "TSQualifiedName",
83
- left: { type: "Identifier", name: state.rootVmId.name },
84
- right: { type: "Identifier", name: "_symbols" },
85
- };
86
- for (const symbolName of [
87
- "Meta",
88
- "Action",
89
- "NamedDefinition",
90
- "Prelude",
91
- ] as const) {
92
- const init = {
93
- type: "TSTypeQuery",
94
- exprName: {
95
- type: "TSQualifiedName",
96
- left: symbolsLhs,
97
- right: { type: "Identifier", name: symbolName },
98
- },
99
- };
100
- const symbolId =
101
- symbolName === "Action"
102
- ? state.ActionId
103
- : symbolName === "Prelude"
104
- ? state.preludeSymbolId
105
- : state.symbolsId[symbolName];
106
- state.typingPendingStatements.push(
107
- {
108
- type: "TSTypeAliasDeclaration",
109
- id: symbolId,
110
- typeAnnotation: init,
111
- } as {} as VariableDeclaration,
112
- {
113
- type: "VariableDeclaration",
114
- kind: "const",
115
- declarations: [
116
- {
117
- type: "VariableDeclarator",
118
- id: {
119
- ...symbolId,
120
- typeAnnotation: {
121
- type: "TSTypeAnnotation",
122
- typeAnnotation: {
123
- type: "TSTypeReference",
124
- typeName: symbolId,
125
- },
126
- },
127
- } as Identifier,
128
- init: ANY_INIT,
129
- },
130
- ],
131
- } as VariableDeclaration
132
- );
133
- }
134
- state.prefaceInserted = true;
135
- };
136
-
137
75
  const enterVMFromRoot = (state: TypingTranspileState) => {
138
- emitPreface(state);
139
76
  let defTypeId: Identifier = {
140
77
  type: "Identifier",
141
78
  name: `__gts_rootVmDefType_${state.idCounter++}`,
@@ -154,7 +91,7 @@ const enterVMFromRoot = (state: TypingTranspileState) => {
154
91
  vm: state.rootVmId.name,
155
92
  defType: defTypeId.name,
156
93
  metaType: metaTypeId.name,
157
- })
94
+ }),
158
95
  );
159
96
  state.vmDefTypeIdStack.push(defTypeId);
160
97
  state.metaTypeIdStack.push(metaTypeId);
@@ -163,7 +100,7 @@ const enterVMFromRoot = (state: TypingTranspileState) => {
163
100
  };
164
101
  const enterVMFromAttr = (
165
102
  state: TypingTranspileState,
166
- returningId: Identifier
103
+ returningId: Identifier,
167
104
  ) => {
168
105
  const defTypeId: Identifier = {
169
106
  type: "Identifier",
@@ -183,7 +120,7 @@ const enterVMFromAttr = (
183
120
  returnType: returningId.name,
184
121
  defType: defTypeId.name,
185
122
  metaType: metaTypeId.name,
186
- })
123
+ }),
187
124
  );
188
125
  state.vmDefTypeIdStack.push(defTypeId);
189
126
  state.metaTypeIdStack.push(metaTypeId);
@@ -204,13 +141,13 @@ const exitVM = (state: TypingTranspileState, errorLoc?: string) => {
204
141
  finalMetaType: finalMetaId.name,
205
142
  collectedAttrs: collectedAttrNames,
206
143
  errorLoc,
207
- })
144
+ }),
208
145
  );
209
146
  };
210
147
 
211
148
  const enterAttr = (
212
149
  state: TypingTranspileState,
213
- attrName: string
150
+ attrName: string,
214
151
  ): { lhsId: Identifier } => {
215
152
  const defTypeId = state.vmDefTypeIdStack.at(-1);
216
153
  const metaTypeId = state.metaTypeIdStack.at(-1);
@@ -229,7 +166,8 @@ const enterAttr = (
229
166
  defType: defTypeId.name,
230
167
  metaType: metaTypeId.name,
231
168
  lhs: lhsId.name,
232
- })
169
+ attrName,
170
+ }),
233
171
  );
234
172
  return { lhsId: lhsId };
235
173
  };
@@ -239,7 +177,7 @@ const genBindingTyping = (
239
177
  info: {
240
178
  attrName: string;
241
179
  typingId: Identifier;
242
- }
180
+ },
243
181
  ) => {
244
182
  const finalMetaId = state.finalMetaTypeIdStack.at(-1);
245
183
  const defTypeId = state.vmDefTypeIdStack.at(-1);
@@ -253,7 +191,7 @@ const genBindingTyping = (
253
191
  defType: defTypeId.name,
254
192
  attrName: info.attrName,
255
193
  typingId: info.typingId.name,
256
- })
194
+ }),
257
195
  );
258
196
  };
259
197
 
@@ -274,7 +212,7 @@ const exitAttr = (state: TypingTranspileState, returningId: Identifier) => {
274
212
  oldMetaType: oldMetaTypeId.name,
275
213
  newMetaType: newMetaTypeId.name,
276
214
  returnType: returningId.name,
277
- })
215
+ }),
278
216
  );
279
217
  };
280
218
 
@@ -311,7 +249,6 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
311
249
  },
312
250
  } as VariableDeclarator,
313
251
  ],
314
- leadingComments: extBinding.leadingComments,
315
252
  };
316
253
  if (extBinding.export) {
317
254
  body.unshift({
@@ -320,43 +257,50 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
320
257
  specifiers: [],
321
258
  source: null,
322
259
  attributes: [],
260
+ leadingComments: extBinding.leadingComments,
323
261
  } as ExportNamedDeclaration);
324
262
  } else {
263
+ varDecl.leadingComments = extBinding.leadingComments;
325
264
  body.unshift(varDecl);
326
265
  }
327
266
  }
328
- if (state.prefaceInserted) {
267
+ if (state.hasQueryExpressions) {
329
268
  body.unshift({
330
269
  type: "ImportDeclaration",
270
+ diagnosticsOnTop: true,
331
271
  specifiers: [
332
272
  {
333
273
  type: "ImportDefaultSpecifier",
334
- local: state.rootVmId,
274
+ local: state.queryFnId,
335
275
  },
336
276
  ],
337
277
  source: {
338
278
  type: "Literal",
339
- value: `${state.providerImportSource}/vm`,
279
+ value: `${state.providerImportSource}/query`,
340
280
  },
341
281
  attributes: [],
342
282
  });
343
283
  }
344
- if (state.hasQueryExpressions) {
345
- body.unshift({
284
+ body.unshift(
285
+ {
346
286
  type: "ImportDeclaration",
287
+ diagnosticsOnTop: true,
347
288
  specifiers: [
348
289
  {
349
290
  type: "ImportDefaultSpecifier",
350
- local: state.queryFnId,
291
+ local: state.rootVmId,
351
292
  },
352
293
  ],
353
294
  source: {
354
295
  type: "Literal",
355
- value: `${state.providerImportSource}/query`,
296
+ value: `${state.providerImportSource}/vm`,
356
297
  },
357
298
  attributes: [],
358
- });
359
- }
299
+ },
300
+ createReplacementHolder(state, {
301
+ type: "preface",
302
+ }),
303
+ );
360
304
  return {
361
305
  ...node,
362
306
  body,
@@ -371,18 +315,24 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
371
315
  GTSNamedAttributeDefinition(node, { visit, state }) {
372
316
  const { name, body, bindingName } = node;
373
317
  const attrName = JSON.stringify(
374
- name.type === "Literal" ? String(name.value) : name.name
318
+ name.type === "Literal" ? String(name.value) : name.name,
375
319
  );
320
+ const attributeNameToken = state.leafTokens.find((t) => t.loc === name.loc);
321
+ if (attributeNameToken) {
322
+ attributeNameToken.sourceLengthOffset = 1;
323
+ }
376
324
  const { lhsId } = enterAttr(state, attrName);
325
+ state.extraMappings.set(
326
+ `${name.loc?.start.line}:${name.loc?.start.column}`,
327
+ `${lhsId.name}${name.type === "Literal" ? `[` : `.`}`,
328
+ );
377
329
  const positionals = body.positionalAttributes.attributes.map(
378
330
  (attr): Expression => {
379
331
  if (attr.type === "Identifier" && /^[a-z_]/.test(attr.name)) {
380
332
  const token = state.leafTokens.find((t) => t.loc === attr.loc);
381
333
  if (token) {
382
- token.locationAdjustment = {
383
- startOffset: 1,
384
- generatedLength: attr.name.length + 2, // quotation mark
385
- };
334
+ token.generatedStartOffset = 1;
335
+ token.generatedLength = attr.name.length + 2; // quotation mark
386
336
  }
387
337
  return {
388
338
  type: "Literal",
@@ -392,7 +342,7 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
392
342
  } else {
393
343
  return visit(attr) as Expression;
394
344
  }
395
- }
345
+ },
396
346
  );
397
347
  const returnValue: Identifier = {
398
348
  type: "Identifier",
@@ -425,7 +375,7 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
425
375
  visit(body.namedAttributes);
426
376
  exitVM(
427
377
  state,
428
- `${body.namedAttributes.loc?.start.line}:${body.namedAttributes.loc?.start.column}`
378
+ `${body.namedAttributes.loc?.start.line}:${body.namedAttributes.loc?.start.column}`,
429
379
  );
430
380
  }
431
381
  if (bindingName) {
@@ -462,7 +412,14 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
462
412
  visit(attr);
463
413
  }
464
414
  if (node.directAction) {
465
- const { lhsId } = enterAttr(state, state.ActionId.name);
415
+ const attrName = JSON.stringify(state.ActionLit.value);
416
+ const { lhsId } = enterAttr(state, attrName);
417
+ const actionNotExistsReplacementStr = `${lhsId.name}[${attrName}]`;
418
+ const actionNotExistsErrorLoc = `${node.directAction.loc?.start.line}:${node.directAction.loc?.start.column}`;
419
+ state.extraMappings.set(
420
+ actionNotExistsErrorLoc,
421
+ actionNotExistsReplacementStr,
422
+ );
466
423
  const fn: ArrowFunctionExpression = {
467
424
  type: "ArrowFunctionExpression",
468
425
  params: state.shortcutFunctionParameters,
@@ -489,7 +446,7 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
489
446
  callee: {
490
447
  type: "MemberExpression",
491
448
  object: lhsId,
492
- property: state.ActionId,
449
+ property: state.ActionLit,
493
450
  computed: true,
494
451
  optional: false,
495
452
  },