@gi-tcg/gts-transpiler 0.7.6 → 0.8.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/dist/index.d.ts +8 -2
- package/dist/index.js +50 -70
- package/package.json +2 -2
- package/src/index.ts +6 -2
- package/src/transform/volar/index.ts +16 -5
- package/src/transform/volar/replacements.ts +31 -59
- package/src/transform/volar/walker.ts +10 -3
package/dist/index.d.ts
CHANGED
|
@@ -105,6 +105,12 @@ interface VolarMappingResult {
|
|
|
105
105
|
mappings: CodeMapping[];
|
|
106
106
|
}
|
|
107
107
|
//#endregion
|
|
108
|
+
//#region src/transform/volar/index.d.ts
|
|
109
|
+
interface VolarTranspileOption extends TranspileOption {
|
|
110
|
+
/** Omit editor-only expressions, retaining all type validation. */
|
|
111
|
+
typeCheckingOnly: boolean;
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
108
114
|
//#region src/transform/index.d.ts
|
|
109
115
|
interface TranspileResult {
|
|
110
116
|
code: string;
|
|
@@ -1534,6 +1540,6 @@ declare function isGtsShortcutArgumentStartingConciseBody(node: AST$1.Expression
|
|
|
1534
1540
|
//#endregion
|
|
1535
1541
|
//#region src/index.d.ts
|
|
1536
1542
|
declare function transpile(source: string, filename: string, option: TranspileOption): TranspileResult;
|
|
1537
|
-
declare function transpileForVolar(source: string, filename: string, option:
|
|
1543
|
+
declare function transpileForVolar(source: string, filename: string, option: VolarTranspileOption): VolarMappingResult;
|
|
1538
1544
|
//#endregion
|
|
1539
|
-
export { type AST, type GtsConfig, type ParseLooseOptions as GtsParseLooseOptions, type ParseOptions as GtsParseOptions, GtsTranspilerError, type PathModule, type ResolveGtsConfigAsyncOptions, type ResolveGtsConfigSyncOptions, type TranspileOption, type TranspileResult, type VolarMappingResult, isGtsShortcutArgumentStartingConciseBody, parse, parseLoose, resolveGtsConfig, resolveGtsConfigSync, transpile, transpileForVolar };
|
|
1545
|
+
export { type AST, type GtsConfig, type ParseLooseOptions as GtsParseLooseOptions, type ParseOptions as GtsParseOptions, GtsTranspilerError, type PathModule, type ResolveGtsConfigAsyncOptions, type ResolveGtsConfigSyncOptions, type TranspileOption, type TranspileResult, type VolarMappingResult, type VolarTranspileOption, isGtsShortcutArgumentStartingConciseBody, parse, parseLoose, resolveGtsConfig, resolveGtsConfigSync, transpile, transpileForVolar };
|
package/dist/index.js
CHANGED
|
@@ -1163,16 +1163,11 @@ function applyReplacements(state, code, mappings) {
|
|
|
1163
1163
|
const NamedDefinition = JSON.stringify(NamedDefinitionLit.value);
|
|
1164
1164
|
const Meta = JSON.stringify(MetaLit.value);
|
|
1165
1165
|
const matchInfos = [];
|
|
1166
|
+
let cumulativeOffset = 0;
|
|
1166
1167
|
const result = code.replace(replacementRegex, (match, rawPayload, offset) => {
|
|
1167
1168
|
const payload = JSON.parse(rawPayload.replace(/\\`/g, "`"));
|
|
1168
1169
|
let replacement;
|
|
1169
|
-
if (payload.type === "
|
|
1170
|
-
namespace ${state.utilNsId.name} {
|
|
1171
|
-
export type UniqueKeyProbSegment = "__gts_unique_prob_seg__";
|
|
1172
|
-
export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
|
|
1173
|
-
}
|
|
1174
|
-
`;
|
|
1175
|
-
else if (payload.type === "enterVMFromRoot") replacement = dedent`
|
|
1170
|
+
if (payload.type === "enterVMFromRoot") replacement = dedent`
|
|
1176
1171
|
type ${payload.defType} = (typeof ${payload.vm})[${NamedDefinition}];
|
|
1177
1172
|
type ${payload.metaType} = ${payload.defType}[${Meta}];
|
|
1178
1173
|
`;
|
|
@@ -1181,11 +1176,10 @@ function applyReplacements(state, code, mappings) {
|
|
|
1181
1176
|
type ${payload.metaType} = ${payload.defType}[${Meta}];
|
|
1182
1177
|
`;
|
|
1183
1178
|
else if (payload.type === "exitVM") {
|
|
1184
|
-
const lhs = `${payload.finalMetaType}_lhs`;
|
|
1185
1179
|
const requiredAttrsNs = `${payload.finalMetaType}_rans`;
|
|
1186
|
-
const collectedAttrsExpr =
|
|
1180
|
+
const collectedAttrsExpr = [...new Set(payload.collectedAttrs)].join(" | ") || "never";
|
|
1187
1181
|
const length = payload.errorRange ? payload.errorRange[1] - payload.errorRange[0] : 0;
|
|
1188
|
-
const needleString = `"${requiredAttrsNs}
|
|
1182
|
+
const needleString = `"${requiredAttrsNs}" as string as ${state.utilNsId.name}.RequiredMessage<${requiredAttrsNs}, ${collectedAttrsExpr}>`;
|
|
1189
1183
|
if (payload.errorRange) state.extraMappings.push({
|
|
1190
1184
|
sourceOffset: payload.errorRange[0],
|
|
1191
1185
|
length,
|
|
@@ -1193,19 +1187,8 @@ function applyReplacements(state, code, mappings) {
|
|
|
1193
1187
|
});
|
|
1194
1188
|
replacement = dedent`
|
|
1195
1189
|
type ${payload.finalMetaType} = ${payload.metaType};
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
namespace ${requiredAttrsNs} {
|
|
1199
|
-
export type Collected = ${collectedAttrsExpr};
|
|
1200
|
-
export type Expected = {
|
|
1201
|
-
[K in keyof ${payload.defType}]: ${lhs}[K] extends { required(this: ${lhs}): true } ? K : never;
|
|
1202
|
-
}[keyof ${payload.defType}];
|
|
1203
|
-
type DiagObj = {
|
|
1204
|
-
[K in Expected]: K extends Collected ? never : \`'\${K}' is a required attribute but not provided\`;
|
|
1205
|
-
}
|
|
1206
|
-
export type DiagMsg = DiagObj[Expected];
|
|
1207
|
-
};
|
|
1208
|
-
((_: ${requiredAttrsNs}.Expected extends ${requiredAttrsNs}.Collected ? string : ${requiredAttrsNs}.Expected) => 0)(${needleString});
|
|
1190
|
+
type ${requiredAttrsNs} = ${state.utilNsId.name}.RequiredAttrs<${state.utilNsId.name}.WithMeta<${payload.defType}, ${payload.metaType}>>;
|
|
1191
|
+
${state.utilNsId.name}.checkRequired<${requiredAttrsNs}, ${collectedAttrsExpr}>(${needleString});
|
|
1209
1192
|
`;
|
|
1210
1193
|
} else if (payload.type === "enterAttr") {
|
|
1211
1194
|
const uniqueKeyLhs = `${payload.lhs}_uniqueKey_lhs`;
|
|
@@ -1214,39 +1197,20 @@ function applyReplacements(state, code, mappings) {
|
|
|
1214
1197
|
const uniqueKeyHelperIntf = `${payload.defType}_uniqueKeyProbeHelper`;
|
|
1215
1198
|
const omittedKeys = `${payload.lhs}_omittedKeys`;
|
|
1216
1199
|
replacement = dedent`
|
|
1217
|
-
|
|
1218
|
-
${Meta}: ${payload.metaType};
|
|
1219
|
-
uniqueKey: ${payload.defType} extends { [${payload.attrName}]: { uniqueKey: infer UniqueKey } } ? UniqueKey : () => 0;
|
|
1220
|
-
};
|
|
1221
|
-
|
|
1222
|
-
let ${uniqueKeyLhs}!: ${uniqueKeyLhs};
|
|
1200
|
+
declare const ${uniqueKeyLhs}: { ${Meta}: ${payload.metaType}; uniqueKey: ${state.utilNsId.name}.Member<${payload.defType}, ${payload.attrName}, "uniqueKey", () => 0> };
|
|
1223
1201
|
let ${uniqueKey} = ${uniqueKeyLhs}.uniqueKey();
|
|
1224
1202
|
type ${uniqueKey} = typeof ${uniqueKey};
|
|
1225
1203
|
let ${uniqueKeyForThis}!: \`\${${uniqueKey}}\${${state.utilNsId.name}.UniqueKeyProbSegment}${payload.lhs}\`;
|
|
1226
1204
|
interface ${uniqueKeyHelperIntf} {
|
|
1227
1205
|
[${uniqueKeyForThis}]: 1;
|
|
1228
1206
|
}
|
|
1229
|
-
type ${omittedKeys} = ${Meta} | (
|
|
1230
|
-
${uniqueKey} extends 0
|
|
1231
|
-
? never /* no unique requirement */
|
|
1232
|
-
: string extends keyof ${uniqueKeyHelperIntf}
|
|
1233
|
-
? keyof ${payload.defType} /* too loose, disable all */
|
|
1234
|
-
: ${state.utilNsId.name}.UnionToIntersection<
|
|
1235
|
-
keyof ${uniqueKeyHelperIntf} & \`\${${uniqueKey}}\${${state.utilNsId.name}.UniqueKeyProbSegment}\${string}\`
|
|
1236
|
-
> extends never
|
|
1237
|
-
? ${payload.attrName} /* have duplicate, disable this */
|
|
1238
|
-
: never
|
|
1239
|
-
);
|
|
1207
|
+
type ${omittedKeys} = ${Meta} | (${uniqueKey} extends 0 ? never : string extends keyof ${uniqueKeyHelperIntf} ? keyof ${payload.defType} : ${state.utilNsId.name}.UnionToIntersection<keyof ${uniqueKeyHelperIntf} & \`\${${uniqueKey}}\${${state.utilNsId.name}.UniqueKeyProbSegment}\${string}\`> extends never ? ${payload.attrName} : never);
|
|
1240
1208
|
let ${payload.lhs}!: ${payload.hintOnly ? `{}` : `{ ${Meta}: ${payload.metaType} }`} & Omit<${payload.defType}, ${omittedKeys}>;
|
|
1241
1209
|
`;
|
|
1242
1210
|
} else if (payload.type === "createBindingTyping") {
|
|
1243
1211
|
const typingIdLhs = `${payload.typingId}_lhs`;
|
|
1244
1212
|
replacement = dedent`
|
|
1245
|
-
|
|
1246
|
-
${Meta}: ${payload.finalMetaType};
|
|
1247
|
-
as: ${payload.defType} extends { [${payload.attrName}]: { as: infer As } } ? As : unknown;
|
|
1248
|
-
};
|
|
1249
|
-
let ${typingIdLhs}!: ${typingIdLhs};
|
|
1213
|
+
declare const ${typingIdLhs}: { ${Meta}: ${payload.finalMetaType}; as: ${state.utilNsId.name}.Member<${payload.defType}, ${payload.attrName}, "as", unknown> };
|
|
1250
1214
|
let ${payload.typingId} = ${typingIdLhs}.as();
|
|
1251
1215
|
type ${payload.typingId} = typeof ${payload.typingId};
|
|
1252
1216
|
`;
|
|
@@ -1257,24 +1221,28 @@ function applyReplacements(state, code, mappings) {
|
|
|
1257
1221
|
replacement = dedent`
|
|
1258
1222
|
type ${payload.returnType} = typeof ${payload.returnType};
|
|
1259
1223
|
type ${rewrittenMeta} = ${payload.returnType} extends { rewriteMeta: infer NewMeta extends {} } ? NewMeta : ${payload.oldMetaType};
|
|
1260
|
-
|
|
1261
|
-
[${payload.attrName}]: { mergeMeta: infer M }
|
|
1262
|
-
} ? M : <const T>(x: T, y: unknown) => T;
|
|
1224
|
+
declare const ${mergeFn}: ${state.utilNsId.name}.MergeMeta<${payload.defType}, ${payload.attrName}>;
|
|
1263
1225
|
let ${mergeFnRet} = ${mergeFn}(null! as ${rewrittenMeta}, null! as ${payload.innerMetaType});
|
|
1264
1226
|
type ${payload.newMetaType} = [typeof ${mergeFn}] extends [null] ? ${rewrittenMeta} : typeof ${mergeFnRet};
|
|
1265
1227
|
`;
|
|
1266
1228
|
} else replacement = "";
|
|
1229
|
+
cumulativeOffset += replacement.length - match.length;
|
|
1267
1230
|
matchInfos.push({
|
|
1268
1231
|
sourceEnd: offset + match.length,
|
|
1269
|
-
lengthOffset:
|
|
1232
|
+
lengthOffset: cumulativeOffset
|
|
1270
1233
|
});
|
|
1271
1234
|
return replacement;
|
|
1272
1235
|
});
|
|
1273
1236
|
for (const mapping of mappings) for (let i = 0; i < mapping.generatedOffsets.length; i++) {
|
|
1274
1237
|
const orig = mapping.generatedOffsets[i];
|
|
1275
|
-
let
|
|
1276
|
-
|
|
1277
|
-
|
|
1238
|
+
let low = 0;
|
|
1239
|
+
let high = matchInfos.length;
|
|
1240
|
+
while (low < high) {
|
|
1241
|
+
const mid = low + high >>> 1;
|
|
1242
|
+
if (matchInfos[mid].sourceEnd <= orig) low = mid + 1;
|
|
1243
|
+
else high = mid;
|
|
1244
|
+
}
|
|
1245
|
+
mapping.generatedOffsets[i] = orig + (matchInfos[low - 1]?.lengthOffset ?? 0);
|
|
1278
1246
|
}
|
|
1279
1247
|
return result;
|
|
1280
1248
|
}
|
|
@@ -1398,6 +1366,7 @@ const exitAttr = (state, attrName, innerVMFinalMetaId, returningId) => {
|
|
|
1398
1366
|
}));
|
|
1399
1367
|
};
|
|
1400
1368
|
const insertHintStatement = (state, whiteSpaceStart, whiteSpaceEnd) => {
|
|
1369
|
+
if (state.typeCheckingOnly) return;
|
|
1401
1370
|
const { lhsId } = enterAttr(state, ATTR_HINT_ATTR_NAME);
|
|
1402
1371
|
state.typingPendingStatements.push({
|
|
1403
1372
|
type: "GTSAttributeNameHintStatement",
|
|
@@ -1465,21 +1434,32 @@ const gtsToTypingsWalker = {
|
|
|
1465
1434
|
attributes: []
|
|
1466
1435
|
}, {
|
|
1467
1436
|
type: "ImportDeclaration",
|
|
1468
|
-
specifiers: [
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1437
|
+
specifiers: [
|
|
1438
|
+
{
|
|
1439
|
+
type: "ImportSpecifier",
|
|
1440
|
+
imported: {
|
|
1441
|
+
type: "Identifier",
|
|
1442
|
+
name: "createDefine"
|
|
1443
|
+
},
|
|
1444
|
+
local: state.createDefineFnId
|
|
1473
1445
|
},
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1446
|
+
{
|
|
1447
|
+
type: "ImportSpecifier",
|
|
1448
|
+
imported: {
|
|
1449
|
+
type: "Identifier",
|
|
1450
|
+
name: "createBinding"
|
|
1451
|
+
},
|
|
1452
|
+
local: state.createBindingFnId
|
|
1480
1453
|
},
|
|
1481
|
-
|
|
1482
|
-
|
|
1454
|
+
{
|
|
1455
|
+
type: "ImportSpecifier",
|
|
1456
|
+
imported: {
|
|
1457
|
+
type: "Identifier",
|
|
1458
|
+
name: "TypingUtils"
|
|
1459
|
+
},
|
|
1460
|
+
local: state.utilNsId
|
|
1461
|
+
}
|
|
1462
|
+
],
|
|
1483
1463
|
source: {
|
|
1484
1464
|
type: "Literal",
|
|
1485
1465
|
value: state.runtimeImportSource
|
|
@@ -1505,7 +1485,7 @@ const gtsToTypingsWalker = {
|
|
|
1505
1485
|
type: "Literal",
|
|
1506
1486
|
value: 0
|
|
1507
1487
|
}
|
|
1508
|
-
}, lastImportDecl
|
|
1488
|
+
}, lastImportDecl);
|
|
1509
1489
|
return {
|
|
1510
1490
|
...node,
|
|
1511
1491
|
body
|
|
@@ -1911,6 +1891,7 @@ function transformForVolar(ast, option, sourceInfo) {
|
|
|
1911
1891
|
metaTypeIdStack: [],
|
|
1912
1892
|
finalMetaTypeIdStack: [],
|
|
1913
1893
|
attrsOfCurrentVm: [],
|
|
1894
|
+
typeCheckingOnly: option.typeCheckingOnly,
|
|
1914
1895
|
sourceNodes: /* @__PURE__ */ new WeakSet(),
|
|
1915
1896
|
attributeNameNodes: /* @__PURE__ */ new WeakSet(),
|
|
1916
1897
|
namedAttributeCalleeLParenRange: /* @__PURE__ */ new WeakMap(),
|
|
@@ -1948,10 +1929,9 @@ function transformForVolar(ast, option, sourceInfo) {
|
|
|
1948
1929
|
for (const extraMapping of state.extraMappings) {
|
|
1949
1930
|
const genOffset = code.indexOf(extraMapping.generatedNeedle);
|
|
1950
1931
|
mappings.push({
|
|
1951
|
-
sourceOffsets: [extraMapping.sourceOffset],
|
|
1952
|
-
lengths: [
|
|
1953
|
-
generatedOffsets: [genOffset],
|
|
1954
|
-
generatedLengths: [extraMapping.generatedNeedle.length],
|
|
1932
|
+
sourceOffsets: [extraMapping.sourceOffset, extraMapping.sourceOffset + extraMapping.length],
|
|
1933
|
+
lengths: [0, 0],
|
|
1934
|
+
generatedOffsets: [genOffset, genOffset + extraMapping.generatedNeedle.length],
|
|
1955
1935
|
data: VERIFICATION_ONLY_MAPPING_DATA
|
|
1956
1936
|
});
|
|
1957
1937
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gi-tcg/gts-transpiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/piovium/gts.git"
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@sveltejs/acorn-typescript": "^1.0.8",
|
|
23
23
|
"acorn": "^8.15.0",
|
|
24
24
|
"dedent": "^1.7.2",
|
|
25
|
-
"espolar": "^0.6.
|
|
25
|
+
"espolar": "^0.6.7",
|
|
26
26
|
"esrap": "2.2.1",
|
|
27
27
|
"magic-string": "^0.30.21",
|
|
28
28
|
"path-browserify-esm": "^1.0.6",
|
package/src/index.ts
CHANGED
|
@@ -10,7 +10,10 @@ import {
|
|
|
10
10
|
type TranspileResult,
|
|
11
11
|
} from "./transform/index.ts";
|
|
12
12
|
import type { TranspileOption } from "./transform/gts.ts";
|
|
13
|
-
import type {
|
|
13
|
+
import type {
|
|
14
|
+
VolarMappingResult,
|
|
15
|
+
VolarTranspileOption,
|
|
16
|
+
} from "./transform/volar/index.ts";
|
|
14
17
|
export { GtsTranspilerError } from "./error.ts";
|
|
15
18
|
export type { AST } from "./types.ts";
|
|
16
19
|
|
|
@@ -29,7 +32,7 @@ export function transpile(
|
|
|
29
32
|
export function transpileForVolar(
|
|
30
33
|
source: string,
|
|
31
34
|
filename: string,
|
|
32
|
-
option:
|
|
35
|
+
option: VolarTranspileOption,
|
|
33
36
|
): VolarMappingResult {
|
|
34
37
|
const ast = parseLoose(source, {
|
|
35
38
|
recordCallLParens: true,
|
|
@@ -47,6 +50,7 @@ export type {
|
|
|
47
50
|
TranspileOption,
|
|
48
51
|
TranspileResult,
|
|
49
52
|
VolarMappingResult,
|
|
53
|
+
VolarTranspileOption,
|
|
50
54
|
};
|
|
51
55
|
export {
|
|
52
56
|
resolveGtsConfig,
|
|
@@ -17,9 +17,14 @@ import {
|
|
|
17
17
|
import { getPrintOptions } from "./printer.ts";
|
|
18
18
|
import { getContentStartOffset } from "./content_start.ts";
|
|
19
19
|
|
|
20
|
+
export interface VolarTranspileOption extends TranspileOption {
|
|
21
|
+
/** Omit editor-only expressions, retaining all type validation. */
|
|
22
|
+
typeCheckingOnly: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
20
25
|
export function transformForVolar(
|
|
21
26
|
ast: Program,
|
|
22
|
-
option:
|
|
27
|
+
option: VolarTranspileOption,
|
|
23
28
|
sourceInfo: Required<SourceInfo>,
|
|
24
29
|
): VolarMappingResult {
|
|
25
30
|
const state: TypingTranspileState = {
|
|
@@ -39,6 +44,7 @@ export function transformForVolar(
|
|
|
39
44
|
metaTypeIdStack: [],
|
|
40
45
|
finalMetaTypeIdStack: [],
|
|
41
46
|
attrsOfCurrentVm: [],
|
|
47
|
+
typeCheckingOnly: option.typeCheckingOnly,
|
|
42
48
|
|
|
43
49
|
sourceNodes: new WeakSet(),
|
|
44
50
|
attributeNameNodes: new WeakSet(),
|
|
@@ -92,10 +98,15 @@ export function transformForVolar(
|
|
|
92
98
|
for (const extraMapping of state.extraMappings) {
|
|
93
99
|
const genOffset = code.indexOf(extraMapping.generatedNeedle);
|
|
94
100
|
mappings.push({
|
|
95
|
-
sourceOffsets: [
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
101
|
+
sourceOffsets: [
|
|
102
|
+
extraMapping.sourceOffset,
|
|
103
|
+
extraMapping.sourceOffset + extraMapping.length,
|
|
104
|
+
],
|
|
105
|
+
lengths: [0, 0],
|
|
106
|
+
generatedOffsets: [
|
|
107
|
+
genOffset,
|
|
108
|
+
genOffset + extraMapping.generatedNeedle.length,
|
|
109
|
+
],
|
|
99
110
|
data: VERIFICATION_ONLY_MAPPING_DATA,
|
|
100
111
|
});
|
|
101
112
|
}
|
|
@@ -9,9 +9,6 @@ interface MatchInfo {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
type ReplacementPayload =
|
|
12
|
-
| {
|
|
13
|
-
type: "preface";
|
|
14
|
-
}
|
|
15
12
|
| {
|
|
16
13
|
type: "enterVMFromRoot";
|
|
17
14
|
vm: string;
|
|
@@ -95,6 +92,7 @@ export function applyReplacements(
|
|
|
95
92
|
const NamedDefinition = JSON.stringify(NamedDefinitionLit.value);
|
|
96
93
|
const Meta = JSON.stringify(MetaLit.value);
|
|
97
94
|
const matchInfos: MatchInfo[] = [];
|
|
95
|
+
let cumulativeOffset = 0;
|
|
98
96
|
|
|
99
97
|
const result = code.replace(
|
|
100
98
|
replacementRegex,
|
|
@@ -103,14 +101,7 @@ export function applyReplacements(
|
|
|
103
101
|
rawPayload.replace(/\\`/g, "`"),
|
|
104
102
|
);
|
|
105
103
|
let replacement: string;
|
|
106
|
-
if (payload.type === "
|
|
107
|
-
replacement = dedent`
|
|
108
|
-
namespace ${state.utilNsId.name} {
|
|
109
|
-
export type UniqueKeyProbSegment = "__gts_unique_prob_seg__";
|
|
110
|
-
export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
|
|
111
|
-
}
|
|
112
|
-
`;
|
|
113
|
-
} else if (payload.type === "enterVMFromRoot") {
|
|
104
|
+
if (payload.type === "enterVMFromRoot") {
|
|
114
105
|
replacement = dedent`
|
|
115
106
|
type ${payload.defType} = (typeof ${payload.vm})[${NamedDefinition}];
|
|
116
107
|
type ${payload.metaType} = ${payload.defType}[${Meta}];
|
|
@@ -121,14 +112,15 @@ export function applyReplacements(
|
|
|
121
112
|
type ${payload.metaType} = ${payload.defType}[${Meta}];
|
|
122
113
|
`;
|
|
123
114
|
} else if (payload.type === "exitVM") {
|
|
124
|
-
const lhs = `${payload.finalMetaType}_lhs`;
|
|
125
115
|
const requiredAttrsNs = `${payload.finalMetaType}_rans`;
|
|
126
|
-
const collectedAttrsExpr =
|
|
116
|
+
const collectedAttrsExpr =
|
|
117
|
+
[...new Set(payload.collectedAttrs)].join(" | ") || "never";
|
|
127
118
|
const length = payload.errorRange
|
|
128
119
|
? payload.errorRange[1] - payload.errorRange[0]
|
|
129
120
|
: 0;
|
|
130
|
-
//
|
|
131
|
-
|
|
121
|
+
// Map both ends explicitly; nested blocks no longer need a copy of
|
|
122
|
+
// their entire source length as padding in every diagnostic string.
|
|
123
|
+
const needleString = `"${requiredAttrsNs}" as string as ${state.utilNsId.name}.RequiredMessage<${requiredAttrsNs}, ${collectedAttrsExpr}>`;
|
|
132
124
|
if (payload.errorRange) {
|
|
133
125
|
state.extraMappings.push({
|
|
134
126
|
sourceOffset: payload.errorRange[0],
|
|
@@ -138,19 +130,8 @@ export function applyReplacements(
|
|
|
138
130
|
}
|
|
139
131
|
replacement = dedent`
|
|
140
132
|
type ${payload.finalMetaType} = ${payload.metaType};
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
namespace ${requiredAttrsNs} {
|
|
144
|
-
export type Collected = ${collectedAttrsExpr};
|
|
145
|
-
export type Expected = {
|
|
146
|
-
[K in keyof ${payload.defType}]: ${lhs}[K] extends { required(this: ${lhs}): true } ? K : never;
|
|
147
|
-
}[keyof ${payload.defType}];
|
|
148
|
-
type DiagObj = {
|
|
149
|
-
[K in Expected]: K extends Collected ? never : \`'\${K}' is a required attribute but not provided\`;
|
|
150
|
-
}
|
|
151
|
-
export type DiagMsg = DiagObj[Expected];
|
|
152
|
-
};
|
|
153
|
-
((_: ${requiredAttrsNs}.Expected extends ${requiredAttrsNs}.Collected ? string : ${requiredAttrsNs}.Expected) => 0)(${needleString});
|
|
133
|
+
type ${requiredAttrsNs} = ${state.utilNsId.name}.RequiredAttrs<${state.utilNsId.name}.WithMeta<${payload.defType}, ${payload.metaType}>>;
|
|
134
|
+
${state.utilNsId.name}.checkRequired<${requiredAttrsNs}, ${collectedAttrsExpr}>(${needleString});
|
|
154
135
|
`;
|
|
155
136
|
} else if (payload.type === "enterAttr") {
|
|
156
137
|
const uniqueKeyLhs = `${payload.lhs}_uniqueKey_lhs`;
|
|
@@ -158,40 +139,25 @@ export function applyReplacements(
|
|
|
158
139
|
const uniqueKeyForThis = `${payload.lhs}_uniqueKeyFor_${payload.lhs}`;
|
|
159
140
|
const uniqueKeyHelperIntf = `${payload.defType}_uniqueKeyProbeHelper`;
|
|
160
141
|
const omittedKeys = `${payload.lhs}_omittedKeys`;
|
|
142
|
+
// Keep Meta inside the receiver's property. Passing it to a generic
|
|
143
|
+
// receiver alias eagerly resolves later probes and can form a cycle.
|
|
161
144
|
replacement = dedent`
|
|
162
|
-
|
|
163
|
-
${Meta}: ${payload.metaType};
|
|
164
|
-
uniqueKey: ${payload.defType} extends { [${payload.attrName}]: { uniqueKey: infer UniqueKey } } ? UniqueKey : () => 0;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
let ${uniqueKeyLhs}!: ${uniqueKeyLhs};
|
|
145
|
+
declare const ${uniqueKeyLhs}: { ${Meta}: ${payload.metaType}; uniqueKey: ${state.utilNsId.name}.Member<${payload.defType}, ${payload.attrName}, "uniqueKey", () => 0> };
|
|
168
146
|
let ${uniqueKey} = ${uniqueKeyLhs}.uniqueKey();
|
|
169
147
|
type ${uniqueKey} = typeof ${uniqueKey};
|
|
170
148
|
let ${uniqueKeyForThis}!: \`\${${uniqueKey}}\${${state.utilNsId.name}.UniqueKeyProbSegment}${payload.lhs}\`;
|
|
171
149
|
interface ${uniqueKeyHelperIntf} {
|
|
172
150
|
[${uniqueKeyForThis}]: 1;
|
|
173
151
|
}
|
|
174
|
-
type ${omittedKeys} = ${Meta} | (
|
|
175
|
-
${uniqueKey} extends 0
|
|
176
|
-
? never /* no unique requirement */
|
|
177
|
-
: string extends keyof ${uniqueKeyHelperIntf}
|
|
178
|
-
? keyof ${payload.defType} /* too loose, disable all */
|
|
179
|
-
: ${state.utilNsId.name}.UnionToIntersection<
|
|
180
|
-
keyof ${uniqueKeyHelperIntf} & \`\${${uniqueKey}}\${${state.utilNsId.name}.UniqueKeyProbSegment}\${string}\`
|
|
181
|
-
> extends never
|
|
182
|
-
? ${payload.attrName} /* have duplicate, disable this */
|
|
183
|
-
: never
|
|
184
|
-
);
|
|
152
|
+
type ${omittedKeys} = ${Meta} | (${uniqueKey} extends 0 ? never : string extends keyof ${uniqueKeyHelperIntf} ? keyof ${payload.defType} : ${state.utilNsId.name}.UnionToIntersection<keyof ${uniqueKeyHelperIntf} & \`\${${uniqueKey}}\${${state.utilNsId.name}.UniqueKeyProbSegment}\${string}\`> extends never ? ${payload.attrName} : never);
|
|
185
153
|
let ${payload.lhs}!: ${payload.hintOnly ? `{}` : `{ ${Meta}: ${payload.metaType} }`} & Omit<${payload.defType}, ${omittedKeys}>;
|
|
186
154
|
`;
|
|
187
155
|
} else if (payload.type === "createBindingTyping") {
|
|
188
156
|
const typingIdLhs = `${payload.typingId}_lhs`;
|
|
157
|
+
// As with uniqueKey, an as() without a Meta-aware this parameter must
|
|
158
|
+
// not force final Meta (which may itself depend on this binding).
|
|
189
159
|
replacement = dedent`
|
|
190
|
-
|
|
191
|
-
${Meta}: ${payload.finalMetaType};
|
|
192
|
-
as: ${payload.defType} extends { [${payload.attrName}]: { as: infer As } } ? As : unknown;
|
|
193
|
-
};
|
|
194
|
-
let ${typingIdLhs}!: ${typingIdLhs};
|
|
160
|
+
declare const ${typingIdLhs}: { ${Meta}: ${payload.finalMetaType}; as: ${state.utilNsId.name}.Member<${payload.defType}, ${payload.attrName}, "as", unknown> };
|
|
195
161
|
let ${payload.typingId} = ${typingIdLhs}.as();
|
|
196
162
|
type ${payload.typingId} = typeof ${payload.typingId};
|
|
197
163
|
`;
|
|
@@ -202,33 +168,39 @@ export function applyReplacements(
|
|
|
202
168
|
replacement = dedent`
|
|
203
169
|
type ${payload.returnType} = typeof ${payload.returnType};
|
|
204
170
|
type ${rewrittenMeta} = ${payload.returnType} extends { rewriteMeta: infer NewMeta extends {} } ? NewMeta : ${payload.oldMetaType};
|
|
205
|
-
|
|
206
|
-
[${payload.attrName}]: { mergeMeta: infer M }
|
|
207
|
-
} ? M : <const T>(x: T, y: unknown) => T;
|
|
171
|
+
declare const ${mergeFn}: ${state.utilNsId.name}.MergeMeta<${payload.defType}, ${payload.attrName}>;
|
|
208
172
|
let ${mergeFnRet} = ${mergeFn}(null! as ${rewrittenMeta}, null! as ${payload.innerMetaType});
|
|
209
173
|
type ${payload.newMetaType} = [typeof ${mergeFn}] extends [null] ? ${rewrittenMeta} : typeof ${mergeFnRet};
|
|
210
174
|
`;
|
|
211
175
|
} else {
|
|
212
176
|
replacement = "";
|
|
213
177
|
}
|
|
178
|
+
cumulativeOffset += replacement.length - match.length;
|
|
214
179
|
matchInfos.push({
|
|
215
180
|
sourceEnd: offset + match.length,
|
|
216
|
-
lengthOffset:
|
|
181
|
+
lengthOffset: cumulativeOffset,
|
|
217
182
|
});
|
|
218
183
|
return replacement;
|
|
219
184
|
},
|
|
220
185
|
);
|
|
221
186
|
|
|
187
|
+
// 调整替换后 mapping 的 generatedOffset
|
|
188
|
+
// 由于替换信息 matchInfos 的 sourceEnd 是有序的,可以二分查找到对应的 lengthOffset
|
|
222
189
|
for (const mapping of mappings) {
|
|
223
190
|
for (let i = 0; i < mapping.generatedOffsets.length; i++) {
|
|
224
191
|
const orig = mapping.generatedOffsets[i];
|
|
225
|
-
let
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
192
|
+
let low = 0;
|
|
193
|
+
let high = matchInfos.length;
|
|
194
|
+
while (low < high) {
|
|
195
|
+
const mid = (low + high) >>> 1;
|
|
196
|
+
if (matchInfos[mid].sourceEnd <= orig) {
|
|
197
|
+
low = mid + 1;
|
|
198
|
+
} else {
|
|
199
|
+
high = mid;
|
|
229
200
|
}
|
|
230
201
|
}
|
|
231
|
-
mapping.generatedOffsets[i] =
|
|
202
|
+
mapping.generatedOffsets[i] =
|
|
203
|
+
orig + (matchInfos[low - 1]?.lengthOffset ?? 0);
|
|
232
204
|
}
|
|
233
205
|
}
|
|
234
206
|
|
|
@@ -32,6 +32,7 @@ interface ExternalizedTypedBinding extends ExternalizedBinding {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export interface TypingTranspileState extends TranspileState {
|
|
35
|
+
typeCheckingOnly: boolean;
|
|
35
36
|
externalizedBindings: ExternalizedTypedBinding[];
|
|
36
37
|
idCounter: number;
|
|
37
38
|
rootVmId: Identifier;
|
|
@@ -283,6 +284,10 @@ const insertHintStatement = (
|
|
|
283
284
|
whiteSpaceStart: number,
|
|
284
285
|
whiteSpaceEnd: number,
|
|
285
286
|
) => {
|
|
287
|
+
if (state.typeCheckingOnly) {
|
|
288
|
+
// type-checking do not need insert hint statement
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
286
291
|
const { lhsId } = enterAttr(state, ATTR_HINT_ATTR_NAME);
|
|
287
292
|
state.typingPendingStatements.push({
|
|
288
293
|
type: "GTSAttributeNameHintStatement",
|
|
@@ -377,6 +382,11 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
377
382
|
imported: { type: "Identifier", name: "createBinding" },
|
|
378
383
|
local: state.createBindingFnId,
|
|
379
384
|
},
|
|
385
|
+
{
|
|
386
|
+
type: "ImportSpecifier",
|
|
387
|
+
imported: { type: "Identifier", name: "TypingUtils" },
|
|
388
|
+
local: state.utilNsId,
|
|
389
|
+
},
|
|
380
390
|
],
|
|
381
391
|
source: { type: "Literal", value: state.runtimeImportSource },
|
|
382
392
|
attributes: [],
|
|
@@ -410,9 +420,6 @@ export const gtsToTypingsWalker: Visitors<Node, TypingTranspileState> = {
|
|
|
410
420
|
},
|
|
411
421
|
},
|
|
412
422
|
lastImportDecl,
|
|
413
|
-
createReplacementHolder(state, {
|
|
414
|
-
type: "preface",
|
|
415
|
-
}),
|
|
416
423
|
);
|
|
417
424
|
return {
|
|
418
425
|
...node,
|