luaut-parser 3.1.0 → 4.0.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/README.md +49 -0
- package/dist/index.cjs +90 -12
- package/dist/index.d.cts +91 -1
- package/dist/index.d.ts +91 -1
- package/dist/index.js +90 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -160,6 +160,55 @@ brings in names that are types and nothing else: unlike TypeScript, using one
|
|
|
160
160
|
as a value is an error, and only type positions — `typeof A` included — may
|
|
161
161
|
name it. Compiled code keeps no trace of it.
|
|
162
162
|
|
|
163
|
+
**Array and string methods** — an array and a string answer to methods
|
|
164
|
+
written with `:`, the way JavaScript writes them:
|
|
165
|
+
|
|
166
|
+
```luau
|
|
167
|
+
const long = names:filter(function(n) return #n > 3 end):map(string.upper)
|
|
168
|
+
const first = names:find(function(n) return n:startsWith("A") end)
|
|
169
|
+
print(names:join(", "), text:trim(), text:replaceAll(",", ";"))
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Which methods those are is not the language's business. The analyzer looks for
|
|
173
|
+
two types by name — `ArrayMethods<T>` and `StringMethods` — and reads an
|
|
174
|
+
array's or a string's members out of whichever type library declared them;
|
|
175
|
+
without such a library an array has no methods at all.
|
|
176
|
+
|
|
177
|
+
Running them is that library's business too. A library points at a JavaScript
|
|
178
|
+
module in its package.json, and the compiler asks it what a call becomes:
|
|
179
|
+
|
|
180
|
+
```json
|
|
181
|
+
"luaut": { "types": "index.d.luaut", "lowering": "lowering.mjs" }
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import type { LoweringPlugin } from "luaut-parser" // the contract, declared here
|
|
186
|
+
|
|
187
|
+
const plugin: LoweringPlugin = {
|
|
188
|
+
runtime: { array: "local __NAME__ = {}\nfunction __NAME__.filter(t, test) ... end" },
|
|
189
|
+
methodCall({ method, receiver, use }) {
|
|
190
|
+
if (receiver?.kind === "array" && method === "filter") {
|
|
191
|
+
return { callee: `${use("array")}.filter` }
|
|
192
|
+
}
|
|
193
|
+
return undefined
|
|
194
|
+
},
|
|
195
|
+
}
|
|
196
|
+
export default plugin
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`receiver` is the luaut type the analyzer worked out, `use(key)` gives the
|
|
200
|
+
local name that table got — emitted once, at the top of the output, only if a
|
|
201
|
+
call needed it — and the receiver is passed as the call's first argument. An
|
|
202
|
+
answer of `undefined` leaves an ordinary Luau method call, which is what
|
|
203
|
+
`text:upper()` wants, since a string already answers to it.
|
|
204
|
+
|
|
205
|
+
The compiler lowers the language and nothing else: `filter` appears nowhere in
|
|
206
|
+
it.
|
|
207
|
+
|
|
208
|
+
`@luaut/lua` ships the JavaScript-shaped set; there, indices are Luau's (the
|
|
209
|
+
first element is 1, `indexOf` answers `nil` rather than -1) and `push`, `pop`,
|
|
210
|
+
`shift`, `unshift`, `sort` and `reverse` change the array they are called on.
|
|
211
|
+
|
|
163
212
|
**Optionality** — there is no `T?` shorthand. `?` in type position always
|
|
164
213
|
belongs to a conditional type, and in expression position to a ternary or an
|
|
165
214
|
optional chain.
|
package/dist/index.cjs
CHANGED
|
@@ -4824,8 +4824,8 @@ var TypeAnalyzer = class {
|
|
|
4824
4824
|
/** Recursion guard for `preVisitBody`. */
|
|
4825
4825
|
preVisitDepth = 0;
|
|
4826
4826
|
run() {
|
|
4827
|
-
this.registerAliasDefs(preludeProgram().body);
|
|
4828
|
-
for (const lib of this.options.libs ?? []) this.registerAliasDefs(lib.body);
|
|
4827
|
+
this.registerAliasDefs(preludeProgram().body, true);
|
|
4828
|
+
for (const lib of this.options.libs ?? []) this.registerAliasDefs(lib.body, true);
|
|
4829
4829
|
this.registerAliasDefs(this.program.body);
|
|
4830
4830
|
for (const lib of this.options.libs ?? []) this.harvestDeclares(lib.body);
|
|
4831
4831
|
this.registerImportedTypes();
|
|
@@ -4903,10 +4903,28 @@ var TypeAnalyzer = class {
|
|
|
4903
4903
|
}
|
|
4904
4904
|
}
|
|
4905
4905
|
}
|
|
4906
|
-
|
|
4906
|
+
/** `layering` is on for the prelude and for definitions files: a second
|
|
4907
|
+
* library that declares an alias already declared *adds* to it, the way a
|
|
4908
|
+
* second `declare` of a table's name does, so `@luaut/roblox` can give
|
|
4909
|
+
* `StringMethods` Luau's `split` without restating Lua's. The file being
|
|
4910
|
+
* analysed is not a layer: its own alias replaces what the libraries
|
|
4911
|
+
* gave, which is how a project opts out of a set. */
|
|
4912
|
+
registerAliasDefs(block, layering = false) {
|
|
4907
4913
|
for (const stmt of block.statements) {
|
|
4908
4914
|
const alias = stmt.type === "TypeAliasStatement" ? stmt : stmt.type === "ExportTypeAliasStatement" ? stmt.alias : void 0;
|
|
4909
|
-
if (alias)
|
|
4915
|
+
if (alias) {
|
|
4916
|
+
const previous = layering ? this.aliasDefs.get(alias.name.name) : void 0;
|
|
4917
|
+
const node = previous && !previous.class ? {
|
|
4918
|
+
type: "IntersectionTypeNode",
|
|
4919
|
+
types: [previous.node, alias.definition],
|
|
4920
|
+
line: alias.definition.line,
|
|
4921
|
+
column: alias.definition.column
|
|
4922
|
+
} : alias.definition;
|
|
4923
|
+
this.aliasDefs.set(alias.name.name, {
|
|
4924
|
+
params: previous && !previous.class && previous.params.length ? previous.params : alias.generics,
|
|
4925
|
+
node
|
|
4926
|
+
});
|
|
4927
|
+
}
|
|
4910
4928
|
if (stmt.type === "DeclareClassStatement") {
|
|
4911
4929
|
this.aliasDefs.set(stmt.name.name, { params: [], node: stmt.body, class: stmt });
|
|
4912
4930
|
}
|
|
@@ -6171,13 +6189,15 @@ var TypeAnalyzer = class {
|
|
|
6171
6189
|
const objects = this.expectedMembers(expected).filter((m) => m.kind === "object");
|
|
6172
6190
|
if (!objects.length) return;
|
|
6173
6191
|
for (const field of e.fields) {
|
|
6174
|
-
if (field.type !== "TableFieldNamed") continue;
|
|
6175
|
-
const key = field.key.type === "Identifier" ? field.key.name : field.key.value;
|
|
6192
|
+
if (field.type !== "TableFieldNamed" && field.type !== "TableFieldShorthand") continue;
|
|
6193
|
+
const key = field.type === "TableFieldShorthand" ? field.name.name : field.key.type === "Identifier" ? field.key.name : field.key.value;
|
|
6176
6194
|
const types = objects.flatMap((o) => {
|
|
6177
6195
|
const property = o.properties.get(key);
|
|
6178
6196
|
return property ? [property.type] : o.indexer ? [o.indexer.value] : [];
|
|
6179
6197
|
});
|
|
6180
|
-
if (types.length)
|
|
6198
|
+
if (types.length) {
|
|
6199
|
+
this.applyContext(field.type === "TableFieldShorthand" ? field.name : field.value, union(types));
|
|
6200
|
+
}
|
|
6181
6201
|
}
|
|
6182
6202
|
}
|
|
6183
6203
|
/** The members of an expected type worth matching a literal against:
|
|
@@ -6976,6 +6996,25 @@ var TypeAnalyzer = class {
|
|
|
6976
6996
|
this.resolvingAliases.delete(t.name);
|
|
6977
6997
|
}
|
|
6978
6998
|
}
|
|
6999
|
+
/** `names:filter(f)`, `text:trim()` — the methods arrays and strings have.
|
|
7000
|
+
* They are written in the prelude as `ArrayMethods<T>` and
|
|
7001
|
+
* `StringMethods`, so a file (or a type library) that declares one of
|
|
7002
|
+
* those names again replaces the whole set, and nothing here is a special
|
|
7003
|
+
* case in the analyzer. The build lowers each call to a plain function. */
|
|
7004
|
+
builtInMethod(t, name) {
|
|
7005
|
+
const element = t.kind === "array" ? t.element : t.kind === "tuple" ? union(t.elements) : void 0;
|
|
7006
|
+
const methodTable = element !== void 0 ? "ArrayMethods" : t.kind === "primitive" && t.name === "string" || t.kind === "literal" && t.base === "string" ? "StringMethods" : void 0;
|
|
7007
|
+
const def = methodTable === void 0 ? void 0 : this.aliasDefs.get(methodTable);
|
|
7008
|
+
if (!def || def.class) return void 0;
|
|
7009
|
+
const table = this.expand(this.instantiateAlias(def, element !== void 0 ? [element] : []));
|
|
7010
|
+
const parts = table.kind === "intersection" ? table.types.map((m) => this.expand(m)) : [table];
|
|
7011
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
7012
|
+
const part = parts[i];
|
|
7013
|
+
const property = part.kind === "object" ? part.properties.get(name) : void 0;
|
|
7014
|
+
if (property) return property.type;
|
|
7015
|
+
}
|
|
7016
|
+
return void 0;
|
|
7017
|
+
}
|
|
6979
7018
|
propertyType(raw, name) {
|
|
6980
7019
|
const t = this.deferredAccess(this.expand(raw));
|
|
6981
7020
|
if (t.kind === "object") {
|
|
@@ -6983,6 +7022,8 @@ var TypeAnalyzer = class {
|
|
|
6983
7022
|
if (p) return p.optional ? optional(p.type) : p.type;
|
|
6984
7023
|
if (t.indexer) return t.indexer.value;
|
|
6985
7024
|
}
|
|
7025
|
+
const built = this.builtInMethod(t, name);
|
|
7026
|
+
if (built) return built;
|
|
6986
7027
|
if (t.kind === "union") return union(t.types.map((m) => this.propertyType(m, name)));
|
|
6987
7028
|
if (t.kind === "intersection") {
|
|
6988
7029
|
const parts = t.types.map((m) => this.propertyType(m, name)).filter((p) => p.kind !== "unknown");
|
|
@@ -7336,7 +7377,21 @@ var TypeAnalyzer = class {
|
|
|
7336
7377
|
}
|
|
7337
7378
|
}
|
|
7338
7379
|
if (asConst && !hadSpread) return tuple(elems);
|
|
7339
|
-
return arrayOf(elems.length ? union(elems.map((t) =>
|
|
7380
|
+
return arrayOf(elems.length ? union(elems.map((t, i) => {
|
|
7381
|
+
const element = expr.elements[i];
|
|
7382
|
+
return asConst || !element || element.type === "SpreadElement" ? t : this.widenUnlessAsked(t, element);
|
|
7383
|
+
})) : unknownType);
|
|
7384
|
+
}
|
|
7385
|
+
/** A literal written inside a fresh table or array widens — `{ n = 1 }` is
|
|
7386
|
+
* `{ n: number }` — unless the surroundings said a literal belongs there.
|
|
7387
|
+
* `request({ Method: "GET" })` keeps `"GET"` when `Method` is a union of
|
|
7388
|
+
* string literals, exactly as TypeScript's contextual typing does, and
|
|
7389
|
+
* goes on widening to `string` when the parameter only says `string`.
|
|
7390
|
+
* The context was recorded by `applyContext` before the value was
|
|
7391
|
+
* inferred, so this is a lookup rather than a second pass. */
|
|
7392
|
+
widenUnlessAsked(value, at) {
|
|
7393
|
+
const wanted = this.expectedTypeOf.get(at);
|
|
7394
|
+
return wanted === void 0 ? widen(value) : this.keepContextualLiterals(value, wanted);
|
|
7340
7395
|
}
|
|
7341
7396
|
inferObject(expr, env, asConst) {
|
|
7342
7397
|
const entries = [];
|
|
@@ -7344,16 +7399,24 @@ var TypeAnalyzer = class {
|
|
|
7344
7399
|
for (const field of expr.fields) {
|
|
7345
7400
|
if (field.type === "TableFieldNamed") {
|
|
7346
7401
|
const key = field.key.type === "Identifier" ? field.key.name : field.key.value;
|
|
7347
|
-
const v = asConst ? this.inferAsConst(field.value, env) :
|
|
7402
|
+
const v = asConst ? this.inferAsConst(field.value, env) : this.widenUnlessAsked(this.infer(field.value, env), field.value);
|
|
7348
7403
|
entries.push([key, { type: v, optional: false, readonly: asConst }]);
|
|
7349
7404
|
} else if (field.type === "TableFieldShorthand") {
|
|
7350
7405
|
const v = this.infer(field.name, env);
|
|
7351
|
-
entries.push([field.name.name, {
|
|
7406
|
+
entries.push([field.name.name, {
|
|
7407
|
+
type: asConst ? v : this.widenUnlessAsked(v, field.name),
|
|
7408
|
+
optional: false,
|
|
7409
|
+
readonly: asConst
|
|
7410
|
+
}]);
|
|
7352
7411
|
} else if (field.type === "TableFieldComputed") {
|
|
7353
7412
|
const k = this.infer(field.key, env);
|
|
7354
7413
|
const v = this.infer(field.value, env);
|
|
7355
7414
|
if (k.kind === "literal" && typeof k.value === "string") {
|
|
7356
|
-
entries.push([k.value, {
|
|
7415
|
+
entries.push([k.value, {
|
|
7416
|
+
type: asConst ? v : this.widenUnlessAsked(v, field.value),
|
|
7417
|
+
optional: false,
|
|
7418
|
+
readonly: asConst
|
|
7419
|
+
}]);
|
|
7357
7420
|
} else {
|
|
7358
7421
|
indexer = mergeIndexer(indexer, { key: widen(k), value: asConst ? v : widen(v) });
|
|
7359
7422
|
}
|
|
@@ -8178,6 +8241,7 @@ function offsetPosition(source, offset) {
|
|
|
8178
8241
|
var import_node_path2 = require("path");
|
|
8179
8242
|
function resolveTypeLibraries(config, host = nodeHost) {
|
|
8180
8243
|
const files = [];
|
|
8244
|
+
const lowerings = [];
|
|
8181
8245
|
const problems = [];
|
|
8182
8246
|
const loaded = /* @__PURE__ */ new Set();
|
|
8183
8247
|
const addFile = (file) => {
|
|
@@ -8195,6 +8259,8 @@ function resolveTypeLibraries(config, host = nodeHost) {
|
|
|
8195
8259
|
if (found) addPackage(found.directory, found.file, visiting);
|
|
8196
8260
|
}
|
|
8197
8261
|
addFile(entryFile);
|
|
8262
|
+
const lowering = loweringModule(directory, host, problems, config);
|
|
8263
|
+
if (lowering) lowerings.push(lowering);
|
|
8198
8264
|
};
|
|
8199
8265
|
for (const entry of config.types) {
|
|
8200
8266
|
const relative = entry.startsWith("./") || entry.startsWith("../") || entry.startsWith("/") || /^[A-Za-z]:[\\/]/.test(entry);
|
|
@@ -8221,7 +8287,19 @@ function resolveTypeLibraries(config, host = nodeHost) {
|
|
|
8221
8287
|
});
|
|
8222
8288
|
}
|
|
8223
8289
|
}
|
|
8224
|
-
return { files, problems };
|
|
8290
|
+
return { files, lowerings, problems };
|
|
8291
|
+
}
|
|
8292
|
+
function loweringModule(directory, host, problems, config) {
|
|
8293
|
+
const manifest = readJson((0, import_node_path2.join)(directory, "package.json"), host);
|
|
8294
|
+
const declared = manifest?.luaut?.lowering;
|
|
8295
|
+
if (typeof declared !== "string") return void 0;
|
|
8296
|
+
const from = typeof manifest?.name === "string" ? manifest.name : directory;
|
|
8297
|
+
const file = (0, import_node_path2.resolve)(directory, declared);
|
|
8298
|
+
if (host.readFile(file) === void 0) {
|
|
8299
|
+
problems.push({ file: config.path, message: `'${from}' names a lowering module '${declared}', which is not there` });
|
|
8300
|
+
return void 0;
|
|
8301
|
+
}
|
|
8302
|
+
return { file, from };
|
|
8225
8303
|
}
|
|
8226
8304
|
var ENTRY_FILE = "index.d.luaut";
|
|
8227
8305
|
function packageEntry(directory, host) {
|
package/dist/index.d.cts
CHANGED
|
@@ -1309,6 +1309,13 @@ resolveModule?: (specifier: string) => ModuleExports | undefined): ModuleExports
|
|
|
1309
1309
|
*
|
|
1310
1310
|
* What a runtime provides — `print`, `string`, `game` — is not here: that is a
|
|
1311
1311
|
* type library's job (`@luaut/lua`, `@luaut/roblox`).
|
|
1312
|
+
*
|
|
1313
|
+
* The methods an array and a string answer to — `names:filter(f)`,
|
|
1314
|
+
* `text:trim()` — are a library's too. `propertyType` reads them from types
|
|
1315
|
+
* named `ArrayMethods<T>` and `StringMethods`, whichever library declares
|
|
1316
|
+
* those; the library also says which of them the compiler must emit code for
|
|
1317
|
+
* (`luaut.methods` in its package.json). Nothing about `filter` is written
|
|
1318
|
+
* into the analyzer.
|
|
1312
1319
|
*/
|
|
1313
1320
|
declare const PRELUDE_SOURCE = "\n-- In Luau only `nil` and `false` are falsy: `0` and `\"\"` are truthy.\n-- These are what truthiness narrowing computes, made available to write down.\ntype Falsy = nil | false\ntype Truthy<T> = T - Falsy\n\n-- `-` is set difference. Over a union it drops members; over a concrete type\n-- it simplifies away; over an opaque type (`unknown`, an unresolved parameter)\n-- it is kept, so `Exclude<unknown, 1>` stays `unknown - 1`.\ntype Exclude<T, U> = T - U\ntype Extract<T, U> = T extends U ? T : never\ntype NonNullable<T> = T - nil\n\ntype ReturnType<T> = T extends (...unknown) -> infer R ? R : never\ntype Parameters<T> = T extends (...infer P) -> unknown ? P : never\n\ntype Partial<T> = { [K in keyof T]?: T[K] }\ntype Required<T> = { [K in keyof T]-?: T[K] }\ntype Readonly<T> = { readonly [K in keyof T]: T[K] }\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] }\n\ntype Pick<T, K> = { [P in K]: T[P] }\ntype Omit<T, K> = Pick<T, Exclude<keyof T, K>>\ntype Record<K, V> = { [P in K]: V }\n";
|
|
1314
1321
|
|
|
@@ -1367,10 +1374,93 @@ declare function stripJsonComments(text: string): string;
|
|
|
1367
1374
|
interface TypeLibraries {
|
|
1368
1375
|
/** Definitions files, dependencies before what depends on them. */
|
|
1369
1376
|
readonly files: readonly string[];
|
|
1377
|
+
/** Lowering modules the libraries ship, in the same order. */
|
|
1378
|
+
readonly lowerings: readonly LoweringModule[];
|
|
1370
1379
|
readonly problems: readonly ConfigProblem[];
|
|
1371
1380
|
}
|
|
1381
|
+
/** A library's own lowering: JavaScript the compiler loads and asks what a
|
|
1382
|
+
* call written against this library's types should become.
|
|
1383
|
+
*
|
|
1384
|
+
* The library declares the *types* in its definitions file; this is the other
|
|
1385
|
+
* half. `names:filter(f)` is a call to a function only because `@luaut/lua`
|
|
1386
|
+
* says so and ships the Luau behind it — the compiler knows how to ask, and
|
|
1387
|
+
* nothing about `filter`.
|
|
1388
|
+
*
|
|
1389
|
+
* `luaut.lowering` in the package.json names the module; what it must export
|
|
1390
|
+
* is the compiler's business (see luaut-build's `LoweringPlugin`). */
|
|
1391
|
+
interface LoweringModule {
|
|
1392
|
+
/** The JavaScript module to load. */
|
|
1393
|
+
readonly file: string;
|
|
1394
|
+
/** The package it came from, for reporting. */
|
|
1395
|
+
readonly from: string;
|
|
1396
|
+
}
|
|
1372
1397
|
declare function resolveTypeLibraries(config: LuautConfig, host?: ProjectHost): TypeLibraries;
|
|
1373
1398
|
|
|
1399
|
+
/**
|
|
1400
|
+
* The contract between a type library and the compiler.
|
|
1401
|
+
*
|
|
1402
|
+
* A library's definitions file says what a value *is*; when what it gives is
|
|
1403
|
+
* not something the value already answers to, the library must also say how
|
|
1404
|
+
* it runs. `names:filter(f)` is a call to a function because `@luaut/lua`
|
|
1405
|
+
* declares the method and ships the Luau behind it — the compiler lowers the
|
|
1406
|
+
* language (`import`, `export`, `?.`, `a ? b : c`, destructuring, spreads)
|
|
1407
|
+
* and asks a library about everything else.
|
|
1408
|
+
*
|
|
1409
|
+
* These types are declarations only: nothing here runs, and the parser never
|
|
1410
|
+
* loads a lowering module. They live here so a library can be written in
|
|
1411
|
+
* TypeScript against the same contract the compiler implements, without
|
|
1412
|
+
* depending on the compiler.
|
|
1413
|
+
*
|
|
1414
|
+
* // lowering.ts, in a type library
|
|
1415
|
+
* import type { LoweringPlugin } from "luaut-parser"
|
|
1416
|
+
*
|
|
1417
|
+
* const plugin: LoweringPlugin = {
|
|
1418
|
+
* runtime: { array: "local __NAME__ = {}\n..." },
|
|
1419
|
+
* methodCall({ method, receiver, use }) {
|
|
1420
|
+
* if (receiver?.kind === "array" && method === "filter") {
|
|
1421
|
+
* return { callee: `${use("array")}.filter` }
|
|
1422
|
+
* }
|
|
1423
|
+
* return undefined
|
|
1424
|
+
* },
|
|
1425
|
+
* }
|
|
1426
|
+
* export default plugin
|
|
1427
|
+
*/
|
|
1428
|
+
|
|
1429
|
+
interface LoweringPlugin {
|
|
1430
|
+
/** Luau the plugin needs in the output, by a key it chooses. Each is a
|
|
1431
|
+
* file's worth of source with `__NAME__` standing for the local the
|
|
1432
|
+
* compiler gives it, and each is emitted once, at the top of the output,
|
|
1433
|
+
* only if `use` asked for it:
|
|
1434
|
+
*
|
|
1435
|
+
* local __NAME__ = {}
|
|
1436
|
+
* function __NAME__.filter(t, test) ... end
|
|
1437
|
+
*/
|
|
1438
|
+
readonly runtime?: Readonly<Record<string, string>>;
|
|
1439
|
+
/** What `receiver:method(...)` becomes. `undefined` leaves a plain Luau
|
|
1440
|
+
* method call, which is what a value that answers to the method itself
|
|
1441
|
+
* wants — `text:upper()` reaches Lua's own. */
|
|
1442
|
+
methodCall?(call: MethodCall): MethodLowering | undefined;
|
|
1443
|
+
}
|
|
1444
|
+
interface MethodCall {
|
|
1445
|
+
/** The name written after `:`. */
|
|
1446
|
+
readonly method: string;
|
|
1447
|
+
/** The receiver's type, as the analyzer worked it out. `undefined` when
|
|
1448
|
+
* nothing typed it, where a plugin should decline rather than guess. */
|
|
1449
|
+
readonly receiver: Type | undefined;
|
|
1450
|
+
/** How many arguments were written. */
|
|
1451
|
+
readonly argumentCount: number;
|
|
1452
|
+
/** The local name the output gives one of `runtime`'s entries, emitting
|
|
1453
|
+
* it if this is the first call that needed it. */
|
|
1454
|
+
use(runtime: string): string;
|
|
1455
|
+
}
|
|
1456
|
+
interface MethodLowering {
|
|
1457
|
+
/** What to call instead: a name, or a `table.member` path — usually built
|
|
1458
|
+
* from `use(...)`. */
|
|
1459
|
+
readonly callee: string;
|
|
1460
|
+
/** Pass the receiver as the first argument. Default: yes. */
|
|
1461
|
+
readonly passReceiver?: boolean;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1374
1464
|
/** Every file `specifier` could mean from `fromFile`, in the order they are
|
|
1375
1465
|
* tried. A resolver that caches should watch all of them: creating an earlier
|
|
1376
1466
|
* candidate changes what the import means. */
|
|
@@ -1417,4 +1507,4 @@ declare const luautparser: {
|
|
|
1417
1507
|
readonly analyzeTypes: typeof analyzeTypes;
|
|
1418
1508
|
};
|
|
1419
1509
|
|
|
1420
|
-
export { type AnalyzeTypesOptions, type AnyType, type ArrayExpression, type ArrayPattern, type ArrayPatternElement, type ArrayType, type ArrayTypeNode, type AsConstExpression, type AssignmentStatement, type BaseNode, type BaseToken, type BinaryExpression, BinaryOperators, type Binding, type BindingId, type BindingKind, type BindingTarget, type Block, type BooleanLiteral, type BreakStatement, CONFIG_FILE_NAMES, type CallExpression, type CallStatement, type ClassInfo, type CompoundAssignmentStatement, type ConditionalType, type ConditionalTypeNode, type ConfigLookup, type ConfigProblem, type ContinueStatement, type DeclareClassStatement, type DeclareStatement, type DifferenceType, type DifferenceTypeNode, type Directive, type DirectiveKind, type DirectiveOutcome, type Directives, type DoStatement, type EOFToken, type ErrorExpression, type ErrorStatement, type ExportAllStatement, type ExportDefaultStatement, type ExportNamedStatement, type ExportSpecifier, type ExportStatement, type ExportTypeAliasStatement, type ExportedType, type Expression, type FunctionBody, type FunctionDeclaration, type FunctionDeclarationStatement, type FunctionExpression, type FunctionName, type FunctionParam, type FunctionParameter, type FunctionSignature, type FunctionType, type FunctionTypeNode, type FunctionTypeParameter, type GenericForStatement, type GenericRefType, type GenericTypeParameter, type Identifier, type IdentifierPattern, type IdentifierToken, type IfClause, type IfElseExpression, type IfStatement, type ImportSpecifier, type ImportStatement, type IndexExpression, type IndexedAccessType, type IndexedAccessTypeNode, type InferType, type InferTypeNode, type InterpolatedStringExpression, type InterpolatedStringPart, type InterpolatedStringPart_Expression, type InterpolatedStringPart_String, type InterpolatedStringToken, type IntersectionType, type IntersectionTypeNode, type KeyofType, type KeyofTypeNode, type KeywordToken, Keywords, LexError, type LiteralToken, type LiteralType, type LuautConfig, type MappedType, type MappedTypeNode, type MemberExpression, type MethodCallExpression, type ModuleExports, type NeverType, type NilLiteral, type Node, type NumberLiteral, type NumericForStatement, type ObjectPattern, type ObjectPatternProperty, type ObjectProperty, type ObjectType, type OperatorToken, Operators, PRELUDE_SOURCE, type ParenthesizedExpression, type ParenthesizedTypeNode, ParseError, type ParserOptions, type PrimitiveName, type PrimitiveType, type Program, type ProjectHost, type PunctuatorToken, Punctuators, type RecoverResult, type RepeatStatement, type ReturnStatement, type SatisfiesExpression, type ScopeAnalysis, type ScopeDiagnostic, type SourceComment, type SourceMapNode, type SourceMapOptions, type SourceMapTypes, type SpreadElement, type Statement, type StringLiteral, type TableExpression, type TableField, type TableTypeNode, type TableTypeProperty, type TemplateLiteralType, type TemplateLiteralTypeNode, type Token, type TokenizeOptions, type TupleType, type TupleTypeNode, type Type, type TypeAliasStatement, type TypeAnalysis, type TypeAssertionExpression, type TypeDiagnostic, type TypeLibraries, type TypeLiteralBoolean, type TypeLiteralNumber, type TypeLiteralString, type TypeNode, type TypePackNode, type TypeParamType, type TypePredicate, type TypePredicateNode, type TypeReference, type TypedIdentifier, type TypeofTypeNode, UNUSED_EXPECT_ERROR, type UnaryExpression, UnaryOperators, type UnionType, type UnionTypeNode, type UnknownType, type VarargExpression, type VariableDeclaration, type VariadicTypeNode, type WhileStatement, analyzeScopes, analyzeTypes, anyType, applyDirectives, arrayOf, booleanType, bufferType, containsTypeParam, luautparser as default, difference, directivesOf, equalTypes, falsyType, findConfig, fn, formatType, getBinding, intersection, isAssignable, isClassType, isGlobal, isPossiblyFalsy, isPossiblyTruthy, isUnassignedGlobal, literal, loadConfig, luautparser, matchInfer, moduleCandidates, moduleExports, narrowExclude, narrowFalsy, narrowTo, narrowTruthy, neverType, nilType, nodeHost, numberType, objectType, optional, overlaps, parse, parseExpressionFromSource, parseTokens, parseWithRecovery, primitive, readDirectives, resolveModulePath, resolveTypeLibraries, setAliasExpander, sourceMapTypes, stringType, stripJsonComments, substitute, templateMatches, threadType, tokenize, tuple, typeParam, unify, union, unknownType, widen };
|
|
1510
|
+
export { type AnalyzeTypesOptions, type AnyType, type ArrayExpression, type ArrayPattern, type ArrayPatternElement, type ArrayType, type ArrayTypeNode, type AsConstExpression, type AssignmentStatement, type BaseNode, type BaseToken, type BinaryExpression, BinaryOperators, type Binding, type BindingId, type BindingKind, type BindingTarget, type Block, type BooleanLiteral, type BreakStatement, CONFIG_FILE_NAMES, type CallExpression, type CallStatement, type ClassInfo, type CompoundAssignmentStatement, type ConditionalType, type ConditionalTypeNode, type ConfigLookup, type ConfigProblem, type ContinueStatement, type DeclareClassStatement, type DeclareStatement, type DifferenceType, type DifferenceTypeNode, type Directive, type DirectiveKind, type DirectiveOutcome, type Directives, type DoStatement, type EOFToken, type ErrorExpression, type ErrorStatement, type ExportAllStatement, type ExportDefaultStatement, type ExportNamedStatement, type ExportSpecifier, type ExportStatement, type ExportTypeAliasStatement, type ExportedType, type Expression, type FunctionBody, type FunctionDeclaration, type FunctionDeclarationStatement, type FunctionExpression, type FunctionName, type FunctionParam, type FunctionParameter, type FunctionSignature, type FunctionType, type FunctionTypeNode, type FunctionTypeParameter, type GenericForStatement, type GenericRefType, type GenericTypeParameter, type Identifier, type IdentifierPattern, type IdentifierToken, type IfClause, type IfElseExpression, type IfStatement, type ImportSpecifier, type ImportStatement, type IndexExpression, type IndexedAccessType, type IndexedAccessTypeNode, type InferType, type InferTypeNode, type InterpolatedStringExpression, type InterpolatedStringPart, type InterpolatedStringPart_Expression, type InterpolatedStringPart_String, type InterpolatedStringToken, type IntersectionType, type IntersectionTypeNode, type KeyofType, type KeyofTypeNode, type KeywordToken, Keywords, LexError, type LiteralToken, type LiteralType, type LoweringModule, type LoweringPlugin, type LuautConfig, type MappedType, type MappedTypeNode, type MemberExpression, type MethodCall, type MethodCallExpression, type MethodLowering, type ModuleExports, type NeverType, type NilLiteral, type Node, type NumberLiteral, type NumericForStatement, type ObjectPattern, type ObjectPatternProperty, type ObjectProperty, type ObjectType, type OperatorToken, Operators, PRELUDE_SOURCE, type ParenthesizedExpression, type ParenthesizedTypeNode, ParseError, type ParserOptions, type PrimitiveName, type PrimitiveType, type Program, type ProjectHost, type PunctuatorToken, Punctuators, type RecoverResult, type RepeatStatement, type ReturnStatement, type SatisfiesExpression, type ScopeAnalysis, type ScopeDiagnostic, type SourceComment, type SourceMapNode, type SourceMapOptions, type SourceMapTypes, type SpreadElement, type Statement, type StringLiteral, type TableExpression, type TableField, type TableTypeNode, type TableTypeProperty, type TemplateLiteralType, type TemplateLiteralTypeNode, type Token, type TokenizeOptions, type TupleType, type TupleTypeNode, type Type, type TypeAliasStatement, type TypeAnalysis, type TypeAssertionExpression, type TypeDiagnostic, type TypeLibraries, type TypeLiteralBoolean, type TypeLiteralNumber, type TypeLiteralString, type TypeNode, type TypePackNode, type TypeParamType, type TypePredicate, type TypePredicateNode, type TypeReference, type TypedIdentifier, type TypeofTypeNode, UNUSED_EXPECT_ERROR, type UnaryExpression, UnaryOperators, type UnionType, type UnionTypeNode, type UnknownType, type VarargExpression, type VariableDeclaration, type VariadicTypeNode, type WhileStatement, analyzeScopes, analyzeTypes, anyType, applyDirectives, arrayOf, booleanType, bufferType, containsTypeParam, luautparser as default, difference, directivesOf, equalTypes, falsyType, findConfig, fn, formatType, getBinding, intersection, isAssignable, isClassType, isGlobal, isPossiblyFalsy, isPossiblyTruthy, isUnassignedGlobal, literal, loadConfig, luautparser, matchInfer, moduleCandidates, moduleExports, narrowExclude, narrowFalsy, narrowTo, narrowTruthy, neverType, nilType, nodeHost, numberType, objectType, optional, overlaps, parse, parseExpressionFromSource, parseTokens, parseWithRecovery, primitive, readDirectives, resolveModulePath, resolveTypeLibraries, setAliasExpander, sourceMapTypes, stringType, stripJsonComments, substitute, templateMatches, threadType, tokenize, tuple, typeParam, unify, union, unknownType, widen };
|
package/dist/index.d.ts
CHANGED
|
@@ -1309,6 +1309,13 @@ resolveModule?: (specifier: string) => ModuleExports | undefined): ModuleExports
|
|
|
1309
1309
|
*
|
|
1310
1310
|
* What a runtime provides — `print`, `string`, `game` — is not here: that is a
|
|
1311
1311
|
* type library's job (`@luaut/lua`, `@luaut/roblox`).
|
|
1312
|
+
*
|
|
1313
|
+
* The methods an array and a string answer to — `names:filter(f)`,
|
|
1314
|
+
* `text:trim()` — are a library's too. `propertyType` reads them from types
|
|
1315
|
+
* named `ArrayMethods<T>` and `StringMethods`, whichever library declares
|
|
1316
|
+
* those; the library also says which of them the compiler must emit code for
|
|
1317
|
+
* (`luaut.methods` in its package.json). Nothing about `filter` is written
|
|
1318
|
+
* into the analyzer.
|
|
1312
1319
|
*/
|
|
1313
1320
|
declare const PRELUDE_SOURCE = "\n-- In Luau only `nil` and `false` are falsy: `0` and `\"\"` are truthy.\n-- These are what truthiness narrowing computes, made available to write down.\ntype Falsy = nil | false\ntype Truthy<T> = T - Falsy\n\n-- `-` is set difference. Over a union it drops members; over a concrete type\n-- it simplifies away; over an opaque type (`unknown`, an unresolved parameter)\n-- it is kept, so `Exclude<unknown, 1>` stays `unknown - 1`.\ntype Exclude<T, U> = T - U\ntype Extract<T, U> = T extends U ? T : never\ntype NonNullable<T> = T - nil\n\ntype ReturnType<T> = T extends (...unknown) -> infer R ? R : never\ntype Parameters<T> = T extends (...infer P) -> unknown ? P : never\n\ntype Partial<T> = { [K in keyof T]?: T[K] }\ntype Required<T> = { [K in keyof T]-?: T[K] }\ntype Readonly<T> = { readonly [K in keyof T]: T[K] }\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] }\n\ntype Pick<T, K> = { [P in K]: T[P] }\ntype Omit<T, K> = Pick<T, Exclude<keyof T, K>>\ntype Record<K, V> = { [P in K]: V }\n";
|
|
1314
1321
|
|
|
@@ -1367,10 +1374,93 @@ declare function stripJsonComments(text: string): string;
|
|
|
1367
1374
|
interface TypeLibraries {
|
|
1368
1375
|
/** Definitions files, dependencies before what depends on them. */
|
|
1369
1376
|
readonly files: readonly string[];
|
|
1377
|
+
/** Lowering modules the libraries ship, in the same order. */
|
|
1378
|
+
readonly lowerings: readonly LoweringModule[];
|
|
1370
1379
|
readonly problems: readonly ConfigProblem[];
|
|
1371
1380
|
}
|
|
1381
|
+
/** A library's own lowering: JavaScript the compiler loads and asks what a
|
|
1382
|
+
* call written against this library's types should become.
|
|
1383
|
+
*
|
|
1384
|
+
* The library declares the *types* in its definitions file; this is the other
|
|
1385
|
+
* half. `names:filter(f)` is a call to a function only because `@luaut/lua`
|
|
1386
|
+
* says so and ships the Luau behind it — the compiler knows how to ask, and
|
|
1387
|
+
* nothing about `filter`.
|
|
1388
|
+
*
|
|
1389
|
+
* `luaut.lowering` in the package.json names the module; what it must export
|
|
1390
|
+
* is the compiler's business (see luaut-build's `LoweringPlugin`). */
|
|
1391
|
+
interface LoweringModule {
|
|
1392
|
+
/** The JavaScript module to load. */
|
|
1393
|
+
readonly file: string;
|
|
1394
|
+
/** The package it came from, for reporting. */
|
|
1395
|
+
readonly from: string;
|
|
1396
|
+
}
|
|
1372
1397
|
declare function resolveTypeLibraries(config: LuautConfig, host?: ProjectHost): TypeLibraries;
|
|
1373
1398
|
|
|
1399
|
+
/**
|
|
1400
|
+
* The contract between a type library and the compiler.
|
|
1401
|
+
*
|
|
1402
|
+
* A library's definitions file says what a value *is*; when what it gives is
|
|
1403
|
+
* not something the value already answers to, the library must also say how
|
|
1404
|
+
* it runs. `names:filter(f)` is a call to a function because `@luaut/lua`
|
|
1405
|
+
* declares the method and ships the Luau behind it — the compiler lowers the
|
|
1406
|
+
* language (`import`, `export`, `?.`, `a ? b : c`, destructuring, spreads)
|
|
1407
|
+
* and asks a library about everything else.
|
|
1408
|
+
*
|
|
1409
|
+
* These types are declarations only: nothing here runs, and the parser never
|
|
1410
|
+
* loads a lowering module. They live here so a library can be written in
|
|
1411
|
+
* TypeScript against the same contract the compiler implements, without
|
|
1412
|
+
* depending on the compiler.
|
|
1413
|
+
*
|
|
1414
|
+
* // lowering.ts, in a type library
|
|
1415
|
+
* import type { LoweringPlugin } from "luaut-parser"
|
|
1416
|
+
*
|
|
1417
|
+
* const plugin: LoweringPlugin = {
|
|
1418
|
+
* runtime: { array: "local __NAME__ = {}\n..." },
|
|
1419
|
+
* methodCall({ method, receiver, use }) {
|
|
1420
|
+
* if (receiver?.kind === "array" && method === "filter") {
|
|
1421
|
+
* return { callee: `${use("array")}.filter` }
|
|
1422
|
+
* }
|
|
1423
|
+
* return undefined
|
|
1424
|
+
* },
|
|
1425
|
+
* }
|
|
1426
|
+
* export default plugin
|
|
1427
|
+
*/
|
|
1428
|
+
|
|
1429
|
+
interface LoweringPlugin {
|
|
1430
|
+
/** Luau the plugin needs in the output, by a key it chooses. Each is a
|
|
1431
|
+
* file's worth of source with `__NAME__` standing for the local the
|
|
1432
|
+
* compiler gives it, and each is emitted once, at the top of the output,
|
|
1433
|
+
* only if `use` asked for it:
|
|
1434
|
+
*
|
|
1435
|
+
* local __NAME__ = {}
|
|
1436
|
+
* function __NAME__.filter(t, test) ... end
|
|
1437
|
+
*/
|
|
1438
|
+
readonly runtime?: Readonly<Record<string, string>>;
|
|
1439
|
+
/** What `receiver:method(...)` becomes. `undefined` leaves a plain Luau
|
|
1440
|
+
* method call, which is what a value that answers to the method itself
|
|
1441
|
+
* wants — `text:upper()` reaches Lua's own. */
|
|
1442
|
+
methodCall?(call: MethodCall): MethodLowering | undefined;
|
|
1443
|
+
}
|
|
1444
|
+
interface MethodCall {
|
|
1445
|
+
/** The name written after `:`. */
|
|
1446
|
+
readonly method: string;
|
|
1447
|
+
/** The receiver's type, as the analyzer worked it out. `undefined` when
|
|
1448
|
+
* nothing typed it, where a plugin should decline rather than guess. */
|
|
1449
|
+
readonly receiver: Type | undefined;
|
|
1450
|
+
/** How many arguments were written. */
|
|
1451
|
+
readonly argumentCount: number;
|
|
1452
|
+
/** The local name the output gives one of `runtime`'s entries, emitting
|
|
1453
|
+
* it if this is the first call that needed it. */
|
|
1454
|
+
use(runtime: string): string;
|
|
1455
|
+
}
|
|
1456
|
+
interface MethodLowering {
|
|
1457
|
+
/** What to call instead: a name, or a `table.member` path — usually built
|
|
1458
|
+
* from `use(...)`. */
|
|
1459
|
+
readonly callee: string;
|
|
1460
|
+
/** Pass the receiver as the first argument. Default: yes. */
|
|
1461
|
+
readonly passReceiver?: boolean;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1374
1464
|
/** Every file `specifier` could mean from `fromFile`, in the order they are
|
|
1375
1465
|
* tried. A resolver that caches should watch all of them: creating an earlier
|
|
1376
1466
|
* candidate changes what the import means. */
|
|
@@ -1417,4 +1507,4 @@ declare const luautparser: {
|
|
|
1417
1507
|
readonly analyzeTypes: typeof analyzeTypes;
|
|
1418
1508
|
};
|
|
1419
1509
|
|
|
1420
|
-
export { type AnalyzeTypesOptions, type AnyType, type ArrayExpression, type ArrayPattern, type ArrayPatternElement, type ArrayType, type ArrayTypeNode, type AsConstExpression, type AssignmentStatement, type BaseNode, type BaseToken, type BinaryExpression, BinaryOperators, type Binding, type BindingId, type BindingKind, type BindingTarget, type Block, type BooleanLiteral, type BreakStatement, CONFIG_FILE_NAMES, type CallExpression, type CallStatement, type ClassInfo, type CompoundAssignmentStatement, type ConditionalType, type ConditionalTypeNode, type ConfigLookup, type ConfigProblem, type ContinueStatement, type DeclareClassStatement, type DeclareStatement, type DifferenceType, type DifferenceTypeNode, type Directive, type DirectiveKind, type DirectiveOutcome, type Directives, type DoStatement, type EOFToken, type ErrorExpression, type ErrorStatement, type ExportAllStatement, type ExportDefaultStatement, type ExportNamedStatement, type ExportSpecifier, type ExportStatement, type ExportTypeAliasStatement, type ExportedType, type Expression, type FunctionBody, type FunctionDeclaration, type FunctionDeclarationStatement, type FunctionExpression, type FunctionName, type FunctionParam, type FunctionParameter, type FunctionSignature, type FunctionType, type FunctionTypeNode, type FunctionTypeParameter, type GenericForStatement, type GenericRefType, type GenericTypeParameter, type Identifier, type IdentifierPattern, type IdentifierToken, type IfClause, type IfElseExpression, type IfStatement, type ImportSpecifier, type ImportStatement, type IndexExpression, type IndexedAccessType, type IndexedAccessTypeNode, type InferType, type InferTypeNode, type InterpolatedStringExpression, type InterpolatedStringPart, type InterpolatedStringPart_Expression, type InterpolatedStringPart_String, type InterpolatedStringToken, type IntersectionType, type IntersectionTypeNode, type KeyofType, type KeyofTypeNode, type KeywordToken, Keywords, LexError, type LiteralToken, type LiteralType, type LuautConfig, type MappedType, type MappedTypeNode, type MemberExpression, type MethodCallExpression, type ModuleExports, type NeverType, type NilLiteral, type Node, type NumberLiteral, type NumericForStatement, type ObjectPattern, type ObjectPatternProperty, type ObjectProperty, type ObjectType, type OperatorToken, Operators, PRELUDE_SOURCE, type ParenthesizedExpression, type ParenthesizedTypeNode, ParseError, type ParserOptions, type PrimitiveName, type PrimitiveType, type Program, type ProjectHost, type PunctuatorToken, Punctuators, type RecoverResult, type RepeatStatement, type ReturnStatement, type SatisfiesExpression, type ScopeAnalysis, type ScopeDiagnostic, type SourceComment, type SourceMapNode, type SourceMapOptions, type SourceMapTypes, type SpreadElement, type Statement, type StringLiteral, type TableExpression, type TableField, type TableTypeNode, type TableTypeProperty, type TemplateLiteralType, type TemplateLiteralTypeNode, type Token, type TokenizeOptions, type TupleType, type TupleTypeNode, type Type, type TypeAliasStatement, type TypeAnalysis, type TypeAssertionExpression, type TypeDiagnostic, type TypeLibraries, type TypeLiteralBoolean, type TypeLiteralNumber, type TypeLiteralString, type TypeNode, type TypePackNode, type TypeParamType, type TypePredicate, type TypePredicateNode, type TypeReference, type TypedIdentifier, type TypeofTypeNode, UNUSED_EXPECT_ERROR, type UnaryExpression, UnaryOperators, type UnionType, type UnionTypeNode, type UnknownType, type VarargExpression, type VariableDeclaration, type VariadicTypeNode, type WhileStatement, analyzeScopes, analyzeTypes, anyType, applyDirectives, arrayOf, booleanType, bufferType, containsTypeParam, luautparser as default, difference, directivesOf, equalTypes, falsyType, findConfig, fn, formatType, getBinding, intersection, isAssignable, isClassType, isGlobal, isPossiblyFalsy, isPossiblyTruthy, isUnassignedGlobal, literal, loadConfig, luautparser, matchInfer, moduleCandidates, moduleExports, narrowExclude, narrowFalsy, narrowTo, narrowTruthy, neverType, nilType, nodeHost, numberType, objectType, optional, overlaps, parse, parseExpressionFromSource, parseTokens, parseWithRecovery, primitive, readDirectives, resolveModulePath, resolveTypeLibraries, setAliasExpander, sourceMapTypes, stringType, stripJsonComments, substitute, templateMatches, threadType, tokenize, tuple, typeParam, unify, union, unknownType, widen };
|
|
1510
|
+
export { type AnalyzeTypesOptions, type AnyType, type ArrayExpression, type ArrayPattern, type ArrayPatternElement, type ArrayType, type ArrayTypeNode, type AsConstExpression, type AssignmentStatement, type BaseNode, type BaseToken, type BinaryExpression, BinaryOperators, type Binding, type BindingId, type BindingKind, type BindingTarget, type Block, type BooleanLiteral, type BreakStatement, CONFIG_FILE_NAMES, type CallExpression, type CallStatement, type ClassInfo, type CompoundAssignmentStatement, type ConditionalType, type ConditionalTypeNode, type ConfigLookup, type ConfigProblem, type ContinueStatement, type DeclareClassStatement, type DeclareStatement, type DifferenceType, type DifferenceTypeNode, type Directive, type DirectiveKind, type DirectiveOutcome, type Directives, type DoStatement, type EOFToken, type ErrorExpression, type ErrorStatement, type ExportAllStatement, type ExportDefaultStatement, type ExportNamedStatement, type ExportSpecifier, type ExportStatement, type ExportTypeAliasStatement, type ExportedType, type Expression, type FunctionBody, type FunctionDeclaration, type FunctionDeclarationStatement, type FunctionExpression, type FunctionName, type FunctionParam, type FunctionParameter, type FunctionSignature, type FunctionType, type FunctionTypeNode, type FunctionTypeParameter, type GenericForStatement, type GenericRefType, type GenericTypeParameter, type Identifier, type IdentifierPattern, type IdentifierToken, type IfClause, type IfElseExpression, type IfStatement, type ImportSpecifier, type ImportStatement, type IndexExpression, type IndexedAccessType, type IndexedAccessTypeNode, type InferType, type InferTypeNode, type InterpolatedStringExpression, type InterpolatedStringPart, type InterpolatedStringPart_Expression, type InterpolatedStringPart_String, type InterpolatedStringToken, type IntersectionType, type IntersectionTypeNode, type KeyofType, type KeyofTypeNode, type KeywordToken, Keywords, LexError, type LiteralToken, type LiteralType, type LoweringModule, type LoweringPlugin, type LuautConfig, type MappedType, type MappedTypeNode, type MemberExpression, type MethodCall, type MethodCallExpression, type MethodLowering, type ModuleExports, type NeverType, type NilLiteral, type Node, type NumberLiteral, type NumericForStatement, type ObjectPattern, type ObjectPatternProperty, type ObjectProperty, type ObjectType, type OperatorToken, Operators, PRELUDE_SOURCE, type ParenthesizedExpression, type ParenthesizedTypeNode, ParseError, type ParserOptions, type PrimitiveName, type PrimitiveType, type Program, type ProjectHost, type PunctuatorToken, Punctuators, type RecoverResult, type RepeatStatement, type ReturnStatement, type SatisfiesExpression, type ScopeAnalysis, type ScopeDiagnostic, type SourceComment, type SourceMapNode, type SourceMapOptions, type SourceMapTypes, type SpreadElement, type Statement, type StringLiteral, type TableExpression, type TableField, type TableTypeNode, type TableTypeProperty, type TemplateLiteralType, type TemplateLiteralTypeNode, type Token, type TokenizeOptions, type TupleType, type TupleTypeNode, type Type, type TypeAliasStatement, type TypeAnalysis, type TypeAssertionExpression, type TypeDiagnostic, type TypeLibraries, type TypeLiteralBoolean, type TypeLiteralNumber, type TypeLiteralString, type TypeNode, type TypePackNode, type TypeParamType, type TypePredicate, type TypePredicateNode, type TypeReference, type TypedIdentifier, type TypeofTypeNode, UNUSED_EXPECT_ERROR, type UnaryExpression, UnaryOperators, type UnionType, type UnionTypeNode, type UnknownType, type VarargExpression, type VariableDeclaration, type VariadicTypeNode, type WhileStatement, analyzeScopes, analyzeTypes, anyType, applyDirectives, arrayOf, booleanType, bufferType, containsTypeParam, luautparser as default, difference, directivesOf, equalTypes, falsyType, findConfig, fn, formatType, getBinding, intersection, isAssignable, isClassType, isGlobal, isPossiblyFalsy, isPossiblyTruthy, isUnassignedGlobal, literal, loadConfig, luautparser, matchInfer, moduleCandidates, moduleExports, narrowExclude, narrowFalsy, narrowTo, narrowTruthy, neverType, nilType, nodeHost, numberType, objectType, optional, overlaps, parse, parseExpressionFromSource, parseTokens, parseWithRecovery, primitive, readDirectives, resolveModulePath, resolveTypeLibraries, setAliasExpander, sourceMapTypes, stringType, stripJsonComments, substitute, templateMatches, threadType, tokenize, tuple, typeParam, unify, union, unknownType, widen };
|
package/dist/index.js
CHANGED
|
@@ -4726,8 +4726,8 @@ var TypeAnalyzer = class {
|
|
|
4726
4726
|
/** Recursion guard for `preVisitBody`. */
|
|
4727
4727
|
preVisitDepth = 0;
|
|
4728
4728
|
run() {
|
|
4729
|
-
this.registerAliasDefs(preludeProgram().body);
|
|
4730
|
-
for (const lib of this.options.libs ?? []) this.registerAliasDefs(lib.body);
|
|
4729
|
+
this.registerAliasDefs(preludeProgram().body, true);
|
|
4730
|
+
for (const lib of this.options.libs ?? []) this.registerAliasDefs(lib.body, true);
|
|
4731
4731
|
this.registerAliasDefs(this.program.body);
|
|
4732
4732
|
for (const lib of this.options.libs ?? []) this.harvestDeclares(lib.body);
|
|
4733
4733
|
this.registerImportedTypes();
|
|
@@ -4805,10 +4805,28 @@ var TypeAnalyzer = class {
|
|
|
4805
4805
|
}
|
|
4806
4806
|
}
|
|
4807
4807
|
}
|
|
4808
|
-
|
|
4808
|
+
/** `layering` is on for the prelude and for definitions files: a second
|
|
4809
|
+
* library that declares an alias already declared *adds* to it, the way a
|
|
4810
|
+
* second `declare` of a table's name does, so `@luaut/roblox` can give
|
|
4811
|
+
* `StringMethods` Luau's `split` without restating Lua's. The file being
|
|
4812
|
+
* analysed is not a layer: its own alias replaces what the libraries
|
|
4813
|
+
* gave, which is how a project opts out of a set. */
|
|
4814
|
+
registerAliasDefs(block, layering = false) {
|
|
4809
4815
|
for (const stmt of block.statements) {
|
|
4810
4816
|
const alias = stmt.type === "TypeAliasStatement" ? stmt : stmt.type === "ExportTypeAliasStatement" ? stmt.alias : void 0;
|
|
4811
|
-
if (alias)
|
|
4817
|
+
if (alias) {
|
|
4818
|
+
const previous = layering ? this.aliasDefs.get(alias.name.name) : void 0;
|
|
4819
|
+
const node = previous && !previous.class ? {
|
|
4820
|
+
type: "IntersectionTypeNode",
|
|
4821
|
+
types: [previous.node, alias.definition],
|
|
4822
|
+
line: alias.definition.line,
|
|
4823
|
+
column: alias.definition.column
|
|
4824
|
+
} : alias.definition;
|
|
4825
|
+
this.aliasDefs.set(alias.name.name, {
|
|
4826
|
+
params: previous && !previous.class && previous.params.length ? previous.params : alias.generics,
|
|
4827
|
+
node
|
|
4828
|
+
});
|
|
4829
|
+
}
|
|
4812
4830
|
if (stmt.type === "DeclareClassStatement") {
|
|
4813
4831
|
this.aliasDefs.set(stmt.name.name, { params: [], node: stmt.body, class: stmt });
|
|
4814
4832
|
}
|
|
@@ -6073,13 +6091,15 @@ var TypeAnalyzer = class {
|
|
|
6073
6091
|
const objects = this.expectedMembers(expected).filter((m) => m.kind === "object");
|
|
6074
6092
|
if (!objects.length) return;
|
|
6075
6093
|
for (const field of e.fields) {
|
|
6076
|
-
if (field.type !== "TableFieldNamed") continue;
|
|
6077
|
-
const key = field.key.type === "Identifier" ? field.key.name : field.key.value;
|
|
6094
|
+
if (field.type !== "TableFieldNamed" && field.type !== "TableFieldShorthand") continue;
|
|
6095
|
+
const key = field.type === "TableFieldShorthand" ? field.name.name : field.key.type === "Identifier" ? field.key.name : field.key.value;
|
|
6078
6096
|
const types = objects.flatMap((o) => {
|
|
6079
6097
|
const property = o.properties.get(key);
|
|
6080
6098
|
return property ? [property.type] : o.indexer ? [o.indexer.value] : [];
|
|
6081
6099
|
});
|
|
6082
|
-
if (types.length)
|
|
6100
|
+
if (types.length) {
|
|
6101
|
+
this.applyContext(field.type === "TableFieldShorthand" ? field.name : field.value, union(types));
|
|
6102
|
+
}
|
|
6083
6103
|
}
|
|
6084
6104
|
}
|
|
6085
6105
|
/** The members of an expected type worth matching a literal against:
|
|
@@ -6878,6 +6898,25 @@ var TypeAnalyzer = class {
|
|
|
6878
6898
|
this.resolvingAliases.delete(t.name);
|
|
6879
6899
|
}
|
|
6880
6900
|
}
|
|
6901
|
+
/** `names:filter(f)`, `text:trim()` — the methods arrays and strings have.
|
|
6902
|
+
* They are written in the prelude as `ArrayMethods<T>` and
|
|
6903
|
+
* `StringMethods`, so a file (or a type library) that declares one of
|
|
6904
|
+
* those names again replaces the whole set, and nothing here is a special
|
|
6905
|
+
* case in the analyzer. The build lowers each call to a plain function. */
|
|
6906
|
+
builtInMethod(t, name) {
|
|
6907
|
+
const element = t.kind === "array" ? t.element : t.kind === "tuple" ? union(t.elements) : void 0;
|
|
6908
|
+
const methodTable = element !== void 0 ? "ArrayMethods" : t.kind === "primitive" && t.name === "string" || t.kind === "literal" && t.base === "string" ? "StringMethods" : void 0;
|
|
6909
|
+
const def = methodTable === void 0 ? void 0 : this.aliasDefs.get(methodTable);
|
|
6910
|
+
if (!def || def.class) return void 0;
|
|
6911
|
+
const table = this.expand(this.instantiateAlias(def, element !== void 0 ? [element] : []));
|
|
6912
|
+
const parts = table.kind === "intersection" ? table.types.map((m) => this.expand(m)) : [table];
|
|
6913
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
6914
|
+
const part = parts[i];
|
|
6915
|
+
const property = part.kind === "object" ? part.properties.get(name) : void 0;
|
|
6916
|
+
if (property) return property.type;
|
|
6917
|
+
}
|
|
6918
|
+
return void 0;
|
|
6919
|
+
}
|
|
6881
6920
|
propertyType(raw, name) {
|
|
6882
6921
|
const t = this.deferredAccess(this.expand(raw));
|
|
6883
6922
|
if (t.kind === "object") {
|
|
@@ -6885,6 +6924,8 @@ var TypeAnalyzer = class {
|
|
|
6885
6924
|
if (p) return p.optional ? optional(p.type) : p.type;
|
|
6886
6925
|
if (t.indexer) return t.indexer.value;
|
|
6887
6926
|
}
|
|
6927
|
+
const built = this.builtInMethod(t, name);
|
|
6928
|
+
if (built) return built;
|
|
6888
6929
|
if (t.kind === "union") return union(t.types.map((m) => this.propertyType(m, name)));
|
|
6889
6930
|
if (t.kind === "intersection") {
|
|
6890
6931
|
const parts = t.types.map((m) => this.propertyType(m, name)).filter((p) => p.kind !== "unknown");
|
|
@@ -7238,7 +7279,21 @@ var TypeAnalyzer = class {
|
|
|
7238
7279
|
}
|
|
7239
7280
|
}
|
|
7240
7281
|
if (asConst && !hadSpread) return tuple(elems);
|
|
7241
|
-
return arrayOf(elems.length ? union(elems.map((t) =>
|
|
7282
|
+
return arrayOf(elems.length ? union(elems.map((t, i) => {
|
|
7283
|
+
const element = expr.elements[i];
|
|
7284
|
+
return asConst || !element || element.type === "SpreadElement" ? t : this.widenUnlessAsked(t, element);
|
|
7285
|
+
})) : unknownType);
|
|
7286
|
+
}
|
|
7287
|
+
/** A literal written inside a fresh table or array widens — `{ n = 1 }` is
|
|
7288
|
+
* `{ n: number }` — unless the surroundings said a literal belongs there.
|
|
7289
|
+
* `request({ Method: "GET" })` keeps `"GET"` when `Method` is a union of
|
|
7290
|
+
* string literals, exactly as TypeScript's contextual typing does, and
|
|
7291
|
+
* goes on widening to `string` when the parameter only says `string`.
|
|
7292
|
+
* The context was recorded by `applyContext` before the value was
|
|
7293
|
+
* inferred, so this is a lookup rather than a second pass. */
|
|
7294
|
+
widenUnlessAsked(value, at) {
|
|
7295
|
+
const wanted = this.expectedTypeOf.get(at);
|
|
7296
|
+
return wanted === void 0 ? widen(value) : this.keepContextualLiterals(value, wanted);
|
|
7242
7297
|
}
|
|
7243
7298
|
inferObject(expr, env, asConst) {
|
|
7244
7299
|
const entries = [];
|
|
@@ -7246,16 +7301,24 @@ var TypeAnalyzer = class {
|
|
|
7246
7301
|
for (const field of expr.fields) {
|
|
7247
7302
|
if (field.type === "TableFieldNamed") {
|
|
7248
7303
|
const key = field.key.type === "Identifier" ? field.key.name : field.key.value;
|
|
7249
|
-
const v = asConst ? this.inferAsConst(field.value, env) :
|
|
7304
|
+
const v = asConst ? this.inferAsConst(field.value, env) : this.widenUnlessAsked(this.infer(field.value, env), field.value);
|
|
7250
7305
|
entries.push([key, { type: v, optional: false, readonly: asConst }]);
|
|
7251
7306
|
} else if (field.type === "TableFieldShorthand") {
|
|
7252
7307
|
const v = this.infer(field.name, env);
|
|
7253
|
-
entries.push([field.name.name, {
|
|
7308
|
+
entries.push([field.name.name, {
|
|
7309
|
+
type: asConst ? v : this.widenUnlessAsked(v, field.name),
|
|
7310
|
+
optional: false,
|
|
7311
|
+
readonly: asConst
|
|
7312
|
+
}]);
|
|
7254
7313
|
} else if (field.type === "TableFieldComputed") {
|
|
7255
7314
|
const k = this.infer(field.key, env);
|
|
7256
7315
|
const v = this.infer(field.value, env);
|
|
7257
7316
|
if (k.kind === "literal" && typeof k.value === "string") {
|
|
7258
|
-
entries.push([k.value, {
|
|
7317
|
+
entries.push([k.value, {
|
|
7318
|
+
type: asConst ? v : this.widenUnlessAsked(v, field.value),
|
|
7319
|
+
optional: false,
|
|
7320
|
+
readonly: asConst
|
|
7321
|
+
}]);
|
|
7259
7322
|
} else {
|
|
7260
7323
|
indexer = mergeIndexer(indexer, { key: widen(k), value: asConst ? v : widen(v) });
|
|
7261
7324
|
}
|
|
@@ -8080,6 +8143,7 @@ function offsetPosition(source, offset) {
|
|
|
8080
8143
|
import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
|
|
8081
8144
|
function resolveTypeLibraries(config, host = nodeHost) {
|
|
8082
8145
|
const files = [];
|
|
8146
|
+
const lowerings = [];
|
|
8083
8147
|
const problems = [];
|
|
8084
8148
|
const loaded = /* @__PURE__ */ new Set();
|
|
8085
8149
|
const addFile = (file) => {
|
|
@@ -8097,6 +8161,8 @@ function resolveTypeLibraries(config, host = nodeHost) {
|
|
|
8097
8161
|
if (found) addPackage(found.directory, found.file, visiting);
|
|
8098
8162
|
}
|
|
8099
8163
|
addFile(entryFile);
|
|
8164
|
+
const lowering = loweringModule(directory, host, problems, config);
|
|
8165
|
+
if (lowering) lowerings.push(lowering);
|
|
8100
8166
|
};
|
|
8101
8167
|
for (const entry of config.types) {
|
|
8102
8168
|
const relative = entry.startsWith("./") || entry.startsWith("../") || entry.startsWith("/") || /^[A-Za-z]:[\\/]/.test(entry);
|
|
@@ -8123,7 +8189,19 @@ function resolveTypeLibraries(config, host = nodeHost) {
|
|
|
8123
8189
|
});
|
|
8124
8190
|
}
|
|
8125
8191
|
}
|
|
8126
|
-
return { files, problems };
|
|
8192
|
+
return { files, lowerings, problems };
|
|
8193
|
+
}
|
|
8194
|
+
function loweringModule(directory, host, problems, config) {
|
|
8195
|
+
const manifest = readJson(join2(directory, "package.json"), host);
|
|
8196
|
+
const declared = manifest?.luaut?.lowering;
|
|
8197
|
+
if (typeof declared !== "string") return void 0;
|
|
8198
|
+
const from = typeof manifest?.name === "string" ? manifest.name : directory;
|
|
8199
|
+
const file = resolve2(directory, declared);
|
|
8200
|
+
if (host.readFile(file) === void 0) {
|
|
8201
|
+
problems.push({ file: config.path, message: `'${from}' names a lowering module '${declared}', which is not there` });
|
|
8202
|
+
return void 0;
|
|
8203
|
+
}
|
|
8204
|
+
return { file, from };
|
|
8127
8205
|
}
|
|
8128
8206
|
var ENTRY_FILE = "index.d.luaut";
|
|
8129
8207
|
function packageEntry(directory, host) {
|