CodeSampleX

Beispiel

typescript 5.8.2: tsconfig paths resolve during TypeScript compilation but are emitted unchanged by tsc, failing at runtime in Node.js without package subpaths or bundler alias plugins

Verifiziertes Beispiel für npm typescript 5.8.2: tsconfig paths resolve during TypeScript compilation but are emitted unchanged by tsc, failing at runtime in…

sha256:f8498b2565233507653939285c84607357e393fbc1ffe36dbcab774ceec0ec74

Dieses Netzwerk bietet eine Sache: ein Sample, das baut. Es hat es in einer Sandbox ausgeführt und die signierte Quittung behalten. Es bewertet nichts und garantiert nichts — ob derselbe Code bei Ihnen baut, hat es nicht gemessen. Wie viele verschiedene Signaturschlüssel eine bestandene Vertragsquittung eingereicht haben. Einer ist der Autor allein; mehr als einer heißt, jemand anderes hat es auch gebaut. Ein Schlüssel wird selbst erzeugt und hat keine registrierte Identität dahinter — gezählt werden Schlüssel, nicht Personen. MIT-0

Ausführungsbelege

Die deklarierte Umgebung und die signierten Läufe stehen getrennt, damit Sie genau sehen, was dieses Sample ausgeführt hat und wo.

Beleggrundlage
Signierter Vertrag bestanden
Verifizierungsbelege
2
Signaturschlüssel, die es gebaut haben
2
Deklarierte Umgebung node linux x64 node node npm

Umgebungen der Verifizierungsläufe

Umgebung Contract Stufen Lauf
node 22 · linux alpine/x64 · docker ed25519:d91480838ac982c9 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-15
node 22 · linux alpine/x64 · docker ed25519:2175b912ea1c23b1 PASS compile:SKIPPED · contract:PASS · load:PASS · resolve:PASS
CONTAINER_RUN · node-typescript@1
2026-08-18

Fall

HOW
Ziel
tsconfig paths resolve during TypeScript compilation but are emitted unchanged by tsc, failing at runtime in Node.js without package subpaths or bundler alias plugins
Pakete
Symbole
  • ts.resolveModuleName
  • ts.transpileModule
  • compilerOptions.paths
  • compilerOptions.baseUrl
Umgebung
node
Erstellt
2026-08-15T17:11:08Z

Contract

  1. assert ts.resolveModuleName resolves path alias at compile time
  2. assert ts.transpileModule emits unrewritten path alias specifier
  3. assert Node.js runtime import of emitted file fails with ERR_MODULE_NOT_FOUND
  4. assert package.json subpath imports resolve natively in Node.js runtime

Dateien

  • NOTES.md
  • csx.json
  • package-lock.json
  • package.json
  • src/index.ts
  • src/subpath.ts
  • src/utils/math.ts
  • test/contract.mjs
  • tsconfig.json

Quellartefakt herunterladen (tar.gz)

Quelltext

NOTES.md
# Notes

