Skip to content
Merged
3 changes: 2 additions & 1 deletion benchmark/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"lib": ["esnext"],
// Dev types are JIT
"types": ["lua-types/jit", "@typescript-to-lua/language-extensions"],
"moduleResolution": "node",
"module": "nodenext",
"moduleResolution": "nodenext",
"outDir": "dist",
"rootDir": "src",
"strict": true,
Expand Down
729 changes: 333 additions & 396 deletions package-lock.json

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"lint:prettier": "prettier --check . || (echo 'Run `npm run fix:prettier` to fix it.' && exit 1)",
"lint:eslint": "eslint .",
"fix:prettier": "prettier --write .",
"check:language-extensions": "tsc --strict language-extensions/index.d.ts",
"check:language-extensions": "tsc --strict --ignoreConfig language-extensions/index.d.ts",
"preversion": "npm run build && npm test",
"postversion": "git push && git push --tags"
},
Expand All @@ -42,7 +42,7 @@
"node": ">=16.10.0"
},
"peerDependencies": {
"typescript": "5.9.3"
"typescript": "6.0.2"
},
"dependencies": {
"@typescript-to-lua/language-extensions": "1.19.0",
Expand All @@ -58,18 +58,18 @@
"@types/node": "^22.10.0",
"@types/picomatch": "^2.3.0",
"@types/resolve": "1.14.0",
"eslint": "^9.22.0",
"eslint-plugin-jest": "^28.8.3",
"eslint": "^9.39.4",
"eslint-plugin-jest": "^28.14.0",
"fs-extra": "^8.1.0",
"javascript-stringify": "^2.0.1",
"jest": "^29.5.0",
"jest": "^29.7.0",
"jest-circus": "^29.7.0",
"lua-types": "^2.14.1",
"lua-wasm-bindings": "^0.5.3",
"prettier": "^2.8.8",
"ts-jest": "^29.2.5",
"ts-jest": "^29.4.9",
"ts-node": "^10.9.2",
"typescript": "5.9.3",
"typescript-eslint": "^8.46.3"
"typescript": "6.0.2",
"typescript-eslint": "^8.58.0"
}
}
4 changes: 0 additions & 4 deletions src/CompilerOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,5 @@ export function validateOptions(options: CompilerOptions): ts.Diagnostic[] {
diagnostics.push(diagnosticFactories.unsupportedJsxEmit());
}

if (options.paths && !options.baseUrl) {
diagnostics.push(diagnosticFactories.pathsWithoutBaseUrl());
}

