@thigasdevelopment/luam 0.19.1 → 0.19.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/lua/class.lua +47 -9
- package/lua/validate.lua +239 -0
- package/luam.mjs +1846 -354
- package/package.json +1 -1
package/luam.mjs
CHANGED
|
@@ -1644,8 +1644,8 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1644
1644
|
* @param {boolean} [combine] - if `true` or omitted, an optional value can be specified directly after the flag.
|
|
1645
1645
|
* @return {Command} `this` command for chaining
|
|
1646
1646
|
*/
|
|
1647
|
-
combineFlagAndOptionalValue(
|
|
1648
|
-
this._combineFlagAndOptionalValue = !!
|
|
1647
|
+
combineFlagAndOptionalValue(combine2 = true) {
|
|
1648
|
+
this._combineFlagAndOptionalValue = !!combine2;
|
|
1649
1649
|
return this;
|
|
1650
1650
|
}
|
|
1651
1651
|
/**
|
|
@@ -3143,6 +3143,7 @@ var DEFAULT_ENVIRONMENT_FILE = ".env";
|
|
|
3143
3143
|
var DEFAULT_LOCAL_ENVIRONMENT_FILE = ".env.local";
|
|
3144
3144
|
var DEFAULT_ASSET_DESTINATION = ".";
|
|
3145
3145
|
var DEFAULT_OUT_DIR = "build";
|
|
3146
|
+
var DEFAULT_CONTRACTS_DIR = ".luam/contracts";
|
|
3146
3147
|
var DEFAULT_RESOURCES_DIR = "mods/deathmatch/resources";
|
|
3147
3148
|
var DEFAULT_COMPILER_OPTIONS = {
|
|
3148
3149
|
strict: true,
|
|
@@ -3492,14 +3493,14 @@ function readEscape(cursor) {
|
|
|
3492
3493
|
}
|
|
3493
3494
|
function scanQuotedString(cursor, diagnostics) {
|
|
3494
3495
|
const position2 = cursor.position();
|
|
3495
|
-
const
|
|
3496
|
+
const quote3 = cursor.peek();
|
|
3496
3497
|
cursor.advance();
|
|
3497
3498
|
let value = "";
|
|
3498
|
-
while (!cursor.isEof() && cursor.peek() !==
|
|
3499
|
+
while (!cursor.isEof() && cursor.peek() !== quote3 && !isLineBreak(cursor.peek())) {
|
|
3499
3500
|
value += cursor.peek() === "\\" ? readEscape(cursor) : cursor.advance();
|
|
3500
3501
|
}
|
|
3501
|
-
if (cursor.peek() !==
|
|
3502
|
-
const message = `Unterminated string. Close it with ${
|
|
3502
|
+
if (cursor.peek() !== quote3) {
|
|
3503
|
+
const message = `Unterminated string. Close it with ${quote3}.`;
|
|
3503
3504
|
diagnostics.push(createDiagnostic("lexer", "lex-unterminated-string", message, position2));
|
|
3504
3505
|
return value;
|
|
3505
3506
|
}
|
|
@@ -3827,19 +3828,21 @@ function parseTypeName(stream) {
|
|
|
3827
3828
|
return { kind: "type-name", name: token.value, typeArguments, position: token.position };
|
|
3828
3829
|
}
|
|
3829
3830
|
function parseParameterType(stream) {
|
|
3831
|
+
let name = null;
|
|
3830
3832
|
if (stream.check("identifier") && stream.checkAhead(1, "punctuation", ":")) {
|
|
3831
|
-
stream.next();
|
|
3833
|
+
name = stream.next().value;
|
|
3832
3834
|
stream.next();
|
|
3833
3835
|
}
|
|
3834
|
-
return parseTypeAnnotation(stream);
|
|
3836
|
+
return { name, annotation: parseTypeAnnotation(stream) };
|
|
3835
3837
|
}
|
|
3836
3838
|
function parseFunctionType(stream) {
|
|
3837
3839
|
const position2 = stream.expect("identifier", FUNCTION_TYPE).position;
|
|
3838
3840
|
if (!stream.check("punctuation", "(")) {
|
|
3839
|
-
return { kind: "type-function", parameters: [], isVariadic: true, returnType: null, position: position2 };
|
|
3841
|
+
return { kind: "type-function", parameters: [], parameterNames: [], isVariadic: true, returnType: null, position: position2 };
|
|
3840
3842
|
}
|
|
3841
3843
|
stream.next();
|
|
3842
3844
|
const parameters = [];
|
|
3845
|
+
const parameterNames = [];
|
|
3843
3846
|
let isVariadic = false;
|
|
3844
3847
|
while (!stream.check("punctuation", ")") && !stream.isEof()) {
|
|
3845
3848
|
if (stream.match("operator", "...")) {
|
|
@@ -3848,7 +3851,9 @@ function parseFunctionType(stream) {
|
|
|
3848
3851
|
parseTypeAnnotation(stream);
|
|
3849
3852
|
}
|
|
3850
3853
|
} else {
|
|
3851
|
-
|
|
3854
|
+
const parameter = parseParameterType(stream);
|
|
3855
|
+
parameters.push(parameter.annotation);
|
|
3856
|
+
parameterNames.push(parameter.name);
|
|
3852
3857
|
}
|
|
3853
3858
|
if (!stream.match("punctuation", ",")) {
|
|
3854
3859
|
break;
|
|
@@ -3856,7 +3861,7 @@ function parseFunctionType(stream) {
|
|
|
3856
3861
|
}
|
|
3857
3862
|
stream.expect("punctuation", ")");
|
|
3858
3863
|
const returnType = stream.match("punctuation", ":") ? parseTypeAnnotation(stream) : null;
|
|
3859
|
-
return { kind: "type-function", parameters, isVariadic, returnType, position: position2 };
|
|
3864
|
+
return { kind: "type-function", parameters, parameterNames, isVariadic, returnType, position: position2 };
|
|
3860
3865
|
}
|
|
3861
3866
|
function parseGroupedType(stream) {
|
|
3862
3867
|
const position2 = stream.expect("punctuation", "(").position;
|
|
@@ -3989,10 +3994,18 @@ function parseNamedAnnotation(stream, declaration) {
|
|
|
3989
3994
|
if (optional === null && annotation?.kind === "type-optional") {
|
|
3990
3995
|
stream.report("parse-optional-position", `Write optional ${label2} as "name?: Type", not "name: Type?".`, annotation.position);
|
|
3991
3996
|
}
|
|
3997
|
+
if (optional !== null && annotation?.kind === "type-optional") {
|
|
3998
|
+
const message = `The marker on the name already allows "nil". Remove the "?" after the type.`;
|
|
3999
|
+
stream.report("parse-redundant-optional", message, annotation.position);
|
|
4000
|
+
}
|
|
3992
4001
|
if (optional !== null && annotation !== null) {
|
|
3993
4002
|
stream.eraseFrom(checkpoint);
|
|
3994
4003
|
}
|
|
3995
|
-
|
|
4004
|
+
if (optional === null || annotation === null) {
|
|
4005
|
+
return annotation;
|
|
4006
|
+
}
|
|
4007
|
+
const element = annotation.kind === "type-optional" ? annotation.element : annotation;
|
|
4008
|
+
return { kind: "type-optional", element, position: optional.position };
|
|
3996
4009
|
}
|
|
3997
4010
|
|
|
3998
4011
|
// ../compiler/src/parser/function-expression.ts
|
|
@@ -4146,7 +4159,8 @@ function parsePrimary(stream) {
|
|
|
4146
4159
|
if (token.kind === "keyword" && token.value === "new" && stream.checkAhead(1, "identifier")) {
|
|
4147
4160
|
stream.next();
|
|
4148
4161
|
const className = stream.next().value;
|
|
4149
|
-
|
|
4162
|
+
const typeArguments = parseNewTypeArguments(stream);
|
|
4163
|
+
return { kind: "new-expression", className, typeArguments, args: parseArguments(stream), position: token.position };
|
|
4150
4164
|
}
|
|
4151
4165
|
if (token.kind === "keyword" && CALLABLE_KEYWORDS.has(token.value) && stream.checkAhead(1, "punctuation", "(")) {
|
|
4152
4166
|
stream.next();
|
|
@@ -4178,6 +4192,20 @@ function parsePrimary(stream) {
|
|
|
4178
4192
|
}
|
|
4179
4193
|
throw stream.error(`Unexpected "${stream.describeCurrent()}" in expression.`, "parse-unexpected-token");
|
|
4180
4194
|
}
|
|
4195
|
+
function parseNewTypeArguments(stream) {
|
|
4196
|
+
const args = [];
|
|
4197
|
+
if (!stream.check("operator", "<")) {
|
|
4198
|
+
return args;
|
|
4199
|
+
}
|
|
4200
|
+
const checkpoint = stream.checkpoint();
|
|
4201
|
+
stream.next();
|
|
4202
|
+
do {
|
|
4203
|
+
args.push(parseTypeAnnotation(stream));
|
|
4204
|
+
} while (stream.match("punctuation", ","));
|
|
4205
|
+
stream.expect("operator", ">");
|
|
4206
|
+
stream.eraseFrom(checkpoint);
|
|
4207
|
+
return args;
|
|
4208
|
+
}
|
|
4181
4209
|
function parseArguments(stream) {
|
|
4182
4210
|
if (stream.check("string") || stream.check("template") || stream.check("punctuation", "{")) {
|
|
4183
4211
|
return [parsePrimary(stream)];
|
|
@@ -4464,8 +4492,23 @@ var TokenStream = class {
|
|
|
4464
4492
|
// ../compiler/src/parser/declarations.ts
|
|
4465
4493
|
var DECLARATION_NAMES = /* @__PURE__ */ new Set(["class", "interface", "enum"]);
|
|
4466
4494
|
var CLASS_MODIFIERS = /* @__PURE__ */ new Set(["extends", "implements"]);
|
|
4495
|
+
function skipTypeParameters(stream, offset) {
|
|
4496
|
+
if (!stream.checkAhead(offset, "operator", "<")) {
|
|
4497
|
+
return offset;
|
|
4498
|
+
}
|
|
4499
|
+
let depth = 0;
|
|
4500
|
+
let index = offset;
|
|
4501
|
+
do {
|
|
4502
|
+
const token = stream.peek(index);
|
|
4503
|
+
depth += token.kind === "operator" && token.value === "<" ? 1 : 0;
|
|
4504
|
+
depth -= token.kind === "operator" && token.value === ">" ? 1 : 0;
|
|
4505
|
+
index += 1;
|
|
4506
|
+
} while (depth > 0 && stream.peek(index).kind !== "eof");
|
|
4507
|
+
return index;
|
|
4508
|
+
}
|
|
4467
4509
|
function isClassHeader(stream, offset = 0) {
|
|
4468
|
-
|
|
4510
|
+
const after = skipTypeParameters(stream, offset + 2);
|
|
4511
|
+
return stream.checkAhead(after, "punctuation", "{") || stream.checkAhead(after, "keyword", "extends") || stream.checkAhead(after, "keyword", "implements");
|
|
4469
4512
|
}
|
|
4470
4513
|
function isDeclarationStart(stream) {
|
|
4471
4514
|
let offset = 0;
|
|
@@ -4595,10 +4638,40 @@ function parseClassMember(stream) {
|
|
|
4595
4638
|
expectClassFieldBoundary(stream, token);
|
|
4596
4639
|
return { kind: "class-field", name: token.value, annotation, value, decorators, isStatic, position: token.position };
|
|
4597
4640
|
}
|
|
4641
|
+
function parseTypeParameters(stream) {
|
|
4642
|
+
const list = { names: [], constraints: [] };
|
|
4643
|
+
if (!stream.check("operator", "<")) {
|
|
4644
|
+
return list;
|
|
4645
|
+
}
|
|
4646
|
+
const checkpoint = stream.checkpoint();
|
|
4647
|
+
stream.next();
|
|
4648
|
+
do {
|
|
4649
|
+
list.names.push(stream.expect("identifier").value);
|
|
4650
|
+
list.constraints.push(stream.match("keyword", "extends") ? parseTypeAnnotation(stream) : null);
|
|
4651
|
+
} while (stream.match("punctuation", ","));
|
|
4652
|
+
stream.expect("operator", ">");
|
|
4653
|
+
stream.eraseFrom(checkpoint);
|
|
4654
|
+
return list;
|
|
4655
|
+
}
|
|
4656
|
+
function parseTypeArguments(stream) {
|
|
4657
|
+
const args = [];
|
|
4658
|
+
if (!stream.check("operator", "<")) {
|
|
4659
|
+
return args;
|
|
4660
|
+
}
|
|
4661
|
+
const checkpoint = stream.checkpoint();
|
|
4662
|
+
stream.next();
|
|
4663
|
+
do {
|
|
4664
|
+
args.push(parseTypeAnnotation(stream));
|
|
4665
|
+
} while (stream.match("punctuation", ","));
|
|
4666
|
+
stream.expect("operator", ">");
|
|
4667
|
+
stream.eraseFrom(checkpoint);
|
|
4668
|
+
return args;
|
|
4669
|
+
}
|
|
4598
4670
|
function parseClassModifiers(stream, declaration) {
|
|
4599
4671
|
while (stream.check("keyword") && CLASS_MODIFIERS.has(stream.current().value)) {
|
|
4600
4672
|
if (stream.next().value === "extends") {
|
|
4601
4673
|
declaration.superClass = stream.expect("identifier").value;
|
|
4674
|
+
declaration.superClassArguments = parseTypeArguments(stream);
|
|
4602
4675
|
continue;
|
|
4603
4676
|
}
|
|
4604
4677
|
do {
|
|
@@ -4609,7 +4682,19 @@ function parseClassModifiers(stream, declaration) {
|
|
|
4609
4682
|
function parseClassDeclaration(stream, decorators) {
|
|
4610
4683
|
const position2 = stream.next().position;
|
|
4611
4684
|
const name = stream.expect("identifier").value;
|
|
4612
|
-
const
|
|
4685
|
+
const typeParameters = parseTypeParameters(stream);
|
|
4686
|
+
const declaration = {
|
|
4687
|
+
kind: "class-declaration",
|
|
4688
|
+
name,
|
|
4689
|
+
typeParameters: typeParameters.names,
|
|
4690
|
+
typeConstraints: typeParameters.constraints,
|
|
4691
|
+
superClass: null,
|
|
4692
|
+
superClassArguments: [],
|
|
4693
|
+
interfaces: [],
|
|
4694
|
+
members: [],
|
|
4695
|
+
decorators,
|
|
4696
|
+
position: position2
|
|
4697
|
+
};
|
|
4613
4698
|
parseClassModifiers(stream, declaration);
|
|
4614
4699
|
stream.expect("punctuation", "{");
|
|
4615
4700
|
while (!stream.check("punctuation", "}") && !stream.isEof()) {
|
|
@@ -5069,8 +5154,8 @@ var TABLE_TYPE = { kind: "table" };
|
|
|
5069
5154
|
function createArray(element) {
|
|
5070
5155
|
return { kind: "array", element };
|
|
5071
5156
|
}
|
|
5072
|
-
function createNamed(name) {
|
|
5073
|
-
return { kind: "named", name };
|
|
5157
|
+
function createNamed(name, typeArguments = []) {
|
|
5158
|
+
return typeArguments.length === 0 ? { kind: "named", name } : { kind: "named", name, typeArguments };
|
|
5074
5159
|
}
|
|
5075
5160
|
function createMap(key, value) {
|
|
5076
5161
|
return { kind: "map", key, value };
|
|
@@ -5183,6 +5268,12 @@ function createUnion(options) {
|
|
|
5183
5268
|
}
|
|
5184
5269
|
return { kind: "union", options: flattened };
|
|
5185
5270
|
}
|
|
5271
|
+
function namedNesting(type) {
|
|
5272
|
+
if (type.kind !== "named") {
|
|
5273
|
+
return 0;
|
|
5274
|
+
}
|
|
5275
|
+
return 1 + Math.max(0, ...(type.typeArguments ?? []).map(namedNesting));
|
|
5276
|
+
}
|
|
5186
5277
|
function typeToString(type) {
|
|
5187
5278
|
if (type.kind === "array") {
|
|
5188
5279
|
return `${typeToString(type.element)}[]`;
|
|
@@ -5202,7 +5293,11 @@ function typeToString(type) {
|
|
|
5202
5293
|
if (type.kind === "boolean-literal" || type.kind === "number-literal") {
|
|
5203
5294
|
return String(type.value);
|
|
5204
5295
|
}
|
|
5205
|
-
if (type.kind === "named"
|
|
5296
|
+
if (type.kind === "named") {
|
|
5297
|
+
const args = type.typeArguments ?? [];
|
|
5298
|
+
return args.length === 0 ? type.name : `${type.name}<${args.map(typeToString).join(", ")}>`;
|
|
5299
|
+
}
|
|
5300
|
+
if (type.kind === "record") {
|
|
5206
5301
|
return type.name;
|
|
5207
5302
|
}
|
|
5208
5303
|
if (type.kind === "tuple") {
|
|
@@ -5292,6 +5387,59 @@ function isRecordAssignable(source, target, options) {
|
|
|
5292
5387
|
}
|
|
5293
5388
|
return true;
|
|
5294
5389
|
}
|
|
5390
|
+
function isNamedAssignable(source, target, options) {
|
|
5391
|
+
const sourceArguments = source.typeArguments ?? [];
|
|
5392
|
+
const targetArguments = target.typeArguments ?? [];
|
|
5393
|
+
if (source.name !== target.name || sourceArguments.length === 0 || targetArguments.length !== sourceArguments.length) {
|
|
5394
|
+
return true;
|
|
5395
|
+
}
|
|
5396
|
+
return sourceArguments.every((argument, index) => isAssignable(argument, targetArguments[index] ?? ANY_TYPE, options));
|
|
5397
|
+
}
|
|
5398
|
+
function asRecord(name, members) {
|
|
5399
|
+
return { kind: "record", name, origin: null, members };
|
|
5400
|
+
}
|
|
5401
|
+
function shapeOf(name, options) {
|
|
5402
|
+
return options.resolveNominal?.(name) ?? null;
|
|
5403
|
+
}
|
|
5404
|
+
function withVisit(options, pair) {
|
|
5405
|
+
return { ...options, visited: /* @__PURE__ */ new Set([...options.visited ?? [], pair]) };
|
|
5406
|
+
}
|
|
5407
|
+
function isNominalAssignable(source, target, options) {
|
|
5408
|
+
const pair = `${source.name}>${target.name}`;
|
|
5409
|
+
if (options.visited?.has(pair) === true) {
|
|
5410
|
+
return true;
|
|
5411
|
+
}
|
|
5412
|
+
const declared = shapeOf(target.name, options);
|
|
5413
|
+
const actual = shapeOf(source.name, options);
|
|
5414
|
+
if (declared === null || actual === null) {
|
|
5415
|
+
return true;
|
|
5416
|
+
}
|
|
5417
|
+
if (declared.kind === "class" || actual.kind === "class") {
|
|
5418
|
+
return actual.kind === "class" && actual.ancestors.has(target.name);
|
|
5419
|
+
}
|
|
5420
|
+
return isRecordAssignable(asRecord(source.name, actual.members), asRecord(target.name, declared.members), withVisit(options, pair));
|
|
5421
|
+
}
|
|
5422
|
+
var SCALAR_KINDS = /* @__PURE__ */ new Set(["string", "number", "boolean", "thread", "userdata", "void", "nil", "function"]);
|
|
5423
|
+
function isNamedSourceAssignable(source, target, options) {
|
|
5424
|
+
const actual = shapeOf(source.name, options);
|
|
5425
|
+
if (actual === null) {
|
|
5426
|
+
return true;
|
|
5427
|
+
}
|
|
5428
|
+
if (actual.kind === "class") {
|
|
5429
|
+
return !SCALAR_KINDS.has(target.kind) && !isLiteralType(target);
|
|
5430
|
+
}
|
|
5431
|
+
return isAssignable(asRecord(source.name, actual.members), target, options);
|
|
5432
|
+
}
|
|
5433
|
+
function isNamedTargetAssignable(source, target, options) {
|
|
5434
|
+
const declared = shapeOf(target.name, options);
|
|
5435
|
+
if (declared === null) {
|
|
5436
|
+
return true;
|
|
5437
|
+
}
|
|
5438
|
+
if (declared.kind === "class") {
|
|
5439
|
+
return false;
|
|
5440
|
+
}
|
|
5441
|
+
return isAssignable(source, asRecord(target.name, declared.members), options);
|
|
5442
|
+
}
|
|
5295
5443
|
function isAssignable(source, target, options = { allowNil: false }) {
|
|
5296
5444
|
if (source.kind === "any" || target.kind === "any" || target.kind === "unknown") {
|
|
5297
5445
|
return true;
|
|
@@ -5299,9 +5447,6 @@ function isAssignable(source, target, options = { allowNil: false }) {
|
|
|
5299
5447
|
if (source.kind === "tuple" || target.kind === "tuple") {
|
|
5300
5448
|
return isAssignable(firstValueOf(source), firstValueOf(target), options);
|
|
5301
5449
|
}
|
|
5302
|
-
if (source.kind === "named" || target.kind === "named") {
|
|
5303
|
-
return true;
|
|
5304
|
-
}
|
|
5305
5450
|
if (source.kind === "nil" && (options.allowNil || target.kind === "optional" || target.kind === "nil")) {
|
|
5306
5451
|
return true;
|
|
5307
5452
|
}
|
|
@@ -5320,6 +5465,15 @@ function isAssignable(source, target, options = { allowNil: false }) {
|
|
|
5320
5465
|
if (target.kind === "optional") {
|
|
5321
5466
|
return isAssignable(source, target.element, options);
|
|
5322
5467
|
}
|
|
5468
|
+
if (source.kind === "named" && target.kind === "named") {
|
|
5469
|
+
return source.name === target.name ? isNamedAssignable(source, target, options) : isNominalAssignable(source, target, options);
|
|
5470
|
+
}
|
|
5471
|
+
if (source.kind === "named") {
|
|
5472
|
+
return isNamedSourceAssignable(source, target, options);
|
|
5473
|
+
}
|
|
5474
|
+
if (target.kind === "named") {
|
|
5475
|
+
return isNamedTargetAssignable(source, target, options);
|
|
5476
|
+
}
|
|
5323
5477
|
if (target.kind === "table") {
|
|
5324
5478
|
return isTableLike(source) || source.kind === "record";
|
|
5325
5479
|
}
|
|
@@ -5458,7 +5612,8 @@ var RUNTIME_HELPERS = {
|
|
|
5458
5612
|
math: { name: "math", file: "math.lua", injection: "automatic", features: ["number-extension"] },
|
|
5459
5613
|
string: { name: "string", file: "string.lua", injection: "automatic", features: ["string-extension", "template-string"] },
|
|
5460
5614
|
table: { name: "table", file: "table.lua", injection: "automatic", features: ["table-extension"] },
|
|
5461
|
-
threads: { name: "threads", file: "threads.lua", injection: "reference", features: ["thread"], globals: ["sleep", "Threads"] }
|
|
5615
|
+
threads: { name: "threads", file: "threads.lua", injection: "reference", features: ["thread"], globals: ["sleep", "Threads"] },
|
|
5616
|
+
validate: { name: "validate", file: "validate.lua", injection: "automatic", features: ["boundary-validation"] }
|
|
5462
5617
|
};
|
|
5463
5618
|
var DEVELOPMENT_RUNTIME_HELPERS = {
|
|
5464
5619
|
"development-logs-client": {
|
|
@@ -5599,6 +5754,11 @@ var MANIFEST_FIELDS = [
|
|
|
5599
5754
|
owner: "dependencies",
|
|
5600
5755
|
allowEmpty: true
|
|
5601
5756
|
}),
|
|
5757
|
+
field("contracts", STRING_TYPE, "Directory the export contract is written to and dependency contracts are read from.", {
|
|
5758
|
+
defaultValue: DEFAULT_CONTRACTS_DIR,
|
|
5759
|
+
rule: "contained-path",
|
|
5760
|
+
owner: "dependencies"
|
|
5761
|
+
}),
|
|
5602
5762
|
table("engine", "Requirements the MTA server and client must meet.", ENGINE_FIELDS, { defaultValue: {}, owner: "engine" }),
|
|
5603
5763
|
table("environment", "Files that declare and override the project environment.", ENVIRONMENT_FIELDS, { defaultValue: {}, owner: "environment" }),
|
|
5604
5764
|
field("outDir", STRING_TYPE, "Directory the built resource is written to.", { defaultValue: DEFAULT_OUT_DIR, rule: "static-path", owner: "output" }),
|
|
@@ -5758,8 +5918,8 @@ function isNumber(type) {
|
|
|
5758
5918
|
function isText(type) {
|
|
5759
5919
|
return type.kind === "string" || type.kind === "string-literal" || type.kind === "any";
|
|
5760
5920
|
}
|
|
5761
|
-
function fail(operator, expected, left, right,
|
|
5762
|
-
const culprit =
|
|
5921
|
+
function fail(operator, expected, left, right, describe2) {
|
|
5922
|
+
const culprit = describe2(isNumber(left) || isText(left) ? right : left);
|
|
5763
5923
|
return { evaluated: null, error: `"${operator}" expects ${expected} on both sides but received ${culprit}.` };
|
|
5764
5924
|
}
|
|
5765
5925
|
function arithmetic(operator, left, right) {
|
|
@@ -5798,22 +5958,22 @@ function applyOr(left, right) {
|
|
|
5798
5958
|
const value = isTruthy(left.value) ? left.value : right.value;
|
|
5799
5959
|
return { value, type: unionOf([left.truthy, right.type]), truthy: unionOf([left.truthy, right.truthy]), falsy: right.falsy };
|
|
5800
5960
|
}
|
|
5801
|
-
function applyOrdering(operator, left, right,
|
|
5961
|
+
function applyOrdering(operator, left, right, describe2) {
|
|
5802
5962
|
const numeric = isNumber(left.type) && isNumber(right.type);
|
|
5803
5963
|
const textual = isText(left.type) && isText(right.type);
|
|
5804
5964
|
if (!numeric && !textual) {
|
|
5805
|
-
return fail(operator, "two numbers or two strings", left.type, right.type,
|
|
5965
|
+
return fail(operator, "two numbers or two strings", left.type, right.type, describe2);
|
|
5806
5966
|
}
|
|
5807
5967
|
return { evaluated: booleanValue(compare(operator, left.value, right.value)), error: null };
|
|
5808
5968
|
}
|
|
5809
|
-
function applyConcat(left, right,
|
|
5969
|
+
function applyConcat(left, right, describe2) {
|
|
5810
5970
|
const usable = (type) => isText(type) || isNumber(type);
|
|
5811
5971
|
if (!usable(left.type) || !usable(right.type)) {
|
|
5812
|
-
return fail("..", "a string or a number", left.type, right.type,
|
|
5972
|
+
return fail("..", "a string or a number", left.type, right.type, describe2);
|
|
5813
5973
|
}
|
|
5814
5974
|
return { evaluated: stringValue(`${left.value}${right.value}`), error: null };
|
|
5815
5975
|
}
|
|
5816
|
-
function applyBinary(operator, left, right,
|
|
5976
|
+
function applyBinary(operator, left, right, describe2) {
|
|
5817
5977
|
if (operator === "and") {
|
|
5818
5978
|
return { evaluated: applyAnd(left, right), error: null };
|
|
5819
5979
|
}
|
|
@@ -5825,23 +5985,23 @@ function applyBinary(operator, left, right, describe) {
|
|
|
5825
5985
|
return { evaluated: booleanValue(operator === "==" ? same : !same), error: null };
|
|
5826
5986
|
}
|
|
5827
5987
|
if (ORDERING.includes(operator)) {
|
|
5828
|
-
return applyOrdering(operator, left, right,
|
|
5988
|
+
return applyOrdering(operator, left, right, describe2);
|
|
5829
5989
|
}
|
|
5830
5990
|
if (operator === "..") {
|
|
5831
|
-
return applyConcat(left, right,
|
|
5991
|
+
return applyConcat(left, right, describe2);
|
|
5832
5992
|
}
|
|
5833
5993
|
if (!isNumber(left.type) || !isNumber(right.type)) {
|
|
5834
|
-
return fail(operator, "a number", left.type, right.type,
|
|
5994
|
+
return fail(operator, "a number", left.type, right.type, describe2);
|
|
5835
5995
|
}
|
|
5836
5996
|
return { evaluated: numberValue(arithmetic(operator, left.value, right.value)), error: null };
|
|
5837
5997
|
}
|
|
5838
|
-
function applyUnary(operator, operand,
|
|
5998
|
+
function applyUnary(operator, operand, describe2) {
|
|
5839
5999
|
if (operator === "not") {
|
|
5840
6000
|
return { evaluated: booleanValue(!isTruthy(operand.value)), error: null };
|
|
5841
6001
|
}
|
|
5842
6002
|
if (operator === "-") {
|
|
5843
6003
|
if (!isNumber(operand.type)) {
|
|
5844
|
-
return { evaluated: null, error: `"-" expects a number but received ${
|
|
6004
|
+
return { evaluated: null, error: `"-" expects a number but received ${describe2(operand.type)}.` };
|
|
5845
6005
|
}
|
|
5846
6006
|
return { evaluated: numberValue(-operand.value), error: null };
|
|
5847
6007
|
}
|
|
@@ -6300,8 +6460,8 @@ var RuleWalk = class {
|
|
|
6300
6460
|
}
|
|
6301
6461
|
};
|
|
6302
6462
|
function normalizeManifest(value, positions) {
|
|
6303
|
-
const
|
|
6304
|
-
return { value:
|
|
6463
|
+
const walk4 = new RuleWalk(positions);
|
|
6464
|
+
return { value: walk4.fields(MANIFEST_FIELDS, value, "", ""), diagnostics: walk4.diagnostics };
|
|
6305
6465
|
}
|
|
6306
6466
|
|
|
6307
6467
|
// ../compiler/src/manifest/manifest-analysis.ts
|
|
@@ -6537,6 +6697,7 @@ function validateConfig(value, positions) {
|
|
|
6537
6697
|
sources: readSourceMapping(value),
|
|
6538
6698
|
assets: readAssetMappings(value),
|
|
6539
6699
|
dependencies,
|
|
6700
|
+
contracts: readString(value, "contracts") ?? "",
|
|
6540
6701
|
engine: readEngine(value),
|
|
6541
6702
|
environment: readEnvironmentFiles(value),
|
|
6542
6703
|
outDir: readString(value, "outDir") ?? "",
|
|
@@ -6601,7 +6762,7 @@ import { tmpdir } from "node:os";
|
|
|
6601
6762
|
import { join as join2 } from "node:path";
|
|
6602
6763
|
|
|
6603
6764
|
// src/cli/version.ts
|
|
6604
|
-
var VERSION = true ? "0.19.
|
|
6765
|
+
var VERSION = true ? "0.19.2" : "0.0.0-dev";
|
|
6605
6766
|
var PROGRAM_NAME = "luam";
|
|
6606
6767
|
var PROGRAM_DESCRIPTION = "luam \u2014 the Luam compiler for Multi Theft Auto resources.";
|
|
6607
6768
|
|
|
@@ -6786,9 +6947,9 @@ function pluralize(count, singular, plural = `${singular}s`) {
|
|
|
6786
6947
|
var MAX_ENTRIES_PER_FILE = 10;
|
|
6787
6948
|
var MAX_FILES = 10;
|
|
6788
6949
|
var GUTTER_WIDTH = 6;
|
|
6789
|
-
function groupByFile(
|
|
6950
|
+
function groupByFile(entries3) {
|
|
6790
6951
|
const groups = /* @__PURE__ */ new Map();
|
|
6791
|
-
for (const entry of
|
|
6952
|
+
for (const entry of entries3) {
|
|
6792
6953
|
const group = groups.get(entry.path);
|
|
6793
6954
|
if (group === void 0) {
|
|
6794
6955
|
groups.set(entry.path, [entry]);
|
|
@@ -6835,19 +6996,19 @@ function emit(reporter, entry, block) {
|
|
|
6835
6996
|
}
|
|
6836
6997
|
reporter.rawWarn(block);
|
|
6837
6998
|
}
|
|
6838
|
-
function reportGroup(reporter, path,
|
|
6999
|
+
function reportGroup(reporter, path, entries3, options) {
|
|
6839
7000
|
const limit = options.maxPerFile ?? MAX_ENTRIES_PER_FILE;
|
|
6840
7001
|
const source = options.sources.get(path);
|
|
6841
7002
|
reporter.raw(reporter.style.paint("strong", path));
|
|
6842
|
-
for (const entry of
|
|
7003
|
+
for (const entry of entries3.slice(0, limit)) {
|
|
6843
7004
|
emit(reporter, entry, entryBlock(reporter, entry, source));
|
|
6844
7005
|
}
|
|
6845
|
-
if (
|
|
6846
|
-
reporter.detail(` ... and ${pluralize(
|
|
7006
|
+
if (entries3.length > limit) {
|
|
7007
|
+
reporter.detail(` ... and ${pluralize(entries3.length - limit, "more diagnostic")} in this file.`);
|
|
6847
7008
|
}
|
|
6848
7009
|
}
|
|
6849
|
-
function reportGroupedDiagnostics(reporter,
|
|
6850
|
-
const groups = [...groupByFile(
|
|
7010
|
+
function reportGroupedDiagnostics(reporter, entries3, options) {
|
|
7011
|
+
const groups = [...groupByFile(entries3)];
|
|
6851
7012
|
const limit = options.maxFiles ?? MAX_FILES;
|
|
6852
7013
|
for (const [path, group] of groups.slice(0, limit)) {
|
|
6853
7014
|
reportGroup(reporter, path, group, options);
|
|
@@ -6863,9 +7024,9 @@ function formatFileDiagnostic(entry) {
|
|
|
6863
7024
|
const location = `${path}:${diagnostic.position.line}:${diagnostic.position.column}`;
|
|
6864
7025
|
return `${location} ${diagnostic.severity} ${diagnostic.code}: ${diagnostic.message}`;
|
|
6865
7026
|
}
|
|
6866
|
-
function countFileDiagnostics(
|
|
6867
|
-
const errors =
|
|
6868
|
-
return { errors, warnings:
|
|
7027
|
+
function countFileDiagnostics(entries3) {
|
|
7028
|
+
const errors = entries3.filter((entry) => entry.diagnostic.severity === "error").length;
|
|
7029
|
+
return { errors, warnings: entries3.length - errors };
|
|
6869
7030
|
}
|
|
6870
7031
|
function countCliDiagnostics(diagnostics) {
|
|
6871
7032
|
const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
@@ -6875,8 +7036,8 @@ function reportManifestDiagnostics(reporter, path, source, diagnostics) {
|
|
|
6875
7036
|
if (diagnostics.length === 0) {
|
|
6876
7037
|
return;
|
|
6877
7038
|
}
|
|
6878
|
-
const
|
|
6879
|
-
reportFileDiagnostics(reporter,
|
|
7039
|
+
const entries3 = diagnostics.map((diagnostic) => ({ path, diagnostic }));
|
|
7040
|
+
reportFileDiagnostics(reporter, entries3, /* @__PURE__ */ new Map([[path, source]]));
|
|
6880
7041
|
}
|
|
6881
7042
|
function reportCliDiagnostics(reporter, diagnostics) {
|
|
6882
7043
|
for (const diagnostic of diagnostics) {
|
|
@@ -6888,8 +7049,8 @@ function reportCliDiagnostics(reporter, diagnostics) {
|
|
|
6888
7049
|
reporter.warn(line2);
|
|
6889
7050
|
}
|
|
6890
7051
|
}
|
|
6891
|
-
function reportPlainFileDiagnostics(reporter,
|
|
6892
|
-
for (const entry of
|
|
7052
|
+
function reportPlainFileDiagnostics(reporter, entries3) {
|
|
7053
|
+
for (const entry of entries3) {
|
|
6893
7054
|
const line2 = formatFileDiagnostic(entry);
|
|
6894
7055
|
if (entry.diagnostic.severity === "error") {
|
|
6895
7056
|
reporter.rawError(line2);
|
|
@@ -6898,12 +7059,12 @@ function reportPlainFileDiagnostics(reporter, entries2) {
|
|
|
6898
7059
|
reporter.rawWarn(line2);
|
|
6899
7060
|
}
|
|
6900
7061
|
}
|
|
6901
|
-
function reportFileDiagnostics(reporter,
|
|
7062
|
+
function reportFileDiagnostics(reporter, entries3, sources) {
|
|
6902
7063
|
if (!reporter.capability.interactive) {
|
|
6903
|
-
reportPlainFileDiagnostics(reporter,
|
|
7064
|
+
reportPlainFileDiagnostics(reporter, entries3);
|
|
6904
7065
|
return;
|
|
6905
7066
|
}
|
|
6906
|
-
reportGroupedDiagnostics(reporter,
|
|
7067
|
+
reportGroupedDiagnostics(reporter, entries3, { sources });
|
|
6907
7068
|
}
|
|
6908
7069
|
function formatCounts(counts) {
|
|
6909
7070
|
return `${pluralize(counts.errors, "error")}, ${pluralize(counts.warnings, "warning")}`;
|
|
@@ -7154,8 +7315,153 @@ function addProjectOptions(command) {
|
|
|
7154
7315
|
return command.addOption(cwdOption()).addOption(manifestOption()).addOption(colorOption());
|
|
7155
7316
|
}
|
|
7156
7317
|
|
|
7318
|
+
// src/build/contract-files.ts
|
|
7319
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
|
|
7320
|
+
import { dirname as dirname2, resolve as resolve4 } from "node:path";
|
|
7321
|
+
|
|
7322
|
+
// ../compiler/src/project/export-abi.ts
|
|
7323
|
+
var ABI_VERSION = 1;
|
|
7324
|
+
var ABI_EXTENSION = ".abi.json";
|
|
7325
|
+
var MAX_ABI_BYTES = 262144;
|
|
7326
|
+
var MAX_EXPORTS = 512;
|
|
7327
|
+
var MAX_PARAMETERS = 32;
|
|
7328
|
+
var MAX_TYPE_LENGTH = 512;
|
|
7329
|
+
var NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
7330
|
+
var RESOURCE_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
7331
|
+
var SIDES = /* @__PURE__ */ new Set(["server", "client", "shared"]);
|
|
7332
|
+
function serializeAbi(abi) {
|
|
7333
|
+
const exports = [...abi.exports].sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
7334
|
+
return `${JSON.stringify({ abi: abi.abi, resource: abi.resource, exports }, null, 4)}
|
|
7335
|
+
`;
|
|
7336
|
+
}
|
|
7337
|
+
function readString2(value, limit) {
|
|
7338
|
+
return typeof value === "string" && value.length > 0 && value.length <= limit ? value : null;
|
|
7339
|
+
}
|
|
7340
|
+
function readParameter(value) {
|
|
7341
|
+
if (typeof value !== "object" || value === null) {
|
|
7342
|
+
return null;
|
|
7343
|
+
}
|
|
7344
|
+
const entry = value;
|
|
7345
|
+
const name = readString2(entry.name, 64);
|
|
7346
|
+
const type = readString2(entry.type, MAX_TYPE_LENGTH);
|
|
7347
|
+
return name === null || type === null || !NAME_PATTERN.test(name) ? null : { name, type };
|
|
7348
|
+
}
|
|
7349
|
+
function readExport(value) {
|
|
7350
|
+
if (typeof value !== "object" || value === null) {
|
|
7351
|
+
return null;
|
|
7352
|
+
}
|
|
7353
|
+
const entry = value;
|
|
7354
|
+
const name = readString2(entry.name, 128);
|
|
7355
|
+
const side2 = readString2(entry.side, 16);
|
|
7356
|
+
const returns = readString2(entry.returns, MAX_TYPE_LENGTH);
|
|
7357
|
+
const parameters = Array.isArray(entry.parameters) ? entry.parameters : null;
|
|
7358
|
+
if (name === null || !NAME_PATTERN.test(name) || side2 === null || !SIDES.has(side2) || returns === null || parameters === null) {
|
|
7359
|
+
return null;
|
|
7360
|
+
}
|
|
7361
|
+
if (parameters.length > MAX_PARAMETERS) {
|
|
7362
|
+
return null;
|
|
7363
|
+
}
|
|
7364
|
+
const read = parameters.map(readParameter);
|
|
7365
|
+
if (read.some((parameter) => parameter === null)) {
|
|
7366
|
+
return null;
|
|
7367
|
+
}
|
|
7368
|
+
return {
|
|
7369
|
+
name,
|
|
7370
|
+
side: side2,
|
|
7371
|
+
http: entry.http === true,
|
|
7372
|
+
parameters: read,
|
|
7373
|
+
minimumArguments: typeof entry.minimumArguments === "number" && Number.isInteger(entry.minimumArguments) ? entry.minimumArguments : 0,
|
|
7374
|
+
variadic: entry.variadic === true,
|
|
7375
|
+
returns
|
|
7376
|
+
};
|
|
7377
|
+
}
|
|
7378
|
+
function parseResourceAbi(text) {
|
|
7379
|
+
if (text.length > MAX_ABI_BYTES) {
|
|
7380
|
+
return null;
|
|
7381
|
+
}
|
|
7382
|
+
let parsed;
|
|
7383
|
+
try {
|
|
7384
|
+
parsed = JSON.parse(text);
|
|
7385
|
+
} catch {
|
|
7386
|
+
return null;
|
|
7387
|
+
}
|
|
7388
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
7389
|
+
return null;
|
|
7390
|
+
}
|
|
7391
|
+
const document = parsed;
|
|
7392
|
+
const resource = readString2(document.resource, 64);
|
|
7393
|
+
const entries3 = Array.isArray(document.exports) ? document.exports : null;
|
|
7394
|
+
if (document.abi !== ABI_VERSION || resource === null || !RESOURCE_PATTERN.test(resource) || entries3 === null || entries3.length > MAX_EXPORTS) {
|
|
7395
|
+
return null;
|
|
7396
|
+
}
|
|
7397
|
+
const read = entries3.map(readExport);
|
|
7398
|
+
if (read.some((entry) => entry === null)) {
|
|
7399
|
+
return null;
|
|
7400
|
+
}
|
|
7401
|
+
return { abi: ABI_VERSION, resource, exports: read };
|
|
7402
|
+
}
|
|
7403
|
+
function abiFileName(resource) {
|
|
7404
|
+
return `${resource}${ABI_EXTENSION}`;
|
|
7405
|
+
}
|
|
7406
|
+
function lookupAbiExport(contracts, resource, name) {
|
|
7407
|
+
return contracts.find((entry) => entry.resource === resource)?.exports.find((entry) => entry.name === name) ?? null;
|
|
7408
|
+
}
|
|
7409
|
+
function knowsResource(contracts, resource) {
|
|
7410
|
+
return contracts.some((entry) => entry.resource === resource);
|
|
7411
|
+
}
|
|
7412
|
+
function buildResourceAbi(resource, contributions) {
|
|
7413
|
+
const exports = contributions.map((contribution) => {
|
|
7414
|
+
const signature = contribution.signature;
|
|
7415
|
+
return {
|
|
7416
|
+
name: contribution.name,
|
|
7417
|
+
side: contribution.side,
|
|
7418
|
+
http: contribution.http,
|
|
7419
|
+
parameters: signature?.parameters ?? [],
|
|
7420
|
+
minimumArguments: signature?.minimumArguments ?? 0,
|
|
7421
|
+
variadic: signature?.variadic ?? true,
|
|
7422
|
+
returns: signature?.returns ?? "any"
|
|
7423
|
+
};
|
|
7424
|
+
});
|
|
7425
|
+
return { abi: ABI_VERSION, resource, exports };
|
|
7426
|
+
}
|
|
7427
|
+
|
|
7428
|
+
// src/build/contract-files.ts
|
|
7429
|
+
function contractPath(root, config, resource) {
|
|
7430
|
+
return resolve4(root, config.contracts, abiFileName(resource));
|
|
7431
|
+
}
|
|
7432
|
+
function readDependencyContracts(root, config) {
|
|
7433
|
+
const contracts = [];
|
|
7434
|
+
const diagnostics = [];
|
|
7435
|
+
for (const dependency of config.dependencies) {
|
|
7436
|
+
const path = contractPath(root, config, dependency);
|
|
7437
|
+
let text;
|
|
7438
|
+
try {
|
|
7439
|
+
text = readFileSync3(path, "utf8");
|
|
7440
|
+
} catch {
|
|
7441
|
+
continue;
|
|
7442
|
+
}
|
|
7443
|
+
const parsed = parseResourceAbi(text);
|
|
7444
|
+
if (parsed === null) {
|
|
7445
|
+
diagnostics.push(cliWarning("build-invalid-contract", `The export contract for "${dependency}" at "${path}" could not be read and was ignored.`));
|
|
7446
|
+
continue;
|
|
7447
|
+
}
|
|
7448
|
+
if (parsed.resource !== dependency) {
|
|
7449
|
+
diagnostics.push(cliWarning("build-invalid-contract", `The export contract at "${path}" names resource "${parsed.resource}" and was ignored.`));
|
|
7450
|
+
continue;
|
|
7451
|
+
}
|
|
7452
|
+
contracts.push(parsed);
|
|
7453
|
+
}
|
|
7454
|
+
return { contracts, diagnostics };
|
|
7455
|
+
}
|
|
7456
|
+
function writeResourceContract(root, config, contract) {
|
|
7457
|
+
const path = contractPath(root, config, config.name);
|
|
7458
|
+
mkdirSync2(dirname2(path), { recursive: true });
|
|
7459
|
+
writeFileSync3(path, serializeAbi(contract), "utf8");
|
|
7460
|
+
return path;
|
|
7461
|
+
}
|
|
7462
|
+
|
|
7157
7463
|
// src/build/helper-files.ts
|
|
7158
|
-
import { existsSync as existsSync3, readFileSync as
|
|
7464
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
|
|
7159
7465
|
import { fileURLToPath } from "node:url";
|
|
7160
7466
|
function bundledHelperPath(file) {
|
|
7161
7467
|
return fileURLToPath(new URL(`./lua/${file}`, import.meta.url));
|
|
@@ -7167,7 +7473,7 @@ function resolveHelperPath(helper, file) {
|
|
|
7167
7473
|
return existsSync3(packaged) ? packaged : bundledHelperPath(file);
|
|
7168
7474
|
}
|
|
7169
7475
|
function readHelperSource(helper, file) {
|
|
7170
|
-
return
|
|
7476
|
+
return readFileSync4(resolveHelperPath(helper, file), "utf8");
|
|
7171
7477
|
}
|
|
7172
7478
|
|
|
7173
7479
|
// src/build/build-phase.ts
|
|
@@ -7236,16 +7542,16 @@ function createPhaseTracker(listener = null) {
|
|
|
7236
7542
|
}
|
|
7237
7543
|
|
|
7238
7544
|
// src/build/project-inputs.ts
|
|
7239
|
-
import { existsSync as existsSync4, readFileSync as
|
|
7240
|
-
import { resolve as
|
|
7545
|
+
import { existsSync as existsSync4, readFileSync as readFileSync5 } from "node:fs";
|
|
7546
|
+
import { resolve as resolve7 } from "node:path";
|
|
7241
7547
|
|
|
7242
7548
|
// src/build/asset-resolution.ts
|
|
7243
7549
|
import { statSync as statSync3 } from "node:fs";
|
|
7244
|
-
import { resolve as
|
|
7550
|
+
import { resolve as resolve6 } from "node:path";
|
|
7245
7551
|
|
|
7246
7552
|
// src/build/project-files.ts
|
|
7247
7553
|
import { readdirSync, statSync as statSync2 } from "node:fs";
|
|
7248
|
-
import { join as join3, relative, resolve as
|
|
7554
|
+
import { join as join3, relative, resolve as resolve5 } from "node:path";
|
|
7249
7555
|
function isDirectory(path) {
|
|
7250
7556
|
try {
|
|
7251
7557
|
return statSync2(path).isDirectory();
|
|
@@ -7257,14 +7563,14 @@ function relativePath(root, absolute) {
|
|
|
7257
7563
|
return normalizePattern(relative(root, absolute));
|
|
7258
7564
|
}
|
|
7259
7565
|
function walk(root, directory, excluded, tree) {
|
|
7260
|
-
let
|
|
7566
|
+
let entries3;
|
|
7261
7567
|
try {
|
|
7262
|
-
|
|
7568
|
+
entries3 = readdirSync(directory, { withFileTypes: true });
|
|
7263
7569
|
} catch (error) {
|
|
7264
7570
|
tree.errors.push(`"${relativePath(root, directory)}" could not be read: ${error instanceof Error ? error.message : String(error)}`);
|
|
7265
7571
|
return;
|
|
7266
7572
|
}
|
|
7267
|
-
for (const entry of
|
|
7573
|
+
for (const entry of entries3) {
|
|
7268
7574
|
const absolute = join3(directory, entry.name);
|
|
7269
7575
|
const path = relativePath(root, absolute);
|
|
7270
7576
|
if (path.length === 0 || isExcludedPath(path, excluded)) {
|
|
@@ -7281,7 +7587,7 @@ function listProjectFiles(root, roots, excluded = []) {
|
|
|
7281
7587
|
const tree = { files: [], errors: [] };
|
|
7282
7588
|
const seen = /* @__PURE__ */ new Set();
|
|
7283
7589
|
for (const start of [...new Set(roots)].sort()) {
|
|
7284
|
-
const absolute =
|
|
7590
|
+
const absolute = resolve5(root, start);
|
|
7285
7591
|
if (!isDirectory(absolute) || seen.has(absolute)) {
|
|
7286
7592
|
continue;
|
|
7287
7593
|
}
|
|
@@ -7425,9 +7731,23 @@ var OCCUPIED_SIDES = {
|
|
|
7425
7731
|
function occupiedSides(contribution) {
|
|
7426
7732
|
return OCCUPIED_SIDES[contribution.side];
|
|
7427
7733
|
}
|
|
7428
|
-
function
|
|
7734
|
+
function exportSignature(type) {
|
|
7735
|
+
if (type === void 0 || type.kind !== "function") {
|
|
7736
|
+
return null;
|
|
7737
|
+
}
|
|
7738
|
+
const parameters = type.parameters.map((parameter, index) => ({ name: type.parameterNames?.[index] ?? `arg${index + 1}`, type: typeToString(parameter) }));
|
|
7739
|
+
return { parameters, minimumArguments: type.minimumArguments, variadic: type.isVariadic, returns: typeToString(type.returnType) };
|
|
7740
|
+
}
|
|
7741
|
+
function toContributions(directives, environment, globals) {
|
|
7429
7742
|
return directives.exports.map(
|
|
7430
|
-
(entry) => ({
|
|
7743
|
+
(entry) => ({
|
|
7744
|
+
kind: "export",
|
|
7745
|
+
name: entry.name,
|
|
7746
|
+
http: entry.http,
|
|
7747
|
+
side: environment,
|
|
7748
|
+
signature: exportSignature(globals.get(entry.name)),
|
|
7749
|
+
position: entry.position
|
|
7750
|
+
})
|
|
7431
7751
|
);
|
|
7432
7752
|
}
|
|
7433
7753
|
var INDENT = " ";
|
|
@@ -7461,8 +7781,8 @@ function attribute(name, value) {
|
|
|
7461
7781
|
function textElement(name, value) {
|
|
7462
7782
|
return `${INDENT}<${name}>${escapeXml(value)}</${name}>`;
|
|
7463
7783
|
}
|
|
7464
|
-
function section(text,
|
|
7465
|
-
return
|
|
7784
|
+
function section(text, entries3) {
|
|
7785
|
+
return entries3.length === 0 ? [] : [`${INDENT}<!-- ${text} -->`, ...entries3];
|
|
7466
7786
|
}
|
|
7467
7787
|
function infoElement(info) {
|
|
7468
7788
|
const attributes = info.author === void 0 ? [] : [attribute("author", info.author)];
|
|
@@ -7590,12 +7910,12 @@ var MISSING_LOAD_ORDER = 'is listed in "loadOrder" but no source file or asset m
|
|
|
7590
7910
|
function missingEntry(entry) {
|
|
7591
7911
|
return { path: entry, diagnostic: createDiagnostic("project", "project-load-order-missing", `"${entry}" ${MISSING_LOAD_ORDER}`, FILE_START) };
|
|
7592
7912
|
}
|
|
7593
|
-
function resolveLoadOrder(
|
|
7913
|
+
function resolveLoadOrder(entries3, scripts, assets) {
|
|
7594
7914
|
const byScript = new Map(scripts.map((script) => [normalizePath(script.source), script]));
|
|
7595
7915
|
const byAsset = new Map(assets.map((asset) => [normalizePath(asset.source), asset]));
|
|
7596
7916
|
const resolved2 = { scripts: [], assets: [], diagnostics: [] };
|
|
7597
7917
|
const seen = /* @__PURE__ */ new Set();
|
|
7598
|
-
for (const entry of
|
|
7918
|
+
for (const entry of entries3) {
|
|
7599
7919
|
const path = normalizePath(entry).replace(/^\.\//, "");
|
|
7600
7920
|
const script = byScript.get(path);
|
|
7601
7921
|
const asset = byAsset.get(path);
|
|
@@ -7671,11 +7991,11 @@ var ENTRY = /^([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$/;
|
|
|
7671
7991
|
var SENSITIVE = /(password|secret|token|key|credential|dsn|private)/i;
|
|
7672
7992
|
var EMPTY_ENV_FILE = { entries: [], errors: [] };
|
|
7673
7993
|
function unquote(raw) {
|
|
7674
|
-
const
|
|
7675
|
-
if (
|
|
7994
|
+
const quote3 = raw.slice(0, 1);
|
|
7995
|
+
if (quote3 !== '"' && quote3 !== "'") {
|
|
7676
7996
|
return null;
|
|
7677
7997
|
}
|
|
7678
|
-
const closing = raw.lastIndexOf(
|
|
7998
|
+
const closing = raw.lastIndexOf(quote3);
|
|
7679
7999
|
return closing > 0 ? raw.slice(1, closing) : null;
|
|
7680
8000
|
}
|
|
7681
8001
|
function stripComment(raw) {
|
|
@@ -7693,7 +8013,7 @@ function classify(raw) {
|
|
|
7693
8013
|
return { value, kind: value.length > 0 && Number.isFinite(Number(value)) ? "number" : "string" };
|
|
7694
8014
|
}
|
|
7695
8015
|
function parseEnvFile(source) {
|
|
7696
|
-
const
|
|
8016
|
+
const entries3 = [];
|
|
7697
8017
|
const errors = [];
|
|
7698
8018
|
source.split(/\r?\n/).forEach((raw, index) => {
|
|
7699
8019
|
const trimmed = raw.trim();
|
|
@@ -7707,9 +8027,9 @@ function parseEnvFile(source) {
|
|
|
7707
8027
|
return;
|
|
7708
8028
|
}
|
|
7709
8029
|
const { value, kind } = classify(match[2]);
|
|
7710
|
-
|
|
8030
|
+
entries3.push({ key: match[1], value, kind, line: line2 });
|
|
7711
8031
|
});
|
|
7712
|
-
return { entries:
|
|
8032
|
+
return { entries: entries3, errors };
|
|
7713
8033
|
}
|
|
7714
8034
|
function isSensitiveKey(key) {
|
|
7715
8035
|
return SENSITIVE.test(key);
|
|
@@ -7875,14 +8195,14 @@ var MANIFEST_OUTPUT = "meta.xml";
|
|
|
7875
8195
|
var HERE = ".";
|
|
7876
8196
|
function isDirectory2(root, path) {
|
|
7877
8197
|
try {
|
|
7878
|
-
return statSync3(
|
|
8198
|
+
return statSync3(resolve6(root, path)).isDirectory();
|
|
7879
8199
|
} catch {
|
|
7880
8200
|
return false;
|
|
7881
8201
|
}
|
|
7882
8202
|
}
|
|
7883
8203
|
function exists(root, path) {
|
|
7884
8204
|
try {
|
|
7885
|
-
statSync3(
|
|
8205
|
+
statSync3(resolve6(root, path));
|
|
7886
8206
|
return true;
|
|
7887
8207
|
} catch {
|
|
7888
8208
|
return false;
|
|
@@ -7950,10 +8270,10 @@ var CONFIGURATION_FILE = "config.lua";
|
|
|
7950
8270
|
var MALFORMED_ENV = "build-env-malformed";
|
|
7951
8271
|
var MISSING_ENV = "config-missing-env-file";
|
|
7952
8272
|
function readTextFile(path) {
|
|
7953
|
-
return existsSync4(path) ?
|
|
8273
|
+
return existsSync4(path) ? readFileSync5(path, "utf8") : null;
|
|
7954
8274
|
}
|
|
7955
8275
|
function readEnvironment(root, file, required, diagnostics) {
|
|
7956
|
-
const source = readTextFile(
|
|
8276
|
+
const source = readTextFile(resolve7(root, file));
|
|
7957
8277
|
if (source === null) {
|
|
7958
8278
|
if (required) {
|
|
7959
8279
|
diagnostics.push(cliError(MISSING_ENV, `"${file}" is configured under "environment" but does not exist. Create it or remove the setting.`));
|
|
@@ -7967,7 +8287,7 @@ function readEnvironment(root, file, required, diagnostics) {
|
|
|
7967
8287
|
return parsed;
|
|
7968
8288
|
}
|
|
7969
8289
|
function readConfiguration(root, diagnostics) {
|
|
7970
|
-
const content = readTextFile(
|
|
8290
|
+
const content = readTextFile(resolve7(root, CONFIGURATION_FILE));
|
|
7971
8291
|
if (content === null) {
|
|
7972
8292
|
return null;
|
|
7973
8293
|
}
|
|
@@ -7986,14 +8306,14 @@ function readProjectInputs(root, options) {
|
|
|
7986
8306
|
}
|
|
7987
8307
|
|
|
7988
8308
|
// src/build/source-discovery.ts
|
|
7989
|
-
import { readFileSync as
|
|
7990
|
-
import { resolve as
|
|
8309
|
+
import { readFileSync as readFileSync6 } from "node:fs";
|
|
8310
|
+
import { resolve as resolve8 } from "node:path";
|
|
7991
8311
|
|
|
7992
8312
|
// ../compiler/src/project/source-mapping.ts
|
|
7993
8313
|
function createSourceResolver(mapping) {
|
|
7994
8314
|
const matchers = new Map(SOURCE_SIDES.map((environment) => [environment, createPatternMatcher(mapping[environment])]));
|
|
7995
8315
|
const patterns = SOURCE_SIDES.flatMap((environment) => [...matchers.get(environment)?.patterns ?? []]);
|
|
7996
|
-
function
|
|
8316
|
+
function resolve19(path) {
|
|
7997
8317
|
const normalized = normalizePattern(path);
|
|
7998
8318
|
const matches = [];
|
|
7999
8319
|
for (const environment of SOURCE_SIDES) {
|
|
@@ -8007,8 +8327,8 @@ function createSourceResolver(mapping) {
|
|
|
8007
8327
|
return {
|
|
8008
8328
|
patterns,
|
|
8009
8329
|
roots: watchRoots(patterns),
|
|
8010
|
-
resolve:
|
|
8011
|
-
side: (path) =>
|
|
8330
|
+
resolve: resolve19,
|
|
8331
|
+
side: (path) => resolve19(path).environment
|
|
8012
8332
|
};
|
|
8013
8333
|
}
|
|
8014
8334
|
function describeMatches(matches) {
|
|
@@ -8040,7 +8360,7 @@ function checkLiterals(root, resolver, found, diagnostics) {
|
|
|
8040
8360
|
}
|
|
8041
8361
|
function readSource(root, path, diagnostics) {
|
|
8042
8362
|
try {
|
|
8043
|
-
return
|
|
8363
|
+
return readFileSync6(resolve8(root, path), "utf8");
|
|
8044
8364
|
} catch (error) {
|
|
8045
8365
|
diagnostics.push(cliError(UNREADABLE_SOURCE, `The source file "${path}" could not be read: ${error instanceof Error ? error.message : String(error)}`));
|
|
8046
8366
|
return null;
|
|
@@ -8120,25 +8440,26 @@ function fn(parameters, returnType, minimumArguments2, isVariadic = false, param
|
|
|
8120
8440
|
var EMPTY_PROJECT_DECLARATIONS = { globals: [] };
|
|
8121
8441
|
var ENV_GLOBAL = "env";
|
|
8122
8442
|
var VALUE_TYPES = { boolean: BOOLEAN, number: NUMBER, string: STRING };
|
|
8123
|
-
function envMembers(
|
|
8443
|
+
function envMembers(entries3) {
|
|
8124
8444
|
const members = /* @__PURE__ */ new Map();
|
|
8125
|
-
for (const entry of
|
|
8445
|
+
for (const entry of entries3) {
|
|
8126
8446
|
members.set(entry.key, { name: entry.key, type: VALUE_TYPES[entry.kind] });
|
|
8127
8447
|
}
|
|
8128
8448
|
return [...members.values()].sort((left, right) => left.name.localeCompare(right.name));
|
|
8129
8449
|
}
|
|
8130
|
-
function envDeclaration(
|
|
8131
|
-
const env = record(ENV_GLOBAL, envMembers(
|
|
8450
|
+
function envDeclaration(entries3, origin) {
|
|
8451
|
+
const env = record(ENV_GLOBAL, envMembers(entries3), origin);
|
|
8132
8452
|
return { name: ENV_GLOBAL, environment: "server", source: "project", type: env };
|
|
8133
8453
|
}
|
|
8134
|
-
function projectDeclarations(
|
|
8135
|
-
if (
|
|
8454
|
+
function projectDeclarations(entries3, origin) {
|
|
8455
|
+
if (entries3 === null) {
|
|
8136
8456
|
return EMPTY_PROJECT_DECLARATIONS;
|
|
8137
8457
|
}
|
|
8138
|
-
return { globals: [envDeclaration(
|
|
8458
|
+
return { globals: [envDeclaration(entries3, origin)] };
|
|
8139
8459
|
}
|
|
8140
8460
|
|
|
8141
8461
|
// ../compiler/src/checker/ambient.ts
|
|
8462
|
+
var EVENT_NAME_PREFIX = "event:";
|
|
8142
8463
|
var EMPTY_AMBIENT = { classes: [], interfaces: [], enums: [], globals: [], events: [] };
|
|
8143
8464
|
function ambientFromRegistry(registry) {
|
|
8144
8465
|
return {
|
|
@@ -8149,9 +8470,9 @@ function ambientFromRegistry(registry) {
|
|
|
8149
8470
|
events: registry.allEvents()
|
|
8150
8471
|
};
|
|
8151
8472
|
}
|
|
8152
|
-
function withoutNames(
|
|
8473
|
+
function withoutNames(entries3, excluded) {
|
|
8153
8474
|
const names = new Set(excluded.map((entry) => entry.name));
|
|
8154
|
-
return
|
|
8475
|
+
return entries3.filter((entry) => !names.has(entry.name));
|
|
8155
8476
|
}
|
|
8156
8477
|
function ownDeclarations(registry, ambient) {
|
|
8157
8478
|
const all = ambientFromRegistry(registry);
|
|
@@ -8328,6 +8649,9 @@ var Binder = class {
|
|
|
8328
8649
|
declaredSymbols() {
|
|
8329
8650
|
return [...this.discarded, ...this.scopes.flatMap((scope) => [...scope.values()])];
|
|
8330
8651
|
}
|
|
8652
|
+
currentScopeNames() {
|
|
8653
|
+
return [...this.scopes[this.scopes.length - 1]?.keys() ?? []];
|
|
8654
|
+
}
|
|
8331
8655
|
declare(symbol) {
|
|
8332
8656
|
const scope = this.scopes[this.scopes.length - 1];
|
|
8333
8657
|
scope?.set(symbol.name, symbol);
|
|
@@ -8366,6 +8690,211 @@ var Binder = class {
|
|
|
8366
8690
|
}
|
|
8367
8691
|
};
|
|
8368
8692
|
|
|
8693
|
+
// ../compiler/src/checker/flow.ts
|
|
8694
|
+
function createFlow() {
|
|
8695
|
+
return { facts: /* @__PURE__ */ new Map(), reachable: true };
|
|
8696
|
+
}
|
|
8697
|
+
function cloneFlow(state) {
|
|
8698
|
+
return { facts: new Map(state.facts), reachable: state.reachable };
|
|
8699
|
+
}
|
|
8700
|
+
function applyFacts(state, facts) {
|
|
8701
|
+
for (const [path, type] of facts) {
|
|
8702
|
+
state.facts.set(path, type);
|
|
8703
|
+
}
|
|
8704
|
+
}
|
|
8705
|
+
function forgetPath(state, path) {
|
|
8706
|
+
for (const key of [...state.facts.keys()]) {
|
|
8707
|
+
if (key === path || key.startsWith(`${path}.`) || path.startsWith(`${key}.`)) {
|
|
8708
|
+
state.facts.delete(key);
|
|
8709
|
+
}
|
|
8710
|
+
}
|
|
8711
|
+
}
|
|
8712
|
+
function joinFlows(states) {
|
|
8713
|
+
const live = states.filter((state) => state.reachable);
|
|
8714
|
+
const [first, ...rest] = live;
|
|
8715
|
+
if (first === void 0) {
|
|
8716
|
+
return { facts: /* @__PURE__ */ new Map(), reachable: false };
|
|
8717
|
+
}
|
|
8718
|
+
const facts = /* @__PURE__ */ new Map();
|
|
8719
|
+
for (const [path, type] of first.facts) {
|
|
8720
|
+
const others = rest.map((state) => state.facts.get(path));
|
|
8721
|
+
if (others.some((other) => other === void 0)) {
|
|
8722
|
+
continue;
|
|
8723
|
+
}
|
|
8724
|
+
facts.set(path, createUnion([type, ...others]));
|
|
8725
|
+
}
|
|
8726
|
+
return { facts, reachable: true };
|
|
8727
|
+
}
|
|
8728
|
+
function optionsOf(declared) {
|
|
8729
|
+
if (declared.kind === "union") {
|
|
8730
|
+
return declared.options;
|
|
8731
|
+
}
|
|
8732
|
+
return declared.kind === "optional" ? [declared.element, NIL_TYPE] : [];
|
|
8733
|
+
}
|
|
8734
|
+
function assignmentFact(narrowed, declared) {
|
|
8735
|
+
const matched = optionsOf(declared).filter((option) => isAssignable(narrowed, option));
|
|
8736
|
+
const [only] = matched;
|
|
8737
|
+
return matched.length === 1 && only !== void 0 ? only : null;
|
|
8738
|
+
}
|
|
8739
|
+
|
|
8740
|
+
// ../compiler/src/checker/type-substitution.ts
|
|
8741
|
+
function substituteType(type, substitutions) {
|
|
8742
|
+
if (type.kind === "named") {
|
|
8743
|
+
const args = type.typeArguments ?? [];
|
|
8744
|
+
if (args.length > 0) {
|
|
8745
|
+
return createNamed(type.name, args.map((argument) => substituteType(argument, substitutions)));
|
|
8746
|
+
}
|
|
8747
|
+
return substitutions.get(type.name) ?? type;
|
|
8748
|
+
}
|
|
8749
|
+
if (type.kind === "array") {
|
|
8750
|
+
return createArray(substituteType(type.element, substitutions));
|
|
8751
|
+
}
|
|
8752
|
+
if (type.kind === "map") {
|
|
8753
|
+
return createMap(substituteType(type.key, substitutions), substituteType(type.value, substitutions));
|
|
8754
|
+
}
|
|
8755
|
+
if (type.kind === "optional") {
|
|
8756
|
+
return createOptional(substituteType(type.element, substitutions));
|
|
8757
|
+
}
|
|
8758
|
+
if (type.kind === "union") {
|
|
8759
|
+
return createUnion(type.options.map((option) => substituteType(option, substitutions)));
|
|
8760
|
+
}
|
|
8761
|
+
if (type.kind === "function") {
|
|
8762
|
+
const parameters = type.parameters.map((parameter) => substituteType(parameter, substitutions));
|
|
8763
|
+
const returnType = substituteType(type.returnType, substitutions);
|
|
8764
|
+
const variadicType = type.variadicType === void 0 ? void 0 : substituteType(type.variadicType, substitutions);
|
|
8765
|
+
return createFunction(parameters, returnType, type.minimumArguments, type.isVariadic, type.parameterNames, variadicType);
|
|
8766
|
+
}
|
|
8767
|
+
if (type.kind === "tuple") {
|
|
8768
|
+
return createTuple(type.elements.map((element) => substituteType(element, substitutions)));
|
|
8769
|
+
}
|
|
8770
|
+
if (type.kind === "record") {
|
|
8771
|
+
const members = new Map([...type.members].map(([name, member]) => [name, substituteType(member, substitutions)]));
|
|
8772
|
+
return createRecord(type.name, members, type.origin);
|
|
8773
|
+
}
|
|
8774
|
+
return type;
|
|
8775
|
+
}
|
|
8776
|
+
|
|
8777
|
+
// ../compiler/src/checker/generic-class.ts
|
|
8778
|
+
var MAX_DEPTH = 32;
|
|
8779
|
+
function classSubstitutions(info, typeArguments) {
|
|
8780
|
+
return new Map(info.typeParameters.map((parameter, index) => [parameter, typeArguments[index] ?? ANY_TYPE]));
|
|
8781
|
+
}
|
|
8782
|
+
function parentSubstitutions(registry, info, current) {
|
|
8783
|
+
const parent = info.superClass === null ? null : registry.lookupClass(info.superClass);
|
|
8784
|
+
if (parent === null) {
|
|
8785
|
+
return null;
|
|
8786
|
+
}
|
|
8787
|
+
return classSubstitutions(parent, info.superArguments.map((argument) => substituteType(argument, current)));
|
|
8788
|
+
}
|
|
8789
|
+
function substitutionsFor(registry, receiver, owner) {
|
|
8790
|
+
const info = registry.lookupClass(receiver.name);
|
|
8791
|
+
if (info === null) {
|
|
8792
|
+
return /* @__PURE__ */ new Map();
|
|
8793
|
+
}
|
|
8794
|
+
let current = info;
|
|
8795
|
+
let substitutions = classSubstitutions(info, receiver.typeArguments ?? []);
|
|
8796
|
+
for (let depth = 0; current !== null && depth < MAX_DEPTH; depth += 1) {
|
|
8797
|
+
if (current.name === owner) {
|
|
8798
|
+
return substitutions;
|
|
8799
|
+
}
|
|
8800
|
+
const next = parentSubstitutions(registry, current, substitutions);
|
|
8801
|
+
if (next === null) {
|
|
8802
|
+
return /* @__PURE__ */ new Map();
|
|
8803
|
+
}
|
|
8804
|
+
substitutions = next;
|
|
8805
|
+
current = current.superClass === null ? null : registry.lookupClass(current.superClass);
|
|
8806
|
+
}
|
|
8807
|
+
return /* @__PURE__ */ new Map();
|
|
8808
|
+
}
|
|
8809
|
+
function specializeMember(context, receiver, member, property) {
|
|
8810
|
+
const owner = context.declarations.lookupMemberOwner(receiver.name, property);
|
|
8811
|
+
if (owner === null || owner === receiver.name) {
|
|
8812
|
+
const own = context.declarations.lookupClass(receiver.name);
|
|
8813
|
+
if (own === null || own.typeParameters.length === 0 || (receiver.typeArguments ?? []).length === 0) {
|
|
8814
|
+
return member;
|
|
8815
|
+
}
|
|
8816
|
+
return { ...member, type: substituteType(member.type, classSubstitutions(own, receiver.typeArguments ?? [])) };
|
|
8817
|
+
}
|
|
8818
|
+
return { ...member, type: substituteType(member.type, substitutionsFor(context.declarations, receiver, owner)) };
|
|
8819
|
+
}
|
|
8820
|
+
function memberOf(context, receiver, property) {
|
|
8821
|
+
const member = context.declarations.lookupMember(receiver.name, property);
|
|
8822
|
+
return member === null ? null : specializeMember(context, receiver, member, property);
|
|
8823
|
+
}
|
|
8824
|
+
function unify(parameter, argument, names, bindings) {
|
|
8825
|
+
if (parameter.kind === "named" && names.has(parameter.name)) {
|
|
8826
|
+
if (!bindings.has(parameter.name) && argument.kind !== "nil") {
|
|
8827
|
+
bindings.set(parameter.name, argument);
|
|
8828
|
+
}
|
|
8829
|
+
return;
|
|
8830
|
+
}
|
|
8831
|
+
if (parameter.kind === "array" && argument.kind === "array") {
|
|
8832
|
+
unify(parameter.element, argument.element, names, bindings);
|
|
8833
|
+
return;
|
|
8834
|
+
}
|
|
8835
|
+
if (parameter.kind === "optional") {
|
|
8836
|
+
unify(parameter.element, argument.kind === "optional" ? argument.element : argument, names, bindings);
|
|
8837
|
+
return;
|
|
8838
|
+
}
|
|
8839
|
+
if (parameter.kind === "map" && argument.kind === "map") {
|
|
8840
|
+
unify(parameter.key, argument.key, names, bindings);
|
|
8841
|
+
unify(parameter.value, argument.value, names, bindings);
|
|
8842
|
+
return;
|
|
8843
|
+
}
|
|
8844
|
+
if (parameter.kind === "named" && argument.kind === "named" && parameter.name === argument.name) {
|
|
8845
|
+
const parameterArguments = parameter.typeArguments ?? [];
|
|
8846
|
+
const argumentArguments = argument.typeArguments ?? [];
|
|
8847
|
+
parameterArguments.forEach((entry, index) => unify(entry, argumentArguments[index] ?? ANY_TYPE, names, bindings));
|
|
8848
|
+
return;
|
|
8849
|
+
}
|
|
8850
|
+
if (parameter.kind === "function" && argument.kind === "function") {
|
|
8851
|
+
parameter.parameters.forEach((entry, index) => unify(entry, argument.parameters[index] ?? ANY_TYPE, names, bindings));
|
|
8852
|
+
unify(parameter.returnType, argument.returnType, names, bindings);
|
|
8853
|
+
}
|
|
8854
|
+
}
|
|
8855
|
+
function inferTypeArguments(typeParameters, parameters, args) {
|
|
8856
|
+
const names = new Set(typeParameters);
|
|
8857
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
8858
|
+
parameters.forEach((parameter, index) => {
|
|
8859
|
+
const argument = args[index];
|
|
8860
|
+
if (argument !== void 0) {
|
|
8861
|
+
unify(parameter, widenInferred(argument), names, bindings);
|
|
8862
|
+
}
|
|
8863
|
+
});
|
|
8864
|
+
return typeParameters.map((parameter) => bindings.get(parameter) ?? ANY_TYPE);
|
|
8865
|
+
}
|
|
8866
|
+
function inheritsFrom(context, name, target) {
|
|
8867
|
+
let current = context.declarations.lookupClass(name);
|
|
8868
|
+
for (let depth = 0; current !== null && depth < MAX_DEPTH; depth += 1) {
|
|
8869
|
+
if (current.name === target || current.interfaces.includes(target)) {
|
|
8870
|
+
return true;
|
|
8871
|
+
}
|
|
8872
|
+
current = current.superClass === null ? null : context.declarations.lookupClass(current.superClass);
|
|
8873
|
+
}
|
|
8874
|
+
return context.declarations.interfaceExtends(name, target);
|
|
8875
|
+
}
|
|
8876
|
+
function satisfies(context, argument, constraint) {
|
|
8877
|
+
if (argument.kind === "named" && constraint.kind === "named") {
|
|
8878
|
+
return argument.name === constraint.name || inheritsFrom(context, argument.name, constraint.name);
|
|
8879
|
+
}
|
|
8880
|
+
return isAssignable(argument, constraint, { allowNil: false });
|
|
8881
|
+
}
|
|
8882
|
+
function checkTypeConstraints(context, name, typeArguments, position2) {
|
|
8883
|
+
const info = context.declarations.lookupClass(name);
|
|
8884
|
+
if (info === null) {
|
|
8885
|
+
return;
|
|
8886
|
+
}
|
|
8887
|
+
info.typeConstraints.forEach((constraint, index) => {
|
|
8888
|
+
const argument = typeArguments[index];
|
|
8889
|
+
if (constraint === null || argument === void 0 || argument.kind === "any" || satisfies(context, argument, constraint)) {
|
|
8890
|
+
return;
|
|
8891
|
+
}
|
|
8892
|
+
const parameter = info.typeParameters[index] ?? "T";
|
|
8893
|
+
const message = `Type argument "${typeToString(argument)}" does not satisfy "${parameter} extends ${typeToString(constraint)}" on class "${name}".`;
|
|
8894
|
+
context.report("check-generic-constraint", message, position2);
|
|
8895
|
+
});
|
|
8896
|
+
}
|
|
8897
|
+
|
|
8369
8898
|
// ../mta-types/src/lua-standard.ts
|
|
8370
8899
|
var LUA_GLOBALS = {
|
|
8371
8900
|
_G: TABLE,
|
|
@@ -13203,6 +13732,18 @@ var DeclarationRegistry = class {
|
|
|
13203
13732
|
}
|
|
13204
13733
|
return null;
|
|
13205
13734
|
}
|
|
13735
|
+
lookupMemberOwner(name, member) {
|
|
13736
|
+
const visited = /* @__PURE__ */ new Set();
|
|
13737
|
+
let current = this.lookupClass(name);
|
|
13738
|
+
while (current !== null && !visited.has(current.name)) {
|
|
13739
|
+
if (current.members.has(member)) {
|
|
13740
|
+
return current.name;
|
|
13741
|
+
}
|
|
13742
|
+
visited.add(current.name);
|
|
13743
|
+
current = current.superClass === null ? null : this.lookupClass(current.superClass);
|
|
13744
|
+
}
|
|
13745
|
+
return null;
|
|
13746
|
+
}
|
|
13206
13747
|
lookupStaticMember(name, member) {
|
|
13207
13748
|
const visited = /* @__PURE__ */ new Set();
|
|
13208
13749
|
let current = this.lookupClass(name);
|
|
@@ -13262,7 +13803,17 @@ function buildRegistry() {
|
|
|
13262
13803
|
procedural: member.procedural
|
|
13263
13804
|
});
|
|
13264
13805
|
}
|
|
13265
|
-
registry.declareClass({
|
|
13806
|
+
registry.declareClass({
|
|
13807
|
+
name: declaration.name,
|
|
13808
|
+
typeParameters: [],
|
|
13809
|
+
typeConstraints: [],
|
|
13810
|
+
superClass: declaration.parent,
|
|
13811
|
+
superArguments: [],
|
|
13812
|
+
interfaces: [],
|
|
13813
|
+
members,
|
|
13814
|
+
statics: /* @__PURE__ */ new Map(),
|
|
13815
|
+
position: MTA_POSITION
|
|
13816
|
+
});
|
|
13266
13817
|
}
|
|
13267
13818
|
return registry;
|
|
13268
13819
|
}
|
|
@@ -13349,12 +13900,22 @@ function missingKeys(source, target) {
|
|
|
13349
13900
|
}
|
|
13350
13901
|
return missing2;
|
|
13351
13902
|
}
|
|
13352
|
-
function
|
|
13903
|
+
function expandCandidate(option, resolveNominal) {
|
|
13904
|
+
if (option.kind === "record") {
|
|
13905
|
+
return option;
|
|
13906
|
+
}
|
|
13907
|
+
if (option.kind !== "named") {
|
|
13908
|
+
return null;
|
|
13909
|
+
}
|
|
13910
|
+
const shape = resolveNominal?.(option.name) ?? null;
|
|
13911
|
+
return shape === null || shape.kind !== "interface" ? null : { kind: "record", name: option.name, origin: null, members: shape.members };
|
|
13912
|
+
}
|
|
13913
|
+
function missingKeyHint(source, target, resolveNominal) {
|
|
13353
13914
|
if (source.kind !== "record" || source.isLiteral !== true) {
|
|
13354
13915
|
return "";
|
|
13355
13916
|
}
|
|
13356
13917
|
const options = target.kind === "union" ? target.options : [target];
|
|
13357
|
-
const candidates3 = options.
|
|
13918
|
+
const candidates3 = options.map((option) => expandCandidate(option, resolveNominal)).filter((option) => option !== null);
|
|
13358
13919
|
const candidate = chooseCandidate(source, candidates3);
|
|
13359
13920
|
if (candidate === null) {
|
|
13360
13921
|
return "";
|
|
@@ -13402,39 +13963,6 @@ function mergeIntersection(context, parts, position2) {
|
|
|
13402
13963
|
return createObjectType(members);
|
|
13403
13964
|
}
|
|
13404
13965
|
|
|
13405
|
-
// ../compiler/src/checker/type-substitution.ts
|
|
13406
|
-
function substituteType(type, substitutions) {
|
|
13407
|
-
if (type.kind === "named") {
|
|
13408
|
-
return substitutions.get(type.name) ?? type;
|
|
13409
|
-
}
|
|
13410
|
-
if (type.kind === "array") {
|
|
13411
|
-
return createArray(substituteType(type.element, substitutions));
|
|
13412
|
-
}
|
|
13413
|
-
if (type.kind === "map") {
|
|
13414
|
-
return createMap(substituteType(type.key, substitutions), substituteType(type.value, substitutions));
|
|
13415
|
-
}
|
|
13416
|
-
if (type.kind === "optional") {
|
|
13417
|
-
return createOptional(substituteType(type.element, substitutions));
|
|
13418
|
-
}
|
|
13419
|
-
if (type.kind === "union") {
|
|
13420
|
-
return createUnion(type.options.map((option) => substituteType(option, substitutions)));
|
|
13421
|
-
}
|
|
13422
|
-
if (type.kind === "function") {
|
|
13423
|
-
const parameters = type.parameters.map((parameter) => substituteType(parameter, substitutions));
|
|
13424
|
-
const returnType = substituteType(type.returnType, substitutions);
|
|
13425
|
-
const variadicType = type.variadicType === void 0 ? void 0 : substituteType(type.variadicType, substitutions);
|
|
13426
|
-
return createFunction(parameters, returnType, type.minimumArguments, type.isVariadic, type.parameterNames, variadicType);
|
|
13427
|
-
}
|
|
13428
|
-
if (type.kind === "tuple") {
|
|
13429
|
-
return createTuple(type.elements.map((element) => substituteType(element, substitutions)));
|
|
13430
|
-
}
|
|
13431
|
-
if (type.kind === "record") {
|
|
13432
|
-
const members = new Map([...type.members].map(([name, member]) => [name, substituteType(member, substitutions)]));
|
|
13433
|
-
return createRecord(type.name, members, type.origin);
|
|
13434
|
-
}
|
|
13435
|
-
return type;
|
|
13436
|
-
}
|
|
13437
|
-
|
|
13438
13966
|
// ../compiler/src/checker/context.ts
|
|
13439
13967
|
var BUILTIN_TYPES = {
|
|
13440
13968
|
any: ANY_TYPE,
|
|
@@ -13450,6 +13978,7 @@ var BUILTIN_TYPES = {
|
|
|
13450
13978
|
};
|
|
13451
13979
|
var TABLE_ANNOTATION = "table";
|
|
13452
13980
|
var MAP_ARGUMENTS = 2;
|
|
13981
|
+
var MAX_GENERIC_DEPTH = 8;
|
|
13453
13982
|
var PROJECT_POSITION = { line: 0, column: 0, offset: 0 };
|
|
13454
13983
|
function nilHint(source, target, mode) {
|
|
13455
13984
|
if (mode !== "strict" || source.kind !== "nil" || target.kind === "nil" || target.kind === "optional") {
|
|
@@ -13462,12 +13991,15 @@ var CheckContext = class {
|
|
|
13462
13991
|
declarations = new DeclarationRegistry();
|
|
13463
13992
|
diagnostics = [];
|
|
13464
13993
|
reported = /* @__PURE__ */ new Set();
|
|
13465
|
-
|
|
13994
|
+
flow = createFlow();
|
|
13466
13995
|
types = /* @__PURE__ */ new Map();
|
|
13467
13996
|
references = /* @__PURE__ */ new Set();
|
|
13468
13997
|
declaredGlobals = /* @__PURE__ */ new Map();
|
|
13998
|
+
moduleGlobals = /* @__PURE__ */ new Map();
|
|
13469
13999
|
externalReferences = /* @__PURE__ */ new Map();
|
|
13470
14000
|
unknownTypes = /* @__PURE__ */ new Map();
|
|
14001
|
+
eventReferences = /* @__PURE__ */ new Set();
|
|
14002
|
+
globalReferences = /* @__PURE__ */ new Set();
|
|
13471
14003
|
calledMembers = /* @__PURE__ */ new Set();
|
|
13472
14004
|
staticAccess = /* @__PURE__ */ new Set();
|
|
13473
14005
|
typeParameters = /* @__PURE__ */ new Set();
|
|
@@ -13475,6 +14007,7 @@ var CheckContext = class {
|
|
|
13475
14007
|
mode;
|
|
13476
14008
|
environment;
|
|
13477
14009
|
mtaClasses;
|
|
14010
|
+
contracts = [];
|
|
13478
14011
|
isDeclarationFile = false;
|
|
13479
14012
|
returnStack = [];
|
|
13480
14013
|
methodStack = [];
|
|
@@ -13500,33 +14033,28 @@ var CheckContext = class {
|
|
|
13500
14033
|
declareModuleGlobal(symbol) {
|
|
13501
14034
|
this.binder.declareGlobal(symbol);
|
|
13502
14035
|
this.declaredGlobals.set(symbol.name, symbol.position);
|
|
14036
|
+
this.moduleGlobals.set(symbol.name, symbol.type);
|
|
13503
14037
|
}
|
|
13504
|
-
|
|
13505
|
-
this.
|
|
14038
|
+
get flowState() {
|
|
14039
|
+
return this.flow;
|
|
13506
14040
|
}
|
|
13507
|
-
|
|
13508
|
-
|
|
14041
|
+
setFlow(state) {
|
|
14042
|
+
this.flow = state;
|
|
13509
14043
|
}
|
|
13510
|
-
|
|
13511
|
-
this.
|
|
14044
|
+
applyFlowFacts(facts) {
|
|
14045
|
+
applyFacts(this.flow, facts);
|
|
14046
|
+
}
|
|
14047
|
+
markUnreachable() {
|
|
14048
|
+
this.flow.reachable = false;
|
|
14049
|
+
}
|
|
14050
|
+
get isNarrowed() {
|
|
14051
|
+
return this.flow.facts.size > 0;
|
|
13512
14052
|
}
|
|
13513
14053
|
forgetNarrowing(path) {
|
|
13514
|
-
|
|
13515
|
-
for (const key of [...frame.keys()]) {
|
|
13516
|
-
if (key === path || key.startsWith(`${path}.`) || path.startsWith(`${key}.`)) {
|
|
13517
|
-
frame.delete(key);
|
|
13518
|
-
}
|
|
13519
|
-
}
|
|
13520
|
-
}
|
|
14054
|
+
forgetPath(this.flow, path);
|
|
13521
14055
|
}
|
|
13522
14056
|
narrowedType(path) {
|
|
13523
|
-
|
|
13524
|
-
const found = this.narrowings[index]?.get(path);
|
|
13525
|
-
if (found !== void 0) {
|
|
13526
|
-
return found;
|
|
13527
|
-
}
|
|
13528
|
-
}
|
|
13529
|
-
return null;
|
|
14057
|
+
return this.flow.facts.get(path) ?? null;
|
|
13530
14058
|
}
|
|
13531
14059
|
predeclareClass(info) {
|
|
13532
14060
|
this.predeclared.set(info.name, info);
|
|
@@ -13593,6 +14121,12 @@ var CheckContext = class {
|
|
|
13593
14121
|
isTypeParameter(name) {
|
|
13594
14122
|
return this.typeParameters.has(name);
|
|
13595
14123
|
}
|
|
14124
|
+
noteGlobalReference(name) {
|
|
14125
|
+
this.globalReferences.add(name);
|
|
14126
|
+
}
|
|
14127
|
+
noteEventReference(name) {
|
|
14128
|
+
this.eventReferences.add(name);
|
|
14129
|
+
}
|
|
13596
14130
|
noteUnknownType(name, position2) {
|
|
13597
14131
|
if (!this.unknownTypes.has(name)) {
|
|
13598
14132
|
this.unknownTypes.set(name, position2);
|
|
@@ -13682,17 +14216,57 @@ var CheckContext = class {
|
|
|
13682
14216
|
const parameters = annotation.parameters.map((parameter) => this.resolveAnnotation(parameter));
|
|
13683
14217
|
const optional = parameters.findIndex((parameter) => parameter.kind === "optional");
|
|
13684
14218
|
const minimum = optional === -1 ? parameters.length : optional;
|
|
13685
|
-
return createFunction(parameters, this.resolveAnnotation(annotation.returnType), minimum, annotation.isVariadic);
|
|
14219
|
+
return createFunction(parameters, this.resolveAnnotation(annotation.returnType), minimum, annotation.isVariadic, [...annotation.parameterNames]);
|
|
13686
14220
|
}
|
|
13687
14221
|
return this.resolveNamedAnnotation(annotation.name, annotation.typeArguments, annotation.position);
|
|
13688
14222
|
}
|
|
14223
|
+
nominalShape(name) {
|
|
14224
|
+
const declaredInterface = this.declarations.lookupInterface(name);
|
|
14225
|
+
if (declaredInterface !== null) {
|
|
14226
|
+
const ancestors = this.declarations.allInterfaces().filter((other) => this.declarations.interfaceExtends(name, other.name));
|
|
14227
|
+
return {
|
|
14228
|
+
kind: "interface",
|
|
14229
|
+
members: new Map(this.declarations.collectInterfaceContract(name).map((member) => [member.name, member.type])),
|
|
14230
|
+
ancestors: new Set(ancestors.map((other) => other.name))
|
|
14231
|
+
};
|
|
14232
|
+
}
|
|
14233
|
+
const declaredClass = this.declarations.lookupClass(name);
|
|
14234
|
+
if (declaredClass === null) {
|
|
14235
|
+
return null;
|
|
14236
|
+
}
|
|
14237
|
+
return {
|
|
14238
|
+
kind: "class",
|
|
14239
|
+
members: new Map(this.declarations.collectMembers(name).map((member) => [member.name, member.type])),
|
|
14240
|
+
ancestors: this.classAncestors(name)
|
|
14241
|
+
};
|
|
14242
|
+
}
|
|
14243
|
+
classAncestors(name) {
|
|
14244
|
+
const ancestors = /* @__PURE__ */ new Set();
|
|
14245
|
+
let current = this.declarations.lookupClass(name);
|
|
14246
|
+
while (current !== null && !ancestors.has(current.name)) {
|
|
14247
|
+
ancestors.add(current.name);
|
|
14248
|
+
for (const contract of current.interfaces) {
|
|
14249
|
+
ancestors.add(contract);
|
|
14250
|
+
for (const other of this.declarations.allInterfaces()) {
|
|
14251
|
+
if (this.declarations.interfaceExtends(contract, other.name)) {
|
|
14252
|
+
ancestors.add(other.name);
|
|
14253
|
+
}
|
|
14254
|
+
}
|
|
14255
|
+
}
|
|
14256
|
+
current = current.superClass === null ? null : this.declarations.lookupClass(current.superClass);
|
|
14257
|
+
}
|
|
14258
|
+
return ancestors;
|
|
14259
|
+
}
|
|
14260
|
+
assignability() {
|
|
14261
|
+
return { allowNil: this.allowNil, resolveNominal: (name) => this.nominalShape(name) };
|
|
14262
|
+
}
|
|
13689
14263
|
expectAssignable(source, target, position2, subject) {
|
|
13690
|
-
if (isAssignable(source, target,
|
|
14264
|
+
if (isAssignable(source, target, this.assignability())) {
|
|
13691
14265
|
return;
|
|
13692
14266
|
}
|
|
13693
14267
|
const received = isLiteralType(target) || target.kind === "union" ? source : widenLiteral(source);
|
|
13694
14268
|
const message = `${subject} expects "${typeToString(target)}" but received "${typeToString(received)}".`;
|
|
13695
|
-
const hint = `${nilHint(source, target, this.mode)}${missingKeyHint(source, target)}`;
|
|
14269
|
+
const hint = `${nilHint(source, target, this.mode)}${missingKeyHint(source, target, (name) => this.nominalShape(name))}`;
|
|
13696
14270
|
this.report("check-type-mismatch", `${message}${hint}`, position2);
|
|
13697
14271
|
}
|
|
13698
14272
|
declareProject(project) {
|
|
@@ -13738,6 +14312,25 @@ var CheckContext = class {
|
|
|
13738
14312
|
}
|
|
13739
14313
|
return createMap(this.resolveAnnotation(key), this.resolveAnnotation(value));
|
|
13740
14314
|
}
|
|
14315
|
+
resolveClassAnnotation(name, typeArguments, position2) {
|
|
14316
|
+
const declared = this.declarations.lookupClass(name)?.typeParameters ?? [];
|
|
14317
|
+
const resolved2 = typeArguments.map((argument) => this.resolveAnnotation(argument));
|
|
14318
|
+
if (declared.length === 0) {
|
|
14319
|
+
return createNamed(name);
|
|
14320
|
+
}
|
|
14321
|
+
if (resolved2.length !== declared.length) {
|
|
14322
|
+
const expected = declared.length === 1 ? "1 type argument" : `${declared.length} type arguments`;
|
|
14323
|
+
this.report("check-generic-arity", `Class "${name}" expects ${expected} but received ${resolved2.length}.`, position2);
|
|
14324
|
+
}
|
|
14325
|
+
const specialized = createNamed(name, declared.map((unused, index) => resolved2[index] ?? ANY_TYPE));
|
|
14326
|
+
if (namedNesting(specialized) > MAX_GENERIC_DEPTH) {
|
|
14327
|
+
const message = `Class "${name}" is nested more than ${MAX_GENERIC_DEPTH} levels deep here. Name the inner type with a "type" alias.`;
|
|
14328
|
+
this.report("check-generic-depth", message, position2);
|
|
14329
|
+
return createNamed(name);
|
|
14330
|
+
}
|
|
14331
|
+
checkTypeConstraints(this, name, specialized.typeArguments ?? [], position2);
|
|
14332
|
+
return specialized;
|
|
14333
|
+
}
|
|
13741
14334
|
resolveNamedAnnotation(name, typeArguments, position2) {
|
|
13742
14335
|
const builtin = BUILTIN_TYPES[name];
|
|
13743
14336
|
if (builtin !== void 0) {
|
|
@@ -13752,7 +14345,7 @@ var CheckContext = class {
|
|
|
13752
14345
|
const alias = this.binder.lookupAlias(name);
|
|
13753
14346
|
if (alias === null) {
|
|
13754
14347
|
this.noteUnknownType(name, position2);
|
|
13755
|
-
return
|
|
14348
|
+
return this.resolveClassAnnotation(name, typeArguments, position2);
|
|
13756
14349
|
}
|
|
13757
14350
|
if (typeArguments.length !== alias.parameters.length) {
|
|
13758
14351
|
const expected = alias.parameters.length === 1 ? "1 type argument" : `${alias.parameters.length} type arguments`;
|
|
@@ -14009,6 +14602,73 @@ function checkOverrides(context, info, statement) {
|
|
|
14009
14602
|
}
|
|
14010
14603
|
}
|
|
14011
14604
|
|
|
14605
|
+
// ../compiler/src/checker/metamethods.ts
|
|
14606
|
+
var CONTRACTS = [
|
|
14607
|
+
{ name: "__tostring", parameters: 0, returns: "string", documentation: 'Renders the instance for "tostring" and string coercion.' },
|
|
14608
|
+
{ name: "__eq", parameters: 1, returns: "boolean", documentation: 'Compares two instances of the same class with "==".' },
|
|
14609
|
+
{ name: "__lt", parameters: 1, returns: "boolean", documentation: 'Orders two instances with "<" and ">".' },
|
|
14610
|
+
{ name: "__le", parameters: 1, returns: "boolean", documentation: 'Orders two instances with "<=" and ">=".' },
|
|
14611
|
+
{ name: "__len", parameters: 0, returns: "number", documentation: 'Answers the length operator "#".' },
|
|
14612
|
+
{ name: "__concat", parameters: 1, returns: "any", documentation: 'Answers the concatenation operator "..".' },
|
|
14613
|
+
{ name: "__unm", parameters: 0, returns: "any", documentation: "Answers unary minus." },
|
|
14614
|
+
{ name: "__add", parameters: 1, returns: "any", documentation: 'Answers "+".' },
|
|
14615
|
+
{ name: "__sub", parameters: 1, returns: "any", documentation: 'Answers "-".' },
|
|
14616
|
+
{ name: "__mul", parameters: 1, returns: "any", documentation: 'Answers "*".' },
|
|
14617
|
+
{ name: "__div", parameters: 1, returns: "any", documentation: 'Answers "/".' },
|
|
14618
|
+
{ name: "__mod", parameters: 1, returns: "any", documentation: 'Answers "%".' },
|
|
14619
|
+
{ name: "__pow", parameters: 1, returns: "any", documentation: 'Answers "^".' }
|
|
14620
|
+
];
|
|
14621
|
+
var BLOCKED = {
|
|
14622
|
+
__index: "it replaces member lookup, which the class helper owns",
|
|
14623
|
+
__newindex: "it swallows a field write, which the class helper owns",
|
|
14624
|
+
__call: "it makes an instance callable, which hides construction",
|
|
14625
|
+
__gc: "Lua 5.1 does not run it for a table",
|
|
14626
|
+
__metatable: "it hides the metatable the class helper needs",
|
|
14627
|
+
__mode: "it turns instances into weak references the class helper cannot track"
|
|
14628
|
+
};
|
|
14629
|
+
var METAMETHOD_CONTRACTS = new Map(CONTRACTS.map((contract) => [contract.name, contract]));
|
|
14630
|
+
function isMetamethodName(name) {
|
|
14631
|
+
return name.startsWith("__");
|
|
14632
|
+
}
|
|
14633
|
+
function isAllowedMetamethod(name) {
|
|
14634
|
+
return METAMETHOD_CONTRACTS.has(name);
|
|
14635
|
+
}
|
|
14636
|
+
function isMetamethodMember(member) {
|
|
14637
|
+
return member.kind === "class-method" && !member.isStatic && isAllowedMetamethod(member.name);
|
|
14638
|
+
}
|
|
14639
|
+
function reportReturn(context, className, contract, type, member) {
|
|
14640
|
+
if (contract.returns === "any" || type.kind === "any" || type.kind === contract.returns) {
|
|
14641
|
+
return;
|
|
14642
|
+
}
|
|
14643
|
+
const message = `Metamethod "${contract.name}" of class "${className}" must return "${contract.returns}" but returns "${typeToString(type)}".`;
|
|
14644
|
+
context.report("check-invalid-metamethod", message, member.position);
|
|
14645
|
+
}
|
|
14646
|
+
function checkMetamethod(context, className, member, type) {
|
|
14647
|
+
const contract = METAMETHOD_CONTRACTS.get(member.name);
|
|
14648
|
+
if (contract === void 0 || type.kind !== "function") {
|
|
14649
|
+
return;
|
|
14650
|
+
}
|
|
14651
|
+
if (type.parameters.length !== contract.parameters) {
|
|
14652
|
+
const expected = contract.parameters === 1 ? "1 parameter" : `${contract.parameters} parameters`;
|
|
14653
|
+
const message = `Metamethod "${contract.name}" of class "${className}" takes ${expected} beside "self" but declares ${type.parameters.length}.`;
|
|
14654
|
+
context.report("check-invalid-metamethod", message, member.position);
|
|
14655
|
+
}
|
|
14656
|
+
reportReturn(context, className, contract, type.returnType, member);
|
|
14657
|
+
}
|
|
14658
|
+
function reportRejectedMetamethod(context, className, member) {
|
|
14659
|
+
if (member.kind !== "class-method" || !isMetamethodName(member.name) || isAllowedMetamethod(member.name)) {
|
|
14660
|
+
return;
|
|
14661
|
+
}
|
|
14662
|
+
const blocked = BLOCKED[member.name];
|
|
14663
|
+
if (blocked !== void 0) {
|
|
14664
|
+
context.report("check-blocked-metamethod", `Metamethod "${member.name}" cannot be declared on class "${className}" because ${blocked}.`, member.position);
|
|
14665
|
+
return;
|
|
14666
|
+
}
|
|
14667
|
+
const known = [...METAMETHOD_CONTRACTS.keys()].map((name) => `"${name}"`).join(", ");
|
|
14668
|
+
const message = `"${member.name}" is not a metamethod Luam exposes on class "${className}". Declared metamethods are ${known}.`;
|
|
14669
|
+
context.report("check-blocked-metamethod", message, member.position);
|
|
14670
|
+
}
|
|
14671
|
+
|
|
14012
14672
|
// ../compiler/src/checker/accessor-names.ts
|
|
14013
14673
|
function capitalize(name) {
|
|
14014
14674
|
return `${name.charAt(0).toUpperCase()}${name.slice(1)}`;
|
|
@@ -14093,6 +14753,25 @@ var KNOWN_DECORATORS = /* @__PURE__ */ new Map([
|
|
|
14093
14753
|
diagnostics: [CONFLICT]
|
|
14094
14754
|
}
|
|
14095
14755
|
],
|
|
14756
|
+
[
|
|
14757
|
+
"Validated",
|
|
14758
|
+
{
|
|
14759
|
+
name: "Validated",
|
|
14760
|
+
documentation: "Generates runtime checks for the declared field types.",
|
|
14761
|
+
targets: ["class"],
|
|
14762
|
+
generates: [
|
|
14763
|
+
"`ClassName.validate(value: table): ClassName`, returning the value or raising a Lua error naming the path and the expected type.",
|
|
14764
|
+
"`ClassName.matches(value: table): boolean`, the same check without raising."
|
|
14765
|
+
],
|
|
14766
|
+
rules: [
|
|
14767
|
+
"Both are static, so they are called on the class and never on an instance.",
|
|
14768
|
+
"The check walks the declared field types with a depth, entry-count and string-length limit, and a failure names the path and the expected type but never the value.",
|
|
14769
|
+
"A field whose type has no runtime shape is `check-unreifiable-type`, reported before anything is emitted.",
|
|
14770
|
+
"It is the only decorator that adds runtime code beyond the class helper; a class without it emits nothing extra."
|
|
14771
|
+
],
|
|
14772
|
+
diagnostics: [CONFLICT, "`check-unreifiable-type` when a field type cannot be checked at runtime."]
|
|
14773
|
+
}
|
|
14774
|
+
],
|
|
14096
14775
|
[
|
|
14097
14776
|
"Serializable",
|
|
14098
14777
|
{
|
|
@@ -14183,6 +14862,102 @@ var KNOWN_DECORATORS = /* @__PURE__ */ new Map([
|
|
|
14183
14862
|
]
|
|
14184
14863
|
]);
|
|
14185
14864
|
|
|
14865
|
+
// ../compiler/src/checker/validation-descriptor.ts
|
|
14866
|
+
var MAX_EXPANSION = 8;
|
|
14867
|
+
var PRIMITIVE_KINDS = {
|
|
14868
|
+
nil: "nil",
|
|
14869
|
+
boolean: "boolean",
|
|
14870
|
+
number: "number",
|
|
14871
|
+
string: "string",
|
|
14872
|
+
table: "table",
|
|
14873
|
+
thread: "thread",
|
|
14874
|
+
userdata: "userdata",
|
|
14875
|
+
function: "function"
|
|
14876
|
+
};
|
|
14877
|
+
function quote2(value) {
|
|
14878
|
+
return `'${value.replace(/([\\'])/g, "\\$1").replace(/\n/g, "\\n")}'`;
|
|
14879
|
+
}
|
|
14880
|
+
function failed(type) {
|
|
14881
|
+
return { lua: null, unreifiable: typeToString(type) };
|
|
14882
|
+
}
|
|
14883
|
+
function entries2(parts) {
|
|
14884
|
+
return `{ ${parts.join(", ")} }`;
|
|
14885
|
+
}
|
|
14886
|
+
function combine(parts, render2) {
|
|
14887
|
+
const broken = parts.find((part) => part.lua === null);
|
|
14888
|
+
if (broken !== void 0) {
|
|
14889
|
+
return broken;
|
|
14890
|
+
}
|
|
14891
|
+
return { lua: render2(parts.map((part) => part.lua)), unreifiable: null };
|
|
14892
|
+
}
|
|
14893
|
+
function namedDescriptor(context, type, name, seen) {
|
|
14894
|
+
if (context.declarations.lookupClass(name) !== null) {
|
|
14895
|
+
return { lua: entries2([`kind = 'instance'`, `name = ${quote2(name)}`]), unreifiable: null };
|
|
14896
|
+
}
|
|
14897
|
+
if (context.declarations.lookupEnum(name) !== null) {
|
|
14898
|
+
return { lua: entries2([`kind = 'number'`]), unreifiable: null };
|
|
14899
|
+
}
|
|
14900
|
+
if (context.declarations.lookupInterface(name) === null || seen.has(name) || seen.size >= MAX_EXPANSION) {
|
|
14901
|
+
return failed(type);
|
|
14902
|
+
}
|
|
14903
|
+
const members = context.declarations.collectMembers(name);
|
|
14904
|
+
const nested = /* @__PURE__ */ new Set([...seen, name]);
|
|
14905
|
+
const parts = members.map((member) => describe(context, member.type, nested));
|
|
14906
|
+
return combine(parts, (values) => {
|
|
14907
|
+
const rendered = members.map((member, index) => entries2([`key = ${quote2(member.name)}`, `value = ${values[index] ?? ""}`]));
|
|
14908
|
+
return entries2([`kind = 'record'`, `name = ${quote2(name)}`, `members = ${entries2(rendered)}`]);
|
|
14909
|
+
});
|
|
14910
|
+
}
|
|
14911
|
+
function describe(context, type, seen = /* @__PURE__ */ new Set()) {
|
|
14912
|
+
const primitive = PRIMITIVE_KINDS[type.kind];
|
|
14913
|
+
if (primitive !== void 0) {
|
|
14914
|
+
return { lua: entries2([`kind = ${quote2(primitive)}`]), unreifiable: null };
|
|
14915
|
+
}
|
|
14916
|
+
if (type.kind === "string-literal") {
|
|
14917
|
+
return { lua: entries2([`kind = 'literal'`, `value = ${quote2(type.value)}`]), unreifiable: null };
|
|
14918
|
+
}
|
|
14919
|
+
if (type.kind === "boolean-literal" || type.kind === "number-literal") {
|
|
14920
|
+
return { lua: entries2([`kind = 'literal'`, `value = ${String(type.value)}`]), unreifiable: null };
|
|
14921
|
+
}
|
|
14922
|
+
if (type.kind === "optional") {
|
|
14923
|
+
return combine([describe(context, type.element, seen)], (values) => entries2([`kind = 'optional'`, `element = ${values[0] ?? ""}`]));
|
|
14924
|
+
}
|
|
14925
|
+
if (type.kind === "array") {
|
|
14926
|
+
return combine([describe(context, type.element, seen)], (values) => entries2([`kind = 'array'`, `element = ${values[0] ?? ""}`]));
|
|
14927
|
+
}
|
|
14928
|
+
if (type.kind === "map") {
|
|
14929
|
+
return combine(
|
|
14930
|
+
[describe(context, type.key, seen), describe(context, type.value, seen)],
|
|
14931
|
+
(values) => entries2([`kind = 'map'`, `key = ${values[0] ?? ""}`, `value = ${values[1] ?? ""}`])
|
|
14932
|
+
);
|
|
14933
|
+
}
|
|
14934
|
+
if (type.kind === "union") {
|
|
14935
|
+
return combine(
|
|
14936
|
+
type.options.map((option) => describe(context, option, seen)),
|
|
14937
|
+
(values) => entries2([`kind = 'union'`, `options = ${entries2(values)}`])
|
|
14938
|
+
);
|
|
14939
|
+
}
|
|
14940
|
+
if (type.kind === "record") {
|
|
14941
|
+
const members = [...type.members];
|
|
14942
|
+
const parts = members.map(([, member]) => describe(context, member, seen));
|
|
14943
|
+
return combine(parts, (values) => {
|
|
14944
|
+
const rendered = members.map(([key], index) => entries2([`key = ${quote2(key)}`, `value = ${values[index] ?? ""}`]));
|
|
14945
|
+
return entries2([`kind = 'record'`, `name = ${quote2(type.name)}`, `members = ${entries2(rendered)}`]);
|
|
14946
|
+
});
|
|
14947
|
+
}
|
|
14948
|
+
if (type.kind === "named") {
|
|
14949
|
+
return namedDescriptor(context, type, type.name, seen);
|
|
14950
|
+
}
|
|
14951
|
+
return failed(type);
|
|
14952
|
+
}
|
|
14953
|
+
function classDescriptor(context, className, fields) {
|
|
14954
|
+
const parts = fields.map((field2) => describe(context, field2.type));
|
|
14955
|
+
return combine(parts, (values) => {
|
|
14956
|
+
const rendered = fields.map((field2, index) => entries2([`key = ${quote2(field2.name)}`, `value = ${values[index] ?? ""}`]));
|
|
14957
|
+
return entries2([`kind = 'record'`, `name = ${quote2(className)}`, `members = ${entries2(rendered)}`]);
|
|
14958
|
+
});
|
|
14959
|
+
}
|
|
14960
|
+
|
|
14186
14961
|
// ../compiler/src/checker/decorators.ts
|
|
14187
14962
|
function memberExpression(field2) {
|
|
14188
14963
|
return {
|
|
@@ -14248,6 +15023,46 @@ function validateDecorators(context, decorators, target) {
|
|
|
14248
15023
|
function decoratorFor(name, classDecorators, fieldDecorators) {
|
|
14249
15024
|
return fieldDecorators.find((decorator) => decorator.name === name) ?? classDecorators.find((decorator) => decorator.name === name) ?? null;
|
|
14250
15025
|
}
|
|
15026
|
+
function staticMethod(statement, name, returnAnnotation, kind, descriptor) {
|
|
15027
|
+
const value = { name: "value", annotation: typeName("table", statement), isVararg: false, position: statement.position };
|
|
15028
|
+
return {
|
|
15029
|
+
kind: "class-method",
|
|
15030
|
+
name,
|
|
15031
|
+
isConstructor: false,
|
|
15032
|
+
isSynthetic: true,
|
|
15033
|
+
isStatic: true,
|
|
15034
|
+
parameters: [value],
|
|
15035
|
+
returnAnnotation,
|
|
15036
|
+
body: [],
|
|
15037
|
+
decorators: [],
|
|
15038
|
+
generated: { kind, fields: [], descriptor },
|
|
15039
|
+
position: statement.position
|
|
15040
|
+
};
|
|
15041
|
+
}
|
|
15042
|
+
function expandValidated(context, statement, classDecorators, fieldTypes, occupied, generated) {
|
|
15043
|
+
const found = classDecorators.find((decorator) => decorator.name === "Validated");
|
|
15044
|
+
if (found === void 0) {
|
|
15045
|
+
return;
|
|
15046
|
+
}
|
|
15047
|
+
const fields = [...fieldTypes].map(([field2, type]) => ({ name: field2.name, type, position: field2.position }));
|
|
15048
|
+
const described = classDescriptor(context, statement.name, fields);
|
|
15049
|
+
if (described.lua === null) {
|
|
15050
|
+
const message = `Decorator "@Validated" cannot check type "${described.unreifiable}", which has no runtime shape. Remove the field, or narrow it to a checkable type.`;
|
|
15051
|
+
context.report("check-unreifiable-type", message, found.position);
|
|
15052
|
+
return;
|
|
15053
|
+
}
|
|
15054
|
+
for (const [name, kind, annotation] of [
|
|
15055
|
+
["validate", "validate", typeName(statement.name, statement)],
|
|
15056
|
+
["matches", "matches", typeName("boolean", statement)]
|
|
15057
|
+
]) {
|
|
15058
|
+
if (occupied.has(name)) {
|
|
15059
|
+
context.report("check-decorator-conflict", `Decorator "@Validated" would generate "${name}", which is already declared by "${occupied.get(name)}".`, found.position);
|
|
15060
|
+
continue;
|
|
15061
|
+
}
|
|
15062
|
+
occupied.set(name, statement.name);
|
|
15063
|
+
generated.push(staticMethod(statement, name, annotation, kind, described.lua));
|
|
15064
|
+
}
|
|
15065
|
+
}
|
|
14251
15066
|
function expandClassDecorators(context, statement, fieldTypes) {
|
|
14252
15067
|
const generated = [];
|
|
14253
15068
|
const occupied = new Map(statement.members.filter((member) => member.kind === "class-method").map((member) => [member.name, member.name]));
|
|
@@ -14309,6 +15124,7 @@ function expandClassDecorators(context, statement, fieldTypes) {
|
|
|
14309
15124
|
Serializable: ["toTable", typeName("table", statement), "serializable"],
|
|
14310
15125
|
Deserialize: ["fromTable", typeName("void", statement), "deserialize"]
|
|
14311
15126
|
};
|
|
15127
|
+
expandValidated(context, statement, classDecorators, fieldTypes, occupied, generated);
|
|
14312
15128
|
for (const [decorator, [name, returnAnnotation, kind]] of Object.entries(classMethods)) {
|
|
14313
15129
|
const found = classDecorators.find((value) => value.name === decorator);
|
|
14314
15130
|
if (found !== void 0 && !occupied.has(name)) {
|
|
@@ -17567,6 +18383,9 @@ function checkEventUsage(context, expression) {
|
|
|
17567
18383
|
return;
|
|
17568
18384
|
}
|
|
17569
18385
|
const name = eventName(expression);
|
|
18386
|
+
if (name !== null) {
|
|
18387
|
+
context.noteEventReference(name);
|
|
18388
|
+
}
|
|
17570
18389
|
const custom = name === null ? null : context.declarations.lookupEvent(name);
|
|
17571
18390
|
const declared = name === null ? null : eventEnvironment(name);
|
|
17572
18391
|
if (name === null || custom?.environment === "shared" || custom?.environment === context.environment || declared === null || declared === context.environment) {
|
|
@@ -17709,6 +18528,105 @@ function specializeEventCall(context, expression, signature) {
|
|
|
17709
18528
|
return candidate === null ? null : specialize(signature, candidate);
|
|
17710
18529
|
}
|
|
17711
18530
|
|
|
18531
|
+
// ../compiler/src/checker/method-receiver.ts
|
|
18532
|
+
var SELF_PARAMETER = "self";
|
|
18533
|
+
function resolveNonNominalMethod(receiver, method) {
|
|
18534
|
+
if (receiver.kind !== "record") {
|
|
18535
|
+
return null;
|
|
18536
|
+
}
|
|
18537
|
+
const member = receiver.members.get(method);
|
|
18538
|
+
return member !== void 0 && member.kind === "function" ? member : null;
|
|
18539
|
+
}
|
|
18540
|
+
function withoutSelfParameter(signature) {
|
|
18541
|
+
const names = signature.parameterNames;
|
|
18542
|
+
if (names === void 0 || names[0] !== SELF_PARAMETER) {
|
|
18543
|
+
return signature;
|
|
18544
|
+
}
|
|
18545
|
+
const minimum = Math.max(0, signature.minimumArguments - 1);
|
|
18546
|
+
return createFunction(signature.parameters.slice(1), signature.returnType, minimum, signature.isVariadic, names.slice(1), signature.variadicType);
|
|
18547
|
+
}
|
|
18548
|
+
|
|
18549
|
+
// ../compiler/src/checker/resource-exports.ts
|
|
18550
|
+
var RESOURCE_LOOKUP = "getResourceFromName";
|
|
18551
|
+
var EXPORTS_TABLE = "exports";
|
|
18552
|
+
var CALL_FUNCTION = "call";
|
|
18553
|
+
function literalString(expression) {
|
|
18554
|
+
return expression?.kind === "string-literal" ? expression.value : null;
|
|
18555
|
+
}
|
|
18556
|
+
function resourceOfLookup(expression) {
|
|
18557
|
+
if (expression?.kind !== "call-expression" || expression.callee.kind !== "identifier" || expression.callee.name !== RESOURCE_LOOKUP) {
|
|
18558
|
+
return null;
|
|
18559
|
+
}
|
|
18560
|
+
return literalString(expression.args[0]);
|
|
18561
|
+
}
|
|
18562
|
+
function resourceOfExports(expression) {
|
|
18563
|
+
if (expression.kind === "member-expression" && expression.object.kind === "identifier" && expression.object.name === EXPORTS_TABLE) {
|
|
18564
|
+
return expression.property;
|
|
18565
|
+
}
|
|
18566
|
+
if (expression.kind !== "index-expression" || expression.object.kind !== "identifier" || expression.object.name !== EXPORTS_TABLE) {
|
|
18567
|
+
return null;
|
|
18568
|
+
}
|
|
18569
|
+
return literalString(expression.index);
|
|
18570
|
+
}
|
|
18571
|
+
function abiType(context, text) {
|
|
18572
|
+
const scanned = scan(text);
|
|
18573
|
+
if (scanned.diagnostics.length > 0) {
|
|
18574
|
+
return ANY_TYPE;
|
|
18575
|
+
}
|
|
18576
|
+
try {
|
|
18577
|
+
return context.resolveAnnotation(parseTypeAnnotation(new TokenStream(scanned.tokens)));
|
|
18578
|
+
} catch {
|
|
18579
|
+
return ANY_TYPE;
|
|
18580
|
+
}
|
|
18581
|
+
}
|
|
18582
|
+
function signatureOf(context, entry) {
|
|
18583
|
+
const parameters = entry.parameters.map((parameter) => abiType(context, parameter.type));
|
|
18584
|
+
const names = entry.parameters.map((parameter) => parameter.name);
|
|
18585
|
+
const minimum = Math.min(Math.max(entry.minimumArguments, 0), parameters.length);
|
|
18586
|
+
return createFunction(parameters, abiType(context, entry.returns), minimum, entry.variadic, names);
|
|
18587
|
+
}
|
|
18588
|
+
function checkSide(context, entry, resource, expression) {
|
|
18589
|
+
if (canReference(context.environment, entry.side)) {
|
|
18590
|
+
return;
|
|
18591
|
+
}
|
|
18592
|
+
const message = `Export "${entry.name}" of resource "${resource}" is ${entry.side}-only and cannot be called from a "${context.environment}" file.`;
|
|
18593
|
+
context.report("check-resource-export-side", message, expression.position);
|
|
18594
|
+
}
|
|
18595
|
+
function checkKnownExport(context, resource, name, args, expression) {
|
|
18596
|
+
const entry = lookupAbiExport(context.contracts, resource, name);
|
|
18597
|
+
if (entry === null) {
|
|
18598
|
+
const known = context.contracts.find((contract) => contract.resource === resource)?.exports.map((declared) => `"${declared.name}"`).join(", ");
|
|
18599
|
+
const hint = known === void 0 || known.length === 0 ? " It exports nothing." : ` It exports ${known}.`;
|
|
18600
|
+
checkValueList(context, args);
|
|
18601
|
+
context.report("check-unknown-resource-export", `Resource "${resource}" has no export "${name}".${hint}`, expression.position);
|
|
18602
|
+
return ANY_TYPE;
|
|
18603
|
+
}
|
|
18604
|
+
checkSide(context, entry, resource, expression);
|
|
18605
|
+
return checkSignature(context, args, signatureOf(context, entry), expression.position);
|
|
18606
|
+
}
|
|
18607
|
+
function checkResourceCall(context, expression) {
|
|
18608
|
+
if (context.contracts.length === 0) {
|
|
18609
|
+
return null;
|
|
18610
|
+
}
|
|
18611
|
+
if (expression.method !== null) {
|
|
18612
|
+
const resource2 = resourceOfExports(expression.callee);
|
|
18613
|
+
if (resource2 === null || !knowsResource(context.contracts, resource2)) {
|
|
18614
|
+
return null;
|
|
18615
|
+
}
|
|
18616
|
+
return checkKnownExport(context, resource2, expression.method, expression.args, expression);
|
|
18617
|
+
}
|
|
18618
|
+
if (expression.callee.kind !== "identifier" || expression.callee.name !== CALL_FUNCTION) {
|
|
18619
|
+
return null;
|
|
18620
|
+
}
|
|
18621
|
+
const resource = resourceOfLookup(expression.args[0]);
|
|
18622
|
+
const name = literalString(expression.args[1]);
|
|
18623
|
+
if (resource === null || name === null || !knowsResource(context.contracts, resource)) {
|
|
18624
|
+
return null;
|
|
18625
|
+
}
|
|
18626
|
+
checkValueList(context, expression.args.slice(0, 2));
|
|
18627
|
+
return checkKnownExport(context, resource, name, expression.args.slice(2), expression);
|
|
18628
|
+
}
|
|
18629
|
+
|
|
17712
18630
|
// ../mta-types/src/library-members.ts
|
|
17713
18631
|
var MATH_MEMBERS = {
|
|
17714
18632
|
abs: fn([NUMBER], NUMBER, 1),
|
|
@@ -17983,12 +18901,13 @@ function resolveStaticMember(context, className, expression) {
|
|
|
17983
18901
|
context.report("check-unknown-member", `Class "${className}" has no static member "${expression.property}".${hint}`, expression.position);
|
|
17984
18902
|
return ANY_TYPE;
|
|
17985
18903
|
}
|
|
17986
|
-
function resolveNamedMember(context,
|
|
18904
|
+
function resolveNamedMember(context, receiver, expression) {
|
|
18905
|
+
const name = receiver.name;
|
|
17987
18906
|
const enumeration = context.declarations.lookupEnum(name);
|
|
17988
18907
|
if (enumeration !== null) {
|
|
17989
18908
|
return checkEnumMember(context, name, enumeration.members, expression);
|
|
17990
18909
|
}
|
|
17991
|
-
const member = context
|
|
18910
|
+
const member = memberOf(context, receiver, expression.property);
|
|
17992
18911
|
if (member !== null) {
|
|
17993
18912
|
if (member.deprecated === true) {
|
|
17994
18913
|
context.warn("check-deprecated-use", `Member "${expression.property}" is deprecated.`, expression.position);
|
|
@@ -18023,6 +18942,29 @@ function nativeConstructor(context, name) {
|
|
|
18023
18942
|
const constructor = symbol.type.members.get(NATIVE_CONSTRUCTOR);
|
|
18024
18943
|
return constructor === void 0 || constructor.kind !== "function" ? null : constructor;
|
|
18025
18944
|
}
|
|
18945
|
+
function constructorParameters(context, className) {
|
|
18946
|
+
const constructor = context.declarations.lookupMember(className, "constructor");
|
|
18947
|
+
return constructor?.type.kind === "function" ? constructor.type.parameters : [];
|
|
18948
|
+
}
|
|
18949
|
+
function resolveConstructorArguments(context, expression, typeParameters) {
|
|
18950
|
+
const explicit = expression.typeArguments.map((argument) => context.resolveAnnotation(argument));
|
|
18951
|
+
if (typeParameters.length === 0) {
|
|
18952
|
+
if (explicit.length > 0) {
|
|
18953
|
+
context.report("check-generic-arity", `Class "${expression.className}" does not accept type arguments.`, expression.position);
|
|
18954
|
+
}
|
|
18955
|
+
return [];
|
|
18956
|
+
}
|
|
18957
|
+
if (explicit.length === typeParameters.length) {
|
|
18958
|
+
return explicit;
|
|
18959
|
+
}
|
|
18960
|
+
if (explicit.length > 0) {
|
|
18961
|
+
const expected = typeParameters.length === 1 ? "1 type argument" : `${typeParameters.length} type arguments`;
|
|
18962
|
+
context.report("check-generic-arity", `Class "${expression.className}" expects ${expected} but received ${explicit.length}.`, expression.position);
|
|
18963
|
+
return typeParameters.map((unused, index) => explicit[index] ?? ANY_TYPE);
|
|
18964
|
+
}
|
|
18965
|
+
const argumentTypes = expression.args.map((argument) => checkExpression(context, argument));
|
|
18966
|
+
return inferTypeArguments(typeParameters, constructorParameters(context, expression.className), argumentTypes);
|
|
18967
|
+
}
|
|
18026
18968
|
function checkNewExpression(context, expression) {
|
|
18027
18969
|
if (context.declarations.lookupClass(expression.className) === null) {
|
|
18028
18970
|
const constructor2 = nativeConstructor(context, expression.className);
|
|
@@ -18041,13 +18983,20 @@ function checkNewExpression(context, expression) {
|
|
|
18041
18983
|
const subject = pending === expression.className ? `Class "${pending}"` : `Class "${expression.className}" extends "${pending}", which`;
|
|
18042
18984
|
context.report("check-class-before-declaration", `${subject} is declared further down this file, so it does not exist yet at this point.`, expression.position);
|
|
18043
18985
|
}
|
|
18986
|
+
const info = context.declarations.lookupClass(expression.className);
|
|
18987
|
+
const typeArguments = resolveConstructorArguments(context, expression, info?.typeParameters ?? []);
|
|
18988
|
+
checkTypeConstraints(context, expression.className, typeArguments, expression.position);
|
|
18044
18989
|
const constructor = context.declarations.lookupMember(expression.className, "constructor");
|
|
18045
18990
|
if (constructor !== null && constructor.type.kind === "function") {
|
|
18046
|
-
|
|
18991
|
+
const substitutions = info === null ? /* @__PURE__ */ new Map() : classSubstitutions(info, typeArguments);
|
|
18992
|
+
const signature = substituteType(constructor.type, substitutions);
|
|
18993
|
+
if (signature.kind === "function") {
|
|
18994
|
+
checkSignature(context, expression.args, signature, expression.position);
|
|
18995
|
+
}
|
|
18047
18996
|
} else {
|
|
18048
18997
|
expression.args.forEach((argument) => checkExpression(context, argument));
|
|
18049
18998
|
}
|
|
18050
|
-
return createNamed(expression.className);
|
|
18999
|
+
return createNamed(expression.className, typeArguments);
|
|
18051
19000
|
}
|
|
18052
19001
|
function resolveSuperMethod(context, expression) {
|
|
18053
19002
|
const frame = context.currentClassMethod();
|
|
@@ -18136,14 +19085,14 @@ function isShapeOption(context, option) {
|
|
|
18136
19085
|
}
|
|
18137
19086
|
return context.declarations.lookupInterface(option.name) !== null || context.declarations.lookupClass(option.name) !== null;
|
|
18138
19087
|
}
|
|
18139
|
-
function
|
|
19088
|
+
function memberOf2(context, option, property) {
|
|
18140
19089
|
if (option.kind === "record") {
|
|
18141
19090
|
return option.members.get(property) ?? null;
|
|
18142
19091
|
}
|
|
18143
19092
|
return option.kind === "named" ? context.declarations.lookupMember(option.name, property)?.type ?? null : null;
|
|
18144
19093
|
}
|
|
18145
19094
|
function optionMemberType(context, option, property) {
|
|
18146
|
-
return isShapeOption(context, option) ?
|
|
19095
|
+
return isShapeOption(context, option) ? memberOf2(context, option, property) : null;
|
|
18147
19096
|
}
|
|
18148
19097
|
function resolveUnionMember(context, type, expression) {
|
|
18149
19098
|
if (!type.options.every((option) => isShapeOption(context, option))) {
|
|
@@ -18152,7 +19101,7 @@ function resolveUnionMember(context, type, expression) {
|
|
|
18152
19101
|
const resolved2 = [];
|
|
18153
19102
|
const missing2 = [];
|
|
18154
19103
|
for (const option of type.options) {
|
|
18155
|
-
const member =
|
|
19104
|
+
const member = memberOf2(context, option, expression.property);
|
|
18156
19105
|
if (member === null) {
|
|
18157
19106
|
missing2.push(`"${typeToString(option)}"`);
|
|
18158
19107
|
} else {
|
|
@@ -18319,26 +19268,6 @@ function conditionFacts(context, condition) {
|
|
|
18319
19268
|
collectFacts(context, condition, facts);
|
|
18320
19269
|
return facts;
|
|
18321
19270
|
}
|
|
18322
|
-
function alwaysExits(body) {
|
|
18323
|
-
const last = body[body.length - 1];
|
|
18324
|
-
if (last === void 0) {
|
|
18325
|
-
return false;
|
|
18326
|
-
}
|
|
18327
|
-
if (last.kind === "return-statement" || last.kind === "break-statement") {
|
|
18328
|
-
return true;
|
|
18329
|
-
}
|
|
18330
|
-
if (last.kind !== "if-statement" || last.alternate === null) {
|
|
18331
|
-
return false;
|
|
18332
|
-
}
|
|
18333
|
-
return last.clauses.every((clause) => alwaysExits(clause.body)) && alwaysExits(last.alternate);
|
|
18334
|
-
}
|
|
18335
|
-
function guardFacts(context, statement) {
|
|
18336
|
-
const [clause] = statement.kind === "if-statement" ? statement.clauses : [];
|
|
18337
|
-
if (statement.kind !== "if-statement" || statement.alternate !== null || statement.clauses.length !== 1 || clause === void 0) {
|
|
18338
|
-
return /* @__PURE__ */ new Map();
|
|
18339
|
-
}
|
|
18340
|
-
return alwaysExits(clause.body) ? negatedFacts(context, clause.condition) : /* @__PURE__ */ new Map();
|
|
18341
|
-
}
|
|
18342
19271
|
function negatedFacts(context, condition) {
|
|
18343
19272
|
const facts = /* @__PURE__ */ new Map();
|
|
18344
19273
|
if (condition.kind === "binary-expression" && condition.operator === "or") {
|
|
@@ -18369,23 +19298,58 @@ var ITERATOR_FUNCTIONS = /* @__PURE__ */ new Set(["pairs", "ipairs"]);
|
|
|
18369
19298
|
function checkBlock(context, body) {
|
|
18370
19299
|
context.binder.pushScope();
|
|
18371
19300
|
checkStatements(context, body);
|
|
19301
|
+
const declared = context.binder.currentScopeNames();
|
|
18372
19302
|
context.binder.popScope();
|
|
19303
|
+
for (const name of declared) {
|
|
19304
|
+
context.forgetNarrowing(name);
|
|
19305
|
+
}
|
|
18373
19306
|
}
|
|
18374
|
-
function
|
|
18375
|
-
|
|
19307
|
+
function branch(context, entry, facts, body) {
|
|
19308
|
+
const state = cloneFlow(entry);
|
|
19309
|
+
applyFacts(state, facts);
|
|
19310
|
+
context.setFlow(state);
|
|
18376
19311
|
checkBlock(context, body);
|
|
18377
|
-
context.
|
|
19312
|
+
return context.flowState;
|
|
18378
19313
|
}
|
|
18379
19314
|
function checkIf(context, clauses, alternate) {
|
|
19315
|
+
const exits = [];
|
|
19316
|
+
let current = context.flowState;
|
|
18380
19317
|
for (const clause of clauses) {
|
|
19318
|
+
context.setFlow(current);
|
|
18381
19319
|
checkExpression(context, clause.condition);
|
|
18382
|
-
|
|
19320
|
+
const taken = conditionFacts(context, clause.condition);
|
|
19321
|
+
const skipped = cloneFlow(current);
|
|
19322
|
+
applyFacts(skipped, negatedFacts(context, clause.condition));
|
|
19323
|
+
exits.push(branch(context, current, taken, clause.body));
|
|
19324
|
+
current = skipped;
|
|
18383
19325
|
}
|
|
18384
19326
|
if (alternate === null) {
|
|
18385
|
-
|
|
19327
|
+
exits.push(current);
|
|
19328
|
+
} else {
|
|
19329
|
+
context.setFlow(current);
|
|
19330
|
+
checkBlock(context, alternate);
|
|
19331
|
+
exits.push(context.flowState);
|
|
18386
19332
|
}
|
|
18387
|
-
|
|
18388
|
-
|
|
19333
|
+
context.setFlow(joinFlows(exits));
|
|
19334
|
+
}
|
|
19335
|
+
function checkLoopBody(context, body, facts) {
|
|
19336
|
+
forgetAssignedPaths(context, body);
|
|
19337
|
+
const entry = context.flowState;
|
|
19338
|
+
const state = cloneFlow(entry);
|
|
19339
|
+
applyFacts(state, facts);
|
|
19340
|
+
context.setFlow(state);
|
|
19341
|
+
checkBlock(context, body);
|
|
19342
|
+
context.setFlow(entry);
|
|
19343
|
+
}
|
|
19344
|
+
function checkWhile(context, statement) {
|
|
19345
|
+
forgetAssignedPaths(context, statement.body);
|
|
19346
|
+
checkExpression(context, statement.condition);
|
|
19347
|
+
checkLoopBody(context, statement.body, conditionFacts(context, statement.condition));
|
|
19348
|
+
context.applyFlowFacts(negatedFacts(context, statement.condition));
|
|
19349
|
+
}
|
|
19350
|
+
function checkRepeat(context, statement) {
|
|
19351
|
+
checkLoopBody(context, statement.body, /* @__PURE__ */ new Map());
|
|
19352
|
+
checkExpression(context, statement.condition);
|
|
18389
19353
|
}
|
|
18390
19354
|
function checkNumericFor(context, statement) {
|
|
18391
19355
|
const bounds = [statement.start, statement.limit, statement.step].filter((value) => value !== null);
|
|
@@ -18396,10 +19360,13 @@ function checkNumericFor(context, statement) {
|
|
|
18396
19360
|
}
|
|
18397
19361
|
}
|
|
18398
19362
|
forgetAssignedPaths(context, statement.body);
|
|
19363
|
+
const entry = context.flowState;
|
|
19364
|
+
context.setFlow(cloneFlow(entry));
|
|
18399
19365
|
context.binder.pushScope();
|
|
18400
19366
|
context.binder.declare({ name: statement.variable.name, type: NUMBER_TYPE, isLocal: true, position: statement.variable.position, origin: "local" });
|
|
18401
19367
|
checkStatements(context, statement.body);
|
|
18402
19368
|
context.binder.popScope();
|
|
19369
|
+
context.setFlow(entry);
|
|
18403
19370
|
}
|
|
18404
19371
|
function iteratedTypes(context, iterators) {
|
|
18405
19372
|
const [iterator] = iterators;
|
|
@@ -18420,6 +19387,8 @@ function checkGenericFor(context, statement) {
|
|
|
18420
19387
|
statement.iterators.forEach((iterator) => checkExpression(context, iterator));
|
|
18421
19388
|
const iterated = iteratedTypes(context, statement.iterators);
|
|
18422
19389
|
forgetAssignedPaths(context, statement.body);
|
|
19390
|
+
const entry = context.flowState;
|
|
19391
|
+
context.setFlow(cloneFlow(entry));
|
|
18423
19392
|
context.binder.pushScope();
|
|
18424
19393
|
statement.variables.forEach((variable, index) => {
|
|
18425
19394
|
const inferred = iterated[index] ?? ANY_TYPE;
|
|
@@ -18428,6 +19397,7 @@ function checkGenericFor(context, statement) {
|
|
|
18428
19397
|
});
|
|
18429
19398
|
checkStatements(context, statement.body);
|
|
18430
19399
|
context.binder.popScope();
|
|
19400
|
+
context.setFlow(entry);
|
|
18431
19401
|
}
|
|
18432
19402
|
|
|
18433
19403
|
// ../compiler/src/checker/events.ts
|
|
@@ -18488,15 +19458,19 @@ function minimumArguments(context, parameters) {
|
|
|
18488
19458
|
}
|
|
18489
19459
|
function buildFunctionType(context, parameters, returnAnnotation, contextual = null) {
|
|
18490
19460
|
const isVariadic = parameters.some((parameter) => parameter.isVararg);
|
|
18491
|
-
const
|
|
18492
|
-
|
|
19461
|
+
const named2 = parameters.filter((parameter) => !parameter.isVararg);
|
|
19462
|
+
const types = named2.map((parameter, index) => parameter.annotation === null ? contextual?.parameters[index] ?? ANY_TYPE : context.resolveAnnotation(parameter.annotation));
|
|
19463
|
+
const names = named2.map((parameter) => parameter.name);
|
|
19464
|
+
return createFunction(types, context.resolveAnnotation(returnAnnotation), minimumArguments(context, parameters), isVariadic, names);
|
|
18493
19465
|
}
|
|
18494
|
-
function checkFunctionBody(context, parameters, returnAnnotation, body, signature,
|
|
19466
|
+
function checkFunctionBody(context, parameters, returnAnnotation, body, signature, selfType2) {
|
|
18495
19467
|
forgetAssignedPaths(context, body);
|
|
19468
|
+
const entry = context.flowState;
|
|
19469
|
+
context.setFlow(cloneFlow(entry));
|
|
18496
19470
|
context.binder.pushScope();
|
|
18497
19471
|
context.pushReturnType(returnAnnotation === null ? null : context.resolveAnnotation(returnAnnotation));
|
|
18498
|
-
if (
|
|
18499
|
-
context.binder.declare({ name: "self", type:
|
|
19472
|
+
if (selfType2 !== null) {
|
|
19473
|
+
context.binder.declare({ name: "self", type: selfType2, isLocal: true, position: ORIGIN });
|
|
18500
19474
|
}
|
|
18501
19475
|
let parameterIndex = 0;
|
|
18502
19476
|
for (const parameter of parameters) {
|
|
@@ -18512,6 +19486,7 @@ function checkFunctionBody(context, parameters, returnAnnotation, body, signatur
|
|
|
18512
19486
|
signature.returnType = inferred;
|
|
18513
19487
|
}
|
|
18514
19488
|
context.binder.popScope();
|
|
19489
|
+
context.setFlow(entry);
|
|
18515
19490
|
}
|
|
18516
19491
|
function valueAt(values, valueTypes, index) {
|
|
18517
19492
|
if (values[index] !== void 0) {
|
|
@@ -18535,6 +19510,9 @@ function checkLocal(context, statement) {
|
|
|
18535
19510
|
context.expectAssignable(valueType, declared, value.position, `Variable "${declaration.name}"`);
|
|
18536
19511
|
}
|
|
18537
19512
|
context.binder.declare({ name: declaration.name, type: declared, isLocal: true, position: declaration.position, origin: "local" });
|
|
19513
|
+
if (value !== void 0) {
|
|
19514
|
+
recordAssignedFact(context, declaration.name, valueType, declared);
|
|
19515
|
+
}
|
|
18538
19516
|
});
|
|
18539
19517
|
}
|
|
18540
19518
|
function checkCompoundOperand(context, operator, type, position2) {
|
|
@@ -18548,6 +19526,15 @@ function checkCompoundOperand(context, operator, type, position2) {
|
|
|
18548
19526
|
context.report("check-invalid-operand", `Operator "${operator}" cannot be applied to "${typeToString(type)}".`, position2);
|
|
18549
19527
|
}
|
|
18550
19528
|
}
|
|
19529
|
+
function recordAssignedFact(context, path, valueType, declared) {
|
|
19530
|
+
if (path === null || valueType.kind === "any") {
|
|
19531
|
+
return;
|
|
19532
|
+
}
|
|
19533
|
+
const fact = assignmentFact(widenInferred(valueType), declared);
|
|
19534
|
+
if (fact !== null) {
|
|
19535
|
+
context.applyFlowFacts(/* @__PURE__ */ new Map([[path, fact]]));
|
|
19536
|
+
}
|
|
19537
|
+
}
|
|
18551
19538
|
function checkAssignment(context, statement) {
|
|
18552
19539
|
const valueTypes = checkValueList(context, statement.values);
|
|
18553
19540
|
statement.targets.forEach((target, index) => {
|
|
@@ -18583,6 +19570,7 @@ function checkAssignment(context, statement) {
|
|
|
18583
19570
|
if (value !== void 0) {
|
|
18584
19571
|
context.expectAssignable(valueType, declared, value.position, "Assignment");
|
|
18585
19572
|
}
|
|
19573
|
+
recordAssignedFact(context, pathOf2(target), valueType, declared);
|
|
18586
19574
|
});
|
|
18587
19575
|
}
|
|
18588
19576
|
function checkFunctionDeclaration(context, statement) {
|
|
@@ -18661,18 +19649,19 @@ function checkStatement(context, statement) {
|
|
|
18661
19649
|
case "function-declaration":
|
|
18662
19650
|
return checkFunctionDeclaration(context, statement);
|
|
18663
19651
|
case "return-statement":
|
|
18664
|
-
|
|
19652
|
+
checkReturn(context, statement);
|
|
19653
|
+
context.markUnreachable();
|
|
19654
|
+
return;
|
|
19655
|
+
case "break-statement":
|
|
19656
|
+
case "continue-statement":
|
|
19657
|
+
context.markUnreachable();
|
|
19658
|
+
return;
|
|
18665
19659
|
case "do-statement":
|
|
18666
19660
|
return checkBlock(context, statement.body);
|
|
18667
19661
|
case "while-statement":
|
|
18668
|
-
|
|
18669
|
-
forgetAssignedPaths(context, statement.body);
|
|
18670
|
-
return checkBlock(context, statement.body);
|
|
19662
|
+
return checkWhile(context, statement);
|
|
18671
19663
|
case "repeat-statement":
|
|
18672
|
-
|
|
18673
|
-
checkBlock(context, statement.body);
|
|
18674
|
-
checkExpression(context, statement.condition);
|
|
18675
|
-
return;
|
|
19664
|
+
return checkRepeat(context, statement);
|
|
18676
19665
|
case "if-statement":
|
|
18677
19666
|
return checkIf(context, statement.clauses, statement.alternate);
|
|
18678
19667
|
case "numeric-for-statement":
|
|
@@ -18702,18 +19691,8 @@ function checkStatement(context, statement) {
|
|
|
18702
19691
|
}
|
|
18703
19692
|
}
|
|
18704
19693
|
function checkStatements(context, statements) {
|
|
18705
|
-
let guards = 0;
|
|
18706
19694
|
for (const statement of statements) {
|
|
18707
19695
|
checkStatement(context, statement);
|
|
18708
|
-
const facts = guardFacts(context, statement);
|
|
18709
|
-
if (facts.size > 0) {
|
|
18710
|
-
context.pushNarrowing(facts);
|
|
18711
|
-
guards += 1;
|
|
18712
|
-
}
|
|
18713
|
-
}
|
|
18714
|
-
while (guards > 0) {
|
|
18715
|
-
context.popNarrowing();
|
|
18716
|
-
guards -= 1;
|
|
18717
19696
|
}
|
|
18718
19697
|
}
|
|
18719
19698
|
|
|
@@ -18788,7 +19767,7 @@ function checkMember(context, expression) {
|
|
|
18788
19767
|
if (objectType.kind === "map" && isAssignable(STRING_TYPE, objectType.key)) {
|
|
18789
19768
|
return context.record(expression, objectType.value);
|
|
18790
19769
|
}
|
|
18791
|
-
const named2 = objectType.kind === "named" ? resolveNamedMember(context, objectType
|
|
19770
|
+
const named2 = objectType.kind === "named" ? resolveNamedMember(context, objectType, expression) : null;
|
|
18792
19771
|
if (named2 !== null) {
|
|
18793
19772
|
return context.record(expression, named2);
|
|
18794
19773
|
}
|
|
@@ -18845,10 +19824,14 @@ function isLegacySuperCall(expression) {
|
|
|
18845
19824
|
}
|
|
18846
19825
|
function checkMethodCall(context, expression, method, receiver) {
|
|
18847
19826
|
if (receiver.kind !== "named") {
|
|
18848
|
-
|
|
18849
|
-
|
|
19827
|
+
const signature = resolveNonNominalMethod(receiver, method);
|
|
19828
|
+
if (signature === null) {
|
|
19829
|
+
checkValueList(context, expression.args);
|
|
19830
|
+
return ANY_TYPE;
|
|
19831
|
+
}
|
|
19832
|
+
return checkSignature(context, expression.args, withoutSelfParameter(signature), expression.position);
|
|
18850
19833
|
}
|
|
18851
|
-
const declared = context
|
|
19834
|
+
const declared = memberOf(context, receiver, method);
|
|
18852
19835
|
if (declared?.type.kind === "function") {
|
|
18853
19836
|
if (declared.deprecated === true) {
|
|
18854
19837
|
context.warn("check-deprecated-use", `Member "${method}" is deprecated.`, expression.position);
|
|
@@ -18867,6 +19850,10 @@ function checkMethodCall(context, expression, method, receiver) {
|
|
|
18867
19850
|
return checkSignature(context, expression.args, member.type, expression.position);
|
|
18868
19851
|
}
|
|
18869
19852
|
function checkCall(context, expression) {
|
|
19853
|
+
const contracted = checkResourceCall(context, expression);
|
|
19854
|
+
if (contracted !== null) {
|
|
19855
|
+
return context.record(expression, contracted);
|
|
19856
|
+
}
|
|
18870
19857
|
if (isLegacySuperCall(expression)) {
|
|
18871
19858
|
checkValueList(context, expression.args);
|
|
18872
19859
|
context.report("check-invalid-super", 'Call "super(...)" directly instead of "self:super(...)".', expression.position);
|
|
@@ -18967,6 +19954,9 @@ function checkMultiValueExpression(context, expression, expected = null) {
|
|
|
18967
19954
|
if (symbol === null) {
|
|
18968
19955
|
checkGlobalReference(context, expression.name, expression.position);
|
|
18969
19956
|
}
|
|
19957
|
+
if (symbol === null || symbol.isLocal !== true) {
|
|
19958
|
+
context.noteGlobalReference(expression.name);
|
|
19959
|
+
}
|
|
18970
19960
|
return context.record(expression, context.narrowedType(expression.name) ?? symbol?.type ?? ANY_TYPE);
|
|
18971
19961
|
}
|
|
18972
19962
|
case "member-expression":
|
|
@@ -19079,6 +20069,11 @@ function registerMembers(context, info, statement) {
|
|
|
19079
20069
|
for (const member of [...statement.members, ...generated]) {
|
|
19080
20070
|
const type = member.kind === "class-field" ? fieldTypes.get(member) ?? ANY_TYPE : member.isSynthetic ? syntheticMethodType(context, member, fieldTypes) : buildFunctionType(context, member.parameters, member.returnAnnotation);
|
|
19081
20071
|
const decorators = member.decorators.map((decorator) => decorator.name);
|
|
20072
|
+
if (member.kind === "class-method" && isMetamethodMember(member)) {
|
|
20073
|
+
checkMetamethod(context, info.name, member, type);
|
|
20074
|
+
continue;
|
|
20075
|
+
}
|
|
20076
|
+
reportRejectedMetamethod(context, info.name, member);
|
|
19082
20077
|
const space = member.isStatic ? info.statics : info.members;
|
|
19083
20078
|
space.set(member.name, {
|
|
19084
20079
|
name: member.name,
|
|
@@ -19106,9 +20101,22 @@ function declareBuilder(context, info, statement) {
|
|
|
19106
20101
|
}
|
|
19107
20102
|
}
|
|
19108
20103
|
members.set("build", { name: "build", type: { kind: "function", parameters: [], minimumArguments: 0, isVariadic: false, returnType: createNamed(statement.name) }, isMethod: true, position: statement.position });
|
|
19109
|
-
context.declarations.declareClass({
|
|
20104
|
+
context.declarations.declareClass({
|
|
20105
|
+
name,
|
|
20106
|
+
typeParameters: [],
|
|
20107
|
+
typeConstraints: [],
|
|
20108
|
+
superClass: null,
|
|
20109
|
+
superArguments: [],
|
|
20110
|
+
interfaces: [],
|
|
20111
|
+
members,
|
|
20112
|
+
statics: /* @__PURE__ */ new Map(),
|
|
20113
|
+
position: statement.position
|
|
20114
|
+
});
|
|
19110
20115
|
context.declareModuleGlobal({ name, type: createNamed(name), isLocal: false, position: statement.position });
|
|
19111
20116
|
}
|
|
20117
|
+
function selfType(info) {
|
|
20118
|
+
return createNamed(info.name, info.typeParameters.map((parameter) => createNamed(parameter)));
|
|
20119
|
+
}
|
|
19112
20120
|
function checkMethodBody(context, info, member) {
|
|
19113
20121
|
const explicitSelf = member.parameters.find((parameter) => parameter.name === "self");
|
|
19114
20122
|
if (explicitSelf !== void 0) {
|
|
@@ -19123,7 +20131,7 @@ function checkMethodBody(context, info, member) {
|
|
|
19123
20131
|
return;
|
|
19124
20132
|
}
|
|
19125
20133
|
context.pushClassMethod({ className: info.name, methodName: member.name });
|
|
19126
|
-
checkFunctionBody(context, member.parameters, member.returnAnnotation, member.body, signature,
|
|
20134
|
+
checkFunctionBody(context, member.parameters, member.returnAnnotation, member.body, signature, selfType(info));
|
|
19127
20135
|
context.popClassMethod();
|
|
19128
20136
|
}
|
|
19129
20137
|
function resolveSuperClass(context, statement) {
|
|
@@ -19146,6 +20154,10 @@ function resolveSuperClass(context, statement) {
|
|
|
19146
20154
|
context.report("check-unknown-class", message, statement.position);
|
|
19147
20155
|
return null;
|
|
19148
20156
|
}
|
|
20157
|
+
function resolveClassHeader(context, info, statement) {
|
|
20158
|
+
info.superArguments = statement.superClassArguments.map((argument) => context.resolveAnnotation(argument));
|
|
20159
|
+
info.typeConstraints = statement.typeConstraints.map((constraint) => constraint === null ? null : context.resolveAnnotation(constraint));
|
|
20160
|
+
}
|
|
19149
20161
|
function declareClassInfo(context, statement) {
|
|
19150
20162
|
if (context.declarations.lookupClass(statement.name) !== null) {
|
|
19151
20163
|
context.report("check-duplicate-class", `Class "${statement.name}" is already defined.`, statement.position);
|
|
@@ -19157,12 +20169,16 @@ function declareClassInfo(context, statement) {
|
|
|
19157
20169
|
}
|
|
19158
20170
|
const info = {
|
|
19159
20171
|
name: statement.name,
|
|
20172
|
+
typeParameters: statement.typeParameters,
|
|
20173
|
+
typeConstraints: [],
|
|
19160
20174
|
superClass: null,
|
|
20175
|
+
superArguments: [],
|
|
19161
20176
|
interfaces: statement.interfaces,
|
|
19162
20177
|
members: /* @__PURE__ */ new Map(),
|
|
19163
20178
|
statics: /* @__PURE__ */ new Map(),
|
|
19164
20179
|
position: statement.position
|
|
19165
20180
|
};
|
|
20181
|
+
context.noteTypeParameters(statement.typeParameters);
|
|
19166
20182
|
context.declarations.declareClass(info);
|
|
19167
20183
|
context.declareModuleGlobal({ name: info.name, type: createNamed(info.name), isLocal: false, position: statement.position });
|
|
19168
20184
|
return info;
|
|
@@ -19173,6 +20189,7 @@ function declareLocalClass(context, statement) {
|
|
|
19173
20189
|
return null;
|
|
19174
20190
|
}
|
|
19175
20191
|
info.superClass = resolveSuperClass(context, statement);
|
|
20192
|
+
resolveClassHeader(context, info, statement);
|
|
19176
20193
|
return info;
|
|
19177
20194
|
}
|
|
19178
20195
|
function checkClassDeclaration(context, statement) {
|
|
@@ -19286,6 +20303,7 @@ function predeclareModule(context, body) {
|
|
|
19286
20303
|
const declared = declareHeaders(context, body);
|
|
19287
20304
|
for (const entry of declared) {
|
|
19288
20305
|
entry.info.superClass = resolveSuperClass(context, entry.statement);
|
|
20306
|
+
resolveClassHeader(context, entry.info, entry.statement);
|
|
19289
20307
|
}
|
|
19290
20308
|
for (const entry of declared) {
|
|
19291
20309
|
breakCycle(context, entry.info);
|
|
@@ -19345,17 +20363,23 @@ function externalReferences(context) {
|
|
|
19345
20363
|
}
|
|
19346
20364
|
return external;
|
|
19347
20365
|
}
|
|
20366
|
+
function referencedNames(context, external) {
|
|
20367
|
+
const events = [...context.eventReferences].map((name) => `${EVENT_NAME_PREFIX}${name}`);
|
|
20368
|
+
return /* @__PURE__ */ new Set([...external.keys(), ...context.globalReferences, ...context.unknownTypes.keys(), ...events]);
|
|
20369
|
+
}
|
|
19348
20370
|
function check(program2, mode, environment = DEFAULT_ENVIRONMENT, options = {}) {
|
|
19349
20371
|
const ambient = options.ambient ?? EMPTY_AMBIENT;
|
|
19350
20372
|
const project = options.project ?? EMPTY_PROJECT_DECLARATIONS;
|
|
19351
20373
|
const context = new CheckContext(mode, environment, ambient, project, options.oop === true);
|
|
19352
20374
|
context.isDeclarationFile = options.isDeclarationFile === true;
|
|
20375
|
+
context.contracts = options.contracts ?? [];
|
|
19353
20376
|
const structure = context.isDeclarationFile ? checkDeclarationFile(program2.body) : checkJumps(program2.body);
|
|
19354
20377
|
const directives = collectDirectives(program2.body, { isDeclarationFile: context.isDeclarationFile });
|
|
19355
20378
|
predeclareModule(context, program2.body);
|
|
19356
20379
|
checkStatements(context, program2.body);
|
|
19357
20380
|
reportUnknownTypes(context);
|
|
19358
20381
|
reportUnusedSymbols(context, options.noUnusedLocals === true, options.noUnusedParameters === true);
|
|
20382
|
+
const external = externalReferences(context);
|
|
19359
20383
|
return {
|
|
19360
20384
|
diagnostics: sortDiagnostics([...structure, ...directives.diagnostics, ...context.diagnostics]),
|
|
19361
20385
|
types: context.types,
|
|
@@ -19364,7 +20388,9 @@ function check(program2, mode, environment = DEFAULT_ENVIRONMENT, options = {})
|
|
|
19364
20388
|
declarations: context.declarations,
|
|
19365
20389
|
aliases: context.binder.resolvedAliases(),
|
|
19366
20390
|
declaredGlobals: context.declaredGlobals,
|
|
19367
|
-
|
|
20391
|
+
moduleGlobals: context.moduleGlobals,
|
|
20392
|
+
externalReferences: external,
|
|
20393
|
+
referencedNames: referencedNames(context, external),
|
|
19368
20394
|
directives: directives.directives,
|
|
19369
20395
|
mode,
|
|
19370
20396
|
environment,
|
|
@@ -19762,6 +20788,13 @@ function emitGeneratedMethod(state, className, member) {
|
|
|
19762
20788
|
end
|
|
19763
20789
|
end`;
|
|
19764
20790
|
}
|
|
20791
|
+
if (member.generated?.kind === "validate" || member.generated?.kind === "matches") {
|
|
20792
|
+
const helper = member.generated.kind === "validate" ? "__luam_validate" : "__luam_matches";
|
|
20793
|
+
requireHelper(state, "validate");
|
|
20794
|
+
return `${member.name} = function(value)
|
|
20795
|
+
return ${helper}(value, ${member.generated.descriptor ?? "{ kind = 'table' }"})
|
|
20796
|
+
end`;
|
|
20797
|
+
}
|
|
19765
20798
|
if (member.generated?.kind === "to-string") {
|
|
19766
20799
|
const values = names.map((name) => `'${name}=' .. tostring(self.${name})`).join(" .. ', ' .. ");
|
|
19767
20800
|
const body = values.length === 0 ? "''" : values;
|
|
@@ -19797,24 +20830,24 @@ ${names.map((name) => ` self.${name} = values.${name}`).join("\n")}
|
|
|
19797
20830
|
end`;
|
|
19798
20831
|
}
|
|
19799
20832
|
function emitMembers(state, statement) {
|
|
19800
|
-
const
|
|
20833
|
+
const entries3 = [];
|
|
19801
20834
|
state.indent += 1;
|
|
19802
20835
|
for (const member of statement.members) {
|
|
19803
20836
|
if (member.kind === "class-method") {
|
|
19804
|
-
|
|
20837
|
+
entries3.push(indentLine(state, `${markSource(state, member.position.line, `${statement.name}${member.isStatic ? "." : ":"}${member.name}`)}${emitMethod(state, statement.name, member)}`));
|
|
19805
20838
|
continue;
|
|
19806
20839
|
}
|
|
19807
20840
|
if (member.decorators.some((decorator) => decorator.name === "Lazy")) {
|
|
19808
20841
|
continue;
|
|
19809
20842
|
}
|
|
19810
20843
|
const value = member.value === null ? "nil" : emitExpression(state, member.value);
|
|
19811
|
-
|
|
20844
|
+
entries3.push(indentLine(state, `${markSource(state, member.position.line, statement.name)}${member.name} = ${value}`));
|
|
19812
20845
|
}
|
|
19813
20846
|
for (const generated of state.generatedMembers.get(statement) ?? []) {
|
|
19814
|
-
|
|
20847
|
+
entries3.push(indentLine(state, `${markSource(state, generated.position.line, `${statement.name}:${generated.name}`)}${emitMethod(state, statement.name, generated)}`));
|
|
19815
20848
|
}
|
|
19816
20849
|
state.indent -= 1;
|
|
19817
|
-
return
|
|
20850
|
+
return entries3;
|
|
19818
20851
|
}
|
|
19819
20852
|
function emitClassHeader(statement) {
|
|
19820
20853
|
const name = emitString(statement.name);
|
|
@@ -19840,15 +20873,15 @@ ${assignments}
|
|
|
19840
20873
|
return value
|
|
19841
20874
|
end`);
|
|
19842
20875
|
state.indent += 1;
|
|
19843
|
-
const
|
|
20876
|
+
const entries3 = methods.map((method) => indentLine(state, method));
|
|
19844
20877
|
state.indent -= 1;
|
|
19845
|
-
return [`class '${name}' {`,
|
|
20878
|
+
return [`class '${name}' {`, entries3.join(",\n"), "}"].join("\n");
|
|
19846
20879
|
}
|
|
19847
20880
|
function emitClassDeclaration(state, statement) {
|
|
19848
20881
|
requireHelper(state, "class");
|
|
19849
20882
|
const header = emitClassHeader(statement);
|
|
19850
|
-
const
|
|
19851
|
-
const declaration =
|
|
20883
|
+
const entries3 = emitMembers(state, statement);
|
|
20884
|
+
const declaration = entries3.length === 0 ? `${header} {}` : [`${header} {`, entries3.join(",\n"), indentLine(state, "}")].join("\n");
|
|
19852
20885
|
const builder = emitBuilder(state, statement);
|
|
19853
20886
|
return builder === null ? declaration : `${declaration}
|
|
19854
20887
|
${builder}`;
|
|
@@ -20786,6 +21819,7 @@ function compile(source, options = {}) {
|
|
|
20786
21819
|
const isDeclarationFile = options.filePath !== void 0 && isDeclarationPath(options.filePath);
|
|
20787
21820
|
const checked = check(parsed.program, mode, environment, {
|
|
20788
21821
|
ambient: options.ambient ?? EMPTY_AMBIENT,
|
|
21822
|
+
contracts: options.contracts ?? [],
|
|
20789
21823
|
project: options.project ?? EMPTY_PROJECT_DECLARATIONS,
|
|
20790
21824
|
isDeclarationFile,
|
|
20791
21825
|
oop: settings.oop,
|
|
@@ -20799,7 +21833,9 @@ function compile(source, options = {}) {
|
|
|
20799
21833
|
environment,
|
|
20800
21834
|
declarations: ownDeclarations(checked.declarations, options.ambient),
|
|
20801
21835
|
declaredGlobals: checked.declaredGlobals,
|
|
21836
|
+
moduleGlobals: checked.moduleGlobals,
|
|
20802
21837
|
externalReferences: checked.externalReferences,
|
|
21838
|
+
referencedNames: checked.referencedNames,
|
|
20803
21839
|
directives: checked.directives,
|
|
20804
21840
|
topLevelReturn: findTopLevelReturn(parsed.program.body),
|
|
20805
21841
|
events: checked.events
|
|
@@ -20890,6 +21926,199 @@ function fingerprintDeclarations(declarations) {
|
|
|
20890
21926
|
const events = declarations.events.map(eventText);
|
|
20891
21927
|
return hashString([...classes, ...interfaces, ...enums, ...globals, ...events].sort(compareText).join(";"));
|
|
20892
21928
|
}
|
|
21929
|
+
function appendText(entries3, name, text) {
|
|
21930
|
+
const existing = entries3.get(name);
|
|
21931
|
+
entries3.set(name, existing === void 0 ? text : `${existing}|${text}`);
|
|
21932
|
+
}
|
|
21933
|
+
function fingerprintByName(declarations) {
|
|
21934
|
+
const entries3 = /* @__PURE__ */ new Map();
|
|
21935
|
+
for (const info of declarations.classes) {
|
|
21936
|
+
appendText(entries3, info.name, classText(info));
|
|
21937
|
+
}
|
|
21938
|
+
for (const info of declarations.interfaces) {
|
|
21939
|
+
appendText(entries3, info.name, interfaceText(info));
|
|
21940
|
+
}
|
|
21941
|
+
for (const info of declarations.enums) {
|
|
21942
|
+
appendText(entries3, info.name, enumText(info));
|
|
21943
|
+
}
|
|
21944
|
+
for (const info of declarations.globals) {
|
|
21945
|
+
appendText(entries3, info.name, globalText(info));
|
|
21946
|
+
}
|
|
21947
|
+
for (const info of declarations.events) {
|
|
21948
|
+
appendText(entries3, `${EVENT_NAME_PREFIX}${info.name}`, eventText(info));
|
|
21949
|
+
}
|
|
21950
|
+
return new Map([...entries3].map(([name, text]) => [name, hashString(text)]));
|
|
21951
|
+
}
|
|
21952
|
+
|
|
21953
|
+
// ../compiler/src/project/dependency-graph.ts
|
|
21954
|
+
var MAX_CLOSURE_SIZE = 512;
|
|
21955
|
+
function indexOwners(nodes, environment) {
|
|
21956
|
+
const owners = /* @__PURE__ */ new Map();
|
|
21957
|
+
for (const node of nodes) {
|
|
21958
|
+
if (!canReference(environment, node.environment)) {
|
|
21959
|
+
continue;
|
|
21960
|
+
}
|
|
21961
|
+
for (const name of node.provides.keys()) {
|
|
21962
|
+
const existing = owners.get(name);
|
|
21963
|
+
if (existing === void 0) {
|
|
21964
|
+
owners.set(name, [node]);
|
|
21965
|
+
continue;
|
|
21966
|
+
}
|
|
21967
|
+
existing.push(node);
|
|
21968
|
+
}
|
|
21969
|
+
}
|
|
21970
|
+
return owners;
|
|
21971
|
+
}
|
|
21972
|
+
function walk3(node, owners) {
|
|
21973
|
+
const visited = /* @__PURE__ */ new Set();
|
|
21974
|
+
const pending = [...node.requires, ...node.provides.keys()];
|
|
21975
|
+
while (pending.length > 0) {
|
|
21976
|
+
const name = pending.pop();
|
|
21977
|
+
if (name === void 0 || visited.has(name)) {
|
|
21978
|
+
continue;
|
|
21979
|
+
}
|
|
21980
|
+
visited.add(name);
|
|
21981
|
+
if (visited.size > MAX_CLOSURE_SIZE) {
|
|
21982
|
+
return null;
|
|
21983
|
+
}
|
|
21984
|
+
for (const owner of owners.get(name) ?? []) {
|
|
21985
|
+
for (const required of owner.requires) {
|
|
21986
|
+
if (!visited.has(required)) {
|
|
21987
|
+
pending.push(required);
|
|
21988
|
+
}
|
|
21989
|
+
}
|
|
21990
|
+
}
|
|
21991
|
+
}
|
|
21992
|
+
return visited;
|
|
21993
|
+
}
|
|
21994
|
+
function ownerText(owners, name) {
|
|
21995
|
+
const list = owners.get(name) ?? [];
|
|
21996
|
+
return `${name}=${list.map((owner) => `${owner.path}:${owner.provides.get(name) ?? ""}`).sort().join("+")}`;
|
|
21997
|
+
}
|
|
21998
|
+
function createDependencyGraph(nodes) {
|
|
21999
|
+
const byPath = new Map(nodes.map((node) => [node.path, node]));
|
|
22000
|
+
const indexes = /* @__PURE__ */ new Map();
|
|
22001
|
+
const closures = /* @__PURE__ */ new Map();
|
|
22002
|
+
const keys = /* @__PURE__ */ new Map();
|
|
22003
|
+
function ownersFor(environment) {
|
|
22004
|
+
const cached = indexes.get(environment);
|
|
22005
|
+
if (cached !== void 0) {
|
|
22006
|
+
return cached;
|
|
22007
|
+
}
|
|
22008
|
+
const owners = indexOwners(nodes, environment);
|
|
22009
|
+
indexes.set(environment, owners);
|
|
22010
|
+
return owners;
|
|
22011
|
+
}
|
|
22012
|
+
function closureOf(path) {
|
|
22013
|
+
if (closures.has(path)) {
|
|
22014
|
+
return closures.get(path) ?? null;
|
|
22015
|
+
}
|
|
22016
|
+
const node = byPath.get(path);
|
|
22017
|
+
const closure = node === void 0 ? null : walk3(node, ownersFor(node.environment));
|
|
22018
|
+
closures.set(path, closure);
|
|
22019
|
+
return closure;
|
|
22020
|
+
}
|
|
22021
|
+
function keyOf(path) {
|
|
22022
|
+
if (keys.has(path)) {
|
|
22023
|
+
return keys.get(path) ?? null;
|
|
22024
|
+
}
|
|
22025
|
+
const node = byPath.get(path);
|
|
22026
|
+
const closure = closureOf(path);
|
|
22027
|
+
if (node === void 0 || closure === null) {
|
|
22028
|
+
keys.set(path, null);
|
|
22029
|
+
return null;
|
|
22030
|
+
}
|
|
22031
|
+
const owners = ownersFor(node.environment);
|
|
22032
|
+
const key = hashString([...closure].sort().map((name) => ownerText(owners, name)).join(";"));
|
|
22033
|
+
keys.set(path, key);
|
|
22034
|
+
return key;
|
|
22035
|
+
}
|
|
22036
|
+
return {
|
|
22037
|
+
closureOf,
|
|
22038
|
+
keyOf,
|
|
22039
|
+
dependentsOf: (names) => {
|
|
22040
|
+
const dependents = /* @__PURE__ */ new Set();
|
|
22041
|
+
for (const node of nodes) {
|
|
22042
|
+
const closure = closureOf(node.path);
|
|
22043
|
+
if (closure === null) {
|
|
22044
|
+
dependents.add(node.path);
|
|
22045
|
+
continue;
|
|
22046
|
+
}
|
|
22047
|
+
for (const name of names) {
|
|
22048
|
+
if (closure.has(name)) {
|
|
22049
|
+
dependents.add(node.path);
|
|
22050
|
+
break;
|
|
22051
|
+
}
|
|
22052
|
+
}
|
|
22053
|
+
}
|
|
22054
|
+
return dependents;
|
|
22055
|
+
},
|
|
22056
|
+
providedBy: (paths) => {
|
|
22057
|
+
const names = /* @__PURE__ */ new Set();
|
|
22058
|
+
for (const path of paths) {
|
|
22059
|
+
for (const name of byPath.get(path)?.provides.keys() ?? []) {
|
|
22060
|
+
names.add(name);
|
|
22061
|
+
}
|
|
22062
|
+
}
|
|
22063
|
+
return names;
|
|
22064
|
+
}
|
|
22065
|
+
};
|
|
22066
|
+
}
|
|
22067
|
+
|
|
22068
|
+
// ../compiler/src/project/ambient-scope.ts
|
|
22069
|
+
function optionsKey(options) {
|
|
22070
|
+
return `${options.strict ? "strict" : ""}|${options.oop ? "oop" : ""}|${options.noUnusedLocals ? "nul" : ""}|${options.noUnusedParameters ? "nup" : ""}`;
|
|
22071
|
+
}
|
|
22072
|
+
function declaresNothing(declarations) {
|
|
22073
|
+
const { classes, interfaces, enums, globals, events } = declarations;
|
|
22074
|
+
return classes.length === 0 && interfaces.length === 0 && enums.length === 0 && globals.length === 0 && events.length === 0;
|
|
22075
|
+
}
|
|
22076
|
+
function sharedEnums(collected) {
|
|
22077
|
+
const declared = new Set(collected.flatMap((entry) => entry.declarations.enums.map((enumeration) => enumeration.name)));
|
|
22078
|
+
if (declared.size === 0) {
|
|
22079
|
+
return declared;
|
|
22080
|
+
}
|
|
22081
|
+
return new Set(collected.flatMap((entry) => entry.externalReferences.filter((name) => declared.has(name))));
|
|
22082
|
+
}
|
|
22083
|
+
function baseKey(context) {
|
|
22084
|
+
const mode = context.development ? "development" : "release";
|
|
22085
|
+
const contracts = context.contracts.map((contract) => serializeAbi(contract)).join(";");
|
|
22086
|
+
return `${optionsKey(context.options)}|${mode}|${JSON.stringify(context.project.globals)}|${[...context.references].sort().join(",")}|${hashString(contracts)}`;
|
|
22087
|
+
}
|
|
22088
|
+
function environmentKeys(collected) {
|
|
22089
|
+
const keys = {};
|
|
22090
|
+
for (const environment of ALL_ENVIRONMENTS) {
|
|
22091
|
+
const visible = collected.filter((entry) => canReference(environment, entry.environment));
|
|
22092
|
+
keys[environment] = hashString(visible.map((entry) => `${entry.path}=${entry.fingerprint}`).join(";"));
|
|
22093
|
+
}
|
|
22094
|
+
return keys;
|
|
22095
|
+
}
|
|
22096
|
+
function createResolver(collected) {
|
|
22097
|
+
const visible = /* @__PURE__ */ new Map();
|
|
22098
|
+
const merged = /* @__PURE__ */ new Map();
|
|
22099
|
+
for (const environment of ALL_ENVIRONMENTS) {
|
|
22100
|
+
const entries3 = collected.filter((entry) => canReference(environment, entry.environment) && !declaresNothing(entry.declarations));
|
|
22101
|
+
visible.set(environment, entries3);
|
|
22102
|
+
merged.set(environment, mergeAmbient(entries3.map((entry) => entry.declarations)));
|
|
22103
|
+
}
|
|
22104
|
+
return (entry) => {
|
|
22105
|
+
if (declaresNothing(entry.declarations)) {
|
|
22106
|
+
return merged.get(entry.environment) ?? EMPTY_AMBIENT;
|
|
22107
|
+
}
|
|
22108
|
+
const entries3 = visible.get(entry.environment) ?? [];
|
|
22109
|
+
return mergeAmbient(entries3.filter((candidate) => candidate.path !== entry.path).map((candidate) => candidate.declarations));
|
|
22110
|
+
};
|
|
22111
|
+
}
|
|
22112
|
+
function createAmbientScope(collected, context) {
|
|
22113
|
+
const graph = createDependencyGraph(collected);
|
|
22114
|
+
const base = baseKey(context);
|
|
22115
|
+
const fallback = environmentKeys(collected);
|
|
22116
|
+
return {
|
|
22117
|
+
graph,
|
|
22118
|
+
keyFor: (entry) => hashString(`${base}|${graph.keyOf(entry.path) ?? fallback[entry.environment]}`),
|
|
22119
|
+
resolve: createResolver(collected)
|
|
22120
|
+
};
|
|
22121
|
+
}
|
|
20893
22122
|
|
|
20894
22123
|
// ../compiler/src/project/module-graph.ts
|
|
20895
22124
|
function buildSymbolTable(modules) {
|
|
@@ -20950,52 +22179,12 @@ function validateModuleReferences(modules) {
|
|
|
20950
22179
|
function flattenDiagnostics(modules) {
|
|
20951
22180
|
return modules.flatMap((module) => module.diagnostics.map((diagnostic) => ({ path: module.path, diagnostic })));
|
|
20952
22181
|
}
|
|
20953
|
-
function optionsKey(options) {
|
|
20954
|
-
return `${options.strict ? "strict" : ""}|${options.oop ? "oop" : ""}|${options.noUnusedLocals ? "nul" : ""}|${options.noUnusedParameters ? "nup" : ""}`;
|
|
20955
|
-
}
|
|
20956
|
-
function sharedEnums(collected) {
|
|
20957
|
-
const declared = new Set(collected.flatMap((entry) => entry.declarations.enums.map((enumeration) => enumeration.name)));
|
|
20958
|
-
if (declared.size === 0) {
|
|
20959
|
-
return declared;
|
|
20960
|
-
}
|
|
20961
|
-
return new Set(collected.flatMap((entry) => entry.externalReferences.filter((name) => declared.has(name))));
|
|
20962
|
-
}
|
|
20963
|
-
function ambientKeys(collected, context) {
|
|
20964
|
-
const keys = {};
|
|
20965
|
-
const { project, references, options } = context;
|
|
20966
|
-
const mode = context.development ? "development" : "release";
|
|
20967
|
-
const projectKey = `${optionsKey(options)}|${mode}|${JSON.stringify(project.globals)}|${[...references].sort().join(",")}`;
|
|
20968
|
-
for (const environment of ALL_ENVIRONMENTS) {
|
|
20969
|
-
const visible = collected.filter((entry) => canReference(environment, entry.environment));
|
|
20970
|
-
keys[environment] = hashString(`${projectKey}|${visible.map((entry) => `${entry.path}=${entry.fingerprint}`).join(";")}`);
|
|
20971
|
-
}
|
|
20972
|
-
return keys;
|
|
20973
|
-
}
|
|
20974
|
-
function declaresNothing(declarations) {
|
|
20975
|
-
const { classes, interfaces, enums, globals, events } = declarations;
|
|
20976
|
-
return classes.length === 0 && interfaces.length === 0 && enums.length === 0 && globals.length === 0 && events.length === 0;
|
|
20977
|
-
}
|
|
20978
|
-
function createAmbientResolver(collected) {
|
|
20979
|
-
const visible = /* @__PURE__ */ new Map();
|
|
20980
|
-
const merged = /* @__PURE__ */ new Map();
|
|
20981
|
-
for (const environment of ALL_ENVIRONMENTS) {
|
|
20982
|
-
const entries2 = collected.filter((entry) => canReference(environment, entry.environment) && !declaresNothing(entry.declarations));
|
|
20983
|
-
visible.set(environment, entries2);
|
|
20984
|
-
merged.set(environment, mergeAmbient(entries2.map((entry) => entry.declarations)));
|
|
20985
|
-
}
|
|
20986
|
-
return (entry) => {
|
|
20987
|
-
if (declaresNothing(entry.declarations)) {
|
|
20988
|
-
return merged.get(entry.environment) ?? EMPTY_AMBIENT;
|
|
20989
|
-
}
|
|
20990
|
-
const entries2 = visible.get(entry.environment) ?? [];
|
|
20991
|
-
return mergeAmbient(entries2.filter((candidate) => candidate.path !== entry.path).map((candidate) => candidate.declarations));
|
|
20992
|
-
};
|
|
20993
|
-
}
|
|
20994
22182
|
function compileModule(file, ambient, context) {
|
|
20995
22183
|
const result = compile(file.source, {
|
|
20996
22184
|
filePath: file.path,
|
|
20997
22185
|
...file.environment === void 0 ? {} : { environment: file.environment },
|
|
20998
22186
|
ambient,
|
|
22187
|
+
contracts: context.contracts,
|
|
20999
22188
|
project: context.project,
|
|
21000
22189
|
projectReferences: context.projectReferences,
|
|
21001
22190
|
compilerOptions: context.compilerOptions,
|
|
@@ -21009,7 +22198,7 @@ function compileModule(file, ambient, context) {
|
|
|
21009
22198
|
requiredHelpers: result.requiredHelpers,
|
|
21010
22199
|
declaredGlobals: result.declaredGlobals,
|
|
21011
22200
|
externalReferences: result.externalReferences,
|
|
21012
|
-
contributions: toContributions(result.directives, result.environment),
|
|
22201
|
+
contributions: toContributions(result.directives, result.environment, result.moduleGlobals),
|
|
21013
22202
|
diagnostics: result.diagnostics,
|
|
21014
22203
|
lines: result.lines,
|
|
21015
22204
|
topLevelReturn: result.topLevelReturn,
|
|
@@ -21050,20 +22239,22 @@ function createProjectCache() {
|
|
|
21050
22239
|
environment: result.environment,
|
|
21051
22240
|
declarations: result.declarations,
|
|
21052
22241
|
externalReferences: [...result.externalReferences.keys()],
|
|
21053
|
-
fingerprint: fingerprintDeclarations(result.declarations)
|
|
22242
|
+
fingerprint: fingerprintDeclarations(result.declarations),
|
|
22243
|
+
provides: fingerprintByName(result.declarations),
|
|
22244
|
+
requires: result.referencedNames
|
|
21054
22245
|
};
|
|
21055
22246
|
declarationCache.set(file.path, entry);
|
|
21056
22247
|
return entry;
|
|
21057
22248
|
}
|
|
21058
22249
|
function moduleFor(pair, context, reused) {
|
|
21059
22250
|
const { file, entry } = pair;
|
|
21060
|
-
const ambientKey = context.
|
|
22251
|
+
const ambientKey = context.scope.keyFor(entry);
|
|
21061
22252
|
const cached = moduleCache.get(file.path);
|
|
21062
22253
|
if (cached !== void 0 && cached.hash === entry.hash && cached.ambientKey === ambientKey) {
|
|
21063
22254
|
reused.count += 1;
|
|
21064
22255
|
return cached.module;
|
|
21065
22256
|
}
|
|
21066
|
-
const module = compileModule(file, context.resolve(entry), context);
|
|
22257
|
+
const module = compileModule(file, context.scope.resolve(entry), context);
|
|
21067
22258
|
moduleCache.set(file.path, { hash: entry.hash, ambientKey, module });
|
|
21068
22259
|
return module;
|
|
21069
22260
|
}
|
|
@@ -21077,9 +22268,10 @@ function createProjectCache() {
|
|
|
21077
22268
|
const collected = pairs.map((pair) => pair.entry);
|
|
21078
22269
|
const projectReferences = sharedEnums(collected);
|
|
21079
22270
|
const development = options.development === true;
|
|
22271
|
+
const contracts = options.contracts ?? [];
|
|
21080
22272
|
const context = {
|
|
21081
|
-
|
|
21082
|
-
|
|
22273
|
+
scope: createAmbientScope(collected, { project, references: projectReferences, options: compilerOptions, development, contracts }),
|
|
22274
|
+
contracts,
|
|
21083
22275
|
project,
|
|
21084
22276
|
projectReferences,
|
|
21085
22277
|
compilerOptions,
|
|
@@ -21159,8 +22351,8 @@ function materialize(build, resource, includeMap) {
|
|
|
21159
22351
|
const bundled = materializeBundles(resource, build.bundles, helperContent);
|
|
21160
22352
|
return { ...build, scripts: bundled.scripts, map: includeMap ? bundled.map : null };
|
|
21161
22353
|
}
|
|
21162
|
-
function diagnosticSources(files,
|
|
21163
|
-
const paths = new Set(
|
|
22354
|
+
function diagnosticSources(files, entries3) {
|
|
22355
|
+
const paths = new Set(entries3.map((entry) => entry.path));
|
|
21164
22356
|
return new Map(files.filter((file) => paths.has(file.path)).map((file) => [file.path, file.source]));
|
|
21165
22357
|
}
|
|
21166
22358
|
function runCompile(root, config, options = {}) {
|
|
@@ -21168,10 +22360,11 @@ function runCompile(root, config, options = {}) {
|
|
|
21168
22360
|
const tracker = options.tracker ?? createPhaseTracker();
|
|
21169
22361
|
const started = performance.now();
|
|
21170
22362
|
tracker.begin("discovery");
|
|
21171
|
-
const excluded = [config.outDir];
|
|
22363
|
+
const excluded = [config.outDir, config.contracts];
|
|
21172
22364
|
const sources = discoverSources(root, config.sources, excluded);
|
|
21173
22365
|
const inputs = readProjectInputs(root, { assets: config.assets, environment: config.environment, excluded });
|
|
21174
|
-
const
|
|
22366
|
+
const contracts = readDependencyContracts(root, config);
|
|
22367
|
+
const diagnostics = [...sources.diagnostics, ...inputs.diagnostics, ...contracts.diagnostics];
|
|
21175
22368
|
if (hasCliErrors(diagnostics)) {
|
|
21176
22369
|
tracker.end("failed");
|
|
21177
22370
|
return {
|
|
@@ -21184,13 +22377,15 @@ function runCompile(root, config, options = {}) {
|
|
|
21184
22377
|
environmentTemplate: null,
|
|
21185
22378
|
phases: tracker.durations(),
|
|
21186
22379
|
sources: /* @__PURE__ */ new Map(),
|
|
21187
|
-
map: null
|
|
22380
|
+
map: null,
|
|
22381
|
+
contract: null
|
|
21188
22382
|
};
|
|
21189
22383
|
}
|
|
21190
22384
|
tracker.begin("compile", sources.files.length);
|
|
21191
22385
|
const declarations = projectDeclarations(inputs.declared?.entries ?? null, config.environment.file);
|
|
21192
22386
|
const project = cache3.compile(sources.files, {
|
|
21193
22387
|
project: declarations,
|
|
22388
|
+
contracts: contracts.contracts,
|
|
21194
22389
|
compilerOptions: config.compilerOptions,
|
|
21195
22390
|
development: options.development === true,
|
|
21196
22391
|
onProgress: (event) => tracker.advance(event.item, event.index, event.total)
|
|
@@ -21217,13 +22412,14 @@ function runCompile(root, config, options = {}) {
|
|
|
21217
22412
|
environmentTemplate: inputs.deployed === null ? null : renderEnvironmentTemplate(inputs.deployed),
|
|
21218
22413
|
phases: tracker.durations(),
|
|
21219
22414
|
sources: diagnosticSources(sources.files, assembly.diagnostics),
|
|
21220
|
-
map: build?.map ?? null
|
|
22415
|
+
map: build?.map ?? null,
|
|
22416
|
+
contract: build === null ? null : buildResourceAbi(config.name, project.modules.flatMap((module) => module.contributions))
|
|
21221
22417
|
};
|
|
21222
22418
|
}
|
|
21223
22419
|
|
|
21224
22420
|
// src/build/resource-map-file.ts
|
|
21225
|
-
import { existsSync as existsSync5, mkdirSync as
|
|
21226
|
-
import { dirname as
|
|
22421
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync7, readdirSync as readdirSync2, unlinkSync, writeFileSync as writeFileSync4 } from "node:fs";
|
|
22422
|
+
import { dirname as dirname3, isAbsolute as isAbsolute3, join as join4, posix, relative as relative2, resolve as resolve9 } from "node:path";
|
|
21227
22423
|
var RESOURCE_MAP_SUFFIX = ".luam-map.json";
|
|
21228
22424
|
function isObject(value) {
|
|
21229
22425
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -21288,10 +22484,10 @@ function reason(error) {
|
|
|
21288
22484
|
return error instanceof Error ? error.message : String(error);
|
|
21289
22485
|
}
|
|
21290
22486
|
function resourceMapPath(root, config) {
|
|
21291
|
-
return
|
|
22487
|
+
return resolve9(root, config.outDir, `${config.name}${RESOURCE_MAP_SUFFIX}`);
|
|
21292
22488
|
}
|
|
21293
22489
|
function explicitResourceMapPath(root, path) {
|
|
21294
|
-
return isAbsolute3(path) ? path :
|
|
22490
|
+
return isAbsolute3(path) ? path : resolve9(root, path);
|
|
21295
22491
|
}
|
|
21296
22492
|
function writeResourceMap(root, config, map) {
|
|
21297
22493
|
const path = resourceMapPath(root, config);
|
|
@@ -21301,15 +22497,15 @@ function writeResourceMap(root, config, map) {
|
|
|
21301
22497
|
}
|
|
21302
22498
|
return;
|
|
21303
22499
|
}
|
|
21304
|
-
|
|
22500
|
+
mkdirSync3(dirname3(path), { recursive: true });
|
|
21305
22501
|
const content = serializeResourceMap(map);
|
|
21306
|
-
if (!existsSync5(path) ||
|
|
21307
|
-
|
|
22502
|
+
if (!existsSync5(path) || readFileSync7(path, "utf8") !== content) {
|
|
22503
|
+
writeFileSync4(path, content, "utf8");
|
|
21308
22504
|
}
|
|
21309
22505
|
}
|
|
21310
22506
|
function readResourceMap(path) {
|
|
21311
22507
|
try {
|
|
21312
|
-
const value = JSON.parse(
|
|
22508
|
+
const value = JSON.parse(readFileSync7(path, "utf8"));
|
|
21313
22509
|
if (isObject(value) && Number.isInteger(value.version) && value.version !== RESOURCE_MAP_VERSION) {
|
|
21314
22510
|
return { map: null, error: `Resource map version ${String(value.version)} is not supported.` };
|
|
21315
22511
|
}
|
|
@@ -21323,13 +22519,13 @@ function isSearchable(name) {
|
|
|
21323
22519
|
return !name.startsWith(".") && !SKIPPED_DIRECTORIES.has(name);
|
|
21324
22520
|
}
|
|
21325
22521
|
function collectResourceMaps(directory, found) {
|
|
21326
|
-
let
|
|
22522
|
+
let entries3;
|
|
21327
22523
|
try {
|
|
21328
|
-
|
|
22524
|
+
entries3 = readdirSync2(directory, { withFileTypes: true });
|
|
21329
22525
|
} catch {
|
|
21330
22526
|
return;
|
|
21331
22527
|
}
|
|
21332
|
-
for (const entry of
|
|
22528
|
+
for (const entry of entries3) {
|
|
21333
22529
|
const absolute = join4(directory, entry.name);
|
|
21334
22530
|
if (entry.isDirectory()) {
|
|
21335
22531
|
if (isSearchable(entry.name)) {
|
|
@@ -21353,8 +22549,8 @@ function discoverResourceMap(root) {
|
|
|
21353
22549
|
}
|
|
21354
22550
|
|
|
21355
22551
|
// src/build/resource-writer.ts
|
|
21356
|
-
import { existsSync as existsSync7, mkdirSync as
|
|
21357
|
-
import { dirname as
|
|
22552
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
22553
|
+
import { dirname as dirname4, isAbsolute as isAbsolute4, relative as relative4, resolve as resolve11 } from "node:path";
|
|
21358
22554
|
|
|
21359
22555
|
// src/build/lua-tokens.ts
|
|
21360
22556
|
var WHITESPACE = /* @__PURE__ */ new Set([" ", " ", "\n", "\r", "\v", "\f"]);
|
|
@@ -21411,7 +22607,7 @@ function readLongBracket2(source, start, level, file) {
|
|
|
21411
22607
|
return end + closing.length;
|
|
21412
22608
|
}
|
|
21413
22609
|
function readShortString(source, start, file) {
|
|
21414
|
-
const
|
|
22610
|
+
const quote3 = source[start];
|
|
21415
22611
|
let index = start + 1;
|
|
21416
22612
|
while (index < source.length) {
|
|
21417
22613
|
const char = source[index];
|
|
@@ -21419,7 +22615,7 @@ function readShortString(source, start, file) {
|
|
|
21419
22615
|
index += 2;
|
|
21420
22616
|
continue;
|
|
21421
22617
|
}
|
|
21422
|
-
if (char ===
|
|
22618
|
+
if (char === quote3) {
|
|
21423
22619
|
return index + 1;
|
|
21424
22620
|
}
|
|
21425
22621
|
if (char === "\n") {
|
|
@@ -21558,7 +22754,7 @@ function minifyLuaFiles(files) {
|
|
|
21558
22754
|
|
|
21559
22755
|
// src/build/resource-prune.ts
|
|
21560
22756
|
import { existsSync as existsSync6, readdirSync as readdirSync3, rmdirSync, unlinkSync as unlinkSync2 } from "node:fs";
|
|
21561
|
-
import { join as join5, relative as relative3, resolve as
|
|
22757
|
+
import { join as join5, relative as relative3, resolve as resolve10 } from "node:path";
|
|
21562
22758
|
var GENERATED_MANIFEST = "meta.xml";
|
|
21563
22759
|
var GENERATED_EXTENSION = ".lua";
|
|
21564
22760
|
var PROTECTED = /* @__PURE__ */ new Set([ENVIRONMENT_FILE, ".env.local"]);
|
|
@@ -21575,14 +22771,14 @@ function isGenerated(relativePath2, options) {
|
|
|
21575
22771
|
return options.generatedRoots.some((root) => isUnderRoot(relativePath2, root));
|
|
21576
22772
|
}
|
|
21577
22773
|
function removeEmptyDirectories(targetDir, directory) {
|
|
21578
|
-
if (
|
|
22774
|
+
if (resolve10(directory) === resolve10(targetDir)) {
|
|
21579
22775
|
return;
|
|
21580
22776
|
}
|
|
21581
22777
|
if (readdirSync3(directory).length > 0) {
|
|
21582
22778
|
return;
|
|
21583
22779
|
}
|
|
21584
22780
|
rmdirSync(directory);
|
|
21585
|
-
removeEmptyDirectories(targetDir,
|
|
22781
|
+
removeEmptyDirectories(targetDir, resolve10(directory, ".."));
|
|
21586
22782
|
}
|
|
21587
22783
|
function pruneResource(targetDir, keep, options) {
|
|
21588
22784
|
if (!existsSync6(targetDir)) {
|
|
@@ -21635,7 +22831,7 @@ function resourceFiles(build) {
|
|
|
21635
22831
|
return files;
|
|
21636
22832
|
}
|
|
21637
22833
|
function resolveInside(targetDir, path) {
|
|
21638
|
-
const absolute =
|
|
22834
|
+
const absolute = resolve11(targetDir, path);
|
|
21639
22835
|
const inside = relative4(targetDir, absolute);
|
|
21640
22836
|
if (inside.startsWith("..") || isAbsolute4(inside)) {
|
|
21641
22837
|
throw new Error(`The generated file "${path}" resolves outside the resource directory "${targetDir}".`);
|
|
@@ -21643,11 +22839,11 @@ function resolveInside(targetDir, path) {
|
|
|
21643
22839
|
return absolute;
|
|
21644
22840
|
}
|
|
21645
22841
|
function writeIfChanged(absolute, content) {
|
|
21646
|
-
if (existsSync7(absolute) &&
|
|
22842
|
+
if (existsSync7(absolute) && readFileSync8(absolute).equals(content)) {
|
|
21647
22843
|
return false;
|
|
21648
22844
|
}
|
|
21649
|
-
|
|
21650
|
-
|
|
22845
|
+
mkdirSync4(dirname4(absolute), { recursive: true });
|
|
22846
|
+
writeFileSync5(absolute, content);
|
|
21651
22847
|
return true;
|
|
21652
22848
|
}
|
|
21653
22849
|
function writeEnvironmentFile(targetDir, template) {
|
|
@@ -21658,8 +22854,8 @@ function writeEnvironmentFile(targetDir, template) {
|
|
|
21658
22854
|
if (existsSync7(absolute)) {
|
|
21659
22855
|
return false;
|
|
21660
22856
|
}
|
|
21661
|
-
|
|
21662
|
-
|
|
22857
|
+
mkdirSync4(dirname4(absolute), { recursive: true });
|
|
22858
|
+
writeFileSync5(absolute, template, "utf8");
|
|
21663
22859
|
return true;
|
|
21664
22860
|
}
|
|
21665
22861
|
function writeResource(targetDir, build, options) {
|
|
@@ -21683,7 +22879,7 @@ function writeResource(targetDir, build, options) {
|
|
|
21683
22879
|
advance(path);
|
|
21684
22880
|
}
|
|
21685
22881
|
for (const asset of build.assets) {
|
|
21686
|
-
if (writeIfChanged(resolveInside(targetDir, asset.path),
|
|
22882
|
+
if (writeIfChanged(resolveInside(targetDir, asset.path), readFileSync8(resolve11(options.root, asset.source)))) {
|
|
21687
22883
|
written.push(asset.path);
|
|
21688
22884
|
advance(asset.path);
|
|
21689
22885
|
continue;
|
|
@@ -21800,16 +22996,16 @@ function reportBuildOutcome(context, outcome, action) {
|
|
|
21800
22996
|
}
|
|
21801
22997
|
|
|
21802
22998
|
// src/commands/resource-targets.ts
|
|
21803
|
-
import { isAbsolute as isAbsolute5, resolve as
|
|
22999
|
+
import { isAbsolute as isAbsolute5, resolve as resolve12 } from "node:path";
|
|
21804
23000
|
function resolveBuildTarget(root, config) {
|
|
21805
|
-
return
|
|
23001
|
+
return resolve12(root, config.outDir, config.name);
|
|
21806
23002
|
}
|
|
21807
23003
|
function resolveServerTarget(root, config) {
|
|
21808
23004
|
if (config.serverPath === null) {
|
|
21809
23005
|
return null;
|
|
21810
23006
|
}
|
|
21811
|
-
const serverRoot = isAbsolute5(config.serverPath) ? config.serverPath :
|
|
21812
|
-
return
|
|
23007
|
+
const serverRoot = isAbsolute5(config.serverPath) ? config.serverPath : resolve12(root, config.serverPath);
|
|
23008
|
+
return resolve12(serverRoot, config.resourcesDir, config.name);
|
|
21813
23009
|
}
|
|
21814
23010
|
|
|
21815
23011
|
// src/reporting/progress-renderer.ts
|
|
@@ -21854,7 +23050,7 @@ function createProgressRenderer(reporter, options = {}) {
|
|
|
21854
23050
|
paint(reporter.style.eraseLine());
|
|
21855
23051
|
dirty = false;
|
|
21856
23052
|
}
|
|
21857
|
-
function
|
|
23053
|
+
function render2(event) {
|
|
21858
23054
|
const columns = Math.max(20, reporter.capability.columns - 1);
|
|
21859
23055
|
const line2 = activeLine(reporter, event);
|
|
21860
23056
|
paint(`${reporter.style.eraseLine()}${line2.slice(0, columns)}`);
|
|
@@ -21874,7 +23070,7 @@ function createProgressRenderer(reporter, options = {}) {
|
|
|
21874
23070
|
if (frames > 0 && now() - lastPaintAt < throttleMs) {
|
|
21875
23071
|
return;
|
|
21876
23072
|
}
|
|
21877
|
-
|
|
23073
|
+
render2(event);
|
|
21878
23074
|
}
|
|
21879
23075
|
return {
|
|
21880
23076
|
listen: (event) => {
|
|
@@ -21932,6 +23128,9 @@ async function runBuildCommand(context, options = {}) {
|
|
|
21932
23128
|
return EXIT_DIAGNOSTICS;
|
|
21933
23129
|
}
|
|
21934
23130
|
writeResourceMap(context.root, context.config, writtenMap(outcome.map, minify));
|
|
23131
|
+
if (outcome.contract !== null && outcome.contract.exports.length > 0) {
|
|
23132
|
+
writeResourceContract(context.root, context.config, outcome.contract);
|
|
23133
|
+
}
|
|
21935
23134
|
tracker.end();
|
|
21936
23135
|
renderer.clear();
|
|
21937
23136
|
reportBuildOutcome(context, outcome, "Build");
|
|
@@ -21982,6 +23181,298 @@ function registerCheckCommand(program2, runtime) {
|
|
|
21982
23181
|
});
|
|
21983
23182
|
}
|
|
21984
23183
|
|
|
23184
|
+
// src/commands/config-command.ts
|
|
23185
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "node:fs";
|
|
23186
|
+
import { dirname as dirname5, relative as relative5, resolve as resolve13 } from "node:path";
|
|
23187
|
+
|
|
23188
|
+
// ../compiler/src/project/config-reader.ts
|
|
23189
|
+
var Reader = class {
|
|
23190
|
+
text;
|
|
23191
|
+
index = 0;
|
|
23192
|
+
problems = [];
|
|
23193
|
+
constructor(text) {
|
|
23194
|
+
this.text = text;
|
|
23195
|
+
}
|
|
23196
|
+
position() {
|
|
23197
|
+
const before = this.text.slice(0, this.index);
|
|
23198
|
+
const lines = before.split("\n");
|
|
23199
|
+
return { line: lines.length, column: (lines[lines.length - 1]?.length ?? 0) + 1 };
|
|
23200
|
+
}
|
|
23201
|
+
fail(message) {
|
|
23202
|
+
const { line: line2, column } = this.position();
|
|
23203
|
+
this.problems.push({ line: line2, column, message });
|
|
23204
|
+
return null;
|
|
23205
|
+
}
|
|
23206
|
+
skipComment() {
|
|
23207
|
+
if (!this.text.startsWith("--", this.index)) {
|
|
23208
|
+
return false;
|
|
23209
|
+
}
|
|
23210
|
+
if (this.text.startsWith("--[[", this.index)) {
|
|
23211
|
+
const end2 = this.text.indexOf("]]", this.index + 4);
|
|
23212
|
+
this.index = end2 === -1 ? this.text.length : end2 + 2;
|
|
23213
|
+
return true;
|
|
23214
|
+
}
|
|
23215
|
+
const end = this.text.indexOf("\n", this.index);
|
|
23216
|
+
this.index = end === -1 ? this.text.length : end + 1;
|
|
23217
|
+
return true;
|
|
23218
|
+
}
|
|
23219
|
+
skip() {
|
|
23220
|
+
for (; ; ) {
|
|
23221
|
+
const before = this.index;
|
|
23222
|
+
while (this.index < this.text.length && /\s/.test(this.text[this.index] ?? "")) {
|
|
23223
|
+
this.index += 1;
|
|
23224
|
+
}
|
|
23225
|
+
this.skipComment();
|
|
23226
|
+
if (this.index === before) {
|
|
23227
|
+
return;
|
|
23228
|
+
}
|
|
23229
|
+
}
|
|
23230
|
+
}
|
|
23231
|
+
done() {
|
|
23232
|
+
this.skip();
|
|
23233
|
+
return this.index >= this.text.length;
|
|
23234
|
+
}
|
|
23235
|
+
peek() {
|
|
23236
|
+
this.skip();
|
|
23237
|
+
return this.text[this.index] ?? "";
|
|
23238
|
+
}
|
|
23239
|
+
take(value) {
|
|
23240
|
+
this.skip();
|
|
23241
|
+
if (!this.text.startsWith(value, this.index)) {
|
|
23242
|
+
return false;
|
|
23243
|
+
}
|
|
23244
|
+
this.index += value.length;
|
|
23245
|
+
return true;
|
|
23246
|
+
}
|
|
23247
|
+
word() {
|
|
23248
|
+
this.skip();
|
|
23249
|
+
const match = /^[A-Za-z_][A-Za-z0-9_]*/.exec(this.text.slice(this.index));
|
|
23250
|
+
if (match === null) {
|
|
23251
|
+
return null;
|
|
23252
|
+
}
|
|
23253
|
+
this.index += match[0].length;
|
|
23254
|
+
return match[0];
|
|
23255
|
+
}
|
|
23256
|
+
literal() {
|
|
23257
|
+
this.skip();
|
|
23258
|
+
const rest = this.text.slice(this.index);
|
|
23259
|
+
const string = /^(\[\[[\s\S]*?\]\]|'(?:\\.|[^'\\])*'|"(?:\\.|[^"\\])*")/.exec(rest);
|
|
23260
|
+
if (string !== null) {
|
|
23261
|
+
this.index += string[0].length;
|
|
23262
|
+
return "string";
|
|
23263
|
+
}
|
|
23264
|
+
const number = /^-?(?:0[xX][0-9a-fA-F]+|\d+\.?\d*(?:[eE][-+]?\d+)?|\.\d+)/.exec(rest);
|
|
23265
|
+
if (number !== null) {
|
|
23266
|
+
this.index += number[0].length;
|
|
23267
|
+
return "number";
|
|
23268
|
+
}
|
|
23269
|
+
const word = /^(true|false|nil)\b/.exec(rest);
|
|
23270
|
+
if (word === null) {
|
|
23271
|
+
return null;
|
|
23272
|
+
}
|
|
23273
|
+
this.index += word[0].length;
|
|
23274
|
+
return word[0] === "nil" ? "nil" : "boolean";
|
|
23275
|
+
}
|
|
23276
|
+
key() {
|
|
23277
|
+
this.skip();
|
|
23278
|
+
if (this.take("[")) {
|
|
23279
|
+
const rest = this.text.slice(this.index);
|
|
23280
|
+
const quoted2 = /^'([A-Za-z_][A-Za-z0-9_]*)'|^"([A-Za-z_][A-Za-z0-9_]*)"/.exec(rest);
|
|
23281
|
+
if (quoted2 === null) {
|
|
23282
|
+
return null;
|
|
23283
|
+
}
|
|
23284
|
+
this.index += quoted2[0].length;
|
|
23285
|
+
return this.take("]") && this.take("=") ? quoted2[1] ?? quoted2[2] ?? null : null;
|
|
23286
|
+
}
|
|
23287
|
+
const start = this.index;
|
|
23288
|
+
const word = this.word();
|
|
23289
|
+
if (word === null || !this.take("=") || this.peek() === "=") {
|
|
23290
|
+
this.index = start;
|
|
23291
|
+
return null;
|
|
23292
|
+
}
|
|
23293
|
+
return word;
|
|
23294
|
+
}
|
|
23295
|
+
};
|
|
23296
|
+
|
|
23297
|
+
// ../compiler/src/project/config-extractor.ts
|
|
23298
|
+
var GENERATED_MARKER = '# Generated by "luam config" from';
|
|
23299
|
+
var MAX_SOURCE_BYTES = 262144;
|
|
23300
|
+
var MAX_DEPTH2 = 8;
|
|
23301
|
+
var MAX_ENTRIES = 512;
|
|
23302
|
+
function renderRecord(fields) {
|
|
23303
|
+
return `{ ${fields.map(([name, type]) => `${name}: ${type}`).join(", ")} }`;
|
|
23304
|
+
}
|
|
23305
|
+
function render(value) {
|
|
23306
|
+
if (value.kind === "literal") {
|
|
23307
|
+
return value.type === "nil" ? "any" : value.type;
|
|
23308
|
+
}
|
|
23309
|
+
if (value.kind === "array") {
|
|
23310
|
+
return `${value.element}[]`;
|
|
23311
|
+
}
|
|
23312
|
+
return value.kind === "record" && value.fields.length > 0 ? renderRecord(value.fields) : "table";
|
|
23313
|
+
}
|
|
23314
|
+
function unify2(types) {
|
|
23315
|
+
const unique = [...new Set(types)];
|
|
23316
|
+
return unique.length === 1 ? unique[0] ?? "any" : "any";
|
|
23317
|
+
}
|
|
23318
|
+
function readTable2(reader, depth) {
|
|
23319
|
+
if (depth > MAX_DEPTH2) {
|
|
23320
|
+
return reader.fail(`A table nested deeper than ${MAX_DEPTH2} levels is not extracted.`);
|
|
23321
|
+
}
|
|
23322
|
+
const fields = [];
|
|
23323
|
+
const elements = [];
|
|
23324
|
+
for (let count = 0; ; count += 1) {
|
|
23325
|
+
if (reader.take("}")) {
|
|
23326
|
+
break;
|
|
23327
|
+
}
|
|
23328
|
+
if (count > MAX_ENTRIES) {
|
|
23329
|
+
return reader.fail(`A table with more than ${MAX_ENTRIES} entries is not extracted.`);
|
|
23330
|
+
}
|
|
23331
|
+
const key = reader.key();
|
|
23332
|
+
const value = readValue(reader, depth + 1);
|
|
23333
|
+
if (value === null) {
|
|
23334
|
+
return null;
|
|
23335
|
+
}
|
|
23336
|
+
if (key === null) {
|
|
23337
|
+
elements.push(render(value));
|
|
23338
|
+
} else {
|
|
23339
|
+
fields.push([key, render(value)]);
|
|
23340
|
+
}
|
|
23341
|
+
if (!reader.take(",") && !reader.take(";") && reader.peek() !== "}") {
|
|
23342
|
+
return reader.fail('Expected "," or "}" in a table.');
|
|
23343
|
+
}
|
|
23344
|
+
}
|
|
23345
|
+
if (fields.length > 0 && elements.length > 0) {
|
|
23346
|
+
return { kind: "table" };
|
|
23347
|
+
}
|
|
23348
|
+
return elements.length > 0 ? { kind: "array", element: unify2(elements) } : { kind: "record", fields };
|
|
23349
|
+
}
|
|
23350
|
+
function readValue(reader, depth) {
|
|
23351
|
+
if (reader.take("{")) {
|
|
23352
|
+
return readTable2(reader, depth);
|
|
23353
|
+
}
|
|
23354
|
+
const literal2 = reader.literal();
|
|
23355
|
+
if (literal2 === null) {
|
|
23356
|
+
return reader.fail("Only literals and table constructors are extracted. Declare this value by hand.");
|
|
23357
|
+
}
|
|
23358
|
+
if (reader.peek() === "." || reader.peek() === "(" || reader.peek() === "+") {
|
|
23359
|
+
return reader.fail("An expression is not extracted. Declare this value by hand.");
|
|
23360
|
+
}
|
|
23361
|
+
return { kind: "literal", type: literal2 };
|
|
23362
|
+
}
|
|
23363
|
+
function extractConfigDeclarations(source) {
|
|
23364
|
+
if (source.length > MAX_SOURCE_BYTES) {
|
|
23365
|
+
return { declarations: [], problems: [{ line: 1, column: 1, message: `A file larger than ${MAX_SOURCE_BYTES} bytes is not extracted.` }] };
|
|
23366
|
+
}
|
|
23367
|
+
const reader = new Reader(source);
|
|
23368
|
+
const declarations = /* @__PURE__ */ new Map();
|
|
23369
|
+
while (!reader.done()) {
|
|
23370
|
+
reader.take("local");
|
|
23371
|
+
const name = reader.word();
|
|
23372
|
+
if (name === null || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(name) || !reader.take("=")) {
|
|
23373
|
+
reader.fail("Only a top-level assignment of a literal or a table is extracted.");
|
|
23374
|
+
break;
|
|
23375
|
+
}
|
|
23376
|
+
const value = readValue(reader, 1);
|
|
23377
|
+
if (value === null) {
|
|
23378
|
+
break;
|
|
23379
|
+
}
|
|
23380
|
+
declarations.set(name, render(value));
|
|
23381
|
+
reader.take(";");
|
|
23382
|
+
}
|
|
23383
|
+
return { declarations: [...declarations].map(([name, type]) => ({ name, type })), problems: reader.problems };
|
|
23384
|
+
}
|
|
23385
|
+
function renderConfigDeclarations(source, declarations) {
|
|
23386
|
+
const lines = declarations.map((declaration) => `declare ${declaration.name}: ${declaration.type}`);
|
|
23387
|
+
return `${GENERATED_MARKER} "${source}". Do not edit.
|
|
23388
|
+
|
|
23389
|
+
${lines.join("\n")}
|
|
23390
|
+
`;
|
|
23391
|
+
}
|
|
23392
|
+
function isGeneratedDeclaration(text) {
|
|
23393
|
+
return text.startsWith(GENERATED_MARKER);
|
|
23394
|
+
}
|
|
23395
|
+
|
|
23396
|
+
// src/commands/config-command.ts
|
|
23397
|
+
var DEFAULT_CONFIG_SOURCE = "config.lua";
|
|
23398
|
+
var DEFAULT_CONFIG_OUTPUT = "config.d.luam";
|
|
23399
|
+
function escapes(root, path) {
|
|
23400
|
+
const inside = relative5(root, path);
|
|
23401
|
+
return inside.startsWith("..") || resolve13(root, inside) !== path;
|
|
23402
|
+
}
|
|
23403
|
+
function readSource2(root, source, reporter) {
|
|
23404
|
+
const path = resolve13(root, source);
|
|
23405
|
+
if (escapes(root, path)) {
|
|
23406
|
+
reporter.error(`Config declarations read only inside the project, but "${source}" points outside it.`);
|
|
23407
|
+
return null;
|
|
23408
|
+
}
|
|
23409
|
+
if (!existsSync8(path)) {
|
|
23410
|
+
reporter.error(`Config declarations found no file at "${source}".`);
|
|
23411
|
+
return null;
|
|
23412
|
+
}
|
|
23413
|
+
try {
|
|
23414
|
+
return readFileSync9(path, "utf8");
|
|
23415
|
+
} catch {
|
|
23416
|
+
reporter.error(`Config declarations could not read "${source}".`);
|
|
23417
|
+
return null;
|
|
23418
|
+
}
|
|
23419
|
+
}
|
|
23420
|
+
function writable(root, out, reporter) {
|
|
23421
|
+
const path = resolve13(root, out);
|
|
23422
|
+
if (escapes(root, path)) {
|
|
23423
|
+
reporter.error(`Config declarations write only inside the project, but "${out}" points outside it.`);
|
|
23424
|
+
return null;
|
|
23425
|
+
}
|
|
23426
|
+
if (existsSync8(path) && !isGeneratedDeclaration(readFileSync9(path, "utf8"))) {
|
|
23427
|
+
reporter.error(`"${out}" was not generated by this command and was left alone. Choose another path with --out.`);
|
|
23428
|
+
return null;
|
|
23429
|
+
}
|
|
23430
|
+
return path;
|
|
23431
|
+
}
|
|
23432
|
+
function runConfigCommand(root, reporter, options = {}) {
|
|
23433
|
+
const source = options.source ?? DEFAULT_CONFIG_SOURCE;
|
|
23434
|
+
const out = options.out ?? DEFAULT_CONFIG_OUTPUT;
|
|
23435
|
+
const text = readSource2(root, source, reporter);
|
|
23436
|
+
if (text === null) {
|
|
23437
|
+
return EXIT_DIAGNOSTICS;
|
|
23438
|
+
}
|
|
23439
|
+
const extracted = extractConfigDeclarations(text);
|
|
23440
|
+
for (const problem of extracted.problems) {
|
|
23441
|
+
reporter.warn(`${source}:${problem.line}:${problem.column} ${problem.message}`);
|
|
23442
|
+
}
|
|
23443
|
+
if (extracted.declarations.length === 0) {
|
|
23444
|
+
reporter.error(`Config declarations found nothing to declare in "${source}".`);
|
|
23445
|
+
return EXIT_DIAGNOSTICS;
|
|
23446
|
+
}
|
|
23447
|
+
const rendered = renderConfigDeclarations(source, extracted.declarations);
|
|
23448
|
+
if (options.write !== true) {
|
|
23449
|
+
reporter.info(rendered.trimEnd());
|
|
23450
|
+
reporter.info(`Config declarations would write ${extracted.declarations.length} name(s) to "${out}". Pass --write to save it.`);
|
|
23451
|
+
return EXIT_OK;
|
|
23452
|
+
}
|
|
23453
|
+
const path = writable(root, out, reporter);
|
|
23454
|
+
if (path === null) {
|
|
23455
|
+
return EXIT_DIAGNOSTICS;
|
|
23456
|
+
}
|
|
23457
|
+
mkdirSync5(dirname5(path), { recursive: true });
|
|
23458
|
+
writeFileSync6(path, rendered, "utf8");
|
|
23459
|
+
reporter.info(`Config declarations wrote ${extracted.declarations.length} name(s) to "${out}".`);
|
|
23460
|
+
return EXIT_OK;
|
|
23461
|
+
}
|
|
23462
|
+
|
|
23463
|
+
// src/cli/registry/config-registration.ts
|
|
23464
|
+
function registerConfigCommand(program2, runtime) {
|
|
23465
|
+
const command = program2.command("config").description('Derive a declaration file from the literal data in a native "config.lua".');
|
|
23466
|
+
addProjectOptions(command).addOption(valueOption("--source <path>", 'Native Lua file to read. Defaults to "config.lua".')).addOption(valueOption("--out <path>", 'Declaration file to write. Defaults to "config.d.luam".')).addOption(new Option("--write", "Write the declaration file instead of printing it."));
|
|
23467
|
+
command.action((options) => {
|
|
23468
|
+
runtime.exitCode = runConfigCommand(commandRoot(runtime, options), runtime.reporter, {
|
|
23469
|
+
source: options.source ?? null,
|
|
23470
|
+
out: options.out ?? null,
|
|
23471
|
+
write: options.write === true
|
|
23472
|
+
});
|
|
23473
|
+
});
|
|
23474
|
+
}
|
|
23475
|
+
|
|
21985
23476
|
// src/commands/ensure-runner.ts
|
|
21986
23477
|
function hasChanges(result) {
|
|
21987
23478
|
return result.written.length > 0 || result.removed.length > 0;
|
|
@@ -22006,7 +23497,7 @@ function restartResource(scope, serverConsole) {
|
|
|
22006
23497
|
reporter.success(`Restarted "${scope.context.config.name}" through the owned server console.`);
|
|
22007
23498
|
return true;
|
|
22008
23499
|
}
|
|
22009
|
-
function
|
|
23500
|
+
function failed2(outcome) {
|
|
22010
23501
|
return { ok: false, outcome, sync: null, restarted: false };
|
|
22011
23502
|
}
|
|
22012
23503
|
async function runOnce(scope, target, cache3, options) {
|
|
@@ -22029,7 +23520,7 @@ async function runOnce(scope, target, cache3, options) {
|
|
|
22029
23520
|
if (outcome.build === null) {
|
|
22030
23521
|
reportBuildOutcome(context, outcome, "Build");
|
|
22031
23522
|
reporter.warn("Skipping sync and restart because the build reported errors.");
|
|
22032
|
-
return
|
|
23523
|
+
return failed2(outcome);
|
|
22033
23524
|
}
|
|
22034
23525
|
const writeOptions = trackedWriteOptions(context.root, context.config, outcome.environmentTemplate, tracker);
|
|
22035
23526
|
reportBuildOutcome(context, outcome, "Build");
|
|
@@ -22077,8 +23568,8 @@ function reportRebuildSeparator(reporter, at = /* @__PURE__ */ new Date()) {
|
|
|
22077
23568
|
}
|
|
22078
23569
|
|
|
22079
23570
|
// src/watch/source-watcher.ts
|
|
22080
|
-
import { existsSync as
|
|
22081
|
-
import { relative as
|
|
23571
|
+
import { existsSync as existsSync9, watch } from "node:fs";
|
|
23572
|
+
import { relative as relative6, resolve as resolve14 } from "node:path";
|
|
22082
23573
|
var DEFAULT_DEBOUNCE_MS = 120;
|
|
22083
23574
|
function watchSources(root, sources, onChange, debounceMs = DEFAULT_DEBOUNCE_MS) {
|
|
22084
23575
|
const resolver = createSourceResolver(sources);
|
|
@@ -22094,12 +23585,12 @@ function watchSources(root, sources, onChange, debounceMs = DEFAULT_DEBOUNCE_MS)
|
|
|
22094
23585
|
}, debounceMs);
|
|
22095
23586
|
};
|
|
22096
23587
|
const isWatched = (directory, filename) => {
|
|
22097
|
-
const path = normalizePattern(
|
|
23588
|
+
const path = normalizePattern(relative6(root, resolve14(directory, filename)));
|
|
22098
23589
|
return path.endsWith(SOURCE_EXTENSION) && resolver.resolve(path).matches.length > 0;
|
|
22099
23590
|
};
|
|
22100
23591
|
for (const entry of resolver.roots) {
|
|
22101
|
-
const absolute =
|
|
22102
|
-
if (!
|
|
23592
|
+
const absolute = resolve14(root, entry);
|
|
23593
|
+
if (!existsSync9(absolute)) {
|
|
22103
23594
|
continue;
|
|
22104
23595
|
}
|
|
22105
23596
|
watchers.push(
|
|
@@ -22306,12 +23797,12 @@ function parseMtaLogLine(line2, activeResource, fallback = /* @__PURE__ */ new D
|
|
|
22306
23797
|
}
|
|
22307
23798
|
|
|
22308
23799
|
// src/logging/server-log-follower.ts
|
|
22309
|
-
import { closeSync, existsSync as
|
|
22310
|
-
import { isAbsolute as isAbsolute6, resolve as
|
|
23800
|
+
import { closeSync, existsSync as existsSync10, openSync, readSync, statSync as statSync4 } from "node:fs";
|
|
23801
|
+
import { isAbsolute as isAbsolute6, resolve as resolve15 } from "node:path";
|
|
22311
23802
|
var DEFAULT_POLL_INTERVAL_MS = 100;
|
|
22312
23803
|
function resolveServerLogPath(root, serverPath) {
|
|
22313
|
-
const server = isAbsolute6(serverPath) ? serverPath :
|
|
22314
|
-
return
|
|
23804
|
+
const server = isAbsolute6(serverPath) ? serverPath : resolve15(root, serverPath);
|
|
23805
|
+
return resolve15(server, "mods/deathmatch/logs/server.log");
|
|
22315
23806
|
}
|
|
22316
23807
|
function identity(path) {
|
|
22317
23808
|
const stats = statSync4(path);
|
|
@@ -22336,7 +23827,7 @@ function followServerLog(path, onLine, options = {}) {
|
|
|
22336
23827
|
let initialized = false;
|
|
22337
23828
|
let closed = false;
|
|
22338
23829
|
const poll = () => {
|
|
22339
|
-
if (closed || !
|
|
23830
|
+
if (closed || !existsSync10(path)) {
|
|
22340
23831
|
return;
|
|
22341
23832
|
}
|
|
22342
23833
|
try {
|
|
@@ -22452,9 +23943,9 @@ function connectServerConsoleInput(input, output, interrupt) {
|
|
|
22452
23943
|
|
|
22453
23944
|
// src/server/server-executable-resolver.ts
|
|
22454
23945
|
import { realpathSync, statSync as statSync5 } from "node:fs";
|
|
22455
|
-
import { isAbsolute as isAbsolute7, relative as
|
|
23946
|
+
import { isAbsolute as isAbsolute7, relative as relative7, resolve as resolve16 } from "node:path";
|
|
22456
23947
|
function isInside(root, path) {
|
|
22457
|
-
const pathFromRoot =
|
|
23948
|
+
const pathFromRoot = relative7(root, path);
|
|
22458
23949
|
return pathFromRoot.length === 0 || !pathFromRoot.startsWith("..") && !isAbsolute7(pathFromRoot);
|
|
22459
23950
|
}
|
|
22460
23951
|
function candidates2(platform) {
|
|
@@ -22467,14 +23958,14 @@ function candidates2(platform) {
|
|
|
22467
23958
|
throw new Error(`Local MTA server startup is not supported on platform "${platform}". Use Windows or Linux.`);
|
|
22468
23959
|
}
|
|
22469
23960
|
function resolveServerExecutable(options) {
|
|
22470
|
-
const serverRoot =
|
|
23961
|
+
const serverRoot = resolve16(options.root, options.serverPath);
|
|
22471
23962
|
const names = options.configured === null ? candidates2(options.platform ?? process.platform) : [options.configured];
|
|
22472
23963
|
const attempted = [];
|
|
22473
|
-
if (options.configured !== null && (isAbsolute7(options.configured) || !isInside(serverRoot,
|
|
23964
|
+
if (options.configured !== null && (isAbsolute7(options.configured) || !isInside(serverRoot, resolve16(serverRoot, options.configured)))) {
|
|
22474
23965
|
throw new Error(`Configured development.server.executable must stay inside serverPath but received "${options.configured}".`);
|
|
22475
23966
|
}
|
|
22476
23967
|
for (const name of names) {
|
|
22477
|
-
const path =
|
|
23968
|
+
const path = resolve16(serverRoot, name);
|
|
22478
23969
|
attempted.push(path);
|
|
22479
23970
|
try {
|
|
22480
23971
|
if (!statSync5(path).isFile()) {
|
|
@@ -22792,7 +24283,7 @@ function registerEnsureCommand(program2, runtime) {
|
|
|
22792
24283
|
}
|
|
22793
24284
|
|
|
22794
24285
|
// src/cli/registry/init-registration.ts
|
|
22795
|
-
import { resolve as
|
|
24286
|
+
import { resolve as resolve18 } from "node:path";
|
|
22796
24287
|
|
|
22797
24288
|
// src/commands/init-command.ts
|
|
22798
24289
|
import { basename as basename2 } from "node:path";
|
|
@@ -22854,7 +24345,7 @@ function renderManifest(source, details) {
|
|
|
22854
24345
|
}
|
|
22855
24346
|
|
|
22856
24347
|
// src/scaffold/template-files.ts
|
|
22857
|
-
import { existsSync as
|
|
24348
|
+
import { existsSync as existsSync11, readFileSync as readFileSync10 } from "node:fs";
|
|
22858
24349
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
22859
24350
|
|
|
22860
24351
|
// ../template/src/template.ts
|
|
@@ -22870,10 +24361,10 @@ function bundledTemplatePath(source) {
|
|
|
22870
24361
|
}
|
|
22871
24362
|
function resolveTemplatePath(source) {
|
|
22872
24363
|
const packaged = fileURLToPath2(resolveTemplateUrl(source));
|
|
22873
|
-
return
|
|
24364
|
+
return existsSync11(packaged) ? packaged : bundledTemplatePath(source);
|
|
22874
24365
|
}
|
|
22875
24366
|
function readTemplateSource(source) {
|
|
22876
|
-
return
|
|
24367
|
+
return readFileSync10(resolveTemplatePath(source), "utf8");
|
|
22877
24368
|
}
|
|
22878
24369
|
|
|
22879
24370
|
// src/scaffold/scaffold-plan.ts
|
|
@@ -22889,19 +24380,19 @@ function buildScaffoldPlan(details) {
|
|
|
22889
24380
|
}
|
|
22890
24381
|
|
|
22891
24382
|
// src/scaffold/scaffold-writer.ts
|
|
22892
|
-
import { existsSync as
|
|
22893
|
-
import { dirname as
|
|
24383
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "node:fs";
|
|
24384
|
+
import { dirname as dirname6, resolve as resolve17 } from "node:path";
|
|
22894
24385
|
function writeScaffold(targetDir, plan, force) {
|
|
22895
24386
|
const written = [];
|
|
22896
24387
|
const skipped = [];
|
|
22897
24388
|
for (const file of plan.files) {
|
|
22898
|
-
const absolute =
|
|
22899
|
-
if (!force &&
|
|
24389
|
+
const absolute = resolve17(targetDir, file.path);
|
|
24390
|
+
if (!force && existsSync12(absolute)) {
|
|
22900
24391
|
skipped.push(file.path);
|
|
22901
24392
|
continue;
|
|
22902
24393
|
}
|
|
22903
|
-
|
|
22904
|
-
|
|
24394
|
+
mkdirSync6(dirname6(absolute), { recursive: true });
|
|
24395
|
+
writeFileSync7(absolute, file.content, "utf8");
|
|
22905
24396
|
written.push(file.path);
|
|
22906
24397
|
}
|
|
22907
24398
|
return { written: written.sort((left, right) => left.localeCompare(right)), skipped: skipped.sort((left, right) => left.localeCompare(right)) };
|
|
@@ -22951,7 +24442,7 @@ function registerInitCommand(program2, runtime) {
|
|
|
22951
24442
|
command.addOption(cwdOption()).addOption(valueOption("--name <name>", "Resource name. Defaults to the destination directory name.")).addOption(new Option("--force", "Overwrite files that already exist.")).addOption(new Option("-y, --yes", "Accept the defaults without prompting.")).addOption(colorOption());
|
|
22952
24443
|
command.action(async (path, options) => {
|
|
22953
24444
|
const root = commandRoot(runtime, options);
|
|
22954
|
-
const target = path === void 0 ? root :
|
|
24445
|
+
const target = path === void 0 ? root : resolve18(root, path);
|
|
22955
24446
|
runtime.exitCode = await runInitCommand(target, runtime.logger, {
|
|
22956
24447
|
name: options.name ?? null,
|
|
22957
24448
|
force: options.force === true,
|
|
@@ -22969,7 +24460,7 @@ async function runSetupCommand(reporter, options) {
|
|
|
22969
24460
|
reporter.detail("Supported commands: code, code-insiders, cursor, codium, windsurf.");
|
|
22970
24461
|
return EXIT_DIAGNOSTICS;
|
|
22971
24462
|
}
|
|
22972
|
-
let
|
|
24463
|
+
let failed3 = false;
|
|
22973
24464
|
for (const editor of editors) {
|
|
22974
24465
|
if (options.editorService.hasExtension(editor)) {
|
|
22975
24466
|
reporter.success(`${editor.name}: Luam is already installed.`);
|
|
@@ -22988,12 +24479,12 @@ async function runSetupCommand(reporter, options) {
|
|
|
22988
24479
|
const result = await options.editorService.install(editor);
|
|
22989
24480
|
if (result.error !== null) {
|
|
22990
24481
|
reporter.error(`${editor.name}: installation failed. ${result.error}`);
|
|
22991
|
-
|
|
24482
|
+
failed3 = true;
|
|
22992
24483
|
continue;
|
|
22993
24484
|
}
|
|
22994
24485
|
reporter.success(`${editor.name}: installed from the ${result.source}.`);
|
|
22995
24486
|
}
|
|
22996
|
-
return
|
|
24487
|
+
return failed3 ? EXIT_DIAGNOSTICS : EXIT_OK;
|
|
22997
24488
|
}
|
|
22998
24489
|
|
|
22999
24490
|
// src/editor/installation-prompt.ts
|
|
@@ -23075,12 +24566,12 @@ function registerServerCommand(program2, runtime) {
|
|
|
23075
24566
|
}
|
|
23076
24567
|
|
|
23077
24568
|
// src/commands/trace-command.ts
|
|
23078
|
-
import { existsSync as
|
|
24569
|
+
import { existsSync as existsSync13, readFileSync as readFileSync11 } from "node:fs";
|
|
23079
24570
|
function defaultMapPath(root, options, reporter) {
|
|
23080
24571
|
const loaded = loadManifest(root, { path: options.manifestPath, mode: manifestMode("trace"), env: options.env });
|
|
23081
24572
|
if (loaded.config !== null) {
|
|
23082
24573
|
const configured = resourceMapPath(root, loaded.config);
|
|
23083
|
-
if (
|
|
24574
|
+
if (existsSync13(configured)) {
|
|
23084
24575
|
return configured;
|
|
23085
24576
|
}
|
|
23086
24577
|
}
|
|
@@ -23100,7 +24591,7 @@ function readStandardInput() {
|
|
|
23100
24591
|
return "";
|
|
23101
24592
|
}
|
|
23102
24593
|
try {
|
|
23103
|
-
return
|
|
24594
|
+
return readFileSync11(0, "utf8");
|
|
23104
24595
|
} catch {
|
|
23105
24596
|
return "";
|
|
23106
24597
|
}
|
|
@@ -23134,30 +24625,30 @@ function runTraceCommand(root, reporter, options) {
|
|
|
23134
24625
|
reporter.detail('A generated line cannot be resolved there. Reproduce the error under "luam dev" or "luam ensure" for a source position.');
|
|
23135
24626
|
return EXIT_DIAGNOSTICS;
|
|
23136
24627
|
}
|
|
23137
|
-
let
|
|
24628
|
+
let failed3 = false;
|
|
23138
24629
|
for (const line2 of lines) {
|
|
23139
24630
|
const generated = parseTracePosition(line2);
|
|
23140
24631
|
if (generated === null) {
|
|
23141
24632
|
reporter.error(`Trace could not find a file and line in "${line2}".`);
|
|
23142
|
-
|
|
24633
|
+
failed3 = true;
|
|
23143
24634
|
continue;
|
|
23144
24635
|
}
|
|
23145
24636
|
const file = generatedFile(loaded.map.resource, generated.file);
|
|
23146
24637
|
const resolution = resolveResourcePosition(loaded.map, file, generated.line);
|
|
23147
24638
|
if (resolution.status === "unknown-version") {
|
|
23148
24639
|
reporter.error(`Resource map version ${resolution.version} is not supported.`);
|
|
23149
|
-
|
|
24640
|
+
failed3 = true;
|
|
23150
24641
|
continue;
|
|
23151
24642
|
}
|
|
23152
24643
|
if (resolution.status === "uncovered") {
|
|
23153
24644
|
reporter.error(`Resource map does not cover "${generated.file}:${generated.line}".`);
|
|
23154
|
-
|
|
24645
|
+
failed3 = true;
|
|
23155
24646
|
continue;
|
|
23156
24647
|
}
|
|
23157
24648
|
const symbol = resolution.position.symbol === void 0 ? "" : ` (${resolution.position.symbol})`;
|
|
23158
24649
|
reporter.raw(`${resolution.position.file}:${resolution.position.line}${symbol}`);
|
|
23159
24650
|
}
|
|
23160
|
-
return
|
|
24651
|
+
return failed3 ? EXIT_DIAGNOSTICS : EXIT_OK;
|
|
23161
24652
|
}
|
|
23162
24653
|
|
|
23163
24654
|
// src/cli/registry/trace-registration.ts
|
|
@@ -23178,6 +24669,7 @@ function registerTraceCommand(program2, runtime) {
|
|
|
23178
24669
|
var COMMAND_REGISTRARS = [
|
|
23179
24670
|
registerBuildCommand,
|
|
23180
24671
|
registerCheckCommand,
|
|
24672
|
+
registerConfigCommand,
|
|
23181
24673
|
registerDevCommand,
|
|
23182
24674
|
registerDoctorCommand,
|
|
23183
24675
|
registerEnsureCommand,
|