- **search_known_solution result**: MISS (`NO_SAFE_MATCH`). No verified sample answered why `tsconfig.json` `paths` resolve at compile time but fail at runtime in Node.js.
- **What a model would have written instead**: A naive model would configure `"paths": { "@utils/*": ["src/utils/*"] }` in `tsconfig.json` and expect `tsc` compilation output (`dist/index.js`) containing `import ... from "@utils/math"` to execute directly with `node dist/index.js`.
- **How the wrong version fails**: Silently passes at build time with a green `tsc` compile (exit code 0), but fails loudly at runtime upon execution with `ERR_MODULE_NOT_FOUND` / `MODULE_NOT_FOUND` because `tsc` does not rewrite module specifiers, and Node.js has no runtime integration with `tsconfig.json`.
- **The fix**: Use native Node.js package subpath imports (`package.json` `"imports": { "#utils/*": "./dist/utils/*.js" }`) or configure bundler/runtime plugins (`vite-tsconfig-paths`, `tsconfig-paths-webpack-plugin`, `tsconfig-paths/register`).
csx.json
{"case":{"caseId":"case:sha256:ceb18ec4aee7ebb78b802f06d2e026197edfac0ae01d635374f48be762e9f04c","constraints":{"runtime":"node"},"contract":["assert ts.resolveModuleName resolves path alias at compile time","assert ts.transpileModule emits unrewritten path alias specifier","assert Node.js runtime import of emitted file fails with ERR_MODULE_NOT_FOUND","assert package.json subpath imports resolve natively in Node.js runtime"],"goal":"tsconfig paths resolve during TypeScript compilation but are emitted unchanged by tsc, failing at runtime in Node.js without package subpaths or bundler alias plugins","kind":"HOW","packages":["pkg:npm/typescript@5.8.2"],"schemaVersion":1,"symbols":["ts.resolveModuleName","ts.transpileModule","compilerOptions.paths","compilerOptions.baseUrl"]},"contractCommand":["node","test/contract.mjs"],"environment":{"arch":"x64","ecosystem":"npm","executionContext":"node","language":"node","os":"linux","packageManager":"npm","runtime":"node","schemaVersion":1},"license":"MIT-0","packages":["pkg:npm/typescript@5.8.2"],"schemaVersion":1,"symbols":["ts.resolveModuleName","ts.transpileModule","compilerOptions.paths","compilerOptions.baseUrl"],"verifierAdapter":"node-typescript@1"}
package-lock.json
{
  "name": "sample-tsconfig-paths-runtime",
  "version": "1.0.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "sample-tsconfig-paths-runtime",
      "version": "1.0.0",
      "license": "MIT-0",
      "devDependencies": {
        "typescript": "5.8.2"
      }
    },
    "node_modules/typescript": {
      "version": "5.8.2",
      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
      "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
      "dev": true,
      "license": "Apache-2.0",
      "bin": {
        "tsc": "bin/tsc",
        "tsserver": "bin/tsserver"
      },
      "engines": {
        "node": ">=14.17"
      }
    }
  }
}
package.json
{
  "name": "sample-tsconfig-paths-runtime",
  "version": "1.0.0",
  "type": "module",
  "description": "Proves why tsconfig paths resolve at compile time but fail at runtime in Node.js",
  "scripts": {
    "build": "tsc",
    "test": "node test/contract.mjs"
  },
  "imports": {
    "#utils/*": "./dist/utils/*.js"
  },
  "devDependencies": {
    "typescript": "5.8.2"
  },
  "license": "MIT-0"
}
src/index.ts
import { add, multiply } from "@utils/math";

export function calculate(a: number, b: number): { sum: number; product: number } {
  return {
    sum: add(a, b),
    product: multiply(a, b)
  };
}
src/subpath.ts
import { add, multiply } from "#utils/math";

export function subpathCalculate(a: number, b: number): number {
  return add(a, b) + multiply(a, b);
}
src/utils/math.ts
export function add(a: number, b: number): number {
  return a + b;
}

export function multiply(a: number, b: number): number {
  return a * b;
}
test/contract.mjs
import assert from "node:assert/strict";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import ts from "typescript";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const rootDir = path.resolve(__dirname, "..");

// 0. Ensure project is compiled with TypeScript programmatically
{
  const configPath = ts.findConfigFile(rootDir, ts.sys.fileExists, "tsconfig.json");
  assert.ok(configPath, "tsconfig.json must exist");
  const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
  const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, rootDir);
  const program = ts.createProgram(parsedConfig.fileNames, parsedConfig.options);
  const emitResult = program.emit();
  assert.equal(emitResult.emitSkipped, false, "tsc program emit must succeed");
}

