Skip to content

Commit 8fa21ca

Browse files
authored
Lualib modules via plugin (#1224)
* fix lualib-build * Finish lualib modules via plugin implementation * Include build-lualib.ts
1 parent b38312c commit 8fa21ca

File tree

27 files changed

+373
-260
lines changed

27 files changed

+373
-260
lines changed

build-lualib.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,16 @@
11
require("ts-node/register/transpile-only");
2-
const fs = require("fs");
32
const path = require("path");
4-
const ts = require("typescript");
53
const tstl = require("./src");
6-
const { loadLuaLibFeatures } = require("./src/LuaLib");
74

85
const configFileName = path.resolve(__dirname, "src/lualib/tsconfig.json");
96
const { diagnostics } = tstl.transpileProject(configFileName);
107
diagnostics.forEach(tstl.createDiagnosticReporter(true));
118

12-
const bundlePath = path.join(__dirname, "dist/lualib/lualib_bundle.lua");
13-
if (fs.existsSync(bundlePath)) {
14-
fs.unlinkSync(bundlePath);
15-
}
9+
const extraDiagnostics = require("./src/lualib-build/build-lualib").writeExtraLualibFiles(
10+
path.resolve(__dirname, "dist/lualib")
11+
);
12+
extraDiagnostics.forEach(tstl.createDiagnosticReporter(true));
1613

17-
fs.writeFileSync(bundlePath, loadLuaLibFeatures(Object.values(tstl.LuaLibFeature), ts.sys));
14+
if (diagnostics.length > 0 || extraDiagnostics.length > 0) {
15+
process.exit(1);
16+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
"dist/**/*.js",
1919
"dist/**/*.lua",
2020
"dist/**/*.ts",
21+
"dist/lualib/*.json",
2122
"language-extensions/**/*.ts"
2223
],
2324
"main": "dist/index.js",

src/LuaLib.ts

Lines changed: 70 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as path from "path";
22
import { EmitHost } from "./transpilation";
3+
import * as lua from "./LuaAST";
34

45
export enum LuaLibFeature {
56
ArrayConcat = "ArrayConcat",
@@ -99,81 +100,52 @@ export enum LuaLibFeature {
99100
Unpack = "Unpack",
100101
}
101102

102-
/* eslint-disable @typescript-eslint/naming-convention */
103-
const luaLibDependencies: Partial<Record<LuaLibFeature, LuaLibFeature[]>> = {
104-
ArrayConcat: [LuaLibFeature.ArrayIsArray],
105-
ArrayFlat: [LuaLibFeature.ArrayConcat, LuaLibFeature.ArrayIsArray],
106-
ArrayFlatMap: [LuaLibFeature.ArrayConcat, LuaLibFeature.ArrayIsArray],
107-
Await: [LuaLibFeature.InstanceOf, LuaLibFeature.New, LuaLibFeature.Promise],
108-
Decorate: [LuaLibFeature.ObjectGetOwnPropertyDescriptor, LuaLibFeature.SetDescriptor, LuaLibFeature.ObjectAssign],
109-
DelegatedYield: [LuaLibFeature.StringAccess],
110-
Delete: [LuaLibFeature.ObjectGetOwnPropertyDescriptors, LuaLibFeature.Error, LuaLibFeature.New],
111-
Error: [LuaLibFeature.Class, LuaLibFeature.ClassExtends, LuaLibFeature.New],
112-
FunctionBind: [LuaLibFeature.Unpack],
113-
Generator: [LuaLibFeature.Symbol],
114-
InstanceOf: [LuaLibFeature.Symbol],
115-
Iterator: [LuaLibFeature.Symbol],
116-
NumberToString: [LuaLibFeature.StringAccess],
117-
ObjectDefineProperty: [LuaLibFeature.CloneDescriptor, LuaLibFeature.SetDescriptor],
118-
ObjectFromEntries: [LuaLibFeature.Iterator, LuaLibFeature.Symbol],
119-
Promise: [
120-
LuaLibFeature.ArrayPush,
121-
LuaLibFeature.Class,
122-
LuaLibFeature.FunctionBind,
123-
LuaLibFeature.InstanceOf,
124-
LuaLibFeature.New,
125-
],
126-
PromiseAll: [LuaLibFeature.InstanceOf, LuaLibFeature.New, LuaLibFeature.Promise, LuaLibFeature.Iterator],
127-
PromiseAllSettled: [LuaLibFeature.InstanceOf, LuaLibFeature.New, LuaLibFeature.Promise, LuaLibFeature.Iterator],
128-
PromiseAny: [
129-
LuaLibFeature.ArrayPush,
130-
LuaLibFeature.InstanceOf,
131-
LuaLibFeature.New,
132-
LuaLibFeature.Promise,
133-
LuaLibFeature.Iterator,
134-
],
135-
PromiseRace: [
136-
LuaLibFeature.ArrayPush,
137-
LuaLibFeature.InstanceOf,
138-
LuaLibFeature.New,
139-
LuaLibFeature.Promise,
140-
LuaLibFeature.Iterator,
141-
],
142-
ParseFloat: [LuaLibFeature.StringAccess],
143-
ParseInt: [LuaLibFeature.StringSubstr, LuaLibFeature.StringSubstring],
144-
SetDescriptor: [LuaLibFeature.CloneDescriptor],
145-
Spread: [LuaLibFeature.Iterator, LuaLibFeature.StringAccess, LuaLibFeature.Unpack],
146-
StringSplit: [LuaLibFeature.StringSubstring, LuaLibFeature.StringAccess],
147-
SymbolRegistry: [LuaLibFeature.Symbol],
148-
149-
Map: [LuaLibFeature.InstanceOf, LuaLibFeature.Iterator, LuaLibFeature.Symbol, LuaLibFeature.Class],
150-
Set: [LuaLibFeature.InstanceOf, LuaLibFeature.Iterator, LuaLibFeature.Symbol, LuaLibFeature.Class],
151-
WeakMap: [LuaLibFeature.InstanceOf, LuaLibFeature.Iterator, LuaLibFeature.Symbol, LuaLibFeature.Class],
152-
WeakSet: [LuaLibFeature.InstanceOf, LuaLibFeature.Iterator, LuaLibFeature.Symbol, LuaLibFeature.Class],
153-
};
154-
/* eslint-enable @typescript-eslint/naming-convention */
155-
156-
export function loadLuaLibFeatures(features: Iterable<LuaLibFeature>, emitHost: EmitHost): string {
103+
export interface LuaLibFeatureInfo {
104+
dependencies?: LuaLibFeature[];
105+
exports: string[];
106+
}
107+
export type LuaLibModulesInfo = Record<LuaLibFeature, LuaLibFeatureInfo>;
108+
109+
export const luaLibModulesInfoFileName = "lualib_module_info.json";
110+
let luaLibModulesInfo: LuaLibModulesInfo | undefined;
111+
export function getLuaLibModulesInfo(emitHost: EmitHost): LuaLibModulesInfo {
112+
if (luaLibModulesInfo === undefined) {
113+
const lualibPath = path.resolve(__dirname, `../dist/lualib/${luaLibModulesInfoFileName}`);
114+
const result = emitHost.readFile(lualibPath);
115+
if (result !== undefined) {
116+
luaLibModulesInfo = JSON.parse(result) as LuaLibModulesInfo;
117+
} else {
118+
throw new Error(`Could not load lualib dependencies from '${lualibPath}'`);
119+
}
120+
}
121+
return luaLibModulesInfo;
122+
}
123+
124+
export function readLuaLibFeature(feature: LuaLibFeature, emitHost: EmitHost): string {
125+
const featurePath = path.resolve(__dirname, `../dist/lualib/${feature}.lua`);
126+
const luaLibFeature = emitHost.readFile(featurePath);
127+
if (luaLibFeature === undefined) {
128+
throw new Error(`Could not load lualib feature from '${featurePath}'`);
129+
}
130+
return luaLibFeature;
131+
}
132+
133+
export function loadInlineLualibFeatures(features: Iterable<LuaLibFeature>, emitHost: EmitHost): string {
157134
let result = "";
158135

136+
const luaLibModulesInfo = getLuaLibModulesInfo(emitHost);
159137
const loadedFeatures = new Set<LuaLibFeature>();
160138

161139
function load(feature: LuaLibFeature): void {
162140
if (loadedFeatures.has(feature)) return;
163141
loadedFeatures.add(feature);
164142

165-
const dependencies = luaLibDependencies[feature];
143+
const dependencies = luaLibModulesInfo[feature]?.dependencies;
166144
if (dependencies) {
167145
dependencies.forEach(load);
168146
}
169-
170-
const featurePath = path.resolve(__dirname, `../dist/lualib/${feature}.lua`);
171-
const luaLibFeature = emitHost.readFile(featurePath);
172-
if (luaLibFeature !== undefined) {
173-
result += luaLibFeature + "\n";
174-
} else {
175-
throw new Error(`Could not load lualib feature from '${featurePath}'`);
176-
}
147+
const luaLibFeature = readLuaLibFeature(feature, emitHost);
148+
result += luaLibFeature + "\n";
177149
}
178150

179151
for (const feature of features) {
@@ -183,6 +155,40 @@ export function loadLuaLibFeatures(features: Iterable<LuaLibFeature>, emitHost:
183155
return result;
184156
}
185157

158+
export function loadImportedLualibFeatures(
159+
features: Iterable<LuaLibFeature>,
160+
emitHost: EmitHost,
161+
alwaysRequire = false
162+
): lua.Statement[] {
163+
const luaLibModuleInfo = getLuaLibModulesInfo(emitHost);
164+
165+
const imports = Array.from(features).flatMap(feature => luaLibModuleInfo[feature].exports);
166+
167+
const requireCall = lua.createCallExpression(lua.createIdentifier("require"), [
168+
lua.createStringLiteral("lualib_bundle"),
169+
]);
170+
if (imports.length === 0) {
171+
if (alwaysRequire) {
172+
return [lua.createExpressionStatement(requireCall)];
173+
}
174+
return [];
175+
}
176+
177+
const luaLibId = lua.createIdentifier("____lualib");
178+
const importStatement = lua.createVariableDeclarationStatement(luaLibId, requireCall);
179+
const statements: lua.Statement[] = [importStatement];
180+
// local <export> = ____luaLib.<export>
181+
for (const item of imports) {
182+
statements.push(
183+
lua.createVariableDeclarationStatement(
184+
lua.createIdentifier(item),
185+
lua.createTableIndexExpression(luaLibId, lua.createStringLiteral(item))
186+
)
187+
);
188+
}
189+
return statements;
190+
}
191+
186192
let luaLibBundleContent: string;
187193
export function getLuaLibBundle(emitHost: EmitHost): string {
188194
if (luaLibBundleContent === undefined) {

src/LuaPrinter.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import * as path from "path";
22
import { Mapping, SourceMapGenerator, SourceNode } from "source-map";
3-
import { getEmitPath } from ".";
43
import * as ts from "typescript";
54
import { CompilerOptions, isBundleEnabled, LuaLibImportKind } from "./CompilerOptions";
65
import * as lua from "./LuaAST";
7-
import { loadLuaLibFeatures, LuaLibFeature } from "./LuaLib";
6+
import { loadInlineLualibFeatures, LuaLibFeature, loadImportedLualibFeatures } from "./LuaLib";
87
import { isValidLuaIdentifier, shouldAllowUnicode } from "./transformation/utils/safe-names";
9-
import { EmitHost } from "./transpilation";
8+
import { EmitHost, getEmitPath } from "./transpilation";
109
import { intersperse, normalizeSlashes } from "./utils";
1110

1211
// https://www.lua.org/pil/2.4.html
@@ -233,18 +232,25 @@ export class LuaPrinter {
233232
if (!this.options.noHeader) {
234233
header += tstlHeader;
235234
}
235+
let statements = file.statements;
236236

237237
const luaLibImport = this.options.luaLibImport ?? LuaLibImportKind.Require;
238238
if (
239239
luaLibImport === LuaLibImportKind.Always ||
240240
(luaLibImport === LuaLibImportKind.Require && file.luaLibFeatures.size > 0)
241241
) {
242-
// Require lualib bundle
243-
header += 'require("lualib_bundle");\n';
242+
// Import lualib features
243+
const importStatements = loadImportedLualibFeatures(
244+
file.luaLibFeatures,
245+
this.emitHost,
246+
luaLibImport === LuaLibImportKind.Always
247+
);
248+
249+
statements = importStatements.concat(statements);
244250
} else if (luaLibImport === LuaLibImportKind.Inline && file.luaLibFeatures.size > 0) {
245251
// Inline lualib features
246252
header += "-- Lua Library inline imports\n";
247-
header += loadLuaLibFeatures(file.luaLibFeatures, this.emitHost);
253+
header += loadInlineLualibFeatures(file.luaLibFeatures, this.emitHost);
248254
}
249255

250256
if (this.options.sourceMapTraceback && !isBundleEnabled(this.options)) {
@@ -253,7 +259,7 @@ export class LuaPrinter {
253259
header += `${LuaPrinter.sourceMapTracebackPlaceholder}\n`;
254260
}
255261

256-
return this.concatNodes(header, ...this.printStatementArray(file.statements));
262+
return this.concatNodes(header, ...this.printStatementArray(statements));
257263
}
258264

259265
protected pushIndent(): void {

src/lualib-build/build-lualib.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import pluginInstance from "./plugin";
2+
import { luaLibModulesInfoFileName, loadInlineLualibFeatures, LuaLibFeature } from "../LuaLib";
3+
import * as path from "path";
4+
import * as ts from "typescript";
5+
6+
// should be called after lualib modules are built
7+
export function writeExtraLualibFiles(destPath: string): ts.Diagnostic[] {
8+
const { result: luaLibModuleInfo, diagnostics } = pluginInstance.createLuaLibModulesInfo();
9+
const emitHost = ts.sys;
10+
emitHost.writeFile(path.join(destPath, luaLibModulesInfoFileName), JSON.stringify(luaLibModuleInfo, null, 2));
11+
12+
const allFeatures = Object.values(LuaLibFeature) as LuaLibFeature[];
13+
14+
let lualibBundle = loadInlineLualibFeatures(allFeatures, emitHost);
15+
const exports = allFeatures.flatMap(feature => luaLibModuleInfo[feature].exports);
16+
lualibBundle += `\nreturn {\n${exports.map(exportName => ` ${exportName} = ${exportName}`).join(",\n")}\n}\n`;
17+
emitHost.writeFile(path.join(destPath, "lualib_bundle.lua"), lualibBundle);
18+
19+
return diagnostics;
20+
}

0 commit comments

Comments
 (0)