luaut-parser 1.1.0 → 2.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 +71 -30
- package/dist/index.cjs +501 -41
- package/dist/index.d.cts +98 -23
- package/dist/index.d.ts +98 -23
- package/dist/index.js +493 -31
- package/package.json +4 -5
- package/dist/luau.d.luaut +0 -245
- package/dist/roblox.d.luaut +0 -503
package/README.md
CHANGED
|
@@ -11,13 +11,20 @@ npm install luaut-parser
|
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
|
+
import { readFileSync } from "node:fs"
|
|
14
15
|
import {
|
|
15
|
-
parse, analyzeScopes, analyzeTypes,
|
|
16
|
+
parse, analyzeScopes, analyzeTypes,
|
|
17
|
+
findConfig, resolveTypeLibraries, resolveModulePath,
|
|
16
18
|
} from "luaut-parser"
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
const
|
|
20
|
+
// The project the file belongs to, and the type libraries it names.
|
|
21
|
+
const { config } = findConfig(file)
|
|
22
|
+
const libs = config ? resolveTypeLibraries(config).files.map(f => parse(readFileSync(f, "utf8"))) : []
|
|
23
|
+
const globals = libs.flatMap(lib => lib.body.statements.flatMap(s => s.type === "DeclareStatement" ? [s.name] : []))
|
|
24
|
+
|
|
25
|
+
const program = parse(readFileSync(file, "utf8"))
|
|
26
|
+
const scopes = analyzeScopes(program, { builtinGlobals: globals })
|
|
27
|
+
const types = analyzeTypes(program, scopes, { libs })
|
|
21
28
|
|
|
22
29
|
for (const d of [...scopes.diagnostics, ...types.diagnostics]) console.log(d.message)
|
|
23
30
|
```
|
|
@@ -28,41 +35,69 @@ for (const d of [...scopes.diagnostics, ...types.diagnostics]) console.log(d.mes
|
|
|
28
35
|
|---|---|---|
|
|
29
36
|
| `parse(source)` | `Program` — every node carries `line`/`column` spans | everything |
|
|
30
37
|
| `analyzeScopes(program, opts)` | `bindingOf`, `bindings`, `references`, `diagnostics` | go-to-definition, find-references, rename |
|
|
31
|
-
| `analyzeTypes(program, scopes, opts)` | `typeOf`, `narrowedTypeOf`, `bindingType`, `typeOfTypeNode`, `aliases`, `diagnostics` | hover,
|
|
38
|
+
| `analyzeTypes(program, scopes, opts)` | `typeOf`, `narrowedTypeOf`, `bindingType`, `typeOfTypeNode`, `expectedTypeOf`, `aliases`, `diagnostics` | hover, completion, type errors |
|
|
32
39
|
|
|
33
40
|
Every name in the AST has a node with its own span — including the ones that
|
|
34
41
|
used to be bare strings: `DeclareStatement.id`, `TableTypeProperty.key`,
|
|
35
42
|
`FunctionTypeParameter.id`, `GenericTypeParameter.id`, `InferTypeNode.id`,
|
|
36
43
|
`MappedTypeNode.parameterId`. `typeOfTypeNode` gives what each type
|
|
37
|
-
annotation resolves to,
|
|
44
|
+
annotation resolves to, and `expectedTypeOf` what each call argument should
|
|
45
|
+
be, so a tool never has to re-derive a type from text.
|
|
38
46
|
|
|
39
47
|
`parseWithRecovery(source)` returns `{ program, errors }` instead of throwing —
|
|
40
48
|
use it for editors, where the text is usually mid-edit.
|
|
41
49
|
|
|
42
50
|
Neither analysis mutates the AST; both return side tables.
|
|
43
51
|
|
|
44
|
-
##
|
|
52
|
+
## Projects
|
|
45
53
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
54
|
+
**No types are built in** — not `print`, not `string`, not `game`. A project
|
|
55
|
+
lists the type libraries it uses in `luaut.config.json`, the way TypeScript
|
|
56
|
+
uses `@types/*`:
|
|
49
57
|
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
analyzeTypes(program, scopes, { libs: [luauLib] }) // core Luau only
|
|
58
|
+
```bash
|
|
59
|
+
npm i -D @luaut/roblox # or just @luaut/luau
|
|
53
60
|
```
|
|
54
61
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
```jsonc
|
|
63
|
+
// luaut.config.json
|
|
64
|
+
{
|
|
65
|
+
"types": ["roblox"], // @luaut/roblox, which brings @luaut/luau
|
|
66
|
+
"paths": { "@shared/*": ["src/shared/*"] }, // import aliases, as in tsconfig
|
|
67
|
+
"sourceMap": "sourcemap.json" // a Rojo sourcemap, or null
|
|
68
|
+
}
|
|
62
69
|
```
|
|
63
70
|
|
|
64
|
-
|
|
65
|
-
|
|
71
|
+
- **Which config applies** — the nearest one in the file's folder or above.
|
|
72
|
+
`luaut.config.json` and `luaut.config.jsonc` in the same folder is an error.
|
|
73
|
+
Both forms accept comments and trailing commas.
|
|
74
|
+
- **`types`** — `"luau"` is looked up as `@luaut/luau`, then as a package named
|
|
75
|
+
`luau`, in `node_modules` from the config upward. A full package name or a
|
|
76
|
+
relative path (`"./types"`, `"./defs.d.luaut"`) works too. A type library's
|
|
77
|
+
own type-library dependencies load first.
|
|
78
|
+
- **`paths`** — tsconfig rules: an exact pattern wins, then the `*` pattern
|
|
79
|
+
with the longest prefix; targets resolve from `baseUrl` (default: the
|
|
80
|
+
config's folder).
|
|
81
|
+
- **`sourceMap`** — the instance tree becomes types: `game` and `workspace`
|
|
82
|
+
follow it, and a file the tree maps gets its own `script`, so
|
|
83
|
+
`script.Parent.Remotes` is typed. A `.luaut` file matches the Luau file of
|
|
84
|
+
the same path.
|
|
85
|
+
|
|
86
|
+
| function | does |
|
|
87
|
+
|---|---|
|
|
88
|
+
| `findConfig(file, host?)` | the config that applies, problems with it, and every path searched |
|
|
89
|
+
| `loadConfig(path, host?)` | read and check one config |
|
|
90
|
+
| `resolveTypeLibraries(config, host?)` | the `.d.luaut` files to load, in order |
|
|
91
|
+
| `moduleCandidates(from, specifier, config?)` / `resolveModulePath(...)` | what an `import` means |
|
|
92
|
+
| `sourceMapTypes(text, path, { classes })` | the tree's types, and `scriptFor(file)` |
|
|
93
|
+
|
|
94
|
+
Every problem comes back as `{ file, message, line, column }`, pointing into
|
|
95
|
+
the config (or sourcemap) it is about. `host` reads files — pass your own to
|
|
96
|
+
read unsaved editor buffers or to record what was read.
|
|
97
|
+
|
|
98
|
+
`type` / `typeof` are **not** special-cased in the analyzer either: they are
|
|
99
|
+
overload sets in `@luaut/luau`, and narrowing is derived from them. Without a
|
|
100
|
+
library that declares them, they narrow nothing.
|
|
66
101
|
|
|
67
102
|
## The language, in brief
|
|
68
103
|
|
|
@@ -82,6 +117,10 @@ name: T | nil -- must be written, but may be nil
|
|
|
82
117
|
Omitting an argument requires `?` (or a default), as in TypeScript — a
|
|
83
118
|
parameter typed `T | nil` still has to be passed something.
|
|
84
119
|
|
|
120
|
+
**Calls** — every argument is checked against its parameter, and a generic
|
|
121
|
+
parameter against its constraint (`GetService<K extends keyof Services>`
|
|
122
|
+
rejects `""`).
|
|
123
|
+
|
|
85
124
|
**Narrowing** follows TypeScript's model: references (`x`, `x.a.b`, `x["k"]`)
|
|
86
125
|
rather than just variables, discriminated unions at any depth, `and`/`or`,
|
|
87
126
|
early return, `break`/`continue`, `error()` (declared `-> never`), user type
|
|
@@ -103,6 +142,8 @@ written in luaut on top of those, not built in.
|
|
|
103
142
|
`typeof(expr)` spelling works too. It is compile-time only, unrelated to the
|
|
104
143
|
`typeof(v)` function that returns a string at runtime.
|
|
105
144
|
|
|
145
|
+
**Modules** — `import` / `export`, export lists, re-exports and `export *`.
|
|
146
|
+
|
|
106
147
|
## Options
|
|
107
148
|
|
|
108
149
|
```ts
|
|
@@ -111,10 +152,10 @@ analyzeScopes(program, {
|
|
|
111
152
|
})
|
|
112
153
|
|
|
113
154
|
analyzeTypes(program, scopes, {
|
|
114
|
-
libs
|
|
155
|
+
libs, // parsed `.d.luaut` definitions
|
|
115
156
|
globalTypes: { … }, // types for specific globals; wins over `libs`
|
|
116
157
|
libTypes: { … }, // extra named types for annotations
|
|
117
|
-
diagnostics: true, // emit
|
|
158
|
+
diagnostics: true, // emit type errors (default)
|
|
118
159
|
resolveModule: specifier => exportsOfThatFile,
|
|
119
160
|
// what an `import` sees; without it imports are `any`
|
|
120
161
|
})
|
|
@@ -123,22 +164,22 @@ analyzeTypes(program, scopes, {
|
|
|
123
164
|
## Known limitations
|
|
124
165
|
|
|
125
166
|
- `export * as ns from` and namespace imports (`import * as ns`) are not
|
|
126
|
-
supported.
|
|
127
|
-
(`export { a } from`) and `export * from` are.
|
|
167
|
+
supported.
|
|
128
168
|
- `setmetatable` and metatables are not modelled.
|
|
129
169
|
- Accessing a property a type does not have yields `unknown` rather than an
|
|
130
|
-
error; assigning to a `readonly` property is not reported
|
|
131
|
-
|
|
170
|
+
error; assigning to a `readonly` property is not reported.
|
|
171
|
+
- A sourcemap child whose name is not an identifier (`"My Part"`) is not typed.
|
|
132
172
|
|
|
133
173
|
## Development
|
|
134
174
|
|
|
135
175
|
```bash
|
|
136
176
|
npm install
|
|
137
|
-
npm test #
|
|
177
|
+
npm test # smoketests (types via smoketest/luaut.config.json), then project tests
|
|
138
178
|
npm run build
|
|
139
179
|
npm run typecheck
|
|
140
180
|
```
|
|
141
181
|
|
|
142
182
|
`smoketest/` is the test suite. Each file annotates its bindings with the type
|
|
143
183
|
they should infer to, so a wrong result surfaces as a diagnostic rather than
|
|
144
|
-
something to eyeball.
|
|
184
|
+
something to eyeball. `scripts/project.test.ts` covers configs, type
|
|
185
|
+
libraries, import paths and sourcemaps against an in-memory file system.
|