Skip to content

Commit 97231eb

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 3f78471 + 308d9d8 commit 97231eb

157 files changed

Lines changed: 5388 additions & 5444 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.eslintignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
/test/cli/errors
44
/test/cli/watch
55
/test/transpile/directories
6-
/test/transpile/module-resolution/*/node_modules
6+
/test/transpile/module-resolution/**/node_modules
7+
/test/transpile/module-resolution/**/dist
78
/test/transpile/outFile

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
node_modules
22
/dist
33
/coverage
4+
/test/transpile/module-resolution/**/dist
5+
/test/transpile/module-resolution/**/tsconfig.tsbuildinfo
46

57
yarn.lock
68
.vscode

.prettierignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@
22
/coverage
33
/test/translation/transformation/characterEscapeSequence.ts
44
/benchmark/dist
5+
/test/transpile/module-resolution/**/node_modules
6+
/test/transpile/module-resolution/**/dist

CHANGELOG.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,36 @@
11
# Changelog
22

3+
## 1.16.0
4+
5+
- Upgraded TypeScript to 5.1.3.
6+
- Added support for [TypeScript 5.0 decorators](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-0.html#decorators).
7+
- Old-style decorators will still work as long as you have `experimentalDecorators` configured, otherwise the new standard is used.
8+
- Added support for [class static initialization blocks](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Static_initialization_blocks).
9+
- Fixed a bug causing the `tstl` object in tsconfig.json not to be properly extended when extending a tsconfig from node_modules.
10+
11+
## 1.15.0
12+
13+
- Using `extends` in tsconfig.json now also correctly merges settings in the `tstl` block (shallow merge).
14+
- Now avoiding assigning default parameter values if the default value is `nil` (`null` or `undefined`).
15+
- Fixed a bug where indexing a `LuaMultiReturn` value with [0] would still return everything.
16+
- Fixed a bug with nested namespaces causing unexpected nil indexing errors.
17+
18+
## 1.14.0
19+
20+
- **[Breaking]** Upgraded TypeScript to 5.0.
21+
- Added support for `Number.toFixed`.
22+
- Added support for spread expressions with `LuaPairsIterable` and `LuaPairsKeysIterable`.
23+
- Fixed a bug breaking module resolution when using a custom file extension.
24+
- Fixed various exceptions that could happen when trying to translate invalid TS.
25+
26+
## 1.13.0
27+
28+
- Fixed alternate file extensions (other than .lua, if configured) breaking module resolution and emitted require statements.
29+
- Added experimental support for `"luaLibImport": "require-minimal"` configuration option. This will output a lualib bundle containing only the lualib functions used by your code. This might not work if you are including external tstl-generated Lua, for example from a npm package.
30+
- Added support for the "exports" field in package.json.
31+
- Fixed some exceptions resulting from invalid language-extensions use.
32+
- Fixed an exception when using compound assignment (like `+=`) with array length.
33+
334
## 1.12.0
435

536
- Reworked how tstl detects and rewrites `require` statements during dependency resolution. This should reduce the amount of false-positive matches of require statements: require statements in string literals or comments should no longer be detected by tstl. This means require statements in string literals or comments can survive the transpiler without causing a 'could not resolve lua sources' error or getting rewritten into nonsense.

jest.config.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@ module.exports = {
1515
testEnvironment: "node",
1616
testRunner: "jest-circus/runner",
1717
preset: "ts-jest",
18-
globals: {
19-
"ts-jest": {
20-
tsconfig: "<rootDir>/test/tsconfig.json",
21-
diagnostics: { warnOnly: !isCI },
22-
},
18+
transform: {
19+
"^.+\\.ts?$": [
20+
"ts-jest",
21+
{
22+
tsconfig: "<rootDir>/test/tsconfig.json",
23+
diagnostics: { warnOnly: !isCI },
24+
},
25+
],
2326
},
2427
};

package-lock.json

Lines changed: 2298 additions & 4477 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@jackmacwindows/typescript-to-lua",
3-
"version": "1.12.0",
3+
"version": "1.16.1",
44
"description": "A generic TypeScript to Lua transpiler. Write your code in TypeScript and publish Lua! (With ComputerCraft support)",
55
"repository": "https://github.com/MCJack123/TypeScriptToLua",
66
"homepage": "https://typescripttolua.github.io/",
@@ -42,7 +42,7 @@
4242
"node": ">=16.10.0"
4343
},
4444
"peerDependencies": {
45-
"typescript": "~4.9.3"
45+
"typescript": "~5.1.3"
4646
},
4747
"dependencies": {
4848
"@typescript-to-lua/language-extensions": "1.0.0",
@@ -56,20 +56,20 @@
5656
"@types/jest": "^27.5.2",
5757
"@types/node": "^13.7.7",
5858
"@types/resolve": "1.14.0",
59-
"@typescript-eslint/eslint-plugin": "^5.36.1",
60-
"@typescript-eslint/parser": "^5.36.1",
61-
"eslint": "^8.23.0",
62-
"eslint-plugin-import": "^2.26.0",
63-
"eslint-plugin-jest": "^26.8.7",
59+
"@typescript-eslint/eslint-plugin": "^5.55.0",
60+
"@typescript-eslint/parser": "^5.55.0",
61+
"eslint": "^8.36.0",
62+
"eslint-plugin-import": "^2.27.5",
63+
"eslint-plugin-jest": "^26.9.0",
6464
"fs-extra": "^8.1.0",
6565
"javascript-stringify": "^2.0.1",
66-
"jest": "^28.1.3",
67-
"jest-circus": "^29.0.1",
66+
"jest": "^29.5.0",
67+
"jest-circus": "^29.5.0",
6868
"@jackmacwindows/lua-types": "^2.13.0",
6969
"lua-wasm-bindings": "^0.3.1",
70-
"prettier": "^2.3.2",
71-
"ts-jest": "^28.0.8",
70+
"prettier": "^2.8.4",
71+
"ts-jest": "^29.1.0",
7272
"ts-node": "^10.9.1",
73-
"typescript": "~4.9.3"
73+
"typescript": "~5.1.3"
7474
}
7575
}

