Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion benchmark/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"lib": ["esnext"],
// Dev types are JIT
"types": ["lua-types/jit", "@typescript-to-lua/language-extensions"],
"moduleResolution": "node",
"moduleResolution": "bundler",
"outDir": "dist",
"rootDir": "src",
"strict": true,
Expand Down
1 change: 1 addition & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ export default tseslint.config(
{
ignores: [
".github/scripts/create_benchmark_check.js",
"coverage/",
"dist/",
"eslint.config.mjs",
"jest.config.js",
Expand Down
753 changes: 492 additions & 261 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 6 additions & 3 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 @@ -69,7 +69,10 @@
"prettier": "^2.8.8",
"ts-jest": "^29.2.5",
"ts-node": "^10.9.2",
"typescript": "5.9.3",
"typescript": "6.0.2",
"typescript-eslint": "^8.46.3"
},
"overrides": {
"typescript": "$typescript"
}
}
4 changes: 1 addition & 3 deletions src/CompilerOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,7 @@ export function validateOptions(options: CompilerOptions): ts.Diagnostic[] {
diagnostics.push(diagnosticFactories.unsupportedJsxEmit());
}

if (options.paths && !options.baseUrl) {
diagnostics.push(diagnosticFactories.pathsWithoutBaseUrl());
}
// paths without baseUrl is now supported (TS 6.0+ resolves paths relative to tsconfig location)

return diagnostics;
}
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: string[];

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(pattern => picomatch.isMatch(required.requirePath, pattern))) {
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",
]
`;
21 changes: 17 additions & 4 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 @@ -471,6 +470,7 @@ describe("module resolution should not try to resolve modules in noResolvePaths"
export function foo(): void;
}`
)
.addExtraFile("preload.d.ts", `declare module "preload" {}`)
.setOptions({ noResolvePaths: ["ignore*"] })
.expectToHaveNoDiagnostics()
.expectToEqual({ result: "foo" });
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,8 +707,21 @@ test("supports complicated paths configuration", () => {
.expectToEqual({ foo: 314, bar: 271 });
});

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

test("supports paths configuration without baseUrl", () => {
const baseProjectPath = path.resolve(__dirname, "module-resolution", "paths-no-baseurl");
const projectPath = path.join(baseProjectPath, "myprogram");
const projectTsConfig = path.join(projectPath, "tsconfig.json");
const mainFile = path.join(projectPath, "main.ts");

// Bundle to have all files required to execute and check result
util.testProject(projectTsConfig)
.setMainFileName(mainFile)
.setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile })
.expectToEqual({ foo: 314, bar: 271 });
});

test("module resolution using plugin", () => {
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
@@ -0,0 +1 @@
export const bar = 271;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const foo = 314;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { foo } from "myOtherPackage";
import { bar } from "myOtherPackage/bar";

export { foo, bar };
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"compilerOptions": {
"rootDir": "..",
"outDir": "dist",
"paths": {
"myOtherPackage": ["../mypackage"],
"myOtherPackage/*": ["../mypackage/*"]
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"compilerOptions": {
"baseUrl": ".",
"ignoreDeprecations": "6.0",
"rootDir": "..",
"outDir": "dist",
"paths": {
"myOtherPackage": ["../mypackage"],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"strict": true,
"moduleResolution": "Node",
"moduleResolution": "Bundler",
"target": "esnext",
"lib": ["esnext"],
"types": [],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"strict": true,
"moduleResolution": "Node",
"moduleResolution": "Bundler",
"target": "esnext",
"lib": ["esnext"],
"types": [],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
{
"compilerOptions": {
"strict": true,
"moduleResolution": "Node",
"moduleResolution": "Bundler",
"noUnusedLocals": true,
"noUnusedParameters": true,
"target": "esnext",
"lib": ["esnext"],
"types": [],
"rootDir": "."
"rootDir": ".",
"noUncheckedSideEffectImports": false
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"strict": true,
"moduleResolution": "Node",
"moduleResolution": "Bundler",
"target": "esnext",
"lib": ["esnext"],
"types": [],
Expand Down
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
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;

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;

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