Skip to content

Commit 0fd7ae7

Browse files
committed
Add option to build with tsgo
tsgo will crash without microsoft/typescript-go#2264 Additional tweaks are required for typescript language changes. There are additional semantic errors (I assume bugs) that affect the tsgo CLI but not language server that also need further investigation.
1 parent ca0ec7e commit 0fd7ae7

File tree

8 files changed

+519
-576
lines changed

8 files changed

+519
-576
lines changed

package-lock.json

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

packages/tools/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,18 @@
6363
"commander": "^14.0.2",
6464
"esbuild": "^0.27.0",
6565
"minimatch": "^10.1.1",
66-
"typescript": "~5.9.3",
6766
"type-fest": "^5.2.0",
6867
"typedoc": "^0.28.15",
69-
"typedoc-github-theme": "^0.3.1"
68+
"typedoc-github-theme": "^0.3.1",
69+
"typescript": "~5.9.3"
7070
},
7171
"optionalDependencies": {
7272
"@esbuild/linux-x64": "^0.27.0"
7373
},
7474
"devDependencies": {
7575
"@types/madge": "^5.0.3",
7676
"@types/node": "^24.10.1",
77+
"@typescript/native-preview": "^7.0.0-dev.20251205.1",
7778
"detective-typescript": "^14.0.0"
7879
},
7980
"publishConfig": {

packages/tools/src/building/cli.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ export async function main(argv = process.argv) {
3737
const program = commander("matter-build", "Builds packages adhering to matter.js standards.")
3838
.option("-p, --prefix <path>", "specify build directory", ".")
3939
.option("-c, --clean", "clean before build", false)
40-
.option("-d, --dependencies", "build dependencies", false);
40+
.option("-d, --dependencies", "build dependencies", false)
41+
.option("--tsgo", "use experimental TS 7 compiler", false);
4142

4243
program
4344
.command("build")

packages/tools/src/building/error.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,16 @@ export class InternalBuildError extends Error {
1111
}
1212

1313
export class BuildError extends Error {
14-
constructor(readonly diagnostics: string) {
14+
constructor(readonly diagnostics?: string) {
1515
super();
1616
}
1717

1818
override get stack() {
19-
return this.diagnostics;
19+
return this.diagnostics ?? super.stack;
2020
}
2121

2222
override toString() {
23-
return this.diagnostics;
23+
return this.diagnostics ?? "Build error";
2424
}
2525

2626
inspect() {

packages/tools/src/building/project-builder.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { Progress } from "../util/progress.js";
99
import { BuildError } from "./error.js";
1010
import { Graph } from "./graph.js";
1111
import { BuildInformation, Project } from "./project.js";
12-
import { createTypescriptContext } from "./typescript.js";
12+
import { createTsgoContext, createTypescriptContext } from "./typescript.js";
1313
import { TypescriptContext } from "./typescript/context.js";
1414

1515
export enum Target {
@@ -23,6 +23,7 @@ export interface Options {
2323
targets?: Target[];
2424
clean?: boolean;
2525
graph?: Graph;
26+
tsgo?: boolean;
2627
}
2728

2829
/**
@@ -34,11 +35,13 @@ export class ProjectBuilder {
3435
unconditional: boolean;
3536
tsContext?: TypescriptContext;
3637
graph?: Graph;
38+
tsgo?: boolean;
3739

3840
constructor(private options: Options = {}) {
3941
this.graph = options.graph;
4042
this.unconditional =
4143
options.clean || (options.targets !== undefined && options.targets?.indexOf(Target.clean) !== -1);
44+
this.tsgo = options.tsgo;
4245
}
4346

4447
get hasClean() {
@@ -112,7 +115,12 @@ export class ProjectBuilder {
112115
// Obtain or initialize typescript solution builder
113116
let context = this.tsContext;
114117
if (context === undefined) {
115-
context = this.tsContext = await createTypescriptContext(project.pkg.workspace, graph);
118+
if (this.tsgo) {
119+
context = createTsgoContext(project.pkg.workspace);
120+
} else {
121+
context = await createTypescriptContext(project.pkg.workspace, graph);
122+
}
123+
this.tsContext = context;
116124
}
117125

118126
const refreshCallback = progress.refresh.bind(progress);
@@ -152,8 +160,10 @@ export class ProjectBuilder {
152160
}
153161
} catch (e) {
154162
if (e instanceof BuildError) {
163+
if (e.diagnostics) {
164+
process.stderr.write(`${e.diagnostics}\n`);
165+
}
155166
progress.failure("Terminating due to type errors");
156-
process.stderr.write(`${e.diagnostics}\n`);
157167
process.exit(1);
158168
}
159169
throw e;

packages/tools/src/building/typescript.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import { type TypescriptContext } from "./typescript/context.js";
88
import { createSolutionBuilderContext } from "./typescript/solution-builder.js";
9+
export { createTsgoContext } from "./typescript/tsgo.js";
910

1011
/**
1112
* Create a {@link TypescriptContext} for validating types and optionally emitting .d.ts file.

packages/tools/src/building/typescript/solution-builder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export async function createSolutionBuilderContext(
3939

4040
function currentContext() {
4141
if (context === undefined) {
42-
throw new Error("Build context accessed with no ");
42+
throw new Error("Build context accessed with no context");
4343
}
4444
return context;
4545
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/**
2+
* @license
3+
* Copyright 2022-2025 Matter.js Authors
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import { spawn } from "node:child_process";
8+
import { cp } from "node:fs/promises";
9+
import { join } from "path";
10+
import { isDirectory } from "../../util/file.js";
11+
import { Package } from "../../util/package.js";
12+
import { BuildError } from "../error.js";
13+
import { TypescriptContext } from "./context.js";
14+
15+
export function createTsgoContext(workspace: Package): TypescriptContext {
16+
const bin = join(workspace.resolve("node_modules", ".bin", "tsgo"));
17+
return {
18+
async build(pkg, path, _refreshCallback, emit) {
19+
const args = ["--project", path];
20+
21+
if (emit === false) {
22+
args.push("--noEmit");
23+
}
24+
25+
await new Promise<void>((resolve, reject) => {
26+
const tsgo = spawn(bin, args, { stdio: "inherit" });
27+
28+
tsgo.on("exit", (code, signal) => {
29+
switch (code) {
30+
case 0:
31+
resolve();
32+
break;
33+
34+
case 1: // Diagnostics present, outputs generated
35+
case 2: // Diagnostics present, outputs skipped
36+
case 3: // Project invalid
37+
case 4: // Reference cycle
38+
case 5: // Not implemented
39+
// TS will have printed an error already
40+
reject(new BuildError());
41+
break;
42+
43+
case null:
44+
reject(new BuildError(`tsgo exited with signal ${signal}`));
45+
break;
46+
47+
default:
48+
reject(new BuildError(`tsgo exited with code ${code}`));
49+
break;
50+
}
51+
});
52+
});
53+
54+
await cp(pkg.resolve("dist/esm"), pkg.resolve("dist/cjs"), {
55+
recursive: true,
56+
filter(src) {
57+
if (isDirectory(src)) {
58+
return true;
59+
}
60+
61+
return src.endsWith(".d.ts") || src.endsWith(".d.ts.map");
62+
},
63+
});
64+
},
65+
};
66+
}

0 commit comments

Comments
 (0)