From dca7135ce54779e0a7e752b2259d5b0928178aae Mon Sep 17 00:00:00 2001 From: Gordon Farquharson Date: Tue, 15 Sep 2026 17:20:10 +0100 Subject: [PATCH 1/3] fix(build): add --ignoreConfig for TypeScript 7 to prevent TS5112 TS 7 (Go rewrite) errors with TS5112 when a file is passed on the command line and a tsconfig.json is present in the CWD. jco@1.34.0 (bundled in 2.5.2-beta.1) brings TypeScript 7 in as a transitive dependency, so any consumer project with a tsconfig.json will hit this error without the flag. Our defaultTscBuildFlags already controls all compiler options explicitly, so ignoring any ambient tsconfig is correct behaviour. --- src/utils/syntax-checker.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/utils/syntax-checker.ts b/src/utils/syntax-checker.ts index 3122968..a9174b3 100644 --- a/src/utils/syntax-checker.ts +++ b/src/utils/syntax-checker.ts @@ -127,6 +127,12 @@ function containsTypeScriptSyntaxErrors(tsInput: string, tsConfigPath?: string): // TS infers from `--target esnext` is es2015, which rejects `import.meta` // with TS1343 even though it is valid ESM that the runtime supports. // `esnext` matches how input is actually consumed: esbuild bundles ESM. + + // TS 7 (Go rewrite) errors (TS5112) when a file is passed on the command + // line and a tsconfig.json exists in the CWD. We control all flags + // explicitly here, so ignoring any ambient tsconfig is correct. + const ignoreConfig = typescript.major >= 7 ? ['--ignoreConfig'] : []; + const defaultTscBuildFlags = [ '--noEmit', '--skipLibCheck', @@ -138,6 +144,7 @@ function containsTypeScriptSyntaxErrors(tsInput: string, tsConfigPath?: string): 'esnext', '--moduleResolution', moduleResolution, + ...ignoreConfig, ...includeFastEdgeTypes, tsInput, ]; From 599c62eb38611c3ffa06e1a3239bdd5302015a2c Mon Sep 17 00:00:00 2001 From: Gordon Farquharson Date: Tue, 15 Sep 2026 17:27:11 +0100 Subject: [PATCH 2/3] test(build): add TS5112 regression case for ambient tsconfig.json + direct file arg Covers the exact trigger the --ignoreConfig fix guards: a .ts input passed directly on the CLI with a tsconfig.json present in the CWD. The prior matrix omitted this combination, so the fix could have been silently dropped without a test failure. --- integration-tests/fastedge-build.test.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/integration-tests/fastedge-build.test.js b/integration-tests/fastedge-build.test.js index d1bc8b4..e62adb3 100644 --- a/integration-tests/fastedge-build.test.js +++ b/integration-tests/fastedge-build.test.js @@ -149,6 +149,29 @@ describe('fastedge-build', () => { await cleanup(); }); + it('should build a .ts file even when tsconfig.json is present in the CWD (TS5112 regression)', async () => { + // TS 7 errors with TS5112 when a file is passed on the CLI and an ambient + // tsconfig.json exists in the CWD. This test locks that path so the flag + // cannot be silently dropped. + expect.assertions(3); + const { execute, cleanup, writeFile, path } = await prepareEnvironment(); + spawnSync('npm', ['install', 'typescript'], { + stdio: 'inherit', + cwd: path, + }); + await writeFile('input.ts', 'function hello() { console.log("Hello World"); }'); + await writeFile('./lib/fastedge-runtime.wasm', 'Some binary data'); + await writeFile('tsconfig.json', JSON.stringify({ compilerOptions: { strict: true } })); + const { code, stdout, stderr } = await execute( + 'node', + './bin/fastedge-build.js input.ts dist/output.wasm', + ); + expect(code).toBe(0); + expect(stderr).toHaveLength(0); + expect(stdout[0]).toContain('Build success!!'); + await cleanup(); + }, 30_000); + it('should exit with an error if the TypeScript is not valid', async () => { expect.assertions(4); const { execute, cleanup, writeFile, path } = await prepareEnvironment(); From fa4309cf492a22017787bcdb2b7ea3ea45ff015b Mon Sep 17 00:00:00 2001 From: Gordon Farquharson Date: Tue, 15 Sep 2026 18:04:06 +0100 Subject: [PATCH 3/3] fix(build): broaden --ignoreConfig guard to TS 6+ and move regression test to version matrix TS5112 (ambient tsconfig.json + CLI file arg) is triggered by TS 6.0.0-beta as well as TS 7; the previous major >= 7 condition was too narrow. The regression case is moved from fastedge-build.test.js (unpinned npm install) into typescript-versions.test.js so it runs against every pinned major in the matrix. The unpinned variant is removed. --- integration-tests/fastedge-build.test.js | 23 ------------------- integration-tests/typescript-versions.test.js | 23 +++++++++++++++++++ src/utils/syntax-checker.ts | 8 +++---- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/integration-tests/fastedge-build.test.js b/integration-tests/fastedge-build.test.js index e62adb3..d1bc8b4 100644 --- a/integration-tests/fastedge-build.test.js +++ b/integration-tests/fastedge-build.test.js @@ -149,29 +149,6 @@ describe('fastedge-build', () => { await cleanup(); }); - it('should build a .ts file even when tsconfig.json is present in the CWD (TS5112 regression)', async () => { - // TS 7 errors with TS5112 when a file is passed on the CLI and an ambient - // tsconfig.json exists in the CWD. This test locks that path so the flag - // cannot be silently dropped. - expect.assertions(3); - const { execute, cleanup, writeFile, path } = await prepareEnvironment(); - spawnSync('npm', ['install', 'typescript'], { - stdio: 'inherit', - cwd: path, - }); - await writeFile('input.ts', 'function hello() { console.log("Hello World"); }'); - await writeFile('./lib/fastedge-runtime.wasm', 'Some binary data'); - await writeFile('tsconfig.json', JSON.stringify({ compilerOptions: { strict: true } })); - const { code, stdout, stderr } = await execute( - 'node', - './bin/fastedge-build.js input.ts dist/output.wasm', - ); - expect(code).toBe(0); - expect(stderr).toHaveLength(0); - expect(stdout[0]).toContain('Build success!!'); - await cleanup(); - }, 30_000); - it('should exit with an error if the TypeScript is not valid', async () => { expect.assertions(4); const { execute, cleanup, writeFile, path } = await prepareEnvironment(); diff --git a/integration-tests/typescript-versions.test.js b/integration-tests/typescript-versions.test.js index 885e9f7..059016e 100644 --- a/integration-tests/typescript-versions.test.js +++ b/integration-tests/typescript-versions.test.js @@ -115,6 +115,29 @@ describe.each(TS_VERSIONS)('with typescript@%s', (version) => { INSTALL_AND_BUILD_TIMEOUT, ); + it( + 'should build a .ts file when tsconfig.json is present in the CWD but no --tsconfig flag is passed (TS5112 regression)', + async () => { + // TS 7 errors with TS5112 when a file is passed on the CLI and an ambient + // tsconfig.json exists in the CWD. --ignoreConfig suppresses it; this test + // locks the exact invocation so the flag cannot be silently dropped. + expect.assertions(2); + const { execute, cleanup, writeFile } = await setup(); + await writeFile('input.ts', VALID_TS); + await writeFile('tsconfig.json', JSON.stringify({ compilerOptions: { strict: true } })); + + const { code, stdout } = await execute( + 'node', + './bin/fastedge-build.js input.ts dist/output.wasm', + ); + + expect(code).toBe(0); + expect(stdout[0]).toContain('Build success!!'); + await cleanup(); + }, + INSTALL_AND_BUILD_TIMEOUT, + ); + it( 'should reject a TypeScript entrypoint with a type error', async () => { diff --git a/src/utils/syntax-checker.ts b/src/utils/syntax-checker.ts index a9174b3..5c3a5d9 100644 --- a/src/utils/syntax-checker.ts +++ b/src/utils/syntax-checker.ts @@ -128,10 +128,10 @@ function containsTypeScriptSyntaxErrors(tsInput: string, tsConfigPath?: string): // with TS1343 even though it is valid ESM that the runtime supports. // `esnext` matches how input is actually consumed: esbuild bundles ESM. - // TS 7 (Go rewrite) errors (TS5112) when a file is passed on the command - // line and a tsconfig.json exists in the CWD. We control all flags - // explicitly here, so ignoring any ambient tsconfig is correct. - const ignoreConfig = typescript.major >= 7 ? ['--ignoreConfig'] : []; + // TS 6+ errors with TS5112 when a file is passed on the command line and a + // tsconfig.json exists in the CWD. We control all flags explicitly here, so + // ignoring any ambient tsconfig is correct. + const ignoreConfig = typescript.major >= 6 ? ['--ignoreConfig'] : []; const defaultTscBuildFlags = [ '--noEmit',