Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
aa02b21
feat(auto-install)!: ESM only; tsc build; migrate tests to Vitest per…
CharlieHelps Oct 29, 2025
1696370
chore(repo): regenerate pnpm lockfile via pnpm i
CharlieHelps Oct 29, 2025
18b6809
test(auto-install): make yarn tests resilient to Corepack by pre-crea…
CharlieHelps Oct 29, 2025
0ddefc2
test(auto-install): skip tests on Node <20.19 to align with engines; …
CharlieHelps Oct 29, 2025
5d7540a
chore(repo): restore pnpm-lock.yaml to pre-change state
CharlieHelps Oct 29, 2025
2e02b83
test(auto-install): dynamically import plugin and use fileURLToPath; …
CharlieHelps Oct 29, 2025
562b002
chore(repo): restore pnpm-lock.yaml (lockfile v9) from 1696370
CharlieHelps Oct 29, 2025
ea6c46a
chore(auto-install): narrow Rollup peer range to ^4
CharlieHelps Oct 31, 2025
c969a11
test(auto-install): mirror alias package setup; map ~package to dist …
CharlieHelps Oct 31, 2025
6f6c22c
chore(repo): restore pnpm-lock.yaml (v9) and keep fixture lockfiles l…
CharlieHelps Oct 31, 2025
83671a6
test(auto-install): extend timeout and tune npm env for npm* tests to…
CharlieHelps Oct 31, 2025
218488a
test(auto-install): scope npm env tweaks to test body and restore via…
CharlieHelps Oct 31, 2025
4e8942a
refactor(auto-install): restore defaults pattern for option handling
CharlieHelps Oct 31, 2025
c0f09d3
test(auto-install): make npm/pnpm bare tests robust on Windows by usi…
CharlieHelps Oct 31, 2025
655a13c
fix(auto-install): use 'pnpm add' for installing new deps
CharlieHelps Oct 31, 2025
772aa32
refactor(auto-install): restore const defaults (incl. commands); allo…
CharlieHelps Oct 31, 2025
b211bee
refactor(auto-install): compute pkgFile after merge; keep defaults in…
CharlieHelps Oct 31, 2025
64ea687
refactor(auto-install): derive validManagers from options.commands keys
CharlieHelps Oct 31, 2025
2775eef
chore(auto-install): remove over-strict 'satisfies' on options; simpl…
CharlieHelps Oct 31, 2025
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
10 changes: 8 additions & 2 deletions .config/vitest.config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,13 @@ export default defineConfig({
path.join(path.dirname(testPath), '.snapshots', path.basename(testPath) + snapExt)
},
resolve: {
// Allow importing the current package under test via `~package`
alias: [{ find: /^~package$/, replacement: path.resolve(process.cwd()) }]
// Allow importing the current package under test via `~package`.
// Point directly at the built entry so dynamic imports in tests work consistently.
alias: [
{
find: /^~package$/,
replacement: path.resolve(process.cwd(), 'dist/index.js')
}
]
}
});
31 changes: 15 additions & 16 deletions packages/auto-install/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,34 +13,33 @@
"author": "Rich Harris",
"homepage": "https://github.com/rollup/plugins/tree/master/packages/auto-install/#readme",
"bugs": "https://github.com/rollup/plugins/issues",
"main": "./dist/cjs/index.js",
"module": "./dist/es/index.js",
"type": "module",
"exports": {
"types": "./types/index.d.ts",
"import": "./dist/es/index.js",
"default": "./dist/cjs/index.js"
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.js"
}
},
"engines": {
"node": ">=14.0.0"
"node": ">=20.19.0"
},
"scripts": {
"build": "rollup -c",
"build": "tsc --project tsconfig.json",
"ci:coverage": "nyc pnpm test && nyc report --reporter=text-lcov > coverage.lcov",
"ci:lint": "pnpm build && pnpm lint",
"ci:lint:commits": "commitlint --from=${CIRCLE_BRANCH} --to=${CIRCLE_SHA1}",
"ci:test": "pnpm test -- --verbose",
"ci:test": "pnpm test -- --reporter=verbose",
"prebuild": "del-cli dist",
"prepare": "if [ ! -d 'dist' ]; then pnpm build; fi",
"prerelease": "pnpm build",
"pretest": "pnpm build",
"release": "pnpm --workspace-root package:release $(pwd)",
"test": "ava",
"test": "vitest --config ../../.config/vitest.config.mts run",
"test:ts": "tsc --noEmit"
},
"files": [
"dist",
"!dist/**/*.map",
"types",
"README.md",
"LICENSE"
],
Expand All @@ -53,7 +52,7 @@
"modules"
],
"peerDependencies": {
"rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
"rollup": "^4"
},
"peerDependenciesMeta": {
"rollup": {
Expand All @@ -62,13 +61,13 @@
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.0.0",
"@rollup/plugin-typescript": "^9.0.1",
"del": "^6.1.1",
"del-cli": "^5.0.0",
"node-noop": "^1.0.0",
"rollup": "^4.0.0-24",
"typescript": "^4.8.3"
"rollup": "^4.0.0",
"typescript": "catalog:"
},
"types": "./types/index.d.ts",
"types": "./dist/index.d.ts",
"ava": {
"workerThreads": false,
"files": [
Expand Down
7 changes: 0 additions & 7 deletions packages/auto-install/rollup.config.mjs

This file was deleted.

67 changes: 54 additions & 13 deletions packages/auto-install/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,63 @@
import * as fs from 'fs';
import * as path from 'path';
import mod from 'module';
import { exec } from 'child_process';
import { promisify } from 'util';
import fs from 'node:fs';
import path from 'node:path';
import { builtinModules } from 'node:module';
import { exec } from 'node:child_process';
import { promisify } from 'node:util';

import type { Plugin } from 'rollup';

import type { RollupAutoInstallOptions } from '../types';
type PackageManager = 'npm' | 'yarn' | 'pnpm';

export interface RollupAutoInstallOptions {
/**
* Specifies the location on disk of the target `package.json` file.
* If the file doesn't exist, it will be created by the plugin,
* as package managers need to populate the `dependencies` property.
* @default '{cwd}/package.json'
*/
pkgFile?: string;

/**
* Specifies the package manager to use.
* If not specified, the plugin will default to `yarn` if `yarn.lock` exists,
* to `pnpm` if `pnpm-lock.yaml` exists, or `npm` otherwise.
*/
manager?: PackageManager;

/**
* Test-only override of package manager commands.
* @internal
*/
commands?: Partial<Record<PackageManager, string>>;
}

const execAsync = promisify(exec);

export default function autoInstall(opts: RollupAutoInstallOptions = {}): Plugin {
// Restore the historic `defaults` object (including `commands`) so tests can
// optionally override command strings via options.
const defaults = {
// intentionally undocumented options. used for tests
commands: {
npm: 'npm install',
pnpm: 'pnpm install',
pnpm: 'pnpm add',
yarn: 'yarn add'
},
} as Record<PackageManager, string>,
manager: fs.existsSync('yarn.lock') ? 'yarn' : fs.existsSync('pnpm-lock.yaml') ? 'pnpm' : 'npm',
pkgFile: path.resolve(opts.pkgFile || 'package.json')
pkgFile: 'package.json'
} as const;

// Shallow-merge, with a one-level deep merge for `commands` to allow partial overrides in tests
const options = {
...defaults,
...opts,
commands: { ...defaults.commands, ...(opts.commands || {}) }
};

const options = Object.assign({}, defaults, opts);
const { manager, pkgFile } = options;
const validManagers = ['npm', 'yarn', 'pnpm'];
const { manager } = options;
const pkgFile = path.resolve(options.pkgFile);

const validManagers = Object.keys(options.commands) as PackageManager[];

if (!validManagers.includes(manager)) {
throw new RangeError(
Expand All @@ -41,7 +74,15 @@ export default function autoInstall(opts: RollupAutoInstallOptions = {}): Plugin
pkg = {};
}

const installed = new Set(Object.keys(pkg.dependencies || {}).concat(mod.builtinModules));
// Normalize core module names to include both `fs` and `node:fs` forms so we never try to
// install built-ins regardless of how Rollup reports them or how they are imported.
const coreModules = new Set<string>([
...builtinModules,
...builtinModules.filter((m) => m.startsWith('node:')).map((m) => m.slice(5)),
...builtinModules.filter((m) => !m.startsWith('node:')).map((m) => `node:${m}`)
]);

const installed = new Set([...Object.keys(pkg.dependencies || {}), ...coreModules]);
const cmd = options.commands[manager];

return {
Expand Down
1 change: 1 addition & 0 deletions packages/auto-install/test/fixtures/yarn/.gitkeep
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Empty file.
33 changes: 0 additions & 33 deletions packages/auto-install/test/npm-bare.js

This file was deleted.

86 changes: 86 additions & 0 deletions packages/auto-install/test/npm-bare.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

import del from 'del';
import { it, afterAll } from 'vitest';
import { rollup } from 'rollup';
import nodeResolve from '@rollup/plugin-node-resolve';

// Dynamically import the plugin within gated tests to avoid Node <20 execution

const DIR = fileURLToPath(new URL('.', import.meta.url));
const cwd = path.join(DIR, 'fixtures/npm-bare');
const file = path.join(cwd, 'output/bundle.js');
// Use a local input inside the cwd so Node resolution finds packages installed
// by the test (e.g., on Windows where upward-only resolution won't see
// `npm-bare/node_modules` from `fixtures/input.js`).
const input = path.join(cwd, 'input.local.js');
const pkgFile = path.join(cwd, 'package.json');

// Helper to temporarily disable slow npm features during the test
function stubNpmQuietEnv() {
const keys = [
'npm_config_audit',
'npm_config_fund',
'npm_config_progress',
'npm_config_update_notifier',
'npm_config_loglevel'
] as const;
const prev: Record<string, string | undefined> = {};
for (const k of keys) prev[k] = process.env[k];
process.env.npm_config_audit = 'false';
process.env.npm_config_fund = 'false';
process.env.npm_config_progress = 'false';
process.env.npm_config_update_notifier = 'false';
process.env.npm_config_loglevel = 'error';
return () => {
for (const k of keys) {
const v = prev[k];
if (v == null) delete (process.env as any)[k];
else (process.env as any)[k] = v;
}
};
}
const [NODE_MAJOR, NODE_MINOR] = process.versions.node.split('.').map(Number);
const RUN_ON_THIS_NODE = NODE_MAJOR > 20 || (NODE_MAJOR === 20 && NODE_MINOR >= 19);

// npm installs can be slower than pnpm/yarn on CI; allow extra time.
it.runIf(RUN_ON_THIS_NODE)(
'npm, bare',
async () => {
const restoreEnv = stubNpmQuietEnv();
const prevCwd = process.cwd();
// Create a local copy of the shared input so resolution starts from `cwd`.
fs.copyFileSync(path.join(cwd, '../input.js'), input);
process.chdir(cwd);
try {
const { default: autoInstall } = await import('~package');
const bundle = await rollup({
input,
// @ts-expect-error - rollup() ignores output here but tests kept it historically
output: { file, format: 'es' },
plugins: [autoInstall({ pkgFile, manager: 'npm' }), nodeResolve()]
});
await bundle.close();

const json = JSON.parse(fs.readFileSync(pkgFile, 'utf-8'));
if (!json.dependencies || !json.dependencies['node-noop']) {
throw new Error('Expected node-noop to be added to dependencies');
}
} finally {
process.chdir(prevCwd);
try {
fs.unlinkSync(input);
} catch {
/* ignore cleanup errors */
}
restoreEnv();
}
},
60000
);

afterAll(async () => {
await del(['node_modules', 'package.json', 'package-lock.json'], { cwd });
});
54 changes: 0 additions & 54 deletions packages/auto-install/test/npm.js

This file was deleted.

Loading
Loading