src/CompilerOptions.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ export interface TransformerImport {
1212
after?: boolean;
1313
afterDeclarations?: boolean;
1414
type?: "program" | "config" | "checker" | "raw" | "compilerOptions";
15+
1516
[option: string]: any;
1617
}
1718

1819
export interface LuaPluginImport {
1920
name: string;
2021
import?: string;
22+
2123
[option: string]: any;
2224
}
2325

@@ -50,6 +52,7 @@ export enum LuaLibImportKind {
5052
None = "none",
5153
Inline = "inline",
5254
Require = "require",
55+
RequireMinimal = "require-minimal",
5356
}
5457

5558
export enum LuaTarget {

src/LuaAST.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export enum SyntaxKind {
4444
MethodCallExpression,
4545
Identifier,
4646
TableIndexExpression,
47+
ParenthesizedExpression,
4748

4849
// Operators
4950

@@ -843,3 +844,20 @@ export function isInlineFunctionExpression(expression: FunctionExpression): expr
843844
(expression.flags & NodeFlags.Inline) !== 0
844845
);
845846
}
847+
848+
export type ParenthesizedExpression = Expression & {
849+
expression: Expression;
850+
};
851+
852+
export function isParenthesizedExpression(node: Node): node is ParenthesizedExpression {
853+
return node.kind === SyntaxKind.ParenthesizedExpression;
854+
}
855+
856+
export function createParenthesizedExpression(expression: Expression, tsOriginal?: ts.Node): ParenthesizedExpression {
857+
const parenthesizedExpression = createNode(
858+
SyntaxKind.ParenthesizedExpression,
859+
tsOriginal
860+
) as ParenthesizedExpression;
861+
parenthesizedExpression.expression = expression;
862+
return parenthesizedExpression;
863+
}

src/LuaLib.ts

Lines changed: 88 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as path from "path";
22
import { EmitHost } from "./transpilation";
33
import * as lua from "./LuaAST";
44
import { LuaTarget } from "./CompilerOptions";
5+
import { getOrUpdate } from "./utils";
56