return diagnostics;
}
4 changes: 1 addition & 3 deletions src/transformation/utils/function-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,7 @@ function computeDeclarationContextType(context: TransformationContext, signature
const thisParameter = getExplicitThisParameter(signatureDeclaration);
if (thisParameter) {
// Explicit 'this'
return thisParameter.type && thisParameter.type.kind === ts.SyntaxKind.VoidKeyword
? ContextType.Void
: ContextType.NonVoid;
return thisParameter.type?.kind === ts.SyntaxKind.VoidKeyword ? ContextType.Void : ContextType.NonVoid;
}

// noSelf declaration on function signature
Expand Down
2 changes: 1 addition & 1 deletion src/transformation/visitors/variable-declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function transformBindingPattern(
}

// Build the path to the table
const tableExpression = propertyAccessStack.reduce<lua.Expression>(
const tableExpression = propertyAccessStack.reduce(
(path, property) => lua.createTableIndexExpression(path, transformPropertyName(context, property)),
table
);
Expand Down
4 changes: 0 additions & 4 deletions src/transpilation/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,6 @@ export const cannotBundleLibrary = createDiagnosticFactory(

export const unsupportedJsxEmit = createDiagnosticFactory(() => 'JSX is only supported with "react" jsx option.');

export const pathsWithoutBaseUrl = createDiagnosticFactory(
() => "When configuring 'paths' in tsconfig.json, the option 'baseUrl' must also be provided."
);

export const emitPathCollision = createDiagnosticFactory(
(outputPath: string, file1: string, file2: string) =>
`Output path '${outputPath}' is used by both '${file1}' and '${file2}'. ` +
Expand Down
24 changes: 12 additions & 12 deletions src/transpilation/resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ interface ResolutionResult {
}

class ResolutionContext {
private noResolvePaths: picomatch.Matcher[];
private noResolvePaths: picomatch.Glob[];

public diagnostics: ts.Diagnostic[] = [];
public resolvedFiles = new Map<string, ProcessedFile>();
Expand All @@ -38,9 +38,7 @@ class ResolutionContext {
private readonly emitHost: EmitHost,
private readonly plugins: Plugin[]
) {
const unique = [...new Set(options.noResolvePaths)];
const matchers = unique.map(x => picomatch(x));
this.noResolvePaths = matchers;
this.noResolvePaths = [...new Set(options.noResolvePaths)];
}

public addAndResolveDependencies(file: ProcessedFile): void {
Expand Down Expand Up @@ -73,7 +71,7 @@ class ResolutionContext {
return;
}

if (this.noResolvePaths.find(isMatch => isMatch(required.requirePath))) {
if (this.noResolvePaths.find(glob => picomatch.isMatch(required.requirePath, glob))) {
if (this.options.tstlVerbose) {
console.log(
`Skipping module resolution of ${required.requirePath} as it is in the tsconfig noResolvePaths.`
Expand Down Expand Up @@ -215,14 +213,16 @@ class ResolutionContext {
const fileFromPath = this.getFileFromPath(resolvedPath);
if (fileFromPath) return fileFromPath;

if (this.options.paths && this.options.baseUrl) {
if (this.options.paths) {
// If no file found yet and paths are present, try to find project file via paths mappings
const fileFromPaths = this.tryGetModuleNameFromPaths(
dependencyPath,
this.options.paths,
this.options.baseUrl
);
if (fileFromPaths) return fileFromPaths;
// When baseUrl is not set, resolve paths relative to the tsconfig directory (TS 6.0+ behavior)
const pathsBase =
this.options.baseUrl ??
(this.options.configFilePath ? path.dirname(this.options.configFilePath) : undefined);
if (pathsBase) {
const fileFromPaths = this.tryGetModuleNameFromPaths(dependencyPath, this.options.paths, pathsBase);
if (fileFromPaths) return fileFromPaths;
}
}

// Not a TS file in our project sources, use resolver to check if we can find dependency
Expand Down
2 changes: 1 addition & 1 deletion src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export function cast<TOriginal, TCast extends TOriginal>(
}

export function assert(value: any, message?: string | Error): asserts value {
nativeAssert(value, message);
nativeAssert.ok(value, message);
}

export function assertNever(_value: never): never {
Expand Down
2 changes: 1 addition & 1 deletion test/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ declare global {

expect.extend({
toHaveDiagnostics(diagnostics: ts.Diagnostic[], expected?: number[]): jest.CustomMatcherResult {
assert(Array.isArray(diagnostics));
assert.ok(Array.isArray(diagnostics));
// @ts-ignore
const matcherHint = this.utils.matcherHint("toHaveDiagnostics", undefined, "", this);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ exports[`supports complicated paths configuration 1`] = `

exports[`supports paths configuration 1`] = `
[
"/paths-simple/myprogram/dist/main.lua",
"/paths-simple/myprogram/dist/mypackage/bar.lua",
"/paths-simple/myprogram/dist/mypackage/index.lua",
"/paths-simple/myprogram/dist/myprogram/main.lua",
]
`;
8 changes: 2 additions & 6 deletions test/transpile/module-resolution.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import * as util from "../util";
import * as ts from "typescript";
import { BuildMode } from "../../src";
import { normalizeSlashes } from "../../src/utils";
import { pathsWithoutBaseUrl } from "../../src/transpilation/diagnostics";

describe("basic module resolution", () => {
const projectPath = path.resolve(__dirname, "module-resolution", "project-with-node-modules");
Expand Down Expand Up @@ -457,6 +456,7 @@ describe("module resolution should not try to resolve modules in noResolvePaths"

export const result = b.foo();
`
.addExtraFile("preload.d.ts", `declare module "preload" {}`)
.addExtraFile("preload.lua", 'package.preload["ignoreme"] = function() return nil end')
.addExtraFile(
"actualfile.ts",
Expand Down Expand Up @@ -598,7 +598,7 @@ test("module resolution uses baseURL to resolve imported files", () => {
return { baz = function() return "baz" end }
`
)
.setOptions({ baseUrl: "./myproject/mydeps" })
.setOptions({ baseUrl: "./myproject/mydeps", ignoreDeprecations: "6.0" })
.expectToEqual({
fooResult: "foo",
barResult: "bar",
Expand Down Expand Up @@ -707,10 +707,6 @@ test("supports complicated paths configuration", () => {
.expectToEqual({ foo: 314, bar: 271 });
});

test("paths without baseUrl is error", () => {
util.testFunction``.setOptions({ paths: {} }).expectToHaveDiagnostics([pathsWithoutBaseUrl.code]);
});

test("module resolution using plugin", () => {
const baseProjectPath = path.resolve(__dirname, "module-resolution", "project-with-module-resolution-plugin");
const projectTsConfig = path.join(baseProjectPath, "tsconfig.json");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"compilerOptions": {
"rootDir": ".",
"baseUrl": ".",
"ignoreDeprecations": "6.0",
"paths": {
"mypackage": ["packages/mypackage/src/index.ts"],
"mypackage/*": ["packages/mypackage/src/*"]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"compilerOptions": {
"baseUrl": ".",
"rootDir": "..",
"outDir": "dist",
"paths": {
"myOtherPackage": ["../mypackage"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"target": "esnext",
"lib": ["esnext"],
"types": [],
"rootDir": "."
"rootDir": ".",
"noUncheckedSideEffectImports": false
}
}
2 changes: 1 addition & 1 deletion test/transpile/transformers/fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const compilerOptions =
(options: tstl.CompilerOptions): ts.TransformerFactory<ts.SourceFile> =>
context =>
file => {
assert(options.plugins?.length === 1);
assert.ok(options.plugins?.length === 1);
return visitAndReplace(context, file, node => {
if (!ts.isReturnStatement(node)) return;
return ts.factory.updateReturnStatement(node, ts.factory.createTrue());
Expand Down
8 changes: 2 additions & 6 deletions test/unit/__snapshots__/identifiers.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,14 @@ exports[`ambient identifier must be a valid lua identifier ("enum $$ {}"): code

exports[`ambient identifier must be a valid lua identifier ("enum $$ {}"): diagnostics 1`] = `"main.ts(3,9): error TSTL: Invalid ambient identifier name '$$$'. Ambient identifiers must be valid lua identifiers."`;

exports[`ambient identifier must be a valid lua identifier ("function $$();"): code 1`] = `"local ____ = _____24_24_24"`;
exports[`ambient identifier must be a valid lua identifier ("function $$(): void;"): code 1`] = `"local ____ = _____24_24_24"`;

exports[`ambient identifier must be a valid lua identifier ("function $$();"): diagnostics 1`] = `"main.ts(3,9): error TSTL: Invalid ambient identifier name '$$$'. Ambient identifiers must be valid lua identifiers."`;
exports[`ambient identifier must be a valid lua identifier ("function $$(): void;"): diagnostics 1`] = `"main.ts(3,9): error TSTL: Invalid ambient identifier name '$$$'. Ambient identifiers must be valid lua identifiers."`;

exports[`ambient identifier must be a valid lua identifier ("let $$: any;"): code 1`] = `"local ____ = _____24_24_24"`;

exports[`ambient identifier must be a valid lua identifier ("let $$: any;"): diagnostics 1`] = `"main.ts(3,9): error TSTL: Invalid ambient identifier name '$$$'. Ambient identifiers must be valid lua identifiers."`;

exports[`ambient identifier must be a valid lua identifier ("module $$ { export const bar: any; }"): code 1`] = `"local ____ = _____24_24_24"`;

exports[`ambient identifier must be a valid lua identifier ("module $$ { export const bar: any; }"): diagnostics 1`] = `"main.ts(3,9): error TSTL: Invalid ambient identifier name '$$$'. Ambient identifiers must be valid lua identifiers."`;

exports[`ambient identifier must be a valid lua identifier ("namespace $$ { export const bar: any; }"): code 1`] = `"local ____ = _____24_24_24"`;

exports[`ambient identifier must be a valid lua identifier ("namespace $$ { export const bar: any; }"): diagnostics 1`] = `"main.ts(3,9): error TSTL: Invalid ambient identifier name '$$$'. Ambient identifiers must be valid lua identifiers."`;
Expand Down
2 changes: 1 addition & 1 deletion test/unit/__snapshots__/loops.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
exports[`forin[Array]: code 1`] = `
"local ____exports = {}
function ____exports.__main(self)
local array = {}
local array = {1, 2, 3}
for key in pairs(array) do
end
end
Expand Down
4 changes: 2 additions & 2 deletions test/unit/annotations/customConstructor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ test("CustomCreate", () => {
const tsHeader = `
/** @customConstructor Point2DCreate */
class Point2D {
public x: number;
public y: number;
public x!: number;
public y!: number;
constructor(x: number, y: number) {
// No values assigned
}
Expand Down
6 changes: 3 additions & 3 deletions test/unit/assignments.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ test.each([

test("local variable declaration referencing self indirectly", () => {
util.testFunction`
let cb: () => void;
let cb: () => void = () => { throw "Expecting this to be overwritten"; };

function foo(newCb: () => void) {
cb = newCb;
Expand All @@ -360,7 +360,7 @@ test("local variable declaration referencing self indirectly", () => {

test("local multiple variable declaration referencing self indirectly", () => {
util.testFunction`
let cb: () => void;
let cb: () => void = () => { throw "Expecting this to be overwritten"; };

function foo(newCb: () => void) {
cb = newCb;
Expand Down Expand Up @@ -395,7 +395,7 @@ describe.each(["x &&= y", "x ||= y"])("boolean compound assignment (%p)", assign

test.each([undefined, 3])("nullish coalescing compound assignment", initialValue => {
util.testFunction`
let x: number = ${util.formatCode(initialValue)};
let x: number | undefined = ${util.formatCode(initialValue)};
x ??= 5;
return x;
`.expectToMatchJsResult();
Expand Down
Loading
Loading