From 6958a403cfea8e53b756c2ae77dc5d150f5d4f0b Mon Sep 17 00:00:00 2001 From: schickling-assistant <261620128+schickling-assistant@users.noreply.github.com> Date: Sun, 19 Jul 2026 10:21:02 +0200 Subject: [PATCH] fix(devenv): handle ts emit edge references --- CHANGELOG.md | 4 ++ .../tasks/shared/tests/ts-task-smoke.test.sh | 41 ++++++++++++++++++- nix/devenv-modules/tasks/shared/ts.nix | 24 ++++++++++- 3 files changed, 67 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87b1d460f1..08a2135f5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ All notable changes to this project will be documented in this file. ### Fixed +- **devenv / ts:emit**: resolve project references with filesystem directory + checks so dotted directory names map to `tsconfig.json`, and treat an + all-`noEmit` reference graph as successful no-work instead of invoking the + compiler with an empty build root. - **@overeng/utils**: restore the downstream `@effect/platform` patch projection in the generated package manifest after vendoring `effect-distributed-lock`. diff --git a/nix/devenv-modules/tasks/shared/tests/ts-task-smoke.test.sh b/nix/devenv-modules/tasks/shared/tests/ts-task-smoke.test.sh index 26d71bdbe2..302c0bf7cf 100644 --- a/nix/devenv-modules/tasks/shared/tests/ts-task-smoke.test.sh +++ b/nix/devenv-modules/tasks/shared/tests/ts-task-smoke.test.sh @@ -87,6 +87,7 @@ mkdir -p \ "$workspace/node_modules/typescript" \ "$workspace/packages/no-emit" \ "$workspace/packages/emit" \ + "$workspace/packages/dotted.name" \ "$tmpdir/bin" cat > "$workspace/tsconfig.all.json" <<'EOF' @@ -96,7 +97,8 @@ cat > "$workspace/tsconfig.all.json" <<'EOF' "references": [ { "path": "packages/no-emit/tsconfig.json" }, // explicit file path // This mid-file comment used to break the old JSON.parse path. - { "path": "packages/emit" } + { "path": "packages/emit" }, + { "path": "packages/dotted.name" } ] } EOF @@ -121,6 +123,15 @@ cat > "$workspace/packages/emit/tsconfig.json" <<'EOF' } EOF +cat > "$workspace/packages/dotted.name/tsconfig.json" <<'EOF' +{ + "compilerOptions": { + "composite": true, + "declaration": true + } +} +EOF + cat > "$workspace/node_modules/typescript/package.json" <<'EOF' {"name":"typescript","main":"./index.js"} EOF @@ -223,6 +234,9 @@ if (paths.includes('packages/no-emit/tsconfig.json')) { if (!paths.includes('packages/emit')) { throw new Error('emit project should remain in generated emit tsconfig') } +if (!paths.includes('packages/dotted.name')) { + throw new Error('dotted directory reference should remain in generated emit tsconfig') +} fs.copyFileSync(configPath, process.env.TEST_CAPTURED_TSCONFIG) NODE @@ -260,5 +274,30 @@ echo "Test 2: ts:emit status uses the same filtered graph" test -f "$TEST_CAPTURED_TSCONFIG" grep -q -- '--dry --noCheck --verbose --pretty false' "$TEST_TSC_LOG" +echo "Test 3: ts:emit succeeds without invoking tsc when every referenced project is noEmit" +cat > "$workspace/tsconfig.all.json" <<'EOF' +{ + "files": [], + "references": [ + { "path": "packages/no-emit" } + ] +} +EOF +( + cd "$workspace" + : > "$TEST_TSC_LOG" + rm -f "$TEST_CAPTURED_TSCONFIG" + bash "$tmpdir/ts-emit.exec.sh" > "$tmpdir/no-work.out" + grep -qF "ts:emit: no emit-capable referenced projects" "$tmpdir/no-work.out" + assert_eq "" "$(cat "$TEST_TSC_LOG")" "ts:emit exec should not invoke tsc for all-noEmit graph" + + set +e + bash "$tmpdir/ts-emit.status.sh" + exit_code=$? + set -e + assert_exit_code 0 "$exit_code" "ts:emit status should succeed for all-noEmit graph" + assert_eq "" "$(cat "$TEST_TSC_LOG")" "ts:emit status should not invoke tsc for all-noEmit graph" +) + echo "" echo "ts task smoke test passed" diff --git a/nix/devenv-modules/tasks/shared/ts.nix b/nix/devenv-modules/tasks/shared/ts.nix index a85b7af2a2..73d3e66f6f 100644 --- a/nix/devenv-modules/tasks/shared/ts.nix +++ b/nix/devenv-modules/tasks/shared/ts.nix @@ -118,7 +118,15 @@ let const resolveReferenceTsconfig = (referencePath) => { const resolvedPath = path.resolve(baseDir, referencePath) - return path.extname(resolvedPath) ? resolvedPath : path.join(resolvedPath, 'tsconfig.json') + try { + return fs.statSync(resolvedPath).isDirectory() + ? path.join(resolvedPath, 'tsconfig.json') + : resolvedPath + } catch { + return path.basename(resolvedPath).endsWith('.json') + ? resolvedPath + : path.join(resolvedPath, 'tsconfig.json') + } } const rootConfig = readTsconfig(sourceTsconfig) @@ -134,6 +142,13 @@ let return refConfig.compilerOptions?.noEmit !== true }) + if ( + rootConfig.references.length === 0 && + (!Array.isArray(rootConfig.files) || rootConfig.files.length === 0) + ) { + rootConfig.__effectUtilsTsEmitNoWork = true + } + fs.writeFileSync(targetTsconfig, JSON.stringify(rootConfig)) NODE } @@ -407,6 +422,10 @@ let _emit_tsconfig="$(mktemp "$_emit_tmpdir/.ts-emit-XXXXXX.json")" trap 'rm -f "$_emit_tsconfig"' EXIT generate_emit_tsconfig "${tsconfigFile}" "$_emit_tsconfig" + if ${pkgs.nodejs}/bin/node -e 'const fs = require("node:fs"); process.exit(JSON.parse(fs.readFileSync(process.argv[1], "utf8")).__effectUtilsTsEmitNoWork === true ? 0 : 1)' "$_emit_tsconfig"; then + echo "ts:emit: no emit-capable referenced projects" + exit 0 + fi ${tscWithDiagnostics "ts:emit" tscBin "--build \"$_emit_tsconfig\"" "--noCheck"} ''; # trace-audit-allow: raw status - argument to trace.withStatus "ts:emit" above. @@ -420,6 +439,9 @@ let _emit_tsconfig="$(mktemp "$_emit_tmpdir/.ts-emit-XXXXXX.json")" trap 'rm -f "$_emit_tsconfig"' EXIT generate_emit_tsconfig "${tsconfigFile}" "$_emit_tsconfig" + if ${pkgs.nodejs}/bin/node -e 'const fs = require("node:fs"); process.exit(JSON.parse(fs.readFileSync(process.argv[1], "utf8")).__effectUtilsTsEmitNoWork === true ? 0 : 1)' "$_emit_tsconfig"; then + exit 0 + fi _out="$(${tscBin} --build "$_emit_tsconfig" --dry --noCheck --verbose --pretty false 2>&1)" || exit 1 # tsc --build --dry reports pending work as: # - "A non-dry build would build project ..."