@tilua/parser 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +423 -83
- package/dist/index.d.cts +73 -3
- package/dist/index.d.ts +73 -3
- package/dist/index.js +412 -82
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -603,7 +603,7 @@ function bindThis(func, at) {
|
|
|
603
603
|
func.isMethod = true;
|
|
604
604
|
}
|
|
605
605
|
function bindThisParam(params, at) {
|
|
606
|
-
params.unshift({ type: "FunctionParameter", name: "this", ...spanFrom(at, at) });
|
|
606
|
+
params.unshift({ type: "FunctionParameter", name: "this", implicit: true, ...spanFrom(at, at) });
|
|
607
607
|
}
|
|
608
608
|
var ParseError = class extends Error {
|
|
609
609
|
constructor(message, line, column) {
|
|
@@ -1660,7 +1660,7 @@ var Parser = class {
|
|
|
1660
1660
|
};
|
|
1661
1661
|
}
|
|
1662
1662
|
if (isMethod) {
|
|
1663
|
-
func.params.unshift({ type: "FunctionParameter", name: "self", ...spanFrom(target, target) });
|
|
1663
|
+
func.params.unshift({ type: "FunctionParameter", name: "self", implicit: true, ...spanFrom(target, target) });
|
|
1664
1664
|
func.isMethod = true;
|
|
1665
1665
|
}
|
|
1666
1666
|
return {
|
|
@@ -1854,47 +1854,7 @@ var Parser = class {
|
|
|
1854
1854
|
const isStatic = modifiers.has("static");
|
|
1855
1855
|
if (this.checkKeyword("function")) {
|
|
1856
1856
|
this.advance();
|
|
1857
|
-
|
|
1858
|
-
if (modifiers.has("abstract")) {
|
|
1859
|
-
const head = this.parseFunctionHead();
|
|
1860
|
-
const func = {
|
|
1861
|
-
type: "FunctionBody",
|
|
1862
|
-
generics: head.generics,
|
|
1863
|
-
params: head.params,
|
|
1864
|
-
hasVarargs: head.hasVarargs,
|
|
1865
|
-
returnType: head.returnType,
|
|
1866
|
-
predicate: head.predicate,
|
|
1867
|
-
body: { type: "Block", statements: [], ...spanFrom(head.start, this.previous()) },
|
|
1868
|
-
...spanFrom(head.start, this.previous())
|
|
1869
|
-
};
|
|
1870
|
-
if (!isStatic) bindThis(func, start);
|
|
1871
|
-
if (!head.returnType) this.problem("An abstract method has no body to infer from: write its return type");
|
|
1872
|
-
if (this.checkPunctuator("{")) this.problem("An abstract method has no body");
|
|
1873
|
-
return { type: "ClassMethod", name: memberName, isStatic, func, isAbstract: true, ...spanFrom(start, this.previous()) };
|
|
1874
|
-
}
|
|
1875
|
-
const signatures = [];
|
|
1876
|
-
let written = memberName;
|
|
1877
|
-
while (true) {
|
|
1878
|
-
const head = this.parseFunctionHead();
|
|
1879
|
-
if (this.isClassOverloadContinuation(memberName.name, isStatic)) {
|
|
1880
|
-
if (!isStatic) bindThisParam(head.params, start);
|
|
1881
|
-
signatures.push({ ...this.headToSignature(head), name: written });
|
|
1882
|
-
if (isStatic) this.advance();
|
|
1883
|
-
this.expectKeyword("function");
|
|
1884
|
-
written = this.parseIdentifier();
|
|
1885
|
-
continue;
|
|
1886
|
-
}
|
|
1887
|
-
const func = this.headToBody(head, start);
|
|
1888
|
-
if (!isStatic) bindThis(func, start);
|
|
1889
|
-
return {
|
|
1890
|
-
type: "ClassMethod",
|
|
1891
|
-
name: memberName,
|
|
1892
|
-
isStatic,
|
|
1893
|
-
func,
|
|
1894
|
-
signatures: signatures.length ? signatures : void 0,
|
|
1895
|
-
...spanFrom(start, this.previous())
|
|
1896
|
-
};
|
|
1897
|
-
}
|
|
1857
|
+
return this.parseClassMethod(start, modifiers);
|
|
1898
1858
|
}
|
|
1899
1859
|
if (!isStatic && this.checkIdentifierValue("constructor") && this.punctuatorAt(1, "(")) {
|
|
1900
1860
|
this.advance();
|
|
@@ -1904,6 +1864,9 @@ var Parser = class {
|
|
|
1904
1864
|
bindThis(func, start);
|
|
1905
1865
|
return { type: "ClassConstructor", func, ...spanFrom(start, this.previous()) };
|
|
1906
1866
|
}
|
|
1867
|
+
if (this.startsMethodShorthand(0)) {
|
|
1868
|
+
return this.parseClassMethod(start, modifiers);
|
|
1869
|
+
}
|
|
1907
1870
|
if ((this.checkIdentifierValue("get") || this.checkIdentifierValue("set")) && this.peek(1).type === "Identifier" && this.punctuatorAt(2, "(")) {
|
|
1908
1871
|
const kind = this.advance().value;
|
|
1909
1872
|
const memberName = this.parseIdentifier();
|
|
@@ -1933,25 +1896,90 @@ var Parser = class {
|
|
|
1933
1896
|
return { type: "ClassField", name: memberName, isStatic, typeAnnotation, init, ...spanFrom(start, this.previous()) };
|
|
1934
1897
|
}
|
|
1935
1898
|
if (this.recover) {
|
|
1936
|
-
this.softError("Expected a class member: a field,
|
|
1899
|
+
this.softError("Expected a class member: a field, a method, 'constructor', 'get' or 'set'");
|
|
1937
1900
|
this.advance();
|
|
1938
1901
|
return void 0;
|
|
1939
1902
|
}
|
|
1940
|
-
this.error("Expected a class member: a field,
|
|
1903
|
+
this.error("Expected a class member: a field, a method, 'constructor', 'get' or 'set'");
|
|
1904
|
+
}
|
|
1905
|
+
/** A name at `offset` opening a method without `function`: `name(` or
|
|
1906
|
+
* `name<T>(`. A field's name is followed by `:` or `=` instead. */
|
|
1907
|
+
startsMethodShorthand(offset) {
|
|
1908
|
+
if (this.peek(offset).type !== "Identifier") return false;
|
|
1909
|
+
const next = this.peek(offset + 1);
|
|
1910
|
+
return (next.type === "Punctuator" || next.type === "Operator") && (next.value === "(" || next.value === "<");
|
|
1911
|
+
}
|
|
1912
|
+
/** A method, from its name on: `function` (if written) and the modifiers
|
|
1913
|
+
* are behind. */
|
|
1914
|
+
parseClassMethod(start, modifiers) {
|
|
1915
|
+
const isStatic = modifiers.has("static");
|
|
1916
|
+
const memberName = this.parseIdentifier();
|
|
1917
|
+
if (modifiers.has("abstract")) {
|
|
1918
|
+
const head = this.parseFunctionHead();
|
|
1919
|
+
const func = {
|
|
1920
|
+
type: "FunctionBody",
|
|
1921
|
+
generics: head.generics,
|
|
1922
|
+
params: head.params,
|
|
1923
|
+
hasVarargs: head.hasVarargs,
|
|
1924
|
+
returnType: head.returnType,
|
|
1925
|
+
predicate: head.predicate,
|
|
1926
|
+
body: { type: "Block", statements: [], ...spanFrom(head.start, this.previous()) },
|
|
1927
|
+
...spanFrom(head.start, this.previous())
|
|
1928
|
+
};
|
|
1929
|
+
if (!isStatic) bindThis(func, start);
|
|
1930
|
+
if (!head.returnType) this.problem("An abstract method has no body to infer from: write its return type");
|
|
1931
|
+
if (this.checkPunctuator("{")) this.problem("An abstract method has no body");
|
|
1932
|
+
return { type: "ClassMethod", name: memberName, isStatic, func, isAbstract: true, ...spanFrom(start, this.previous()) };
|
|
1933
|
+
}
|
|
1934
|
+
const signatures = [];
|
|
1935
|
+
let written = memberName;
|
|
1936
|
+
while (true) {
|
|
1937
|
+
const head = this.parseFunctionHead();
|
|
1938
|
+
const skip = this.classOverloadContinuation(memberName.name, isStatic);
|
|
1939
|
+
if (skip !== void 0) {
|
|
1940
|
+
if (!isStatic) bindThisParam(head.params, start);
|
|
1941
|
+
signatures.push({ ...this.headToSignature(head), name: written });
|
|
1942
|
+
for (let i = 0; i < skip; i++) this.advance();
|
|
1943
|
+
written = this.parseIdentifier();
|
|
1944
|
+
continue;
|
|
1945
|
+
}
|
|
1946
|
+
const func = this.headToBody(head, start);
|
|
1947
|
+
if (!isStatic) bindThis(func, start);
|
|
1948
|
+
return {
|
|
1949
|
+
type: "ClassMethod",
|
|
1950
|
+
name: memberName,
|
|
1951
|
+
isStatic,
|
|
1952
|
+
func,
|
|
1953
|
+
signatures: signatures.length ? signatures : void 0,
|
|
1954
|
+
...spanFrom(start, this.previous())
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1941
1957
|
}
|
|
1942
|
-
/** Give a class method its `this`: a real first parameter, the way
|
|
1943
|
-
* `function T:m()` gets a real `self`. Every later pass — scopes, types,
|
|
1944
|
-
* arity, lowering — then sees an ordinary parameter and needs to know
|
|
1945
|
-
* nothing about classes. */
|
|
1946
1958
|
/** After a bodyless head inside a class body, does another declaration of
|
|
1947
|
-
* the same member follow? Then the head was an overload signature
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1959
|
+
* the same member follow? Then the head was an overload signature, and
|
|
1960
|
+
* this is how many tokens stand before the next one's name: a `;` that
|
|
1961
|
+
* ends the head, as TypeScript writes it, the modifiers, and `function`
|
|
1962
|
+
* when it is written. `undefined` when no overload follows. Each head may
|
|
1963
|
+
* be written either way, with `function` or without. */
|
|
1964
|
+
classOverloadContinuation(name, isStatic) {
|
|
1965
|
+
let offset = 0;
|
|
1966
|
+
if (this.punctuatorAt(0, ";")) offset++;
|
|
1967
|
+
let sawStatic = false;
|
|
1968
|
+
while (true) {
|
|
1969
|
+
const t = this.peek(offset);
|
|
1970
|
+
const word = t.type === "Identifier" ? t.value : void 0;
|
|
1971
|
+
if (word === void 0 || !CLASS_MODIFIERS.has(word) || this.startsMethodShorthand(offset)) break;
|
|
1972
|
+
if (word === "static") sawStatic = true;
|
|
1973
|
+
offset++;
|
|
1974
|
+
}
|
|
1975
|
+
if (sawStatic !== isStatic) return void 0;
|
|
1951
1976
|
const keyword = this.peek(offset);
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1977
|
+
const hasKeyword = keyword.type === "Keyword" && keyword.value === "function";
|
|
1978
|
+
if (hasKeyword) offset++;
|
|
1979
|
+
const named = this.peek(offset);
|
|
1980
|
+
if (!(named.type === "Identifier" && named.value === name)) return void 0;
|
|
1981
|
+
if (!hasKeyword && !this.startsMethodShorthand(offset)) return void 0;
|
|
1982
|
+
return offset;
|
|
1955
1983
|
}
|
|
1956
1984
|
operatorAt(ahead, value) {
|
|
1957
1985
|
const token = this.peek(ahead);
|
|
@@ -3788,6 +3816,10 @@ var Analyzer = class {
|
|
|
3788
3816
|
moduleScope = this.globalScope;
|
|
3789
3817
|
/** How many function bodies enclose the walk. */
|
|
3790
3818
|
functionDepth = 0;
|
|
3819
|
+
/** How many loops enclose the walk inside the nearest function body. A
|
|
3820
|
+
* function starts again at 0: `continue` in `xs:map(x => ...)` has no
|
|
3821
|
+
* loop to go on with, whatever loop the call is written in. */
|
|
3822
|
+
loopDepth = 0;
|
|
3791
3823
|
/** Function declarations already declared by their block's hoisting, with
|
|
3792
3824
|
* the function depth of that block. */
|
|
3793
3825
|
hoisted = /* @__PURE__ */ new Map();
|
|
@@ -4140,11 +4172,11 @@ var Analyzer = class {
|
|
|
4140
4172
|
return;
|
|
4141
4173
|
case "WhileStatement":
|
|
4142
4174
|
this.visitExpression(stmt.condition, scope);
|
|
4143
|
-
this.visitBlockInNewScope(stmt.body, scope);
|
|
4175
|
+
this.inLoop(() => this.visitBlockInNewScope(stmt.body, scope));
|
|
4144
4176
|
return;
|
|
4145
4177
|
case "RepeatStatement": {
|
|
4146
4178
|
const bodyScope = childScope(scope);
|
|
4147
|
-
this.visitBlock(stmt.body, bodyScope);
|
|
4179
|
+
this.inLoop(() => this.visitBlock(stmt.body, bodyScope));
|
|
4148
4180
|
this.visitExpression(stmt.condition, bodyScope);
|
|
4149
4181
|
return;
|
|
4150
4182
|
}
|
|
@@ -4162,14 +4194,14 @@ var Analyzer = class {
|
|
|
4162
4194
|
if (stmt.step) this.visitExpression(stmt.step, scope);
|
|
4163
4195
|
const bodyScope = childScope(scope);
|
|
4164
4196
|
this.declare(bodyScope, stmt.variable.name, "for-numeric", stmt.variable);
|
|
4165
|
-
this.visitBlock(stmt.body, bodyScope);
|
|
4197
|
+
this.inLoop(() => this.visitBlock(stmt.body, bodyScope));
|
|
4166
4198
|
return;
|
|
4167
4199
|
}
|
|
4168
4200
|
case "GenericForStatement": {
|
|
4169
4201
|
this.visitExpression(stmt.iterator, scope);
|
|
4170
4202
|
const bodyScope = childScope(scope);
|
|
4171
4203
|
this.declarePattern(bodyScope, stmt.variable, "for-generic", scope, stmt.kind === "const");
|
|
4172
|
-
this.visitBlock(stmt.body, bodyScope);
|
|
4204
|
+
this.inLoop(() => this.visitBlock(stmt.body, bodyScope));
|
|
4173
4205
|
return;
|
|
4174
4206
|
}
|
|
4175
4207
|
case "ReturnStatement":
|
|
@@ -4177,6 +4209,15 @@ var Analyzer = class {
|
|
|
4177
4209
|
return;
|
|
4178
4210
|
case "BreakStatement":
|
|
4179
4211
|
case "ContinueStatement":
|
|
4212
|
+
if (this.loopDepth === 0) {
|
|
4213
|
+
const word = stmt.type === "BreakStatement" ? "break" : "continue";
|
|
4214
|
+
this.diagnostics.push({
|
|
4215
|
+
node: stmt,
|
|
4216
|
+
message: this.functionDepth > 0 ? `'${word}' is not inside a loop of this function; to skip a value in a callback, 'return' from it` : `'${word}' is not inside a loop`,
|
|
4217
|
+
kind: "outside-loop"
|
|
4218
|
+
});
|
|
4219
|
+
}
|
|
4220
|
+
return;
|
|
4180
4221
|
case "ErrorStatement":
|
|
4181
4222
|
return;
|
|
4182
4223
|
case "DeclareStatement":
|
|
@@ -4232,6 +4273,15 @@ var Analyzer = class {
|
|
|
4232
4273
|
return;
|
|
4233
4274
|
}
|
|
4234
4275
|
}
|
|
4276
|
+
/** Visit a loop's body, where `break` and `continue` have a loop. */
|
|
4277
|
+
inLoop(visit) {
|
|
4278
|
+
this.loopDepth++;
|
|
4279
|
+
try {
|
|
4280
|
+
visit();
|
|
4281
|
+
} finally {
|
|
4282
|
+
this.loopDepth--;
|
|
4283
|
+
}
|
|
4284
|
+
}
|
|
4235
4285
|
// ---------------- functions ----------------
|
|
4236
4286
|
visitFunctionBody(func, outerScope, isMethod = false) {
|
|
4237
4287
|
const fnScope = childScope(outerScope);
|
|
@@ -4247,11 +4297,14 @@ var Analyzer = class {
|
|
|
4247
4297
|
}
|
|
4248
4298
|
});
|
|
4249
4299
|
this.visitType(func.returnType, fnScope);
|
|
4300
|
+
const loops = this.loopDepth;
|
|
4250
4301
|
this.functionDepth++;
|
|
4302
|
+
this.loopDepth = 0;
|
|
4251
4303
|
try {
|
|
4252
4304
|
this.visitBlock(func.body, fnScope);
|
|
4253
4305
|
} finally {
|
|
4254
4306
|
this.functionDepth--;
|
|
4307
|
+
this.loopDepth = loops;
|
|
4255
4308
|
}
|
|
4256
4309
|
}
|
|
4257
4310
|
/** An overload signature: no body and no bindings, but its types can hold
|
|
@@ -4505,11 +4558,14 @@ declare metatable string: {
|
|
|
4505
4558
|
-- answer \`nil\` rather than JavaScript's -1. \`sort\` takes Lua's comparator
|
|
4506
4559
|
-- (true when \`a\` comes first). \`push\`, \`pop\`, \`shift\`, \`unshift\`,
|
|
4507
4560
|
-- \`sort\` and \`reverse\` change the array; everything else returns a new one.
|
|
4561
|
+
-- A \`filter\` whose test is a guard \u2014 \`v is S\`, or \`v => v ~= nil\` \u2014
|
|
4562
|
+
-- answers an array of what it let through.
|
|
4508
4563
|
declare metatable<T> T[]: {
|
|
4509
4564
|
__index: {
|
|
4510
4565
|
find: (self: T[], test: (value: T, index: number) => boolean) => T | nil,
|
|
4511
4566
|
findIndex: (self: T[], test: (value: T, index: number) => boolean) => number | nil,
|
|
4512
|
-
filter: (self: T[], test: (value: T, index: number) =>
|
|
4567
|
+
filter: (<S extends T>(self: T[], test: (value: T, index: number) => value is S) => S[])
|
|
4568
|
+
& ((self: T[], test: (value: T, index: number) => boolean) => T[]),
|
|
4513
4569
|
map: <U>(self: T[], transform: (value: T, index: number) => U) => U[],
|
|
4514
4570
|
forEach: (self: T[], visit: (value: T, index: number) => nil) => nil,
|
|
4515
4571
|
some: (self: T[], test: (value: T, index: number) => boolean) => boolean,
|
|
@@ -4543,11 +4599,34 @@ declare metatable<T> T[]: {
|
|
|
4543
4599
|
-- \`nil\` when it is missing; an indexer's keys follow the named ones. A
|
|
4544
4600
|
-- member of the table's own with one of these names is what a call reaches,
|
|
4545
4601
|
-- and a class instance, which has a metatable of its own, has none of them.
|
|
4602
|
+
--
|
|
4603
|
+
-- The rest are JavaScript's \`Object\` functions, written as methods of the
|
|
4604
|
+
-- table they take first:
|
|
4605
|
+
--
|
|
4606
|
+
-- point:hasOwn("x") -- Object.hasOwn(point, "x")
|
|
4607
|
+
-- point:assign({ z: 3 }) -- Object.assign(point, { z: 3 })
|
|
4608
|
+
-- point:freeze() -- Object.freeze(point)
|
|
4609
|
+
--
|
|
4610
|
+
-- \`hasOwn\` asks the table itself, not what its metatable's \`__index\`
|
|
4611
|
+
-- lends it. \`assign\` copies each source's members onto the table, later ones
|
|
4612
|
+
-- over earlier ones, skipping a \`nil\` source, and answers the table. \`freeze\`
|
|
4613
|
+
-- is Luau's \`table.freeze\`; under Lua 5.1, which cannot freeze a table, the
|
|
4614
|
+
-- table is answered as it is and \`isFrozen\` is false.
|
|
4546
4615
|
declare metatable<T extends {}> T: {
|
|
4547
4616
|
__index: {
|
|
4548
4617
|
keys: (self: T) => ObjectKeys<T>,
|
|
4549
4618
|
values: (self: T) => ObjectValues<T>,
|
|
4550
4619
|
entries: (self: T) => ObjectEntries<T>,
|
|
4620
|
+
|
|
4621
|
+
hasOwn: (self: T, key: unknown) => boolean,
|
|
4622
|
+
hasOwnProperty: (self: T, key: unknown) => boolean,
|
|
4623
|
+
assign: (<U>(self: T, source: U) => T & U)
|
|
4624
|
+
& (<U, V>(self: T, a: U, b: V) => T & U & V)
|
|
4625
|
+
& (<U, V, W>(self: T, a: U, b: V, c: W) => T & U & V & W)
|
|
4626
|
+
-- Generic like the rest: a plain signature would be tried first.
|
|
4627
|
+
& (<S extends {}>(self: T, ...sources: S[]) => T),
|
|
4628
|
+
freeze: (self: T) => Readonly<T>,
|
|
4629
|
+
isFrozen: (self: T) => boolean,
|
|
4551
4630
|
},
|
|
4552
4631
|
}
|
|
4553
4632
|
`;
|
|
@@ -4648,8 +4727,14 @@ function substitute(t, subst) {
|
|
|
4648
4727
|
if (subst.size === 0) return t;
|
|
4649
4728
|
if (!containsTypeParam(t)) return t;
|
|
4650
4729
|
switch (t.kind) {
|
|
4651
|
-
case "typeParam":
|
|
4652
|
-
|
|
4730
|
+
case "typeParam": {
|
|
4731
|
+
const bound = subst.get(t.name);
|
|
4732
|
+
if (bound) return bound;
|
|
4733
|
+
if (t.constraint && containsTypeParam(t.constraint)) {
|
|
4734
|
+
return { ...t, constraint: substitute(t.constraint, new Map([...subst, [t.name, t]])) };
|
|
4735
|
+
}
|
|
4736
|
+
return t;
|
|
4737
|
+
}
|
|
4653
4738
|
case "array":
|
|
4654
4739
|
return { ...arrayOf(substitute(t.element, subst)), readonly: t.readonly };
|
|
4655
4740
|
case "tuple":
|
|
@@ -4794,6 +4879,10 @@ function unify(param, arg, vars, out, how = "union") {
|
|
|
4794
4879
|
if (arg.kind === "function") {
|
|
4795
4880
|
param.params.forEach((p, i) => arg.params[i] && unify(p.type, arg.params[i].type, vars, out));
|
|
4796
4881
|
unify(param.returns, arg.returns, vars, out);
|
|
4882
|
+
const wanted = param.predicate, given = arg.predicate;
|
|
4883
|
+
if (wanted?.type && given?.type && wanted.param === given.param && !wanted.asserts && !given.asserts) {
|
|
4884
|
+
unify(wanted.type, given.type, vars, out);
|
|
4885
|
+
}
|
|
4797
4886
|
}
|
|
4798
4887
|
return;
|
|
4799
4888
|
case "object":
|
|
@@ -5049,6 +5138,11 @@ function isAssignableInner(a, b) {
|
|
|
5049
5138
|
for (let i = 0; i < n; i++) {
|
|
5050
5139
|
if (!isAssignable(a.params[i].type, b.params[i].type) && !isAssignable(b.params[i].type, a.params[i].type)) return false;
|
|
5051
5140
|
}
|
|
5141
|
+
const wanted = b.predicate;
|
|
5142
|
+
if (wanted?.type && !wanted.asserts) {
|
|
5143
|
+
const given = a.predicate;
|
|
5144
|
+
if (!given?.type || given.asserts || given.param !== wanted.param || !isAssignable(given.type, wanted.type)) return false;
|
|
5145
|
+
}
|
|
5052
5146
|
return isAssignable(a.returns, b.returns);
|
|
5053
5147
|
}
|
|
5054
5148
|
if (b.kind === "typeParam") return false;
|
|
@@ -5183,7 +5277,7 @@ function containsFreeTypeParam(t, seen, bound) {
|
|
|
5183
5277
|
return [...t.properties.values()].some((v) => containsTypeParam(v.type, seen, bound)) || !!t.indexer && (containsTypeParam(t.indexer.key, seen, bound) || containsTypeParam(t.indexer.value, seen, bound));
|
|
5184
5278
|
case "function": {
|
|
5185
5279
|
const inner = t.typeParams?.length ? /* @__PURE__ */ new Set([...bound, ...t.typeParams]) : bound;
|
|
5186
|
-
return t.params.some((p) => containsTypeParam(p.type, seen, inner)) || !!t.varargs && containsTypeParam(t.varargs, seen, inner) || containsTypeParam(t.returns, seen, inner);
|
|
5280
|
+
return t.params.some((p) => containsTypeParam(p.type, seen, inner)) || !!t.varargs && containsTypeParam(t.varargs, seen, inner) || containsTypeParam(t.returns, seen, inner) || !!t.predicate?.type && containsTypeParam(t.predicate.type, seen, inner);
|
|
5187
5281
|
}
|
|
5188
5282
|
case "genericRef":
|
|
5189
5283
|
return t.typeArguments.some((a) => containsTypeParam(a, seen, bound));
|
|
@@ -5608,9 +5702,14 @@ function isFreshLiteralExpr(e) {
|
|
|
5608
5702
|
case "StringLiteral":
|
|
5609
5703
|
case "BooleanLiteral":
|
|
5610
5704
|
case "InterpolatedStringExpression":
|
|
5705
|
+
return true;
|
|
5706
|
+
// A table or array literal widened the literals written in it as it
|
|
5707
|
+
// was read (`widenUnlessAsked`), and keeps what it read from
|
|
5708
|
+
// elsewhere — `{ key: name }`, `[...names]` — so there is nothing
|
|
5709
|
+
// left for the binding to widen.
|
|
5611
5710
|
case "TableExpression":
|
|
5612
5711
|
case "ArrayExpression":
|
|
5613
|
-
return
|
|
5712
|
+
return false;
|
|
5614
5713
|
case "ParenthesizedExpression":
|
|
5615
5714
|
return isFreshLiteralExpr(e.expression);
|
|
5616
5715
|
case "UnaryExpression":
|
|
@@ -5625,6 +5724,30 @@ function isFreshLiteralExpr(e) {
|
|
|
5625
5724
|
return false;
|
|
5626
5725
|
}
|
|
5627
5726
|
}
|
|
5727
|
+
function overloadTier(f) {
|
|
5728
|
+
const takesGuard = f.params.some((p) => p.type.kind === "function" && p.type.predicate?.type && !p.type.predicate.asserts);
|
|
5729
|
+
if (takesGuard) return 0;
|
|
5730
|
+
return (f.typeParams?.length ?? 0) > 0 ? 2 : 1;
|
|
5731
|
+
}
|
|
5732
|
+
function writesLiteral(e) {
|
|
5733
|
+
switch (e.type) {
|
|
5734
|
+
case "NumberLiteral":
|
|
5735
|
+
case "StringLiteral":
|
|
5736
|
+
case "BooleanLiteral":
|
|
5737
|
+
case "InterpolatedStringExpression":
|
|
5738
|
+
return true;
|
|
5739
|
+
case "ParenthesizedExpression":
|
|
5740
|
+
return writesLiteral(e.expression);
|
|
5741
|
+
case "UnaryExpression":
|
|
5742
|
+
return writesLiteral(e.argument);
|
|
5743
|
+
case "IfElseExpression":
|
|
5744
|
+
return e.clauses.some((c) => writesLiteral(c.body)) || writesLiteral(e.alternate);
|
|
5745
|
+
case "BinaryExpression":
|
|
5746
|
+
return (e.operator === "or" || e.operator === "and") && (writesLiteral(e.left) || writesLiteral(e.right));
|
|
5747
|
+
default:
|
|
5748
|
+
return false;
|
|
5749
|
+
}
|
|
5750
|
+
}
|
|
5628
5751
|
function collectInferNames(node) {
|
|
5629
5752
|
const out = [];
|
|
5630
5753
|
const walk = (n) => {
|
|
@@ -8007,7 +8130,9 @@ var TypeAnalyzer = class {
|
|
|
8007
8130
|
const waits = written.map((a, i) => this.waitsOnInference(a, expected[i]));
|
|
8008
8131
|
if (!waits.some(Boolean)) return written.map((a) => this.infer(a, env));
|
|
8009
8132
|
const argTypes = written.map((a, i) => waits[i] ? this.quietly(() => this.infer(a, env)) : this.infer(a, env));
|
|
8010
|
-
const
|
|
8133
|
+
const spread = this.spreadOf(written, selfOf(fns[0]));
|
|
8134
|
+
const provisional = argTypes.map((t, i) => waits[i] ? anyType : t);
|
|
8135
|
+
const picked = this.pickOverload(fns, argTypes, (f) => extra(f, argTypes), spread) ?? this.pickOverload(fns, provisional, (f) => extra(f, provisional), spread);
|
|
8011
8136
|
const subst = picked?.typeParams?.length ? this.inferTypeArgs(picked, extra(picked, argTypes)) : void 0;
|
|
8012
8137
|
written.forEach((a, i) => {
|
|
8013
8138
|
if (!waits[i]) return;
|
|
@@ -8198,6 +8323,16 @@ var TypeAnalyzer = class {
|
|
|
8198
8323
|
}
|
|
8199
8324
|
const id = this.bindingIdByName(p.name, p);
|
|
8200
8325
|
const t = this.paramType(p, env);
|
|
8326
|
+
if (p.default && p.typeAnnotation) {
|
|
8327
|
+
const given = this.infer(p.default, env);
|
|
8328
|
+
const declared = this.resolveType(p.typeAnnotation);
|
|
8329
|
+
if (this.emitDiagnostics && !isAssignable(given, declared)) {
|
|
8330
|
+
this.diagnostics.push({
|
|
8331
|
+
node: p.default,
|
|
8332
|
+
message: `Type '${briefType(given)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(given, declared)
|
|
8333
|
+
});
|
|
8334
|
+
}
|
|
8335
|
+
}
|
|
8201
8336
|
if (id !== void 0) {
|
|
8202
8337
|
this.bindingType.set(id, t);
|
|
8203
8338
|
this.setBinding(env, id, t);
|
|
@@ -8295,9 +8430,9 @@ var TypeAnalyzer = class {
|
|
|
8295
8430
|
* lets `typeof` declare `(v: number) -> "number"` alongside a trailing
|
|
8296
8431
|
* `<T>(v: T) -> string` and still pick the precise one. */
|
|
8297
8432
|
pickOverload(fns, argTypes, argsFor, spread) {
|
|
8298
|
-
for (const
|
|
8433
|
+
for (const tier of [0, 1, 2]) {
|
|
8299
8434
|
for (const f of fns) {
|
|
8300
|
-
if ((f
|
|
8435
|
+
if (overloadTier(f) !== tier) continue;
|
|
8301
8436
|
if (this.overloadAccepts(f, argsFor ? argsFor(f) : argTypes, spread)) return f;
|
|
8302
8437
|
}
|
|
8303
8438
|
}
|
|
@@ -8323,7 +8458,7 @@ var TypeAnalyzer = class {
|
|
|
8323
8458
|
if (position < 0) return void 0;
|
|
8324
8459
|
const members = this.expand(argTypes[position]).types;
|
|
8325
8460
|
if (members.length > 32) return void 0;
|
|
8326
|
-
const rank = (f) => (
|
|
8461
|
+
const rank = (f) => overloadTier(f) * fns.length + fns.indexOf(f);
|
|
8327
8462
|
const limit = picked ? rank(picked) : Infinity;
|
|
8328
8463
|
const results = [];
|
|
8329
8464
|
for (const member of members) {
|
|
@@ -8716,10 +8851,38 @@ var TypeAnalyzer = class {
|
|
|
8716
8851
|
returns,
|
|
8717
8852
|
void 0,
|
|
8718
8853
|
names,
|
|
8719
|
-
this.resolvePredicate(func.predicate, params)
|
|
8854
|
+
func.predicate || func.returnType ? this.resolvePredicate(func.predicate, params) : this.inferPredicate(func, params, returns, bodyEnv)
|
|
8720
8855
|
), func.params);
|
|
8721
8856
|
});
|
|
8722
8857
|
}
|
|
8858
|
+
/** `v => v ~= nil` is a guard without saying so, as TypeScript reads it:
|
|
8859
|
+
* a body that is one `return` of a boolean which, true, narrows a
|
|
8860
|
+
* parameter — and, false, leaves it exactly what that excludes. Then
|
|
8861
|
+
* `list:filter(v => v ~= nil)` knows what it kept. */
|
|
8862
|
+
inferPredicate(func, params, returns, env) {
|
|
8863
|
+
const only = func.body.statements.length === 1 ? func.body.statements[0] : void 0;
|
|
8864
|
+
if (only?.type !== "ReturnStatement" || !only.argument) return void 0;
|
|
8865
|
+
if (returns.kind === "any" || returns.kind === "never" || !isAssignable(returns, booleanType)) return void 0;
|
|
8866
|
+
const cond = only.argument;
|
|
8867
|
+
for (let i = 0; i < params.length; i++) {
|
|
8868
|
+
const p = func.params[i];
|
|
8869
|
+
if (p.pattern || p.rest) continue;
|
|
8870
|
+
const id = this.bindingIdByName(p.name, p);
|
|
8871
|
+
if (id === void 0) continue;
|
|
8872
|
+
const declared = params[i].type;
|
|
8873
|
+
const key = bindKey(id);
|
|
8874
|
+
const t = forkEnv(env);
|
|
8875
|
+
const f = forkEnv(env);
|
|
8876
|
+
this.silently(() => this.applyNarrowing(cond, env, t, f));
|
|
8877
|
+
const yes = t.get(key);
|
|
8878
|
+
if (!yes || equalTypes(yes, declared) || !isAssignable(yes, declared)) continue;
|
|
8879
|
+
const no = this.expand(f.get(key) ?? declared);
|
|
8880
|
+
const rest = no.kind === "union" ? no.types : [no];
|
|
8881
|
+
if (rest.some((m) => m.kind !== "never" && isAssignable(m, yes))) continue;
|
|
8882
|
+
return { param: i, type: yes, asserts: false };
|
|
8883
|
+
}
|
|
8884
|
+
return void 0;
|
|
8885
|
+
}
|
|
8723
8886
|
/** Where the return types of the function being walked are collected, so
|
|
8724
8887
|
* each is read where it is written — inside the branch that narrowed it —
|
|
8725
8888
|
* rather than in whatever state the body ends in. */
|
|
@@ -9841,6 +10004,27 @@ var TypeAnalyzer = class {
|
|
|
9841
10004
|
elseEnv = whenFalse;
|
|
9842
10005
|
}
|
|
9843
10006
|
branches.push(this.infer(expr.alternate, elseEnv));
|
|
10007
|
+
const bodies = [...expr.clauses.map((c) => c.body), expr.alternate].map(unwrapParens);
|
|
10008
|
+
const isEmpty = (e) => e.type === "TableExpression" && e.fields.length === 0 || e.type === "ArrayExpression" && e.elements.length === 0;
|
|
10009
|
+
const others = union(branches.filter((_, i) => !isEmpty(bodies[i])));
|
|
10010
|
+
const parts = (others.kind === "union" ? others.types : [others]).map((m) => this.expand(m)).filter((m) => !(m.kind === "primitive" && m.name === "nil"));
|
|
10011
|
+
const fits = (e) => {
|
|
10012
|
+
if (!parts.length) return void 0;
|
|
10013
|
+
if (e.type === "ArrayExpression") {
|
|
10014
|
+
if (parts.every((m) => m.kind === "array")) return others;
|
|
10015
|
+
return parts.every((m) => m.kind === "array" || m.kind === "tuple") ? tuple([]) : void 0;
|
|
10016
|
+
}
|
|
10017
|
+
return parts.every((m) => m.kind === "object" && !m.class && [...m.properties.values()].every((prop) => prop.optional)) ? others : void 0;
|
|
10018
|
+
};
|
|
10019
|
+
if (bodies.some(isEmpty) && others.kind !== "never" && others.kind !== "any" && bodies.every((body) => !isEmpty(body) || fits(body))) {
|
|
10020
|
+
const typed = bodies.map((body, i) => {
|
|
10021
|
+
if (!isEmpty(body)) return branches[i];
|
|
10022
|
+
const t = fits(body);
|
|
10023
|
+
this.typeOf.set(body, t);
|
|
10024
|
+
return t;
|
|
10025
|
+
});
|
|
10026
|
+
return union(typed);
|
|
10027
|
+
}
|
|
9844
10028
|
return union(branches);
|
|
9845
10029
|
}
|
|
9846
10030
|
}
|
|
@@ -10147,23 +10331,87 @@ var TypeAnalyzer = class {
|
|
|
10147
10331
|
if (wantedTuple && !asConst) return this.inferTupleLiteral(expr, env, wantedTuple);
|
|
10148
10332
|
const elems = [];
|
|
10149
10333
|
let hadSpread = false;
|
|
10334
|
+
const written = [];
|
|
10150
10335
|
for (const el of expr.elements) {
|
|
10151
10336
|
if (el.type === "SpreadElement") {
|
|
10152
10337
|
hadSpread = true;
|
|
10153
|
-
const
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
else elems.push(unknownType);
|
|
10338
|
+
const spread = this.infer(el.argument, env);
|
|
10339
|
+
written.push({ spread: true, type: spread });
|
|
10340
|
+
elems.push(...this.spreadElements(spread));
|
|
10157
10341
|
} else {
|
|
10158
|
-
|
|
10342
|
+
const t = asConst ? this.inferAsConst(el, env) : this.infer(el, env);
|
|
10343
|
+
written.push({ spread: false, type: asConst ? t : this.widenUnlessAsked(t, el) });
|
|
10344
|
+
elems.push(t);
|
|
10159
10345
|
}
|
|
10160
10346
|
}
|
|
10161
10347
|
if (asConst && !hadSpread) return tuple(elems);
|
|
10348
|
+
if (hadSpread) {
|
|
10349
|
+
const tuples = this.spreadTuples(written);
|
|
10350
|
+
if (tuples) return union(tuples.map((elements) => tuple(elements)));
|
|
10351
|
+
}
|
|
10162
10352
|
return arrayOf(elems.length ? union(elems.map((t, i) => {
|
|
10163
10353
|
const element = expr.elements[i];
|
|
10164
10354
|
return asConst || !element || element.type === "SpreadElement" ? t : this.widenUnlessAsked(t, element);
|
|
10165
10355
|
})) : unknownType);
|
|
10166
10356
|
}
|
|
10357
|
+
/** The tuples an array literal with spreads is, when every spread is a
|
|
10358
|
+
* tuple of known length or a union of them: one per way the unions go.
|
|
10359
|
+
* `undefined` when a spread's length is not known (an array, a tuple
|
|
10360
|
+
* with a rest), or the ways multiply past what is worth listing. */
|
|
10361
|
+
spreadTuples(written) {
|
|
10362
|
+
const MAX = 32;
|
|
10363
|
+
let variants = [[]];
|
|
10364
|
+
for (const { spread, type } of written) {
|
|
10365
|
+
if (!spread) {
|
|
10366
|
+
variants = variants.map((v) => [...v, type]);
|
|
10367
|
+
continue;
|
|
10368
|
+
}
|
|
10369
|
+
const options = this.fixedTuples(type);
|
|
10370
|
+
if (!options) return void 0;
|
|
10371
|
+
const next = [];
|
|
10372
|
+
for (const v of variants) for (const option of options) next.push([...v, ...option]);
|
|
10373
|
+
if (next.length > MAX) return void 0;
|
|
10374
|
+
variants = next;
|
|
10375
|
+
}
|
|
10376
|
+
return variants;
|
|
10377
|
+
}
|
|
10378
|
+
/** A tuple of known length's members, or a union of such tuples' — each
|
|
10379
|
+
* a way the value can be. `nil` in a union spreads nothing. */
|
|
10380
|
+
fixedTuples(t, depth = 0) {
|
|
10381
|
+
if (depth > 8) return void 0;
|
|
10382
|
+
const held = this.expand(t);
|
|
10383
|
+
if (held.kind === "tuple") return held.rest ? void 0 : [held.elements];
|
|
10384
|
+
if (held.kind !== "union") return void 0;
|
|
10385
|
+
const out = [];
|
|
10386
|
+
for (const member of held.types) {
|
|
10387
|
+
if (member.kind === "primitive" && member.name === "nil") continue;
|
|
10388
|
+
const options = this.fixedTuples(member, depth + 1);
|
|
10389
|
+
if (!options) return void 0;
|
|
10390
|
+
out.push(...options);
|
|
10391
|
+
}
|
|
10392
|
+
return out.length ? out : void 0;
|
|
10393
|
+
}
|
|
10394
|
+
/** What `...xs` puts into an array: an array's element, a tuple's members,
|
|
10395
|
+
* and for a union each member's — `...(host ? extra : [])` adds `extra`'s
|
|
10396
|
+
* elements and the empty tuple nothing. Anything else, `unknown`. */
|
|
10397
|
+
spreadElements(t, depth = 0) {
|
|
10398
|
+
const held = this.expand(t);
|
|
10399
|
+
if (depth > 8) return [unknownType];
|
|
10400
|
+
switch (held.kind) {
|
|
10401
|
+
case "array":
|
|
10402
|
+
return [held.element];
|
|
10403
|
+
case "tuple":
|
|
10404
|
+
return tupleMembers(held);
|
|
10405
|
+
case "union":
|
|
10406
|
+
return held.types.filter((m) => !(m.kind === "primitive" && m.name === "nil")).flatMap((m) => this.spreadElements(m, depth + 1));
|
|
10407
|
+
case "typeParam":
|
|
10408
|
+
return held.constraint ? this.spreadElements(held.constraint, depth + 1) : [unknownType];
|
|
10409
|
+
case "any":
|
|
10410
|
+
return [anyType];
|
|
10411
|
+
default:
|
|
10412
|
+
return [unknownType];
|
|
10413
|
+
}
|
|
10414
|
+
}
|
|
10167
10415
|
/** `[a, b, ...more]` where a tuple is wanted: the places it names are the
|
|
10168
10416
|
* tuple's, and what comes after them — more elements, or a spread — is
|
|
10169
10417
|
* its rest. */
|
|
@@ -10174,10 +10422,7 @@ var TypeAnalyzer = class {
|
|
|
10174
10422
|
expr.elements.forEach((element, i) => {
|
|
10175
10423
|
if (element.type === "SpreadElement") {
|
|
10176
10424
|
spread = true;
|
|
10177
|
-
|
|
10178
|
-
if (held.kind === "array") tail.push(held.element);
|
|
10179
|
-
else if (held.kind === "tuple") tail.push(...tupleMembers(held));
|
|
10180
|
-
else tail.push(unknownType);
|
|
10425
|
+
tail.push(...this.spreadElements(this.infer(element.argument, env)));
|
|
10181
10426
|
return;
|
|
10182
10427
|
}
|
|
10183
10428
|
const t = this.widenUnlessAsked(this.infer(element, env), element);
|
|
@@ -10194,6 +10439,7 @@ var TypeAnalyzer = class {
|
|
|
10194
10439
|
* The context was recorded by `applyContext` before the value was
|
|
10195
10440
|
* inferred, so this is a lookup rather than a second pass. */
|
|
10196
10441
|
widenUnlessAsked(value, at) {
|
|
10442
|
+
if (!writesLiteral(at)) return value;
|
|
10197
10443
|
const wanted = this.expectedTypeOf.get(at);
|
|
10198
10444
|
if (value.kind === "literal") {
|
|
10199
10445
|
const arms = this.contextArms.get(at);
|
|
@@ -11566,7 +11812,53 @@ function aliasTargets(config, specifier) {
|
|
|
11566
11812
|
|
|
11567
11813
|
// src/project/sourcemap.ts
|
|
11568
11814
|
import { dirname as dirname4, extname, resolve as resolve4 } from "path";
|
|
11815
|
+
|
|
11816
|
+
// src/luau.ts
|
|
11817
|
+
var LUAU_KEYWORDS = /* @__PURE__ */ new Set([
|
|
11818
|
+
"and",
|
|
11819
|
+
"break",
|
|
11820
|
+
"do",
|
|
11821
|
+
"else",
|
|
11822
|
+
"elseif",
|
|
11823
|
+
"end",
|
|
11824
|
+
"false",
|
|
11825
|
+
"for",
|
|
11826
|
+
"function",
|
|
11827
|
+
"if",
|
|
11828
|
+
"in",
|
|
11829
|
+
"local",
|
|
11830
|
+
"nil",
|
|
11831
|
+
"not",
|
|
11832
|
+
"or",
|
|
11833
|
+
"repeat",
|
|
11834
|
+
"return",
|
|
11835
|
+
"then",
|
|
11836
|
+
"true",
|
|
11837
|
+
"until",
|
|
11838
|
+
"while"
|
|
11839
|
+
]);
|
|
11569
11840
|
var IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
11841
|
+
function isIdentifier(name) {
|
|
11842
|
+
return IDENTIFIER.test(name);
|
|
11843
|
+
}
|
|
11844
|
+
function isLuauName(name) {
|
|
11845
|
+
return IDENTIFIER.test(name) && !LUAU_KEYWORDS.has(name);
|
|
11846
|
+
}
|
|
11847
|
+
var NAMED_ESCAPES = {
|
|
11848
|
+
"\\": "\\\\",
|
|
11849
|
+
'"': '\\"',
|
|
11850
|
+
"\n": "\\n",
|
|
11851
|
+
"\r": "\\r",
|
|
11852
|
+
" ": "\\t"
|
|
11853
|
+
};
|
|
11854
|
+
function escapeLuauString(text) {
|
|
11855
|
+
return text.replace(/[\\"\x00-\x1f\x7f]/g, (c) => NAMED_ESCAPES[c] ?? `\\${c.charCodeAt(0).toString().padStart(3, "0")}`);
|
|
11856
|
+
}
|
|
11857
|
+
function luauString(text) {
|
|
11858
|
+
return `"${escapeLuauString(text)}"`;
|
|
11859
|
+
}
|
|
11860
|
+
|
|
11861
|
+
// src/project/sourcemap.ts
|
|
11570
11862
|
var INSTANCE_MEMBERS = /* @__PURE__ */ new Set(["Name", "ClassName", "Parent", "Archivable"]);
|
|
11571
11863
|
var SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([".tilua", ".luau", ".lua"]);
|
|
11572
11864
|
function sourceMapTypes(text, path, options) {
|
|
@@ -11593,7 +11885,7 @@ function sourceMapTypes(text, path, options) {
|
|
|
11593
11885
|
const alias = aliasFor(segments);
|
|
11594
11886
|
aliasOfNode.set(node, alias);
|
|
11595
11887
|
for (const filePath of node.filePaths ?? []) aliasOfFile.set(fileKey(resolve4(directory, filePath)), alias);
|
|
11596
|
-
const className =
|
|
11888
|
+
const className = isIdentifier(node.className) && options.classes.has(node.className) ? node.className : "Instance";
|
|
11597
11889
|
const taken = options.membersOf?.(className) ?? INSTANCE_MEMBERS;
|
|
11598
11890
|
const members = [];
|
|
11599
11891
|
if (parent) members.push(`Parent: ${parent}`);
|
|
@@ -11602,7 +11894,7 @@ function sourceMapTypes(text, path, options) {
|
|
|
11602
11894
|
if (!isNode(child)) continue;
|
|
11603
11895
|
const childAlias = visit(child, [...segments, child.name], alias);
|
|
11604
11896
|
const service = child.name === child.className && options.classes.has(child.className);
|
|
11605
|
-
if (!
|
|
11897
|
+
if (!isIdentifier(child.name) || taken.has(child.name) && !service || named.has(child.name)) continue;
|
|
11606
11898
|
named.add(child.name);
|
|
11607
11899
|
members.push(`${child.name}: ${childAlias}`);
|
|
11608
11900
|
}
|
|
@@ -11644,6 +11936,34 @@ function fileKey(path) {
|
|
|
11644
11936
|
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
11645
11937
|
}
|
|
11646
11938
|
|
|
11939
|
+
// src/loweringHelpers.ts
|
|
11940
|
+
function withoutNil2(type) {
|
|
11941
|
+
if (!type) return [];
|
|
11942
|
+
const members = type.kind === "union" ? type.types : [type];
|
|
11943
|
+
return members.filter((m) => !(m.kind === "primitive" && m.name === "nil"));
|
|
11944
|
+
}
|
|
11945
|
+
function isFunctionType(type) {
|
|
11946
|
+
const members = withoutNil2(type);
|
|
11947
|
+
return members.length > 0 && members.every((m) => m.kind === "function" || m.kind === "intersection" && m.types.every(isFunctionType));
|
|
11948
|
+
}
|
|
11949
|
+
function everyObject(type, test) {
|
|
11950
|
+
const members = withoutNil2(type);
|
|
11951
|
+
return members.length > 0 && members.every((m) => m.kind === "object" && test(m));
|
|
11952
|
+
}
|
|
11953
|
+
function isInstanceOf(type, className) {
|
|
11954
|
+
return everyObject(type, (m) => m.class !== void 0 && m.class.ancestors.includes(className));
|
|
11955
|
+
}
|
|
11956
|
+
function hasMembers(type, names) {
|
|
11957
|
+
return everyObject(type, (m) => names.every((name) => m.properties.has(name)));
|
|
11958
|
+
}
|
|
11959
|
+
function returnsTuple(receiver, method) {
|
|
11960
|
+
return everyObject(receiver, (m) => {
|
|
11961
|
+
const found = m.properties.get(method)?.type;
|
|
11962
|
+
const signatures = !found ? [] : found.kind === "intersection" ? found.types : [found];
|
|
11963
|
+
return signatures.length > 0 && signatures.every((f) => f.kind === "function" && f.returns.kind === "tuple");
|
|
11964
|
+
});
|
|
11965
|
+
}
|
|
11966
|
+
|
|
11647
11967
|
// src/index.ts
|
|
11648
11968
|
var tilua = {
|
|
11649
11969
|
tokenize,
|
|
@@ -11663,6 +11983,7 @@ export {
|
|
|
11663
11983
|
CONFIG_FILE_NAMES,
|
|
11664
11984
|
Keywords,
|
|
11665
11985
|
LANGUAGE_GLOBALS,
|
|
11986
|
+
LUAU_KEYWORDS,
|
|
11666
11987
|
LexError,
|
|
11667
11988
|
Operators,
|
|
11668
11989
|
PRELUDE_SOURCE,
|
|
@@ -11684,20 +12005,27 @@ export {
|
|
|
11684
12005
|
difference,
|
|
11685
12006
|
directivesOf,
|
|
11686
12007
|
equalTypes,
|
|
12008
|
+
escapeLuauString,
|
|
11687
12009
|
falsyType,
|
|
11688
12010
|
findConfig,
|
|
11689
12011
|
fn,
|
|
11690
12012
|
formatType,
|
|
11691
12013
|
getBinding,
|
|
12014
|
+
hasMembers,
|
|
11692
12015
|
intersection,
|
|
11693
12016
|
isAssignable,
|
|
11694
12017
|
isClassType,
|
|
12018
|
+
isFunctionType,
|
|
11695
12019
|
isGlobal,
|
|
12020
|
+
isIdentifier,
|
|
12021
|
+
isInstanceOf,
|
|
12022
|
+
isLuauName,
|
|
11696
12023
|
isPossiblyFalsy,
|
|
11697
12024
|
isPossiblyTruthy,
|
|
11698
12025
|
isUnassignedGlobal,
|
|
11699
12026
|
literal,
|
|
11700
12027
|
loadConfig,
|
|
12028
|
+
luauString,
|
|
11701
12029
|
matchInfer,
|
|
11702
12030
|
moduleCandidates,
|
|
11703
12031
|
moduleExports,
|
|
@@ -11720,6 +12048,7 @@ export {
|
|
|
11720
12048
|
readDirectives,
|
|
11721
12049
|
resolveModulePath,
|
|
11722
12050
|
resolveTypeLibraries,
|
|
12051
|
+
returnsTuple,
|
|
11723
12052
|
setAliasExpander,
|
|
11724
12053
|
setDeferredBound,
|
|
11725
12054
|
sourceMapTypes,
|
|
@@ -11738,5 +12067,6 @@ export {
|
|
|
11738
12067
|
unknownType,
|
|
11739
12068
|
widen,
|
|
11740
12069
|
withAliasName,
|
|
11741
|
-
withoutAliasName
|
|
12070
|
+
withoutAliasName,
|
|
12071
|
+
withoutNil2 as withoutNil
|
|
11742
12072
|
};
|