@tilua/compiler 9.0.0 → 9.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -6
- package/dist/{chunk-VUIF62OE.js → chunk-3RJVN4KC.js} +41 -3
- package/dist/cli.cjs +196 -38
- package/dist/cli.js +162 -39
- package/dist/index.cjs +38 -3
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -18,9 +18,11 @@ tilua [<entry>] [--out <file>] [--output single|chunks|files] [--config <tilua.c
|
|
|
18
18
|
tilua init
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
`--watch` builds again whenever a
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
`--watch` builds again whenever a file it reads changes — a `.tilua`, the
|
|
22
|
+
config, a Rojo project, and with an `outDir` of its own anything under
|
|
23
|
+
`rootDir` — and keeps going. `tilua init` sets up the Rojo project in the
|
|
24
|
+
folder: `src` compiled to `out`, and the project pointed at `out` — see
|
|
25
|
+
`@tilua-types/roblox`.
|
|
24
26
|
|
|
25
27
|
Every module the entry imports is bundled in. Problems are reported as
|
|
26
28
|
`file:line:column message`. Type errors are checked against the config's
|
|
@@ -486,7 +488,12 @@ tilua # the config above this folder says the rest
|
|
|
486
488
|
`rootDir` is the config's folder unless set, and `outDir` is `rootDir`, so
|
|
487
489
|
each `.luau` lands beside its `.tilua`. Every file it writes ends with a line
|
|
488
490
|
saying tilua wrote it: the CLI never replaces a `.luau` without that line —
|
|
489
|
-
one written by hand — and removes one with it whose `.tilua` is gone.
|
|
491
|
+
one written by hand — and removes one with it whose `.tilua` is gone.
|
|
492
|
+
|
|
493
|
+
With an `outDir` of its own, the CLI makes it a mirror of `rootDir`: every
|
|
494
|
+
`.tilua` compiled, every other file copied as it is, every folder there, and
|
|
495
|
+
nothing else. `outDir` is the build's alone, so it may not hold `rootDir` or
|
|
496
|
+
the project, nor sit under `rootDir`. `--out` names the folder in place of
|
|
490
497
|
`outDir`. From the API, `bundle({ output: "files", root })` without an
|
|
491
498
|
`entry`; `result.files` holds every file, its path from `outDir`.
|
|
492
499
|
|
|
@@ -539,12 +546,12 @@ a script that requires any of them knows what it got.
|
|
|
539
546
|
<!-- compatibility:start -->
|
|
540
547
|
## Compatibility
|
|
541
548
|
|
|
542
|
-
`@tilua/compiler@9.0.
|
|
549
|
+
`@tilua/compiler@9.0.2` works with these tilua packages. *Released with* is the
|
|
543
550
|
version it was built and tested against.
|
|
544
551
|
|
|
545
552
|
| Package | Range | Released with | |
|
|
546
553
|
| --- | --- | --- | --- |
|
|
547
|
-
| `@tilua/parser` | `^9.0.
|
|
554
|
+
| `@tilua/parser` | `^9.0.2` | 9.0.2 | dependency |
|
|
548
555
|
<!-- compatibility:end -->
|
|
549
556
|
|
|
550
557
|
## Development
|
|
@@ -2805,6 +2805,9 @@ import {
|
|
|
2805
2805
|
resolveModulePath,
|
|
2806
2806
|
resolveTypeLibraries,
|
|
2807
2807
|
importExpressions,
|
|
2808
|
+
sourceMapOf,
|
|
2809
|
+
sourceMapTypes,
|
|
2810
|
+
outputPathOf,
|
|
2808
2811
|
directivesOf,
|
|
2809
2812
|
applyDirectives,
|
|
2810
2813
|
UNUSED_EXPECT_ERROR,
|
|
@@ -4095,6 +4098,35 @@ function importedSpecifiers(program2) {
|
|
|
4095
4098
|
return program2.body.statements.flatMap((s) => s.type === "ImportStatement" || s.type === "ExportAllStatement" || s.type === "ExportNamedStatement" && s.source ? [s.source.value] : []);
|
|
4096
4099
|
}
|
|
4097
4100
|
var libraryCache = /* @__PURE__ */ new Map();
|
|
4101
|
+
var treeCache;
|
|
4102
|
+
function instanceTree(config, libs, noLib) {
|
|
4103
|
+
const tree = sourceMapOf(config);
|
|
4104
|
+
if (!tree) return void 0;
|
|
4105
|
+
if ("problem" in tree) return tree.named ? { problem: { file: config.path, message: tree.problem } } : void 0;
|
|
4106
|
+
const declares = (name) => libs.some((lib) => lib.body.statements.some((s) => s.type === "DeclareStatement" && s.name === name));
|
|
4107
|
+
if (!tree.named && !declares("game")) return void 0;
|
|
4108
|
+
if (treeCache && treeCache.text === tree.text && treeCache.libs.length === libs.length && treeCache.libs.every((lib, i) => lib === libs[i])) {
|
|
4109
|
+
return { types: treeCache.types };
|
|
4110
|
+
}
|
|
4111
|
+
const empty = parse("");
|
|
4112
|
+
const aliases = analyzeTypes(empty, analyzeScopes(empty, { noLib }), { libs, noLib, diagnostics: false }).aliases;
|
|
4113
|
+
const membersOf = (className) => {
|
|
4114
|
+
const type = aliases.get(className);
|
|
4115
|
+
return new Set(type?.kind === "object" ? type.properties.keys() : ["Name", "ClassName", "Parent", "Archivable"]);
|
|
4116
|
+
};
|
|
4117
|
+
const { types, problem } = sourceMapTypes(tree.text, tree.path, { classes: new Set(aliases.keys()), membersOf });
|
|
4118
|
+
if (!types) return tree.named && problem ? { problem: { file: tree.path, message: problem } } : void 0;
|
|
4119
|
+
const scripts = /* @__PURE__ */ new Map();
|
|
4120
|
+
const cached = {
|
|
4121
|
+
program: types.program,
|
|
4122
|
+
scriptFor: (file) => {
|
|
4123
|
+
if (!scripts.has(file)) scripts.set(file, types.scriptFor(file));
|
|
4124
|
+
return scripts.get(file);
|
|
4125
|
+
}
|
|
4126
|
+
};
|
|
4127
|
+
treeCache = { text: tree.text, libs: [...libs], types: cached, scripts };
|
|
4128
|
+
return { types: cached };
|
|
4129
|
+
}
|
|
4098
4130
|
function parsedLibrary(file) {
|
|
4099
4131
|
let stamp;
|
|
4100
4132
|
try {
|
|
@@ -4118,6 +4150,10 @@ function analyzeModules(modules, config) {
|
|
|
4118
4150
|
const libs = libraries.files.map(parsedLibrary);
|
|
4119
4151
|
const noLib = config?.noLib ?? false;
|
|
4120
4152
|
const checked = libs.length > 0 || !noLib;
|
|
4153
|
+
const tree = config ? instanceTree(config, libs, noLib) : void 0;
|
|
4154
|
+
if (tree?.problem) out.push({ file: tree.problem.file, message: tree.problem.message, line: 1, column: 1, category: "config" });
|
|
4155
|
+
if (tree?.types) libs.push(tree.types.program);
|
|
4156
|
+
const scriptOf = (file) => tree?.types && config ? tree.types.scriptFor(outputPathOf(config, file)) : void 0;
|
|
4121
4157
|
const globals = libs.flatMap((lib) => lib.body.statements.flatMap((s) => s.type === "DeclareStatement" ? [s.name] : []));
|
|
4122
4158
|
const byFile = new Map(modules.map((m) => [m.file, m]));
|
|
4123
4159
|
const exportsCache = /* @__PURE__ */ new Map();
|
|
@@ -4130,7 +4166,9 @@ function analyzeModules(modules, config) {
|
|
|
4130
4166
|
const done = analyzed.get(file);
|
|
4131
4167
|
if (done) return done;
|
|
4132
4168
|
const remembered = moduleAnalyses.get(file);
|
|
4133
|
-
|
|
4169
|
+
const script = scriptOf(file);
|
|
4170
|
+
const ownLibs = script ? [...libs, script] : libs;
|
|
4171
|
+
if (remembered && remembered.setting === settingFor && ownLibs.length === remembered.libs.length && remembered.libs.every((lib, i) => lib === ownLibs[i]) && remembered.program === program2 && [...remembered.read].every(([read3, stamp]) => stampOf(read3) === stamp)) {
|
|
4134
4172
|
analyzed.set(file, remembered.result);
|
|
4135
4173
|
readWhile.set(file, remembered.read);
|
|
4136
4174
|
return remembered.result;
|
|
@@ -4143,7 +4181,7 @@ function analyzeModules(modules, config) {
|
|
|
4143
4181
|
}
|
|
4144
4182
|
const before = partials;
|
|
4145
4183
|
const types = analyzeTypes(program2, scopes, {
|
|
4146
|
-
libs,
|
|
4184
|
+
libs: ownLibs,
|
|
4147
4185
|
resolveModule: resolverFor(file),
|
|
4148
4186
|
noLib,
|
|
4149
4187
|
reportUnknownTypes: checked
|
|
@@ -4160,7 +4198,7 @@ function analyzeModules(modules, config) {
|
|
|
4160
4198
|
if (own !== void 0) read3.set(file, own);
|
|
4161
4199
|
readWhile.set(file, read3);
|
|
4162
4200
|
if ([...read3.values()].every((stamp) => stamp !== void 0)) {
|
|
4163
|
-
moduleAnalyses.set(file, { setting: settingFor, libs: [...
|
|
4201
|
+
moduleAnalyses.set(file, { setting: settingFor, libs: [...ownLibs], program: program2, read: read3, result: result2 });
|
|
4164
4202
|
}
|
|
4165
4203
|
}
|
|
4166
4204
|
return result2;
|
package/dist/cli.cjs
CHANGED
|
@@ -4091,6 +4091,35 @@ function importedSpecifiers(program2) {
|
|
|
4091
4091
|
return program2.body.statements.flatMap((s) => s.type === "ImportStatement" || s.type === "ExportAllStatement" || s.type === "ExportNamedStatement" && s.source ? [s.source.value] : []);
|
|
4092
4092
|
}
|
|
4093
4093
|
var libraryCache = /* @__PURE__ */ new Map();
|
|
4094
|
+
var treeCache;
|
|
4095
|
+
function instanceTree(config2, libs, noLib) {
|
|
4096
|
+
const tree = (0, import_parser7.sourceMapOf)(config2);
|
|
4097
|
+
if (!tree) return void 0;
|
|
4098
|
+
if ("problem" in tree) return tree.named ? { problem: { file: config2.path, message: tree.problem } } : void 0;
|
|
4099
|
+
const declares = (name) => libs.some((lib) => lib.body.statements.some((s) => s.type === "DeclareStatement" && s.name === name));
|
|
4100
|
+
if (!tree.named && !declares("game")) return void 0;
|
|
4101
|
+
if (treeCache && treeCache.text === tree.text && treeCache.libs.length === libs.length && treeCache.libs.every((lib, i) => lib === libs[i])) {
|
|
4102
|
+
return { types: treeCache.types };
|
|
4103
|
+
}
|
|
4104
|
+
const empty = (0, import_parser7.parse)("");
|
|
4105
|
+
const aliases = (0, import_parser7.analyzeTypes)(empty, (0, import_parser7.analyzeScopes)(empty, { noLib }), { libs, noLib, diagnostics: false }).aliases;
|
|
4106
|
+
const membersOf = (className) => {
|
|
4107
|
+
const type = aliases.get(className);
|
|
4108
|
+
return new Set(type?.kind === "object" ? type.properties.keys() : ["Name", "ClassName", "Parent", "Archivable"]);
|
|
4109
|
+
};
|
|
4110
|
+
const { types, problem } = (0, import_parser7.sourceMapTypes)(tree.text, tree.path, { classes: new Set(aliases.keys()), membersOf });
|
|
4111
|
+
if (!types) return tree.named && problem ? { problem: { file: tree.path, message: problem } } : void 0;
|
|
4112
|
+
const scripts = /* @__PURE__ */ new Map();
|
|
4113
|
+
const cached = {
|
|
4114
|
+
program: types.program,
|
|
4115
|
+
scriptFor: (file) => {
|
|
4116
|
+
if (!scripts.has(file)) scripts.set(file, types.scriptFor(file));
|
|
4117
|
+
return scripts.get(file);
|
|
4118
|
+
}
|
|
4119
|
+
};
|
|
4120
|
+
treeCache = { text: tree.text, libs: [...libs], types: cached, scripts };
|
|
4121
|
+
return { types: cached };
|
|
4122
|
+
}
|
|
4094
4123
|
function parsedLibrary(file) {
|
|
4095
4124
|
let stamp;
|
|
4096
4125
|
try {
|
|
@@ -4114,6 +4143,10 @@ function analyzeModules(modules, config2) {
|
|
|
4114
4143
|
const libs = libraries.files.map(parsedLibrary);
|
|
4115
4144
|
const noLib = config2?.noLib ?? false;
|
|
4116
4145
|
const checked = libs.length > 0 || !noLib;
|
|
4146
|
+
const tree = config2 ? instanceTree(config2, libs, noLib) : void 0;
|
|
4147
|
+
if (tree?.problem) out2.push({ file: tree.problem.file, message: tree.problem.message, line: 1, column: 1, category: "config" });
|
|
4148
|
+
if (tree?.types) libs.push(tree.types.program);
|
|
4149
|
+
const scriptOf = (file) => tree?.types && config2 ? tree.types.scriptFor((0, import_parser7.outputPathOf)(config2, file)) : void 0;
|
|
4117
4150
|
const globals = libs.flatMap((lib) => lib.body.statements.flatMap((s) => s.type === "DeclareStatement" ? [s.name] : []));
|
|
4118
4151
|
const byFile = new Map(modules.map((m) => [m.file, m]));
|
|
4119
4152
|
const exportsCache = /* @__PURE__ */ new Map();
|
|
@@ -4126,7 +4159,9 @@ function analyzeModules(modules, config2) {
|
|
|
4126
4159
|
const done = analyzed.get(file);
|
|
4127
4160
|
if (done) return done;
|
|
4128
4161
|
const remembered = moduleAnalyses.get(file);
|
|
4129
|
-
|
|
4162
|
+
const script = scriptOf(file);
|
|
4163
|
+
const ownLibs = script ? [...libs, script] : libs;
|
|
4164
|
+
if (remembered && remembered.setting === settingFor && ownLibs.length === remembered.libs.length && remembered.libs.every((lib, i) => lib === ownLibs[i]) && remembered.program === program2 && [...remembered.read].every(([read3, stamp]) => stampOf(read3) === stamp)) {
|
|
4130
4165
|
analyzed.set(file, remembered.result);
|
|
4131
4166
|
readWhile.set(file, remembered.read);
|
|
4132
4167
|
return remembered.result;
|
|
@@ -4139,7 +4174,7 @@ function analyzeModules(modules, config2) {
|
|
|
4139
4174
|
}
|
|
4140
4175
|
const before = partials;
|
|
4141
4176
|
const types = (0, import_parser7.analyzeTypes)(program2, scopes, {
|
|
4142
|
-
libs,
|
|
4177
|
+
libs: ownLibs,
|
|
4143
4178
|
resolveModule: resolverFor(file),
|
|
4144
4179
|
noLib,
|
|
4145
4180
|
reportUnknownTypes: checked
|
|
@@ -4156,7 +4191,7 @@ function analyzeModules(modules, config2) {
|
|
|
4156
4191
|
if (own !== void 0) read3.set(file, own);
|
|
4157
4192
|
readWhile.set(file, read3);
|
|
4158
4193
|
if ([...read3.values()].every((stamp) => stamp !== void 0)) {
|
|
4159
|
-
moduleAnalyses.set(file, { setting: settingFor, libs: [...
|
|
4194
|
+
moduleAnalyses.set(file, { setting: settingFor, libs: [...ownLibs], program: program2, read: read3, result: result2 });
|
|
4160
4195
|
}
|
|
4161
4196
|
}
|
|
4162
4197
|
return result2;
|
|
@@ -4234,6 +4269,7 @@ function allStatements(statements) {
|
|
|
4234
4269
|
}
|
|
4235
4270
|
|
|
4236
4271
|
// src/cli.ts
|
|
4272
|
+
var import_parser8 = require("@tilua/parser");
|
|
4237
4273
|
var usage = "usage: tilua [<entry>] [--out <file>] [--output single|chunks|files] [--config <tilua.config.json>] [--noCheck] [--noLineMap] [--minify] [--watch]\n tilua init";
|
|
4238
4274
|
var args = process.argv.slice(2);
|
|
4239
4275
|
var entry;
|
|
@@ -4272,46 +4308,97 @@ for (let i = 0; i < args.length; i++) {
|
|
|
4272
4308
|
} else entry = arg;
|
|
4273
4309
|
}
|
|
4274
4310
|
var shown = (path) => (0, import_node_path3.relative)(process.cwd(), path) || ".";
|
|
4311
|
+
var OUT = "out";
|
|
4275
4312
|
function init() {
|
|
4276
4313
|
const folder = process.cwd();
|
|
4314
|
+
let existing;
|
|
4277
4315
|
for (const name of ["tilua.config.json", "tilua.config.jsonc"]) {
|
|
4278
|
-
|
|
4316
|
+
const path = (0, import_node_path3.join)(folder, name);
|
|
4317
|
+
if (!(0, import_node_fs4.existsSync)(path)) continue;
|
|
4318
|
+
let options2 = {};
|
|
4319
|
+
try {
|
|
4320
|
+
options2 = JSON.parse((0, import_parser8.stripJsonComments)((0, import_node_fs4.readFileSync)(path, "utf8")));
|
|
4321
|
+
} catch (error) {
|
|
4322
|
+
console.error(`${name}: ${error.message}`);
|
|
4323
|
+
process.exit(1);
|
|
4324
|
+
}
|
|
4325
|
+
if (options2.output !== "files" || options2.outDir !== void 0 && options2.outDir !== options2.rootDir) {
|
|
4279
4326
|
console.error(`${name} is already here`);
|
|
4280
4327
|
process.exit(1);
|
|
4281
4328
|
}
|
|
4329
|
+
existing = { path, options: options2 };
|
|
4282
4330
|
}
|
|
4283
|
-
let rootDir = "src";
|
|
4284
4331
|
const project = (0, import_node_path3.join)(folder, "default.project.json");
|
|
4285
|
-
if ((0, import_node_fs4.existsSync)(project)) {
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4293
|
-
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
|
|
4305
|
-
|
|
4332
|
+
if (!(0, import_node_fs4.existsSync)(project)) {
|
|
4333
|
+
console.error("No default.project.json here: run `tilua init` in the folder of a Rojo project (`rojo init` makes one)");
|
|
4334
|
+
process.exit(1);
|
|
4335
|
+
}
|
|
4336
|
+
const text = (0, import_node_fs4.readFileSync)(project, "utf8");
|
|
4337
|
+
const paths = [];
|
|
4338
|
+
const walk2 = (node) => {
|
|
4339
|
+
if (!node || typeof node !== "object") return;
|
|
4340
|
+
const path = node.$path;
|
|
4341
|
+
const named = typeof path === "string" ? path : path?.optional;
|
|
4342
|
+
if (typeof named === "string") paths.push(named);
|
|
4343
|
+
for (const [key, child] of Object.entries(node)) if (!key.startsWith("$")) walk2(child);
|
|
4344
|
+
};
|
|
4345
|
+
try {
|
|
4346
|
+
walk2(JSON.parse(text).tree);
|
|
4347
|
+
} catch (error) {
|
|
4348
|
+
console.error(`default.project.json: ${error.message}`);
|
|
4349
|
+
process.exit(1);
|
|
4350
|
+
}
|
|
4351
|
+
const split = paths.map((path) => path.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/$/, "").split("/"));
|
|
4352
|
+
const shared = [];
|
|
4353
|
+
for (let i = 0; split.length && split.every((parts) => parts.length > i && parts[i] === split[0][i]); i++) shared.push(split[0][i]);
|
|
4354
|
+
if (split.length === 1 && /\.\w+$/.test(shared.at(-1) ?? "")) shared.pop();
|
|
4355
|
+
const rootDir = shared.join("/");
|
|
4356
|
+
if (!rootDir) {
|
|
4357
|
+
console.error("The project's $paths share no folder to compile from: put the code under one (src), as `rojo init` does");
|
|
4358
|
+
process.exit(1);
|
|
4359
|
+
}
|
|
4360
|
+
if (rootDir === OUT || rootDir.startsWith(`${OUT}/`)) {
|
|
4361
|
+
console.error(`The project already reads from ${OUT}: set tilua.config.json up by hand`);
|
|
4362
|
+
process.exit(1);
|
|
4363
|
+
}
|
|
4364
|
+
const out2 = (0, import_node_path3.join)(folder, OUT);
|
|
4365
|
+
if ((0, import_node_fs4.existsSync)(out2) && (0, import_node_fs4.readdirSync)(out2).length) {
|
|
4366
|
+
console.error(`${OUT} is already here and not empty: the build would replace what is in it`);
|
|
4367
|
+
process.exit(1);
|
|
4368
|
+
}
|
|
4369
|
+
const options = existing ? { ...existing.options, rootDir: existing.options.rootDir ?? rootDir, outDir: OUT } : { types: ["roblox"], output: "files", rootDir, outDir: OUT };
|
|
4370
|
+
(0, import_node_fs4.writeFileSync)(existing?.path ?? (0, import_node_path3.join)(folder, "tilua.config.json"), JSON.stringify(options, null, 4) + "\n");
|
|
4371
|
+
console.log(`${(0, import_node_path3.basename)(existing?.path ?? "tilua.config.json")}: ${String(options.rootDir)} compiled to ${OUT}`);
|
|
4372
|
+
const escaped = rootDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4373
|
+
let changed = 0;
|
|
4374
|
+
const pointed = text.replace(new RegExp(`("\\$path"\\s*:\\s*")(\\./)?${escaped}(?=/|")`, "g"), (_, head) => {
|
|
4375
|
+
changed++;
|
|
4376
|
+
return `${head}${OUT}`;
|
|
4377
|
+
});
|
|
4378
|
+
(0, import_node_fs4.writeFileSync)(project, pointed);
|
|
4379
|
+
console.log(`default.project.json: ${changed} $path${changed === 1 ? "" : "s"} now read from ${OUT}`);
|
|
4380
|
+
const ignore = (0, import_node_path3.join)(folder, ".gitignore");
|
|
4381
|
+
const ignored = (0, import_node_fs4.existsSync)(ignore) ? (0, import_node_fs4.readFileSync)(ignore, "utf8") : "";
|
|
4382
|
+
if (!ignored.split(/\r?\n/).some((line) => [OUT, `/${OUT}`, `${OUT}/`, `/${OUT}/`].includes(line.trim()))) {
|
|
4383
|
+
(0, import_node_fs4.writeFileSync)(ignore, `${ignored}${ignored && !ignored.endsWith("\n") ? "\n" : ""}
|
|
4384
|
+
# What tilua builds from ${rootDir}
|
|
4385
|
+
/${OUT}
|
|
4386
|
+
`);
|
|
4387
|
+
console.log(`.gitignore: /${OUT}`);
|
|
4306
4388
|
}
|
|
4307
|
-
const written = { types: ["roblox"], output: "files", rootDir };
|
|
4308
|
-
(0, import_node_fs4.writeFileSync)((0, import_node_path3.join)(folder, "tilua.config.json"), JSON.stringify(written, null, 4) + "\n");
|
|
4309
|
-
console.log("tilua.config.json: a .luau beside each .tilua under " + rootDir);
|
|
4310
4389
|
console.log("Next: npm i -D @tilua/compiler @tilua-types/roblox, then run `npx tilua --watch` beside `rojo serve`.");
|
|
4311
4390
|
process.exit(0);
|
|
4312
4391
|
}
|
|
4313
|
-
function
|
|
4314
|
-
const
|
|
4392
|
+
function isInside(folder, path) {
|
|
4393
|
+
const below = (0, import_node_path3.relative)(folder, path);
|
|
4394
|
+
return below === "" || !below.startsWith("..") && !(0, import_node_path3.isAbsolute)(below);
|
|
4395
|
+
}
|
|
4396
|
+
function filesUnder(folder) {
|
|
4397
|
+
return under(folder).files;
|
|
4398
|
+
}
|
|
4399
|
+
function under(folder) {
|
|
4400
|
+
const files = [];
|
|
4401
|
+
const folders = [];
|
|
4315
4402
|
const walk2 = (at) => {
|
|
4316
4403
|
let items;
|
|
4317
4404
|
try {
|
|
@@ -4320,14 +4407,27 @@ function writtenByTilua(folder) {
|
|
|
4320
4407
|
return;
|
|
4321
4408
|
}
|
|
4322
4409
|
for (const item of items) {
|
|
4323
|
-
if (item.name
|
|
4410
|
+
if (item.name === "node_modules" || item.name === ".git") continue;
|
|
4324
4411
|
const path = (0, import_node_path3.join)(at, item.name);
|
|
4325
|
-
if (item.isDirectory())
|
|
4326
|
-
|
|
4412
|
+
if (item.isDirectory()) {
|
|
4413
|
+
folders.push(path);
|
|
4414
|
+
walk2(path);
|
|
4415
|
+
} else files.push(path);
|
|
4327
4416
|
}
|
|
4328
4417
|
};
|
|
4329
4418
|
walk2(folder);
|
|
4330
|
-
return
|
|
4419
|
+
return { files, folders };
|
|
4420
|
+
}
|
|
4421
|
+
function removeEmpty(folder, keep) {
|
|
4422
|
+
for (const item of (0, import_node_fs4.readdirSync)(folder, { withFileTypes: true })) {
|
|
4423
|
+
if (!item.isDirectory()) continue;
|
|
4424
|
+
const path = (0, import_node_path3.join)(folder, item.name);
|
|
4425
|
+
removeEmpty(path, keep);
|
|
4426
|
+
if (!(0, import_node_fs4.readdirSync)(path).length && !keep.has((0, import_node_path3.resolve)(path))) (0, import_node_fs4.rmdirSync)(path);
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4429
|
+
function writtenByTilua(folder) {
|
|
4430
|
+
return filesUnder(folder).filter((path) => /\.luau?$/.test(path) && isTilua(path));
|
|
4331
4431
|
}
|
|
4332
4432
|
function isTilua(path) {
|
|
4333
4433
|
try {
|
|
@@ -4347,6 +4447,12 @@ async function build(from) {
|
|
|
4347
4447
|
const perFile = mode === "files";
|
|
4348
4448
|
const entryFile = perFile || from === void 0 ? void 0 : out ?? from.replace(/\.tilua$/, "") + ".luau";
|
|
4349
4449
|
const folder = (0, import_node_path3.resolve)(entryFile !== void 0 ? (0, import_node_path3.dirname)(entryFile) : out ?? found?.outDir ?? process.cwd());
|
|
4450
|
+
const rootDir = (0, import_node_path3.resolve)(found?.rootDir ?? process.cwd());
|
|
4451
|
+
const mirror = perFile && (0, import_node_path3.resolve)(folder) !== rootDir;
|
|
4452
|
+
if (mirror && (isInside(folder, rootDir) || isInside(rootDir, folder) || isInside(folder, found?.directory ?? process.cwd()))) {
|
|
4453
|
+
console.error(`outDir (${shown(folder)}) has to be a folder of its own: not ${isInside(rootDir, folder) ? "under" : "around"} rootDir (${shown(rootDir)}), and not around the project`);
|
|
4454
|
+
return false;
|
|
4455
|
+
}
|
|
4350
4456
|
const result2 = await bundle({
|
|
4351
4457
|
entry: perFile ? void 0 : from,
|
|
4352
4458
|
config,
|
|
@@ -4365,6 +4471,54 @@ async function build(from) {
|
|
|
4365
4471
|
return false;
|
|
4366
4472
|
}
|
|
4367
4473
|
const targets = result2.files.map((file) => ({ ...file, target: (0, import_node_path3.join)(folder, file.path) }));
|
|
4474
|
+
if (mirror) {
|
|
4475
|
+
const compiled = new Map(targets.map((file) => [(0, import_node_path3.resolve)(file.target), file]));
|
|
4476
|
+
const copies = [];
|
|
4477
|
+
let clash = false;
|
|
4478
|
+
const leftovers = [];
|
|
4479
|
+
for (const source of filesUnder(rootDir)) {
|
|
4480
|
+
if (source.endsWith(".tilua")) continue;
|
|
4481
|
+
if (/\.luau?$/.test(source) && isTilua(source)) {
|
|
4482
|
+
leftovers.push(source);
|
|
4483
|
+
continue;
|
|
4484
|
+
}
|
|
4485
|
+
const target = (0, import_node_path3.resolve)(folder, (0, import_node_path3.relative)(rootDir, source));
|
|
4486
|
+
if (compiled.has(target)) {
|
|
4487
|
+
console.error(`${shown(source)}: a .tilua compiles to the same file \u2014 rename or remove one of them`);
|
|
4488
|
+
clash = true;
|
|
4489
|
+
continue;
|
|
4490
|
+
}
|
|
4491
|
+
copies.push({ source, target });
|
|
4492
|
+
}
|
|
4493
|
+
if (clash) return false;
|
|
4494
|
+
for (const leftover of leftovers) {
|
|
4495
|
+
(0, import_node_fs4.rmSync)(leftover);
|
|
4496
|
+
console.log(`${shown(leftover)}: removed \u2014 tilua compiled it beside its .tilua, and the output is in ${shown(folder)} now`);
|
|
4497
|
+
}
|
|
4498
|
+
for (const file of targets) {
|
|
4499
|
+
if ((0, import_node_fs4.existsSync)(file.target) && (0, import_node_fs4.readFileSync)(file.target, "utf8") === file.code) continue;
|
|
4500
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path3.dirname)(file.target), { recursive: true });
|
|
4501
|
+
(0, import_node_fs4.writeFileSync)(file.target, file.code);
|
|
4502
|
+
console.log(`${shown(file.target)}: ${file.modules.length} module${file.modules.length === 1 ? "" : "s"}`);
|
|
4503
|
+
}
|
|
4504
|
+
for (const { source, target } of copies) {
|
|
4505
|
+
const bytes = (0, import_node_fs4.readFileSync)(source);
|
|
4506
|
+
if ((0, import_node_fs4.existsSync)(target) && (0, import_node_fs4.readFileSync)(target).equals(bytes)) continue;
|
|
4507
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path3.dirname)(target), { recursive: true });
|
|
4508
|
+
(0, import_node_fs4.copyFileSync)(source, target);
|
|
4509
|
+
console.log(`${shown(target)}: copied`);
|
|
4510
|
+
}
|
|
4511
|
+
const kept = /* @__PURE__ */ new Set([...compiled.keys(), ...copies.map((copy) => copy.target)]);
|
|
4512
|
+
for (const stale of filesUnder(folder)) {
|
|
4513
|
+
if (kept.has((0, import_node_path3.resolve)(stale))) continue;
|
|
4514
|
+
(0, import_node_fs4.rmSync)(stale);
|
|
4515
|
+
console.log(`${shown(stale)}: removed`);
|
|
4516
|
+
}
|
|
4517
|
+
const folders = /* @__PURE__ */ new Set([folder, ...under(rootDir).folders.map((path) => (0, import_node_path3.resolve)(folder, (0, import_node_path3.relative)(rootDir, path)))]);
|
|
4518
|
+
for (const path of folders) (0, import_node_fs4.mkdirSync)(path, { recursive: true });
|
|
4519
|
+
removeEmpty(folder, folders);
|
|
4520
|
+
return !result2.diagnostics.some((d) => d.severity !== "warning");
|
|
4521
|
+
}
|
|
4368
4522
|
if (perFile) {
|
|
4369
4523
|
const theirs = targets.filter((file) => (0, import_node_fs4.existsSync)(file.target) && !isTilua(file.target));
|
|
4370
4524
|
for (const file of theirs) {
|
|
@@ -4391,6 +4545,9 @@ async function build(from) {
|
|
|
4391
4545
|
async function watchAndBuild(from) {
|
|
4392
4546
|
const found = resolveConfig(config, (0, import_node_path3.resolve)(from ?? "tilua.config.json")).config;
|
|
4393
4547
|
const folder = found?.directory ?? (from !== void 0 ? (0, import_node_path3.dirname)((0, import_node_path3.resolve)(from)) : process.cwd());
|
|
4548
|
+
const rootDir = found?.rootDir;
|
|
4549
|
+
const outDir = (0, import_node_path3.resolve)(out ?? found?.outDir ?? folder);
|
|
4550
|
+
const mirrored = (output ?? found?.output) === "files" && rootDir !== void 0 && outDir !== (0, import_node_path3.resolve)(rootDir);
|
|
4394
4551
|
let running = false;
|
|
4395
4552
|
let again = false;
|
|
4396
4553
|
let timer;
|
|
@@ -4414,9 +4571,10 @@ async function watchAndBuild(from) {
|
|
|
4414
4571
|
await run();
|
|
4415
4572
|
(0, import_node_fs4.watch)(folder, { recursive: true }, (_event, name) => {
|
|
4416
4573
|
if (!name) return;
|
|
4417
|
-
const path = name.toString();
|
|
4418
|
-
if (path.split(
|
|
4419
|
-
|
|
4574
|
+
const path = (0, import_node_path3.resolve)(folder, name.toString());
|
|
4575
|
+
if ((0, import_node_path3.relative)(folder, path).split(/[\\/]/).includes("node_modules") || isInside(outDir, path) && mirrored) return;
|
|
4576
|
+
const read3 = /\.tilua$|tilua\.config\.jsonc?$|\.project\.json$/.test(path) || mirrored && isInside(rootDir, path);
|
|
4577
|
+
if (!read3) return;
|
|
4420
4578
|
clearTimeout(timer);
|
|
4421
4579
|
timer = setTimeout(() => void run(), 100);
|
|
4422
4580
|
});
|
package/dist/cli.js
CHANGED
|
@@ -3,11 +3,12 @@ import {
|
|
|
3
3
|
WRITTEN_BY_TILUA,
|
|
4
4
|
bundle,
|
|
5
5
|
resolveConfig
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-3RJVN4KC.js";
|
|
7
7
|
|
|
8
8
|
// src/cli.ts
|
|
9
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, watch, writeFileSync } from "fs";
|
|
10
|
-
import { basename, dirname, join, relative, resolve
|
|
9
|
+
import { copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, rmdirSync, rmSync, watch, writeFileSync } from "fs";
|
|
10
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from "path";
|
|
11
|
+
import { stripJsonComments } from "@tilua/parser";
|
|
11
12
|
var usage = "usage: tilua [<entry>] [--out <file>] [--output single|chunks|files] [--config <tilua.config.json>] [--noCheck] [--noLineMap] [--minify] [--watch]\n tilua init";
|
|
12
13
|
var args = process.argv.slice(2);
|
|
13
14
|
var entry;
|
|
@@ -46,46 +47,97 @@ for (let i = 0; i < args.length; i++) {
|
|
|
46
47
|
} else entry = arg;
|
|
47
48
|
}
|
|
48
49
|
var shown = (path) => relative(process.cwd(), path) || ".";
|
|
50
|
+
var OUT = "out";
|
|
49
51
|
function init() {
|
|
50
52
|
const folder = process.cwd();
|
|
53
|
+
let existing;
|
|
51
54
|
for (const name of ["tilua.config.json", "tilua.config.jsonc"]) {
|
|
52
|
-
|
|
55
|
+
const path = join(folder, name);
|
|
56
|
+
if (!existsSync(path)) continue;
|
|
57
|
+
let options2 = {};
|
|
58
|
+
try {
|
|
59
|
+
options2 = JSON.parse(stripJsonComments(readFileSync(path, "utf8")));
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.error(`${name}: ${error.message}`);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
if (options2.output !== "files" || options2.outDir !== void 0 && options2.outDir !== options2.rootDir) {
|
|
53
65
|
console.error(`${name} is already here`);
|
|
54
66
|
process.exit(1);
|
|
55
67
|
}
|
|
68
|
+
existing = { path, options: options2 };
|
|
56
69
|
}
|
|
57
|
-
let rootDir = "src";
|
|
58
70
|
const project = join(folder, "default.project.json");
|
|
59
|
-
if (existsSync(project)) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
71
|
+
if (!existsSync(project)) {
|
|
72
|
+
console.error("No default.project.json here: run `tilua init` in the folder of a Rojo project (`rojo init` makes one)");
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
const text = readFileSync(project, "utf8");
|
|
76
|
+
const paths = [];
|
|
77
|
+
const walk = (node) => {
|
|
78
|
+
if (!node || typeof node !== "object") return;
|
|
79
|
+
const path = node.$path;
|
|
80
|
+
const named = typeof path === "string" ? path : path?.optional;
|
|
81
|
+
if (typeof named === "string") paths.push(named);
|
|
82
|
+
for (const [key, child] of Object.entries(node)) if (!key.startsWith("$")) walk(child);
|
|
83
|
+
};
|
|
84
|
+
try {
|
|
85
|
+
walk(JSON.parse(text).tree);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error(`default.project.json: ${error.message}`);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
const split = paths.map((path) => path.replace(/\\/g, "/").replace(/^\.\//, "").replace(/\/$/, "").split("/"));
|
|
91
|
+
const shared = [];
|
|
92
|
+
for (let i = 0; split.length && split.every((parts) => parts.length > i && parts[i] === split[0][i]); i++) shared.push(split[0][i]);
|
|
93
|
+
if (split.length === 1 && /\.\w+$/.test(shared.at(-1) ?? "")) shared.pop();
|
|
94
|
+
const rootDir = shared.join("/");
|
|
95
|
+
if (!rootDir) {
|
|
96
|
+
console.error("The project's $paths share no folder to compile from: put the code under one (src), as `rojo init` does");
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
if (rootDir === OUT || rootDir.startsWith(`${OUT}/`)) {
|
|
100
|
+
console.error(`The project already reads from ${OUT}: set tilua.config.json up by hand`);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
const out2 = join(folder, OUT);
|
|
104
|
+
if (existsSync(out2) && readdirSync(out2).length) {
|
|
105
|
+
console.error(`${OUT} is already here and not empty: the build would replace what is in it`);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
const options = existing ? { ...existing.options, rootDir: existing.options.rootDir ?? rootDir, outDir: OUT } : { types: ["roblox"], output: "files", rootDir, outDir: OUT };
|
|
109
|
+
writeFileSync(existing?.path ?? join(folder, "tilua.config.json"), JSON.stringify(options, null, 4) + "\n");
|
|
110
|
+
console.log(`${basename(existing?.path ?? "tilua.config.json")}: ${String(options.rootDir)} compiled to ${OUT}`);
|
|
111
|
+
const escaped = rootDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
112
|
+
let changed = 0;
|
|
113
|
+
const pointed = text.replace(new RegExp(`("\\$path"\\s*:\\s*")(\\./)?${escaped}(?=/|")`, "g"), (_, head) => {
|
|
114
|
+
changed++;
|
|
115
|
+
return `${head}${OUT}`;
|
|
116
|
+
});
|
|
117
|
+
writeFileSync(project, pointed);
|
|
118
|
+
console.log(`default.project.json: ${changed} $path${changed === 1 ? "" : "s"} now read from ${OUT}`);
|
|
119
|
+
const ignore = join(folder, ".gitignore");
|
|
120
|
+
const ignored = existsSync(ignore) ? readFileSync(ignore, "utf8") : "";
|
|
121
|
+
if (!ignored.split(/\r?\n/).some((line) => [OUT, `/${OUT}`, `${OUT}/`, `/${OUT}/`].includes(line.trim()))) {
|
|
122
|
+
writeFileSync(ignore, `${ignored}${ignored && !ignored.endsWith("\n") ? "\n" : ""}
|
|
123
|
+
# What tilua builds from ${rootDir}
|
|
124
|
+
/${OUT}
|
|
125
|
+
`);
|
|
126
|
+
console.log(`.gitignore: /${OUT}`);
|
|
127
|
+
}
|
|
84
128
|
console.log("Next: npm i -D @tilua/compiler @tilua-types/roblox, then run `npx tilua --watch` beside `rojo serve`.");
|
|
85
129
|
process.exit(0);
|
|
86
130
|
}
|
|
87
|
-
function
|
|
88
|
-
const
|
|
131
|
+
function isInside(folder, path) {
|
|
132
|
+
const below = relative(folder, path);
|
|
133
|
+
return below === "" || !below.startsWith("..") && !isAbsolute(below);
|
|
134
|
+
}
|
|
135
|
+
function filesUnder(folder) {
|
|
136
|
+
return under(folder).files;
|
|
137
|
+
}
|
|
138
|
+
function under(folder) {
|
|
139
|
+
const files = [];
|
|
140
|
+
const folders = [];
|
|
89
141
|
const walk = (at) => {
|
|
90
142
|
let items;
|
|
91
143
|
try {
|
|
@@ -94,14 +146,27 @@ function writtenByTilua(folder) {
|
|
|
94
146
|
return;
|
|
95
147
|
}
|
|
96
148
|
for (const item of items) {
|
|
97
|
-
if (item.name
|
|
149
|
+
if (item.name === "node_modules" || item.name === ".git") continue;
|
|
98
150
|
const path = join(at, item.name);
|
|
99
|
-
if (item.isDirectory())
|
|
100
|
-
|
|
151
|
+
if (item.isDirectory()) {
|
|
152
|
+
folders.push(path);
|
|
153
|
+
walk(path);
|
|
154
|
+
} else files.push(path);
|
|
101
155
|
}
|
|
102
156
|
};
|
|
103
157
|
walk(folder);
|
|
104
|
-
return
|
|
158
|
+
return { files, folders };
|
|
159
|
+
}
|
|
160
|
+
function removeEmpty(folder, keep) {
|
|
161
|
+
for (const item of readdirSync(folder, { withFileTypes: true })) {
|
|
162
|
+
if (!item.isDirectory()) continue;
|
|
163
|
+
const path = join(folder, item.name);
|
|
164
|
+
removeEmpty(path, keep);
|
|
165
|
+
if (!readdirSync(path).length && !keep.has(resolve(path))) rmdirSync(path);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function writtenByTilua(folder) {
|
|
169
|
+
return filesUnder(folder).filter((path) => /\.luau?$/.test(path) && isTilua(path));
|
|
105
170
|
}
|
|
106
171
|
function isTilua(path) {
|
|
107
172
|
try {
|
|
@@ -121,6 +186,12 @@ async function build(from) {
|
|
|
121
186
|
const perFile = mode === "files";
|
|
122
187
|
const entryFile = perFile || from === void 0 ? void 0 : out ?? from.replace(/\.tilua$/, "") + ".luau";
|
|
123
188
|
const folder = resolve(entryFile !== void 0 ? dirname(entryFile) : out ?? found?.outDir ?? process.cwd());
|
|
189
|
+
const rootDir = resolve(found?.rootDir ?? process.cwd());
|
|
190
|
+
const mirror = perFile && resolve(folder) !== rootDir;
|
|
191
|
+
if (mirror && (isInside(folder, rootDir) || isInside(rootDir, folder) || isInside(folder, found?.directory ?? process.cwd()))) {
|
|
192
|
+
console.error(`outDir (${shown(folder)}) has to be a folder of its own: not ${isInside(rootDir, folder) ? "under" : "around"} rootDir (${shown(rootDir)}), and not around the project`);
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
124
195
|
const result = await bundle({
|
|
125
196
|
entry: perFile ? void 0 : from,
|
|
126
197
|
config,
|
|
@@ -139,6 +210,54 @@ async function build(from) {
|
|
|
139
210
|
return false;
|
|
140
211
|
}
|
|
141
212
|
const targets = result.files.map((file) => ({ ...file, target: join(folder, file.path) }));
|
|
213
|
+
if (mirror) {
|
|
214
|
+
const compiled = new Map(targets.map((file) => [resolve(file.target), file]));
|
|
215
|
+
const copies = [];
|
|
216
|
+
let clash = false;
|
|
217
|
+
const leftovers = [];
|
|
218
|
+
for (const source of filesUnder(rootDir)) {
|
|
219
|
+
if (source.endsWith(".tilua")) continue;
|
|
220
|
+
if (/\.luau?$/.test(source) && isTilua(source)) {
|
|
221
|
+
leftovers.push(source);
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
const target = resolve(folder, relative(rootDir, source));
|
|
225
|
+
if (compiled.has(target)) {
|
|
226
|
+
console.error(`${shown(source)}: a .tilua compiles to the same file \u2014 rename or remove one of them`);
|
|
227
|
+
clash = true;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
copies.push({ source, target });
|
|
231
|
+
}
|
|
232
|
+
if (clash) return false;
|
|
233
|
+
for (const leftover of leftovers) {
|
|
234
|
+
rmSync(leftover);
|
|
235
|
+
console.log(`${shown(leftover)}: removed \u2014 tilua compiled it beside its .tilua, and the output is in ${shown(folder)} now`);
|
|
236
|
+
}
|
|
237
|
+
for (const file of targets) {
|
|
238
|
+
if (existsSync(file.target) && readFileSync(file.target, "utf8") === file.code) continue;
|
|
239
|
+
mkdirSync(dirname(file.target), { recursive: true });
|
|
240
|
+
writeFileSync(file.target, file.code);
|
|
241
|
+
console.log(`${shown(file.target)}: ${file.modules.length} module${file.modules.length === 1 ? "" : "s"}`);
|
|
242
|
+
}
|
|
243
|
+
for (const { source, target } of copies) {
|
|
244
|
+
const bytes = readFileSync(source);
|
|
245
|
+
if (existsSync(target) && readFileSync(target).equals(bytes)) continue;
|
|
246
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
247
|
+
copyFileSync(source, target);
|
|
248
|
+
console.log(`${shown(target)}: copied`);
|
|
249
|
+
}
|
|
250
|
+
const kept = /* @__PURE__ */ new Set([...compiled.keys(), ...copies.map((copy) => copy.target)]);
|
|
251
|
+
for (const stale of filesUnder(folder)) {
|
|
252
|
+
if (kept.has(resolve(stale))) continue;
|
|
253
|
+
rmSync(stale);
|
|
254
|
+
console.log(`${shown(stale)}: removed`);
|
|
255
|
+
}
|
|
256
|
+
const folders = /* @__PURE__ */ new Set([folder, ...under(rootDir).folders.map((path) => resolve(folder, relative(rootDir, path)))]);
|
|
257
|
+
for (const path of folders) mkdirSync(path, { recursive: true });
|
|
258
|
+
removeEmpty(folder, folders);
|
|
259
|
+
return !result.diagnostics.some((d) => d.severity !== "warning");
|
|
260
|
+
}
|
|
142
261
|
if (perFile) {
|
|
143
262
|
const theirs = targets.filter((file) => existsSync(file.target) && !isTilua(file.target));
|
|
144
263
|
for (const file of theirs) {
|
|
@@ -165,6 +284,9 @@ async function build(from) {
|
|
|
165
284
|
async function watchAndBuild(from) {
|
|
166
285
|
const found = resolveConfig(config, resolve(from ?? "tilua.config.json")).config;
|
|
167
286
|
const folder = found?.directory ?? (from !== void 0 ? dirname(resolve(from)) : process.cwd());
|
|
287
|
+
const rootDir = found?.rootDir;
|
|
288
|
+
const outDir = resolve(out ?? found?.outDir ?? folder);
|
|
289
|
+
const mirrored = (output ?? found?.output) === "files" && rootDir !== void 0 && outDir !== resolve(rootDir);
|
|
168
290
|
let running = false;
|
|
169
291
|
let again = false;
|
|
170
292
|
let timer;
|
|
@@ -188,9 +310,10 @@ async function watchAndBuild(from) {
|
|
|
188
310
|
await run();
|
|
189
311
|
watch(folder, { recursive: true }, (_event, name) => {
|
|
190
312
|
if (!name) return;
|
|
191
|
-
const path = name.toString();
|
|
192
|
-
if (path.split(
|
|
193
|
-
|
|
313
|
+
const path = resolve(folder, name.toString());
|
|
314
|
+
if (relative(folder, path).split(/[\\/]/).includes("node_modules") || isInside(outDir, path) && mirrored) return;
|
|
315
|
+
const read = /\.tilua$|tilua\.config\.jsonc?$|\.project\.json$/.test(path) || mirrored && isInside(rootDir, path);
|
|
316
|
+
if (!read) return;
|
|
194
317
|
clearTimeout(timer);
|
|
195
318
|
timer = setTimeout(() => void run(), 100);
|
|
196
319
|
});
|
package/dist/index.cjs
CHANGED
|
@@ -4115,6 +4115,35 @@ function importedSpecifiers(program2) {
|
|
|
4115
4115
|
return program2.body.statements.flatMap((s) => s.type === "ImportStatement" || s.type === "ExportAllStatement" || s.type === "ExportNamedStatement" && s.source ? [s.source.value] : []);
|
|
4116
4116
|
}
|
|
4117
4117
|
var libraryCache = /* @__PURE__ */ new Map();
|
|
4118
|
+
var treeCache;
|
|
4119
|
+
function instanceTree(config, libs, noLib) {
|
|
4120
|
+
const tree = (0, import_parser7.sourceMapOf)(config);
|
|
4121
|
+
if (!tree) return void 0;
|
|
4122
|
+
if ("problem" in tree) return tree.named ? { problem: { file: config.path, message: tree.problem } } : void 0;
|
|
4123
|
+
const declares = (name) => libs.some((lib) => lib.body.statements.some((s) => s.type === "DeclareStatement" && s.name === name));
|
|
4124
|
+
if (!tree.named && !declares("game")) return void 0;
|
|
4125
|
+
if (treeCache && treeCache.text === tree.text && treeCache.libs.length === libs.length && treeCache.libs.every((lib, i) => lib === libs[i])) {
|
|
4126
|
+
return { types: treeCache.types };
|
|
4127
|
+
}
|
|
4128
|
+
const empty = (0, import_parser7.parse)("");
|
|
4129
|
+
const aliases = (0, import_parser7.analyzeTypes)(empty, (0, import_parser7.analyzeScopes)(empty, { noLib }), { libs, noLib, diagnostics: false }).aliases;
|
|
4130
|
+
const membersOf = (className) => {
|
|
4131
|
+
const type = aliases.get(className);
|
|
4132
|
+
return new Set(type?.kind === "object" ? type.properties.keys() : ["Name", "ClassName", "Parent", "Archivable"]);
|
|
4133
|
+
};
|
|
4134
|
+
const { types, problem } = (0, import_parser7.sourceMapTypes)(tree.text, tree.path, { classes: new Set(aliases.keys()), membersOf });
|
|
4135
|
+
if (!types) return tree.named && problem ? { problem: { file: tree.path, message: problem } } : void 0;
|
|
4136
|
+
const scripts = /* @__PURE__ */ new Map();
|
|
4137
|
+
const cached = {
|
|
4138
|
+
program: types.program,
|
|
4139
|
+
scriptFor: (file) => {
|
|
4140
|
+
if (!scripts.has(file)) scripts.set(file, types.scriptFor(file));
|
|
4141
|
+
return scripts.get(file);
|
|
4142
|
+
}
|
|
4143
|
+
};
|
|
4144
|
+
treeCache = { text: tree.text, libs: [...libs], types: cached, scripts };
|
|
4145
|
+
return { types: cached };
|
|
4146
|
+
}
|
|
4118
4147
|
function parsedLibrary(file) {
|
|
4119
4148
|
let stamp;
|
|
4120
4149
|
try {
|
|
@@ -4138,6 +4167,10 @@ function analyzeModules(modules, config) {
|
|
|
4138
4167
|
const libs = libraries.files.map(parsedLibrary);
|
|
4139
4168
|
const noLib = config?.noLib ?? false;
|
|
4140
4169
|
const checked = libs.length > 0 || !noLib;
|
|
4170
|
+
const tree = config ? instanceTree(config, libs, noLib) : void 0;
|
|
4171
|
+
if (tree?.problem) out.push({ file: tree.problem.file, message: tree.problem.message, line: 1, column: 1, category: "config" });
|
|
4172
|
+
if (tree?.types) libs.push(tree.types.program);
|
|
4173
|
+
const scriptOf = (file) => tree?.types && config ? tree.types.scriptFor((0, import_parser7.outputPathOf)(config, file)) : void 0;
|
|
4141
4174
|
const globals = libs.flatMap((lib) => lib.body.statements.flatMap((s) => s.type === "DeclareStatement" ? [s.name] : []));
|
|
4142
4175
|
const byFile = new Map(modules.map((m) => [m.file, m]));
|
|
4143
4176
|
const exportsCache = /* @__PURE__ */ new Map();
|
|
@@ -4150,7 +4183,9 @@ function analyzeModules(modules, config) {
|
|
|
4150
4183
|
const done = analyzed.get(file);
|
|
4151
4184
|
if (done) return done;
|
|
4152
4185
|
const remembered = moduleAnalyses.get(file);
|
|
4153
|
-
|
|
4186
|
+
const script = scriptOf(file);
|
|
4187
|
+
const ownLibs = script ? [...libs, script] : libs;
|
|
4188
|
+
if (remembered && remembered.setting === settingFor && ownLibs.length === remembered.libs.length && remembered.libs.every((lib, i) => lib === ownLibs[i]) && remembered.program === program2 && [...remembered.read].every(([read3, stamp]) => stampOf(read3) === stamp)) {
|
|
4154
4189
|
analyzed.set(file, remembered.result);
|
|
4155
4190
|
readWhile.set(file, remembered.read);
|
|
4156
4191
|
return remembered.result;
|
|
@@ -4163,7 +4198,7 @@ function analyzeModules(modules, config) {
|
|
|
4163
4198
|
}
|
|
4164
4199
|
const before = partials;
|
|
4165
4200
|
const types = (0, import_parser7.analyzeTypes)(program2, scopes, {
|
|
4166
|
-
libs,
|
|
4201
|
+
libs: ownLibs,
|
|
4167
4202
|
resolveModule: resolverFor(file),
|
|
4168
4203
|
noLib,
|
|
4169
4204
|
reportUnknownTypes: checked
|
|
@@ -4180,7 +4215,7 @@ function analyzeModules(modules, config) {
|
|
|
4180
4215
|
if (own !== void 0) read3.set(file, own);
|
|
4181
4216
|
readWhile.set(file, read3);
|
|
4182
4217
|
if ([...read3.values()].every((stamp) => stamp !== void 0)) {
|
|
4183
|
-
moduleAnalyses.set(file, { setting: settingFor, libs: [...
|
|
4218
|
+
moduleAnalyses.set(file, { setting: settingFor, libs: [...ownLibs], program: program2, read: read3, result: result2 });
|
|
4184
4219
|
}
|
|
4185
4220
|
}
|
|
4186
4221
|
return result2;
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tilua/compiler",
|
|
3
|
-
"version": "9.0.
|
|
3
|
+
"version": "9.0.2",
|
|
4
4
|
"description": "Compiles tilua to Luau: lowers the @tilua/parser AST to a luau-parser AST and prints it",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"license": "ISC",
|
|
38
38
|
"type": "module",
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@tilua/parser": "^9.0.
|
|
40
|
+
"@tilua/parser": "^9.0.2",
|
|
41
41
|
"luau-parser": "^1.1.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|