Skip to content
Merged
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
47 changes: 23 additions & 24 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,85 +29,85 @@
"exports": {
"./core": {
"import": {
"types": "./dist/esm/core.d.mts",
"types": "./dist/esm/core.d.ts",
"default": "./dist/esm/core.mjs"
},
"require": {
"types": "./dist/cjs/core.d.cts",
"types": "./dist/cjs/core.d.ts",
"default": "./dist/cjs/core.cjs"
}
},
"./rollup": {
"import": {
"types": "./dist/esm/rollup.d.mts",
"types": "./dist/esm/rollup.d.ts",
"default": "./dist/esm/rollup.mjs"
},
"require": {
"types": "./dist/cjs/rollup.d.cts",
"types": "./dist/cjs/rollup.d.ts",
"default": "./dist/cjs/rollup.cjs"
}
},
"./webpack": {
"import": {
"types": "./dist/esm/webpack.d.mts",
"types": "./dist/esm/webpack.d.ts",
"default": "./dist/esm/webpack.mjs"
},
"require": {
"types": "./dist/cjs/webpack.d.cts",
"types": "./dist/cjs/webpack.d.ts",
"default": "./dist/cjs/webpack.cjs"
}
},
"./vite": {
"import": {
"types": "./dist/esm/vite.d.mts",
"types": "./dist/esm/vite.d.ts",
"default": "./dist/esm/vite.mjs"
},
"require": {
"types": "./dist/cjs/vite.d.cts",
"types": "./dist/cjs/vite.d.ts",
"default": "./dist/cjs/vite.cjs"
}
},
"./esbuild": {
"import": {
"types": "./dist/esm/esbuild.d.mts",
"types": "./dist/esm/esbuild.d.ts",
"default": "./dist/esm/esbuild.mjs"
},
"require": {
"types": "./dist/cjs/esbuild.d.cts",
"types": "./dist/cjs/esbuild.d.ts",
"default": "./dist/cjs/esbuild.cjs"
}
},
"./bun": {
"types": "./dist/esm/bun.d.mts",
"types": "./dist/esm/bun.d.ts",
"default": "./dist/esm/bun.mjs"
},
"./webpack-loader": {
"types": "./dist/cjs/webpack-loader.d.cts",
"types": "./dist/cjs/webpack-loader.d.ts",
"default": "./dist/cjs/webpack-loader.cjs"
}
},
"typesVersions": {
"*": {
"core": [
"./dist/types/core.d.ts"
"./dist/esm/core.d.ts"
],
"rollup": [
"./dist/types/rollup.d.ts"
"./dist/esm/rollup.d.ts"
],
"webpack": [
"./dist/types/webpack.d.ts"
"./dist/esm/webpack.d.ts"
],
"vite": [
"./dist/types/vite.d.ts"
"./dist/esm/vite.d.ts"
],
"esbuild": [
"./dist/types/esbuild.d.ts"
"./dist/esm/esbuild.d.ts"
],
"bun": [
"./dist/types/bun.d.ts"
"./dist/esm/bun.d.ts"
],
"webpack-loader": [
"./dist/types/webpack-loader.d.ts"
"./dist/esm/webpack-loader.d.ts"
]
}
},
Expand All @@ -120,18 +120,17 @@
"dependencies": {
"@apm-js-collab/code-transformer": "^0.18.0",
"es-module-lexer": "^2.1.0",
"module-details-from-path": "^1.0.4",
"magic-string": "^0.30.21"
"magic-string": "^0.30.21",
"module-details-from-path": "^1.0.4"
},
"devDependencies": {
"@types/node": "^20.10.4",
"meriyah": "^6.1.4",
"@types/bun": "^1.3.14",
"@types/node": "^20.10.4",
"esbuild": "^0.28.1",
"meriyah": "^6.1.4",
"rollup": "^4.62.0",
"tslib": "^2.6.2",
"typescript": "^6.0.3",
"unplugin-dts": "^1.0.1",
"vite": "^8.0.16",
"vitest": "^4.1.9",
"webpack": "^5.107.2"
Expand Down
4 changes: 2 additions & 2 deletions src/bun.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
createCodeTransformer,
isJsFile,
type CodeTransformerPluginOptions,
} from './core';
} from './core.js';
import type { Plugin } from 'esbuild';

