@defold-typescript/transpiler 0.42.0 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/emitted-requires.d.ts +1 -1
- package/dist/hsm/defold.ts +33 -0
- package/dist/hsm/index.ts +696 -0
- package/dist/hsm-builtin.d.ts +25 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +298 -127
- package/dist/transpile.d.ts +10 -0
- package/package.json +3 -3
- package/src/emitted-requires.ts +6 -4
- package/src/hsm-builtin.ts +195 -0
- package/src/index.ts +10 -1
- package/src/session.ts +2 -0
- package/src/transpile.ts +108 -26
package/dist/transpile.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export interface TranspileProjectResult {
|
|
|
21
21
|
readonly companions?: Readonly<Record<string, string>>;
|
|
22
22
|
readonly lualib?: string;
|
|
23
23
|
readonly timersRuntime?: string;
|
|
24
|
+
readonly hsmModules?: Readonly<Record<string, string>>;
|
|
24
25
|
}
|
|
25
26
|
export declare const AMBIENT_FILES: Readonly<Record<string, string>>;
|
|
26
27
|
interface CollectableFile {
|
|
@@ -31,5 +32,14 @@ interface CollectableFile {
|
|
|
31
32
|
}
|
|
32
33
|
export declare function collectOutputs(transpiledFiles: readonly CollectableFile[], diagnostics: readonly ts.Diagnostic[], userKeys: ReadonlySet<string>, emittedCompanions?: ReadonlyMap<string, string>): TranspileProjectResult;
|
|
33
34
|
export declare function transpileProject(input: TranspileProjectInput): TranspileProjectResult;
|
|
35
|
+
/**
|
|
36
|
+
* Every `hsm` module compiled to Lua, keyed by module name, once per process.
|
|
37
|
+
* The source is the shipped TypeScript, never a prebuilt Lua copy, so the Lua a
|
|
38
|
+
* project gets always comes from the compiler that builds it. Each file sits at
|
|
39
|
+
* `defold_typescript_hsm/<module>.ts`, so a require between modules is already
|
|
40
|
+
* spelled the way the user's lowered imports are. Throws when the compile
|
|
41
|
+
* reports anything or needs the lualib bundle, which hsm must never do.
|
|
42
|
+
*/
|
|
43
|
+
export declare function compileHsmModules(): Readonly<Record<string, string>>;
|
|
34
44
|
export declare function transpile(source: string): TranspileResult;
|
|
35
45
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@defold-typescript/transpiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
4
4
|
"description": "TypeScript-to-Lua build pipeline tuned for Defold's runtime.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -30,12 +30,12 @@
|
|
|
30
30
|
"access": "public"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
|
-
"build": "rm -rf dist && bun build src/index.ts --target=node --format=esm --packages=external --outdir=dist && tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
33
|
+
"build": "rm -rf dist && bun scripts/copy-hsm.ts && bun build src/index.ts --target=node --format=esm --packages=external --outdir=dist && tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
34
34
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
35
35
|
"test": "bun test"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@defold-typescript/types": "0.
|
|
38
|
+
"@defold-typescript/types": "0.43.0",
|
|
39
39
|
"@typescript-to-lua/language-extensions": "1.19.0",
|
|
40
40
|
"typescript-to-lua": "^1.36.0"
|
|
41
41
|
}
|
package/src/emitted-requires.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { isHsmRequireName } from "./hsm-builtin";
|
|
1
2
|
import { scanEmittedRequires } from "./lua-require-scan";
|
|
2
3
|
import { TIMERS_REQUIRE_NAME } from "./timers-runtime";
|
|
3
4
|
|
|
4
5
|
export const LUALIB_REQUIRE_NAME = "lualib_bundle";
|
|
5
6
|
|
|
6
|
-
// Written
|
|
7
|
-
// source, so a require of
|
|
7
|
+
// Written by both build paths whenever a chunk requires them and backed by no
|
|
8
|
+
// TypeScript source, so a require of one can never be unresolvable. The hsm
|
|
9
|
+
// modules join them through `isHsmRequireName`.
|
|
8
10
|
const GENERATED_RUNTIME_REQUIRES: ReadonlySet<string> = new Set([
|
|
9
11
|
LUALIB_REQUIRE_NAME,
|
|
10
12
|
TIMERS_REQUIRE_NAME,
|
|
@@ -12,7 +14,7 @@ const GENERATED_RUNTIME_REQUIRES: ReadonlySet<string> = new Set([
|
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* The distinct string-literal module paths an emitted Lua chunk requires, in
|
|
15
|
-
* source order. The
|
|
17
|
+
* source order. The generated runtimes and hsm modules are skipped, as is any require whose
|
|
16
18
|
* argument is not a string literal — neither can name a source the build owns.
|
|
17
19
|
*/
|
|
18
20
|
export function findEmittedRequires(lua: string): string[] {
|
|
@@ -20,7 +22,7 @@ export function findEmittedRequires(lua: string): string[] {
|
|
|
20
22
|
const seen = new Set<string>();
|
|
21
23
|
|
|
22
24
|
for (const { path } of scanEmittedRequires(lua)) {
|
|
23
|
-
if (GENERATED_RUNTIME_REQUIRES.has(path) || seen.has(path)) {
|
|
25
|
+
if (GENERATED_RUNTIME_REQUIRES.has(path) || isHsmRequireName(path) || seen.has(path)) {
|
|
24
26
|
continue;
|
|
25
27
|
}
|
|
26
28
|
seen.add(path);
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import * as ts from "typescript";
|
|
5
|
+
import type { Plugin } from "typescript-to-lua";
|
|
6
|
+
import { scanEmittedRequires } from "./lua-require-scan";
|
|
7
|
+
|
|
8
|
+
// `@defold-typescript/types/hsm` is the `index` module; every other module is a
|
|
9
|
+
// subpath of it. Each lowers to a require of `defold_typescript_hsm.<module>`,
|
|
10
|
+
// which the CLI writes under the output root the way it writes the timers
|
|
11
|
+
// runtime.
|
|
12
|
+
export const HSM_MODULE_SPECIFIER = "@defold-typescript/types/hsm";
|
|
13
|
+
export const HSM_REQUIRE_ROOT = "defold_typescript_hsm";
|
|
14
|
+
|
|
15
|
+
export interface HsmModule {
|
|
16
|
+
/** The source file's basename without `.ts`. */
|
|
17
|
+
readonly name: string;
|
|
18
|
+
/** What a user imports. */
|
|
19
|
+
readonly specifier: string;
|
|
20
|
+
/** What the import lowers to, and the module path the CLI writes it under. */
|
|
21
|
+
readonly requireName: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function hsmRequireName(name: string): string {
|
|
25
|
+
return `${HSM_REQUIRE_ROOT}.${name}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function hsmSpecifier(name: string): string {
|
|
29
|
+
return name === "index" ? HSM_MODULE_SPECIFIER : `${HSM_MODULE_SPECIFIER}/${name}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Same anchoring as the CLI's version lookup: the module URL, never
|
|
33
|
+
// `import.meta.dir`, so the bundled `dist/index.js` under node still lands on
|
|
34
|
+
// the package root.
|
|
35
|
+
function defaultPackageRoot(): string {
|
|
36
|
+
return path.join(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// The one filter the transpiler build (what ships in `dist/hsm/`) and the
|
|
40
|
+
// compile (what reaches a project) both read, so a new module ships without
|
|
41
|
+
// editing a list.
|
|
42
|
+
export function hsmSourceFiles(dir: string): string[] {
|
|
43
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
44
|
+
.filter(
|
|
45
|
+
(entry) =>
|
|
46
|
+
entry.isFile() &&
|
|
47
|
+
entry.name.endsWith(".ts") &&
|
|
48
|
+
!entry.name.endsWith(".test.ts") &&
|
|
49
|
+
!entry.name.endsWith(".d.ts"),
|
|
50
|
+
)
|
|
51
|
+
.map((entry) => entry.name)
|
|
52
|
+
.sort();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// The workspace sibling wins so a stale `dist/` never shadows the live source
|
|
56
|
+
// inside this repo; a published install has only the `dist/hsm/` copy.
|
|
57
|
+
export function resolveHsmSourceDir(packageRoot: string = defaultPackageRoot()): string | null {
|
|
58
|
+
const workspace = path.join(packageRoot, "..", "hsm", "src");
|
|
59
|
+
if (existsSync(workspace)) {
|
|
60
|
+
return workspace;
|
|
61
|
+
}
|
|
62
|
+
const shipped = path.join(packageRoot, "dist", "hsm");
|
|
63
|
+
if (existsSync(shipped)) {
|
|
64
|
+
return shipped;
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function requireHsmSourceDir(): string {
|
|
70
|
+
const dir = resolveHsmSourceDir();
|
|
71
|
+
if (dir === null) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
"@defold-typescript/transpiler: the hsm source is missing from this install; reinstall @defold-typescript/transpiler.",
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
return dir;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let modules: readonly HsmModule[] | undefined;
|
|
80
|
+
|
|
81
|
+
export function hsmModules(): readonly HsmModule[] {
|
|
82
|
+
modules ??= hsmSourceFiles(requireHsmSourceDir()).map((file) => {
|
|
83
|
+
const name = file.slice(0, -".ts".length);
|
|
84
|
+
return { name, specifier: hsmSpecifier(name), requireName: hsmRequireName(name) };
|
|
85
|
+
});
|
|
86
|
+
return modules;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The hsm module a require path names, or undefined for any other require. */
|
|
90
|
+
function hsmModuleOfRequire(requirePath: string): HsmModule | undefined {
|
|
91
|
+
if (!requirePath.startsWith(`${HSM_REQUIRE_ROOT}.`)) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
return hsmModules().find((module) => module.requireName === requirePath);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function isHsmRequireName(requirePath: string): boolean {
|
|
98
|
+
return hsmModuleOfRequire(requirePath) !== undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function hsmModuleOfSpecifier(node: ts.Expression | undefined): HsmModule | undefined {
|
|
102
|
+
if (node === undefined || !ts.isStringLiteral(node)) {
|
|
103
|
+
return undefined;
|
|
104
|
+
}
|
|
105
|
+
const { text } = node;
|
|
106
|
+
if (text !== HSM_MODULE_SPECIFIER && !text.startsWith(`${HSM_MODULE_SPECIFIER}/`)) {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
return hsmModules().find((module) => module.specifier === text);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function requiredHsmNames(lua: string): string[] {
|
|
113
|
+
const names: string[] = [];
|
|
114
|
+
for (const { path: requirePath } of scanEmittedRequires(lua)) {
|
|
115
|
+
const module = hsmModuleOfRequire(requirePath);
|
|
116
|
+
if (module !== undefined) {
|
|
117
|
+
names.push(module.name);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return names;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The hsm modules a program needs: every one its chunks require, plus every one
|
|
125
|
+
* those require in turn. Read from emitted Lua rather than the import AST, so an
|
|
126
|
+
* import TypeScript elides (`import type`, an unused binding) selects nothing.
|
|
127
|
+
* `compile` runs only when some chunk requires a module.
|
|
128
|
+
*/
|
|
129
|
+
export function hsmClosure(
|
|
130
|
+
chunks: Iterable<string>,
|
|
131
|
+
compile: () => Readonly<Record<string, string>>,
|
|
132
|
+
): Record<string, string> | undefined {
|
|
133
|
+
const pending: string[] = [];
|
|
134
|
+
for (const lua of chunks) {
|
|
135
|
+
pending.push(...requiredHsmNames(lua));
|
|
136
|
+
}
|
|
137
|
+
if (pending.length === 0) {
|
|
138
|
+
return undefined;
|
|
139
|
+
}
|
|
140
|
+
const compiled = compile();
|
|
141
|
+
const selected: Record<string, string> = {};
|
|
142
|
+
for (let name = pending.pop(); name !== undefined; name = pending.pop()) {
|
|
143
|
+
const lua = compiled[name];
|
|
144
|
+
if (name in selected || lua === undefined) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
selected[name] = lua;
|
|
148
|
+
pending.push(...requiredHsmNames(lua));
|
|
149
|
+
}
|
|
150
|
+
return Object.fromEntries(Object.entries(selected).sort(([a], [b]) => a.localeCompare(b)));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Rewrite an hsm specifier to its flat require, the way the timers lowering
|
|
154
|
+
// does: `@NoResolution:` keeps TSTL from looking for a Lua source behind the
|
|
155
|
+
// `.d.ts` the import type-checks against. No lualib feature is registered,
|
|
156
|
+
// because hsm compiles to plain Lua.
|
|
157
|
+
function lowerSpecifier(module: HsmModule): ts.StringLiteral {
|
|
158
|
+
return ts.factory.createStringLiteral(`@NoResolution:${module.requireName}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export const hsmLoweringPlugin: Plugin = {
|
|
162
|
+
visitors: {
|
|
163
|
+
[ts.SyntaxKind.ImportDeclaration]: (node, context) => {
|
|
164
|
+
const module = hsmModuleOfSpecifier(node.moduleSpecifier);
|
|
165
|
+
if (module === undefined) {
|
|
166
|
+
return context.superTransformStatements(node);
|
|
167
|
+
}
|
|
168
|
+
return context.superTransformStatements(
|
|
169
|
+
ts.factory.updateImportDeclaration(
|
|
170
|
+
node,
|
|
171
|
+
node.modifiers,
|
|
172
|
+
node.importClause,
|
|
173
|
+
lowerSpecifier(module),
|
|
174
|
+
node.attributes,
|
|
175
|
+
),
|
|
176
|
+
);
|
|
177
|
+
},
|
|
178
|
+
[ts.SyntaxKind.ExportDeclaration]: (node, context) => {
|
|
179
|
+
const module = hsmModuleOfSpecifier(node.moduleSpecifier);
|
|
180
|
+
if (module === undefined) {
|
|
181
|
+
return context.superTransformStatements(node);
|
|
182
|
+
}
|
|
183
|
+
return context.superTransformStatements(
|
|
184
|
+
ts.factory.updateExportDeclaration(
|
|
185
|
+
node,
|
|
186
|
+
node.modifiers,
|
|
187
|
+
node.isTypeOnly,
|
|
188
|
+
node.exportClause,
|
|
189
|
+
lowerSpecifier(module),
|
|
190
|
+
node.attributes,
|
|
191
|
+
),
|
|
192
|
+
);
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -20,6 +20,15 @@ export {
|
|
|
20
20
|
guiScriptResourceOf,
|
|
21
21
|
guiScriptResourcesOf,
|
|
22
22
|
} from "./gui-node-index";
|
|
23
|
+
export type { HsmModule } from "./hsm-builtin";
|
|
24
|
+
export {
|
|
25
|
+
HSM_MODULE_SPECIFIER,
|
|
26
|
+
HSM_REQUIRE_ROOT,
|
|
27
|
+
hsmModules,
|
|
28
|
+
hsmRequireName,
|
|
29
|
+
hsmSourceFiles,
|
|
30
|
+
resolveHsmSourceDir,
|
|
31
|
+
} from "./hsm-builtin";
|
|
23
32
|
export { buildInputActionIndex } from "./input-action-index";
|
|
24
33
|
export type {
|
|
25
34
|
ExtensionDependency,
|
|
@@ -99,7 +108,7 @@ export type {
|
|
|
99
108
|
TranspileProjectResult,
|
|
100
109
|
TranspileResult,
|
|
101
110
|
} from "./transpile";
|
|
102
|
-
export { transpile, transpileProject } from "./transpile";
|
|
111
|
+
export { compileHsmModules, transpile, transpileProject } from "./transpile";
|
|
103
112
|
export type { ClassifiedSlot } from "./url-address-slots";
|
|
104
113
|
export {
|
|
105
114
|
addressClassOfArgument,
|
package/src/session.ts
CHANGED
|
@@ -5,6 +5,7 @@ import * as ts from "typescript";
|
|
|
5
5
|
import * as tstl from "typescript-to-lua";
|
|
6
6
|
import { type CompanionEmitter, createCompanionEmitPlugin } from "./companion-emit";
|
|
7
7
|
import { editorScriptErasurePlugin } from "./editor-script-erasure";
|
|
8
|
+
import { hsmLoweringPlugin } from "./hsm-builtin";
|
|
8
9
|
import { lifecycleErasurePlugin } from "./lifecycle-erasure";
|
|
9
10
|
import { messageDispatchLoweringPlugin } from "./message-dispatch-lowering";
|
|
10
11
|
import { messageGuardLoweringPlugin } from "./message-guard-lowering";
|
|
@@ -44,6 +45,7 @@ function compilerOptions(companionEmit: CompanionEmitter): tstl.CompilerOptions
|
|
|
44
45
|
{ plugin: windowEventGuardLoweringPlugin },
|
|
45
46
|
{ plugin: messageDispatchLoweringPlugin },
|
|
46
47
|
{ plugin: timersLoweringPlugin },
|
|
48
|
+
{ plugin: hsmLoweringPlugin },
|
|
47
49
|
{ plugin: typeApplicationErasurePlugin },
|
|
48
50
|
{ plugin: companionEmit.plugin },
|
|
49
51
|
],
|
package/src/transpile.ts
CHANGED
|
@@ -9,6 +9,13 @@ import {
|
|
|
9
9
|
findDirectGoPropertyCalls,
|
|
10
10
|
GO_PROPERTY_DIRECT_CALL_MESSAGE,
|
|
11
11
|
} from "./go-property-direct-call";
|
|
12
|
+
import {
|
|
13
|
+
HSM_REQUIRE_ROOT,
|
|
14
|
+
hsmClosure,
|
|
15
|
+
hsmLoweringPlugin,
|
|
16
|
+
hsmModules,
|
|
17
|
+
requireHsmSourceDir,
|
|
18
|
+
} from "./hsm-builtin";
|
|
12
19
|
import { lifecycleErasurePlugin } from "./lifecycle-erasure";
|
|
13
20
|
import { messageDispatchLoweringPlugin } from "./message-dispatch-lowering";
|
|
14
21
|
import { messageGuardLoweringPlugin } from "./message-guard-lowering";
|
|
@@ -63,6 +70,13 @@ export interface TranspileProjectResult {
|
|
|
63
70
|
// must write this to the output root. Present only when a user file imports
|
|
64
71
|
// the module (pay-for-use).
|
|
65
72
|
readonly timersRuntime?: string;
|
|
73
|
+
// The compiled `hsm` modules the program requires, keyed by module name
|
|
74
|
+
// (`index`, `defold`, ...). An hsm import lowers to
|
|
75
|
+
// `require("defold_typescript_hsm.<module>")`, so the CLI must write each one
|
|
76
|
+
// under the output root. Present only when some chunk requires one
|
|
77
|
+
// (pay-for-use); never carries a source map, since the `.ts` it would point
|
|
78
|
+
// at is not in the user's project.
|
|
79
|
+
readonly hsmModules?: Readonly<Record<string, string>>;
|
|
66
80
|
}
|
|
67
81
|
|
|
68
82
|
function flattenDiagnosticMessage(
|
|
@@ -180,6 +194,14 @@ function buildAmbientFiles(): Record<string, string> {
|
|
|
180
194
|
"",
|
|
181
195
|
].join("\n"),
|
|
182
196
|
};
|
|
197
|
+
// The generated `hsm` declarations, at the paths the package `exports` map
|
|
198
|
+
// names, so the build types user code against the same `.d.ts` tsserver
|
|
199
|
+
// reads. hsm's own `.ts` compiles separately, in `compileHsmModules`.
|
|
200
|
+
for (const entry of readdirSync(path.join(TYPES_PKG_ROOT, "hsm"))) {
|
|
201
|
+
if (entry.endsWith(".d.ts")) {
|
|
202
|
+
files[`node_modules/@defold-typescript/types/hsm/${entry}`] = readAmbient(`hsm/${entry}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
183
205
|
// Seed the Lua 5.1 standard library (math/os/string/table/coroutine + base
|
|
184
206
|
// globals) so user code can call e.g. `math.randomseed(os.time())`.
|
|
185
207
|
Object.assign(files, collectLuaTypesClosure());
|
|
@@ -287,6 +309,8 @@ export function collectOutputs(
|
|
|
287
309
|
}
|
|
288
310
|
}
|
|
289
311
|
|
|
312
|
+
const hsm = hsmClosure([...Object.values(lua), ...Object.values(companions)], compileHsmModules);
|
|
313
|
+
|
|
290
314
|
return {
|
|
291
315
|
lua,
|
|
292
316
|
sourceMaps,
|
|
@@ -294,38 +318,48 @@ export function collectOutputs(
|
|
|
294
318
|
...(Object.keys(companions).length > 0 ? { companions } : {}),
|
|
295
319
|
...(lualib !== undefined ? { lualib } : {}),
|
|
296
320
|
...(timersImported ? { timersRuntime: TIMERS_RUNTIME } : {}),
|
|
321
|
+
...(hsm !== undefined ? { hsmModules: hsm } : {}),
|
|
297
322
|
};
|
|
298
323
|
}
|
|
299
324
|
|
|
325
|
+
function transpileVirtual(
|
|
326
|
+
files: Readonly<Record<string, string>>,
|
|
327
|
+
companionEmit: ReturnType<typeof createCompanionEmitPlugin>,
|
|
328
|
+
): tstl.TranspileVirtualProjectResult {
|
|
329
|
+
return tstl.transpileVirtualProject(
|
|
330
|
+
{ ...AMBIENT_FILES, ...files },
|
|
331
|
+
{
|
|
332
|
+
luaTarget: tstl.LuaTarget.Lua51,
|
|
333
|
+
sourceMap: true,
|
|
334
|
+
// Already-resolved CompilerOptions, so the value is the lib file name, not
|
|
335
|
+
// the tsconfig spelling "ES2022". Drops the default `lib.dom`, whose
|
|
336
|
+
// `declare var window` shadows Defold's `window` namespace.
|
|
337
|
+
lib: ["lib.es2022.d.ts"],
|
|
338
|
+
// Don't cross-check the seeded ambient .d.ts surface against itself; we only
|
|
339
|
+
// care about diagnostics on user files (mirrors the editor's skipLibCheck).
|
|
340
|
+
skipLibCheck: true,
|
|
341
|
+
// Defold scripts are not OO: free helper functions never receive a context,
|
|
342
|
+
// so suppress TSTL's implicit `self` parameter and the `_G` call-site filler.
|
|
343
|
+
noImplicitSelf: true,
|
|
344
|
+
luaPlugins: [
|
|
345
|
+
{ plugin: lifecycleErasurePlugin },
|
|
346
|
+
{ plugin: editorScriptErasurePlugin },
|
|
347
|
+
{ plugin: messageGuardLoweringPlugin },
|
|
348
|
+
{ plugin: windowEventGuardLoweringPlugin },
|
|
349
|
+
{ plugin: messageDispatchLoweringPlugin },
|
|
350
|
+
{ plugin: timersLoweringPlugin },
|
|
351
|
+
{ plugin: hsmLoweringPlugin },
|
|
352
|
+
{ plugin: typeApplicationErasurePlugin },
|
|
353
|
+
{ plugin: companionEmit.plugin },
|
|
354
|
+
],
|
|
355
|
+
},
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
300
359
|
export function transpileProject(input: TranspileProjectInput): TranspileProjectResult {
|
|
301
360
|
const userKeys = new Set(Object.keys(input.files));
|
|
302
|
-
const merged: Record<string, string> = { ...AMBIENT_FILES, ...input.files };
|
|
303
361
|
const companionEmit = createCompanionEmitPlugin();
|
|
304
|
-
|
|
305
|
-
const result = tstl.transpileVirtualProject(merged, {
|
|
306
|
-
luaTarget: tstl.LuaTarget.Lua51,
|
|
307
|
-
sourceMap: true,
|
|
308
|
-
// Already-resolved CompilerOptions, so the value is the lib file name, not
|
|
309
|
-
// the tsconfig spelling "ES2022". Drops the default `lib.dom`, whose
|
|
310
|
-
// `declare var window` shadows Defold's `window` namespace.
|
|
311
|
-
lib: ["lib.es2022.d.ts"],
|
|
312
|
-
// Don't cross-check the seeded ambient .d.ts surface against itself; we only
|
|
313
|
-
// care about diagnostics on user files (mirrors the editor's skipLibCheck).
|
|
314
|
-
skipLibCheck: true,
|
|
315
|
-
// Defold scripts are not OO: free helper functions never receive a context,
|
|
316
|
-
// so suppress TSTL's implicit `self` parameter and the `_G` call-site filler.
|
|
317
|
-
noImplicitSelf: true,
|
|
318
|
-
luaPlugins: [
|
|
319
|
-
{ plugin: lifecycleErasurePlugin },
|
|
320
|
-
{ plugin: editorScriptErasurePlugin },
|
|
321
|
-
{ plugin: messageGuardLoweringPlugin },
|
|
322
|
-
{ plugin: windowEventGuardLoweringPlugin },
|
|
323
|
-
{ plugin: messageDispatchLoweringPlugin },
|
|
324
|
-
{ plugin: timersLoweringPlugin },
|
|
325
|
-
{ plugin: typeApplicationErasurePlugin },
|
|
326
|
-
{ plugin: companionEmit.plugin },
|
|
327
|
-
],
|
|
328
|
-
});
|
|
362
|
+
const result = transpileVirtual(input.files, companionEmit);
|
|
329
363
|
|
|
330
364
|
return collectOutputs(
|
|
331
365
|
result.transpiledFiles,
|
|
@@ -335,6 +369,54 @@ export function transpileProject(input: TranspileProjectInput): TranspileProject
|
|
|
335
369
|
);
|
|
336
370
|
}
|
|
337
371
|
|
|
372
|
+
let compiledHsm: Readonly<Record<string, string>> | undefined;
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Every `hsm` module compiled to Lua, keyed by module name, once per process.
|
|
376
|
+
* The source is the shipped TypeScript, never a prebuilt Lua copy, so the Lua a
|
|
377
|
+
* project gets always comes from the compiler that builds it. Each file sits at
|
|
378
|
+
* `defold_typescript_hsm/<module>.ts`, so a require between modules is already
|
|
379
|
+
* spelled the way the user's lowered imports are. Throws when the compile
|
|
380
|
+
* reports anything or needs the lualib bundle, which hsm must never do.
|
|
381
|
+
*/
|
|
382
|
+
export function compileHsmModules(): Readonly<Record<string, string>> {
|
|
383
|
+
if (compiledHsm !== undefined) {
|
|
384
|
+
return compiledHsm;
|
|
385
|
+
}
|
|
386
|
+
const sourceDir = requireHsmSourceDir();
|
|
387
|
+
const nameByKey = new Map<string, string>();
|
|
388
|
+
const files: Record<string, string> = {};
|
|
389
|
+
for (const { name } of hsmModules()) {
|
|
390
|
+
const key = `${HSM_REQUIRE_ROOT}/${name}.ts`;
|
|
391
|
+
nameByKey.set(key, name);
|
|
392
|
+
files[key] = readFileSync(path.join(sourceDir, `${name}.ts`), "utf8");
|
|
393
|
+
}
|
|
394
|
+
const result = transpileVirtual(files, createCompanionEmitPlugin());
|
|
395
|
+
|
|
396
|
+
const problems = result.diagnostics.map(
|
|
397
|
+
(d) => `${d.file?.fileName ?? "(project)"}: ${flattenDiagnosticMessage(d.messageText)}`,
|
|
398
|
+
);
|
|
399
|
+
const lua: Record<string, string> = {};
|
|
400
|
+
for (const file of result.transpiledFiles) {
|
|
401
|
+
if (isLualibBundle(file)) {
|
|
402
|
+
problems.push("the compiled Lua requires lualib_bundle");
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
const source = file.sourceFiles.find((s) => nameByKey.has(s.fileName));
|
|
406
|
+
const name = source === undefined ? undefined : nameByKey.get(source.fileName);
|
|
407
|
+
if (name !== undefined && typeof file.lua === "string") {
|
|
408
|
+
lua[name] = file.lua;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
if (problems.length > 0) {
|
|
412
|
+
throw new Error(
|
|
413
|
+
`@defold-typescript/transpiler: the hsm library does not compile:\n ${problems.join("\n ")}`,
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
compiledHsm = lua;
|
|
417
|
+
return lua;
|
|
418
|
+
}
|
|
419
|
+
|
|
338
420
|
export function transpile(source: string): TranspileResult {
|
|
339
421
|
const project = transpileProject({ files: { "main.ts": source } });
|
|
340
422
|
return {
|