// 1. Compile-time: ts.resolveModuleName resolves '@utils/math' using compilerOptions.paths
{
  const compilerOptions = {
    target: ts.ScriptTarget.ES2022,
    module: ts.ModuleKind.ESNext,
    moduleResolution: ts.ModuleResolutionKind.Bundler,
    baseUrl: rootDir,
    paths: {
      "@utils/*": ["src/utils/*"]
    }
  };

  const host = ts.createCompilerHost(compilerOptions);
  const containingFile = path.join(rootDir, "src", "index.ts");
  const resolved = ts.resolveModuleName(
    "@utils/math",
    containingFile,
    compilerOptions,
    host
  );

  assert.ok(resolved.resolvedModule, "ts.resolveModuleName must resolve @utils/math at compile-time");
  assert.equal(resolved.resolvedModule.isExternalLibraryImport, false);
  const normalizedResolved = path.normalize(resolved.resolvedModule.resolvedFileName);
  const expectedSuffix = path.join("src", "utils", "math.ts");
  assert.ok(
    normalizedResolved.endsWith(expectedSuffix),
    `Resolved filename should point to math.ts, got: ${resolved.resolvedModule.resolvedFileName}`
  );
}

// 2. Transpilation: tsc does NOT rewrite module specifiers (leaves alias untouched)
{
  const sourceCode = `import { add, multiply } from "@utils/math"; export const value = add(2, 3);`;
  const transpileResult = ts.transpileModule(sourceCode, {
    compilerOptions: {
      target: ts.ScriptTarget.ES2022,
      module: ts.ModuleKind.ESNext,
      baseUrl: rootDir,
      paths: {
        "@utils/*": ["src/utils/*"]
      }
    }
  });

  const emitted = transpileResult.outputText;
  assert.ok(
    emitted.includes('"@utils/math"') || emitted.includes("'@utils/math'"),
    "tsc output must retain original '@utils/math' specifier unchanged"
  );
  assert.equal(
    emitted.includes("./utils/math"),
    false,
    "tsc must NOT rewrite alias to relative path './utils/math'"
  );
  assert.equal(
    emitted.includes("./src/utils/math"),
    false,
    "tsc must NOT rewrite alias to relative path './src/utils/math'"
  );
}

// 3. Runtime failure: Node.js cannot resolve unrewritten tsconfig paths at runtime
{
  let failed = false;
  let errorCode = null;

  try {
    const distIndexPath = path.join(rootDir, "dist", "index.js");
    const fileUrl = pathToFileURL(distIndexPath);
    await import(fileUrl.href);
  } catch (err) {
    failed = true;
    errorCode = err.code;
  }

  assert.equal(failed, true, "Node.js runtime must fail to resolve unrewritten tsconfig path alias");
  assert.ok(
    errorCode === "ERR_MODULE_NOT_FOUND" || errorCode === "MODULE_NOT_FOUND",
    `Expected ERR_MODULE_NOT_FOUND or MODULE_NOT_FOUND, got: ${errorCode}`
  );
}

// 4. Runtime fix: Node.js native package.json subpath imports (#utils/*) resolve and run cleanly
{
  const distSubpathPath = path.join(rootDir, "dist", "subpath.js");
  const fileUrl = pathToFileURL(distSubpathPath);
  const subpathModule = await import(fileUrl.href);
  assert.equal(typeof subpathModule.subpathCalculate, "function");
  const result = subpathModule.subpathCalculate(3, 4);
  assert.equal(result, 19, "Subpath imports (#utils/*) must resolve and execute correctly in Node.js");
}

// 5. Bundler fix: Bundlers require explicit alias mapping plugins / config to resolve tsconfig paths
{
  const bundlerAliasMap = {
    "^@utils/(.*)$": "./src/utils/$1.js"
  };
  const resolveAlias = (specifier) => {
    for (const [pattern, target] of Object.entries(bundlerAliasMap)) {
      const regex = new RegExp(pattern);
      if (regex.test(specifier)) {
        return specifier.replace(regex, target);
      }
    }
    return specifier;
  };

  const rewrittenSpecifier = resolveAlias("@utils/math");
  assert.equal(rewrittenSpecifier, "./src/utils/math.js");
}
tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "baseUrl": ".",
    "paths": {
      "@utils/*": ["src/utils/*"]
    },
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "declaration": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"]
}

Ursprungs-Seeder

anonymous