/**
Expand Down Expand Up @@ -117,4 +117,4 @@ export default function codeTransformerBun(
};
}

export type { CodeTransformerPluginOptions } from './core';
export type { CodeTransformerPluginOptions } from './core.js';
4 changes: 2 additions & 2 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,9 @@ export type { CustomTransform, InstrumentationConfig, ModuleMatcher, FunctionBeh
// Keep type and value exports in separate statements: inline `type` modifiers
// in a mixed export emit TS 4.5+ syntax into the declarations, which breaks
// consumers type-checking with older TypeScript.
export { serializeInstrumentations, deserializeInstrumentations } from './instrumentation-serde';
export { serializeInstrumentations, deserializeInstrumentations } from './instrumentation-serde.js';
export type {
SerializedRegExp,
SerializableInstrumentationConfig,
AnyInstrumentationConfig,
} from './instrumentation-serde';
} from './instrumentation-serde.js';
4 changes: 2 additions & 2 deletions src/esbuild.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
createCodeTransformer,
isJsFile,
type CodeTransformerPluginOptions,
} from './core';
} from './core.js';

const filter = /\.(cjs|mjs|cts|mts|tsx|jsx|ts|js)$/;

Expand Down Expand Up @@ -170,4 +170,4 @@ export default function codeTransformerEsbuild(
};
}

export type { CodeTransformerPluginOptions } from './core';
export type { CodeTransformerPluginOptions } from './core.js';
4 changes: 2 additions & 2 deletions src/rollup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
isJsFile,
shouldSkipCodeInjection,
type CodeTransformerPluginOptions,
} from "./core";
} from "./core.js";
import MagicString, { SourceMap } from "magic-string";

export default function codeTransformerRollup(
Expand Down Expand Up @@ -124,4 +124,4 @@ export default function codeTransformerRollup(
};
}

export type { CodeTransformerPluginOptions } from "./core";
export type { CodeTransformerPluginOptions } from "./core.js";
6 changes: 3 additions & 3 deletions src/vite.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Plugin } from 'vite';
import type { CodeTransformerPluginOptions } from './core';
import codeTransformerRollup from './rollup'; // Ensure rollup types are included for TransformResult
import type { CodeTransformerPluginOptions } from './core.js';
import codeTransformerRollup from './rollup.js'; // Ensure rollup types are included for TransformResult

export default function codeTransformerVite(
options: CodeTransformerPluginOptions,
Expand All @@ -11,4 +11,4 @@ export default function codeTransformerVite(
};
}

export type { CodeTransformerPluginOptions } from './core';
export type { CodeTransformerPluginOptions } from './core.js';
2 changes: 1 addition & 1 deletion src/webpack-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
deserializeInstrumentations,
serializeInstrumentations,
type AnyInstrumentationConfig,
} from './instrumentation-serde';
} from './instrumentation-serde.js';

// Handle CJS default export - module-details-from-path exports a function directly
const moduleDetailsFromPath = (moduleDetailsFromPathImport as any).default || moduleDetailsFromPathImport as any;
Expand Down
6 changes: 3 additions & 3 deletions src/webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';
import {
type CodeTransformerPluginOptions,
} from './core';
import { serializeInstrumentations } from './instrumentation-serde';
} from './core.js';
import { serializeInstrumentations } from './instrumentation-serde.js';

const __dirname = dirname(fileURLToPath(import.meta.url));

Expand Down Expand Up @@ -121,4 +121,4 @@ export default function codeTransformerWebpack(
return new CodeTransformerWebpackPlugin(options);
}

export type { CodeTransformerPluginOptions } from './core';
export type { CodeTransformerPluginOptions } from './core.js';
141 changes: 141 additions & 0 deletions test/type-resolution.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import {
mkdtempSync,
mkdirSync,
writeFileSync,
symlinkSync,
unlinkSync,
rmSync,
existsSync,
} from 'fs';
import { join, dirname } from 'path';
import { spawnSync } from 'child_process';
import { fileURLToPath } from 'url';
import { createRequire } from 'module';

// Regression tests for https://github.com/apm-js-collab/code-transformer-bundler-plugins/issues/40
//
// The published declarations must reference their sibling modules with explicit
// extensions, otherwise they fail to resolve under TypeScript's Node16/NodeNext
// module resolution with `error TS2307: Cannot find module` (or `TS2834`).
// These tests type-check a consumer that imports every advertised subpath under
// each resolution mode.

const __dirname = dirname(fileURLToPath(import.meta.url));
const root = join(__dirname, '..');
const tsc = createRequire(import.meta.url).resolve('typescript/bin/tsc');

const PKG = '@apm-js-collab/code-transformer-bundler-plugins';
const ALL_SUBPATHS = ['core', 'rollup', 'webpack', 'vite', 'esbuild', 'bun', 'webpack-loader'] as const;

// `vite` ships `exports`-only type declarations that legacy `node10` resolution
// cannot follow, so the `/vite` subpath's peer types are unresolvable there —
// a limitation of vite itself, not of our declarations (which resolve fine).
const NODE10_SUBPATHS = ALL_SUBPATHS.filter((s) => s !== 'vite');

// A consumer module that imports (and references) each subpath, so an
// unresolved declaration surfaces as a hard error rather than being elided.
function consumerFor(subpaths: readonly string[]): string {
return (
subpaths.map((s, i) => `import * as m${i} from '${PKG}/${s}';`).join('\n') +
`\nexport const used = [${subpaths.map((_, i) => `m${i}`).join(', ')}];\n`
);
}

// `skipLibCheck: false` is essential: issue #40's TS2307 originates inside the
// package's own declaration files, which `skipLibCheck: true` would hide.
const baseCompilerOptions = {
target: 'esnext',
strict: true,
skipLibCheck: false,
esModuleInterop: true,
types: ['node'],
noEmit: true,
};

const modes = [
// Node16/NodeNext — the resolution that broke in #40. `.mts` exercises the
// `import` condition; `.cts` exercises `require`.
{
name: 'nodenext',
compilerOptions: { ...baseCompilerOptions, module: 'nodenext' },
files: ['consumer.mts', 'consumer.cts'],
subpaths: ALL_SUBPATHS,
},
{
name: 'bundler',
compilerOptions: { ...baseCompilerOptions, module: 'esnext', moduleResolution: 'bundler' },
files: ['consumer.ts'],
subpaths: ALL_SUBPATHS,
},
// Legacy resolution routes subpaths through `typesVersions` → the `.d.ts`
// files. `ignoreDeprecations` silences TS6's non-fatal `node10` deprecation
// warning so the mode actually runs; TS7 removes `node10` outright, which
// the skip below detects.
{
name: 'node10',
compilerOptions: {
...baseCompilerOptions,
module: 'commonjs',
moduleResolution: 'node10',
ignoreDeprecations: '6.0',
},
files: ['consumer.ts'],
subpaths: NODE10_SUBPATHS,
},
];

let fixture: string;
let symlink: string;

beforeAll(() => {
expect(
existsSync(join(root, 'dist/esm/core.d.ts')),
'Declarations are missing — run `yarn build` before the tests.',
).toBe(true);

// The fixture lives under the repo's own node_modules so that `@types/node`
// and `@apm-js-collab/code-transformer` resolve up the tree, exactly as
// they would for a real installed consumer.
fixture = mkdtempSync(join(root, 'node_modules', '.type-resolution-'));

// Symlink the package into place so resolution flows through package.json
// `exports` / `typesVersions` rather than a `paths` alias — that routing is
// precisely what we are testing.
const scope = join(fixture, 'node_modules', '@apm-js-collab');
mkdirSync(scope, { recursive: true });
symlink = join(scope, 'code-transformer-bundler-plugins');
symlinkSync(root, symlink, process.platform === 'win32' ? 'junction' : 'dir');
});

afterAll(() => {
// Unlink the symlink explicitly first so cleanup can never recurse into the
// repo it points at.
if (symlink && existsSync(symlink)) unlinkSync(symlink);
if (fixture) rmSync(fixture, { recursive: true, force: true });
});

describe('published type declarations resolve for consumers', () => {
it.each(modes)('$name: every subpath type-checks cleanly', (mode) => {
const source = consumerFor(mode.subpaths);
for (const file of mode.files) writeFileSync(join(fixture, file), source);

const tsconfig = join(fixture, `tsconfig.${mode.name}.json`);
writeFileSync(
tsconfig,
JSON.stringify({ compilerOptions: mode.compilerOptions, files: mode.files }, null, 2),
);

const { status, stdout, stderr } = spawnSync(process.execPath, [tsc, '-p', tsconfig], {
cwd: fixture,
encoding: 'utf8',
});
const output = stdout + stderr;

// `node10` was removed as a `moduleResolution` value in TypeScript 7;
// skip the mode if the installed compiler no longer accepts it.
if (/error TS510[78]|moduleResolution.*removed/.test(output)) return;

expect(status, output).toBe(0);
});
});
Loading