67
export enum LuaLibFeature {
78
ArrayConcat = "ArrayConcat",
@@ -37,6 +38,7 @@ export enum LuaLibFeature {
3738
CloneDescriptor = "CloneDescriptor",
3839
CountVarargs = "CountVarargs",
3940
Decorate = "Decorate",
41+
DecorateLegacy = "DecorateLegacy",
4042
DecorateParam = "DecorateParam",
4143
Delete = "Delete",
4244
DelegatedYield = "DelegatedYield",
@@ -57,6 +59,7 @@ export enum LuaLibFeature {
5759
NumberIsFinite = "NumberIsFinite",
5860
NumberIsNaN = "NumberIsNaN",
5961
NumberToString = "NumberToString",
62+
NumberToFixed = "NumberToFixed",
6063
ObjectAssign = "ObjectAssign",
6164
ObjectDefineProperty = "ObjectDefineProperty",
6265
ObjectEntries = "ObjectEntries",
@@ -109,6 +112,7 @@ export interface LuaLibFeatureInfo {
109112
dependencies?: LuaLibFeature[];
110113
exports: string[];
111114
}
115+
112116
export type LuaLibModulesInfo = Record<LuaLibFeature, LuaLibFeatureInfo>;
113117

114118
export function resolveLuaLibDir(luaTarget: LuaTarget) {
@@ -117,27 +121,55 @@ export function resolveLuaLibDir(luaTarget: LuaTarget) {
117121
}
118122

119123
export const luaLibModulesInfoFileName = "lualib_module_info.json";
120-
const luaLibModulesInfo = new Map<string, LuaLibModulesInfo>();
124+
const luaLibModulesInfo = new Map<LuaTarget, LuaLibModulesInfo>();
125+
121126
export function getLuaLibModulesInfo(luaTarget: LuaTarget, emitHost: EmitHost): LuaLibModulesInfo {
122-
const lualibPath = path.join(resolveLuaLibDir(luaTarget), luaLibModulesInfoFileName);
123-
if (!luaLibModulesInfo.has(lualibPath)) {
127+
if (!luaLibModulesInfo.has(luaTarget)) {
128+
const lualibPath = path.join(resolveLuaLibDir(luaTarget), luaLibModulesInfoFileName);
124129
const result = emitHost.readFile(lualibPath);
125130
if (result !== undefined) {
126-
luaLibModulesInfo.set(lualibPath, JSON.parse(result) as LuaLibModulesInfo);
131+
luaLibModulesInfo.set(luaTarget, JSON.parse(result) as LuaLibModulesInfo);
127132
} else {
128133
throw new Error(`Could not load lualib dependencies from '${lualibPath}'`);
129134
}
130135
}
131-
return luaLibModulesInfo.get(lualibPath) as LuaLibModulesInfo;
136+
return luaLibModulesInfo.get(luaTarget)!;
137+
}
138+
139+
// This caches the names of lualib exports to their LuaLibFeature, avoiding a linear search for every lookup
140+
const lualibExportToFeature = new Map<LuaTarget, ReadonlyMap<string, LuaLibFeature>>();
141+
142+
export function getLuaLibExportToFeatureMap(
143+
luaTarget: LuaTarget,
144+
emitHost: EmitHost
145+
): ReadonlyMap<string, LuaLibFeature> {
146+
if (!lualibExportToFeature.has(luaTarget)) {
147+
const luaLibModulesInfo = getLuaLibModulesInfo(luaTarget, emitHost);
148+
const map = new Map<string, LuaLibFeature>();
149+
for (const [feature, info] of Object.entries(luaLibModulesInfo)) {
150+
for (const exportName of info.exports) {
151+
map.set(exportName, feature as LuaLibFeature);
152+
}
153+
}
154+
lualibExportToFeature.set(luaTarget, map);
155+
}
156+
157+
return lualibExportToFeature.get(luaTarget)!;
132158
}
133159

160+
const lualibFeatureCache = new Map<LuaTarget, Map<LuaLibFeature, string>>();
161+
134162
export function readLuaLibFeature(feature: LuaLibFeature, luaTarget: LuaTarget, emitHost: EmitHost): string {
135-
const featurePath = path.join(resolveLuaLibDir(luaTarget), `${feature}.lua`);
136-
const luaLibFeature = emitHost.readFile(featurePath);
137-
if (luaLibFeature === undefined) {
138-
throw new Error(`Could not load lualib feature from '${featurePath}'`);
163+
const featureMap = getOrUpdate(lualibFeatureCache, luaTarget, () => new Map());
164+
if (!featureMap.has(feature)) {
165+
const featurePath = path.join(resolveLuaLibDir(luaTarget), `${feature}.lua`);
166+
const luaLibFeature = emitHost.readFile(featurePath);
167+
if (luaLibFeature === undefined) {
168+
throw new Error(`Could not load lualib feature from '${featurePath}'`);
169+
}
170+
featureMap.set(feature, luaLibFeature);
139171
}
140-
return luaLibFeature;
172+
return featureMap.get(feature)!;
141173
}
142174

143175
export function resolveRecursiveLualibFeatures(
@@ -173,14 +205,9 @@ export function loadInlineLualibFeatures(
173205
luaTarget: LuaTarget,
174206
emitHost: EmitHost
175207
): string {
176-
let result = "";
177-
178-
for (const feature of resolveRecursiveLualibFeatures(features, luaTarget, emitHost)) {
179-
const luaLibFeature = readLuaLibFeature(feature, luaTarget, emitHost);
180-
result += luaLibFeature + "\n";
181-
}
182-
183-
return result;
208+
return resolveRecursiveLualibFeatures(features, luaTarget, emitHost)
209+
.map(feature => readLuaLibFeature(feature, luaTarget, emitHost))
210+
.join("\n");
184211
}
185212

186213
export function loadImportedLualibFeatures(
@@ -192,13 +219,13 @@ export function loadImportedLualibFeatures(
192219
const luaLibModuleInfo = getLuaLibModulesInfo(luaTarget, emitHost);
193220

194221
const imports = Array.from(features).flatMap(feature => luaLibModuleInfo[feature].exports);
222+
if (imports.length === 0) {
223+
return [];
224+
}
195225

196226
const requireCall = lua.createCallExpression(lua.createIdentifier("require"), [
197227
lua.createStringLiteral(luaLibName),
198228
]);
199-
if (imports.length === 0) {
200-
return [];
201-
}
202229

203230
const luaLibId = lua.createIdentifier("____lualib");
204231
const importStatement = lua.createVariableDeclarationStatement(luaLibId, requireCall);
@@ -216,6 +243,7 @@ export function loadImportedLualibFeatures(
216243
}
217244

218245
const luaLibBundleContent = new Map<string, string>();
246+
219247
export function getLuaLibBundle(luaTarget: LuaTarget, emitHost: EmitHost): string {
220248
const lualibPath = path.join(resolveLuaLibDir(luaTarget), "lualib_bundle.lua");
221249
if (!luaLibBundleContent.has(lualibPath)) {
@@ -229,3 +257,42 @@ export function getLuaLibBundle(luaTarget: LuaTarget, emitHost: EmitHost): strin
229257

230258
return luaLibBundleContent.get(lualibPath) as string;
231259
}
260+
261+
export function getLualibBundleReturn(exportedValues: string[]): string {
262+
return `\nreturn {\n${exportedValues.map(exportName => ` ${exportName} = ${exportName}`).join(",\n")}\n}\n`;
263+
}
264+
265+
export function buildMinimalLualibBundle(
266+
features: Iterable<LuaLibFeature>,
267+
luaTarget: LuaTarget,
268+
emitHost: EmitHost
269+
): string {
270+
const code = loadInlineLualibFeatures(features, luaTarget, emitHost);
271+
const moduleInfo = getLuaLibModulesInfo(luaTarget, emitHost);
272+
const exports = Array.from(features).flatMap(feature => moduleInfo[feature].exports);
273+
274+
return code + getLualibBundleReturn(exports);
275+
}
276+
277+
export function findUsedLualibFeatures(
278+
luaTarget: LuaTarget,
279+
emitHost: EmitHost,
280+
luaContents: string[]
281+
): Set<LuaLibFeature> {
282+
const features = new Set<LuaLibFeature>();
283+
const exportToFeatureMap = getLuaLibExportToFeatureMap(luaTarget, emitHost);
284+
285+
for (const lua of luaContents) {
286+
const regex = /^local (\w+) = ____lualib\.(\w+)$/gm;
287+
while (true) {
288+
const match = regex.exec(lua);
289+
if (!match) break;
290+
const [, localName, exportName] = match;
291+
if (localName !== exportName) continue;
292+
const feature = exportToFeatureMap.get(exportName);
293+
if (feature) features.add(feature);
294+
}
295+
}
296+
297+
return features;
298+
}

0 commit comments

Comments
 (0)