Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
9724408
chore(repo): add infrastructure to test against earliest supported ro…
lukastaegert Oct 23, 2026
b84de80
chore(repo): redirect all declared earliest dependencies in earliest …
lukastaegert Oct 23, 2026
3ab43a2
chore(repo): support version-specific snapshots in earliest mode
lukastaegert Oct 23, 2026
19332ff
chore(repo): define catalog version of rollup
lukastaegert Oct 27, 2026
a210ef7
test(alias): test against earliest supported rollup
lukastaegert Oct 23, 2026
3813491
test(virtual): test against earliest supported rollup
lukastaegert Oct 23, 2026
7de81f3
test(dsv): test against earliest supported rollup
lukastaegert Oct 23, 2026
f54efc2
test(image): test against earliest supported rollup
lukastaegert Oct 23, 2026
8cbce64
test(data-uri): test against earliest supported rollup
lukastaegert Oct 23, 2026
77f4487
test(graphql): test against earliest supported rollup
lukastaegert Oct 23, 2026
42180e3
test(buble): test against earliest supported rollup
lukastaegert Oct 23, 2026
45b4a5e
test(legacy): test against earliest supported rollup
lukastaegert Oct 23, 2026
bbc17a0
test(yaml): test against earliest supported rollup
lukastaegert Oct 23, 2026
2c2326e
test(pluginutils): test against earliest supported rollup
lukastaegert Oct 23, 2026
96d20ae
test(strip): test against earliest supported rollup
lukastaegert Oct 23, 2026
57d4b3c
test(inject): test against earliest supported rollup
lukastaegert Oct 23, 2026
4dab471
test(run): test against earliest supported rollup
lukastaegert Oct 23, 2026
1ec2d71
test(swc): test against earliest supported rollup
lukastaegert Oct 23, 2026
ca6262f
test(multi-entry): test against earliest supported rollup
lukastaegert Oct 23, 2026
08faa64
test(terser): test against earliest supported rollup
lukastaegert Oct 23, 2026
007ec07
test(sucrase): test against earliest supported rollup
lukastaegert Oct 23, 2026
e8912f8
test(esm-shim): test against earliest supported rollup
lukastaegert Oct 23, 2026
2be6960
test(wasm): test against earliest supported rollup
lukastaegert Oct 23, 2026
3b6b9f5
test(replace): test against earliest supported rollup
lukastaegert Oct 23, 2026
f246e0a
test(json): test against earliest supported rollup
lukastaegert Oct 23, 2026
48ae3e0
test(eslint): test against earliest supported rollup
lukastaegert Oct 23, 2026
a86ec54
test(babel): test against earliest supported rollup
lukastaegert Oct 23, 2026
fda135c
test(auto-install): test against earliest supported rollup
lukastaegert Oct 23, 2026
4055509
test(commonjs): test against earliest supported rollup
lukastaegert Oct 23, 2026
a124f3a
test(typescript): test against earliest supported rollup
lukastaegert Oct 23, 2026
8d89c12
test(dynamic-import-vars): test against earliest supported rollup
lukastaegert Oct 23, 2026
0af4f8b
test(url): test against earliest supported rollup
lukastaegert Oct 23, 2026
1710ba5
test(html): test against earliest supported rollup
lukastaegert Oct 23, 2026
dc6a172
test(node-resolve): test against earliest supported rollup
lukastaegert Oct 23, 2026
fac1c8b
test(beep): test against earliest supported rollup
lukastaegert Oct 23, 2026
98bd40d
chore(repo): test against the earliest supported rollup in CI
lukastaegert Oct 23, 2026
f79b4a2
test(auto-install): prevent pnpm workspace resolution in fixture tests
lukastaegert Oct 27, 2026
02a74a2
test(repo): increase the test timeout for slow CI runners
lukastaegert Oct 27, 2026
313c38d
test(auto-install): run the fixture cleanup even when the guard fails
lukastaegert Oct 28, 2026
4609c55
test(virtual): enable lib checks in the types test
lukastaegert Oct 28, 2026
0a6eb17
test(html): make the unloaded dynamic import emit a separate chunk
lukastaegert Oct 28, 2026
8f04f20
chore(repo): remove the stale html snapshot copy
lukastaegert Oct 28, 2026
31966b0
test(typescript): await restoring the fixture file
lukastaegert Oct 28, 2026
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
48 changes: 43 additions & 5 deletions .config/vitest.config.mts
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
import { defineConfig } from 'vitest/config';
import path from 'node:path';
import { existsSync } from 'node:fs';
import { fileURLToPath } from 'node:url';

import { getEarliestDependencies } from './vitest.earliest.mts';

const isAutoInstallPackage = path.basename(process.cwd()) === 'auto-install';
const escapeRegExp = (value: string) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');

const getEarliestAliases = (mode: string) =>
mode === 'earliest'
? getEarliestDependencies().map(({ name, path: dependencyPath }) => ({
find: new RegExp(`^${escapeRegExp(name)}$`),
replacement: dependencyPath
}))
: [];

const getSnapshotPath = (testPath: string, snapExt: string, mode: string) => {
const defaultPath = path.join(
path.dirname(testPath),
'snapshots',
path.basename(testPath) + snapExt
);
// Packages opt in to version-specific snapshots by adding the file to this directory.
const earliestPath = path.join(
path.dirname(testPath),
'snapshots-earliest',
path.basename(testPath) + snapExt
);
return mode === 'earliest' && existsSync(earliestPath) ? earliestPath : defaultPath;
};

export default defineConfig({
export default defineConfig(({ mode }) => ({
test: {
// Enable global APIs for CommonJS test files.
globals: true,
Expand All @@ -20,16 +48,26 @@ export default defineConfig({
'**/test/recipes/**',
'**/test/output/**',
'**/test/snapshots/**',
'**/test/snapshots-earliest/**',
'**/test/types.ts'
],
// These tests switch process cwd and invoke package managers; run files serially there.
fileParallelism: !isAutoInstallPackage,
// Some tests, e.g. TypeScript-based ones, can take several seconds on slow CI runners.
testTimeout: 20000,
// Keep snapshots in the same location used by Ava.
resolveSnapshotPath: (testPath, snapExt) =>
path.join(path.dirname(testPath), 'snapshots', path.basename(testPath) + snapExt)
resolveSnapshotPath: (testPath, snapExt) => getSnapshotPath(testPath, snapExt, mode),
setupFiles:
mode === 'earliest'
? [fileURLToPath(new URL('./vitest.setup.earliest.mts', import.meta.url))]
: []
},
resolve: {
// Allow importing the current package under test via `~package`
alias: [{ find: /^~package$/, replacement: path.resolve(process.cwd()) }]
alias: [
{ find: /^~package$/, replacement: path.resolve(process.cwd()) },
// In earliest mode, resolve imports of aliased dependencies to the earliest supported versions.
...getEarliestAliases(mode)
]
}
});
}));
26 changes: 26 additions & 0 deletions .config/vitest.earliest.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { existsSync, readFileSync } from 'node:fs';
import { join } from 'node:path';

const EARLIEST_SUFFIX = '-earliest';

export function getEarliestDependencies() {
const packageJsonPath = join(process.cwd(), 'package.json');
const { devDependencies = {} } = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
const dependencies = Object.keys(devDependencies)
.filter((name) => name.endsWith(EARLIEST_SUFFIX))
.map((name) => ({
name: name.slice(0, -EARLIEST_SUFFIX.length),
path: join(process.cwd(), 'node_modules', name)
}));
if (!dependencies.some(({ name }) => name === 'rollup')) {
throw new Error(`Missing "rollup-earliest" devDependency in ${packageJsonPath}.`);
}
for (const { path } of dependencies) {
if (!existsSync(path)) {
throw new Error(
`Missing "${path}". Run "pnpm install" to test against the earliest supported versions.`
);
}
}
return dependencies;
}
35 changes: 35 additions & 0 deletions .config/vitest.setup.earliest.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// The vite aliases in vitest.config.mts redirect ESM imports of the aliased dependencies to
// their earliest supported versions, but they do not affect require(...). Patch the Node
// module resolution so CommonJS tests resolve the same versions.
import Module from 'node:module';

import { getEarliestDependencies } from './vitest.earliest.mts';

type ResolveFilename = (
request: string,
parent?: unknown,
isMain?: boolean,
options?: unknown
) => string;

const earliestPaths = new Map(
getEarliestDependencies().map(({ name, path }) => [name, path] as const)
);

const originalResolveFilename = (Module as { _resolveFilename?: ResolveFilename })._resolveFilename;

if (
originalResolveFilename &&
!originalResolveFilename.name.includes('redirectEarliestDependencies')
) {
(Module as { _resolveFilename: ResolveFilename })._resolveFilename =
function redirectEarliestDependencies(this: unknown, request, parent, isMain, options) {
return originalResolveFilename.call(
this,
earliestPaths.get(request) ?? request,
parent,
isMain,
options
);
};
}
4 changes: 3 additions & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Any code change should be submitted as a pull request. Our guidelines for Pull R
Some packages use Vitest for tests. When converting or running tests locally inside a package directory (e.g. `packages/alias`):

- Tests import the package under test via `~package`. A shared config at `.config/vitest.config.mts` provides a runtime alias that resolves `~package` to the current working directory.
- Snapshots are written alongside each test under a local `.snapshots` directory.
- Snapshots are written to a `snapshots` directory alongside the tests.

Example (from a package directory):

Expand All @@ -40,6 +40,8 @@ pnpm build
pnpm test
```

To also run the tests against the earliest version of rollup the package supports, use `pnpm test:earliest`. In this mode, snapshots for a test file are taken from `test/snapshots-earliest` when a file with the same name exists there, falling back to the regular snapshots otherwise. When regenerating snapshots with `-u` in this mode, test files without a counterpart in `test/snapshots-earliest` get their regular snapshots overwritten with the output generated for the earliest rollup version, so when version-specific snapshots are intended, first copy the snapshot file into `test/snapshots-earliest` and then regenerate.

## Code review process

The bigger the pull request, the longer it will take to review and merge. Try to break down large pull requests in smaller chunks that are easier to review and merge.
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/node-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,6 @@ jobs:

- name: run tests
run: pnpm --workspace-concurrency 1 --filter "...[origin/master]" ci:test

- name: run tests against earliest supported rollup
run: pnpm --workspace-concurrency 1 --filter "...[origin/master]" test:earliest
3 changes: 3 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,6 @@ jobs:

- name: Run Tests
run: pnpm --filter "...[origin/master]" ci:coverage

- name: Run Tests Against Earliest Supported Rollup
run: pnpm --filter "...[origin/master]" test:earliest
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ packages/yaml/test/fixtures/**/*
**/dist/
**/fixtures/
**/snapshots/
**/snapshots-earliest/
pnpm-lock.yaml
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,14 @@ To run tests on a specific package:
$ pnpm --filter ./packages/<name> test
```

To also run the tests of a package against the earliest version of rollup it supports:

```console
$ pnpm --filter ./packages/<name> test:earliest
```

In this mode, snapshots are read from the `test/snapshots-earliest` directory of a package when a file with the same name exists there, falling back to the regular snapshots otherwise. Be careful when regenerating snapshots with `-u` in this mode: for test files without a counterpart in `test/snapshots-earliest`, this overwrites the regular snapshots with the output generated for the earliest rollup version. To add version-specific snapshots for a test file, first copy its snapshot file from `test/snapshots` to `test/snapshots-earliest`, then regenerate in this mode.

Linting:

To lint all packages which have changes:
Expand Down
6 changes: 4 additions & 2 deletions packages/alias/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"prerelease": "pnpm build",
"pretest": "pnpm build",
"release": "pnpm --workspace-root package:release $(pwd)",
"test": "vitest --config ../../.config/vitest.config.mts run"
"test": "vitest --config ../../.config/vitest.config.mts run",
"test:earliest": "pnpm test -- --mode earliest"
},
"files": [
"dist",
Expand All @@ -58,7 +59,8 @@
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.0.0",
"del-cli": "^5.0.0",
"rollup": "^4.0.0",
"rollup": "catalog:",
"rollup-earliest": "npm:rollup@4.0.0",
"typescript": "catalog:"
},
"types": "./dist/index.d.ts",
Expand Down
13 changes: 9 additions & 4 deletions packages/alias/test/alias.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@ import { fileURLToPath } from 'node:url';
import os from 'node:os';

import { describe, it, expect } from 'vitest';
import { rollup } from 'rollup';
import { rollup, VERSION } from 'rollup';
import nodeResolvePlugin from '@rollup/plugin-node-resolve';

import alias from '~package';

const DIRNAME = fileURLToPath(new URL('.', import.meta.url));
const isWindows = os.platform() === 'win32';

// rollup 4.13.0 added a prefix to plugin warning messages
const [rollupMajor, rollupMinor] = VERSION.split('.').map(Number);
const warningPrefix =
rollupMajor > 4 || (rollupMajor === 4 && rollupMinor >= 13) ? '[plugin alias] ' : '';

/**
* Helper function to test configuration with Rollup
* @param plugins is an array of plugins for Rollup, they should include "alias"
Expand Down Expand Up @@ -506,9 +511,9 @@ describe('plugin-alias', () => {
expect(warnList).toMatchObject([
{
message:
'[plugin alias] rewrote @/warn-importee.js to test/fixtures/folder/warn-importee.js but was not an absolute path and was not handled by other plugins. ' +
'This will lead to duplicated modules for the same path. ' +
'To avoid duplicating modules, you should resolve to an absolute path.',
`${warningPrefix}rewrote @/warn-importee.js to test/fixtures/folder/warn-importee.js but was not an absolute path and was not handled by other plugins. ` +
`This will lead to duplicated modules for the same path. ` +
`To avoid duplicating modules, you should resolve to an absolute path.`,
code: 'PLUGIN_WARNING',
plugin: 'alias'
}
Expand Down
5 changes: 4 additions & 1 deletion packages/auto-install/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"pretest": "pnpm build",
"release": "pnpm --workspace-root package:release $(pwd)",
"test": "vitest --config ../../.config/vitest.config.mts run",
"test:earliest": "pnpm test -- --mode earliest",
"test:ts": "tsc --noEmit"
},
"files": [
Expand Down Expand Up @@ -63,10 +64,12 @@
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^15.0.0",
"@rollup/plugin-node-resolve-earliest": "npm:@rollup/plugin-node-resolve@11.2.1",
"@rollup/plugin-typescript": "^9.0.1",
"del": "^6.1.1",
"node-noop": "^1.0.0",
"rollup": "^4.0.0-24",
"rollup": "catalog:",
"rollup-earliest": "npm:rollup@1.20.0",
"typescript": "^4.8.3"
},
"types": "./types/index.d.ts"
Expand Down
3 changes: 3 additions & 0 deletions packages/auto-install/test/fixtures/pnpm-bare/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# pnpm refuses to add dependencies at a workspace root without an explicit opt-in;
# this also means the root .npmrc no longer applies here, which is fine for this fixture
ignore-workspace-root-check=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Anchor pnpm workspace detection to this fixture so that package installs spawned here do not
# operate on the monorepo workspace root and regenerate its lockfile.
3 changes: 3 additions & 0 deletions packages/auto-install/test/fixtures/pnpm/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# pnpm refuses to add dependencies at a workspace root without an explicit opt-in;
# this also means the root .npmrc no longer applies here, which is fine for this fixture
ignore-workspace-root-check=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Anchor pnpm workspace detection to this fixture so that package installs spawned here do not
# operate on the monorepo workspace root and regenerate its lockfile.
15 changes: 14 additions & 1 deletion packages/auto-install/test/pnpm-bare.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ const input = join(cwd, '../input.js');

process.chdir(cwd);

// pnpm would otherwise discover the monorepo workspace root above the fixture and regenerate
// its lockfile. The fixture pnpm-workspace.yaml anchors workspace detection to the fixture
// directory; ensure the fixture installs leave the workspace root lockfile untouched.
const rootLockfile = join(__dirname, '../../../pnpm-lock.yaml');
const originalLockfileContent = readFileSync(rootLockfile);

test('pnpm, bare', async () => {
await rollup({
input,
Expand All @@ -28,5 +34,12 @@ test('pnpm, bare', async () => {
}, 50000);

afterAll(async () => {
await del(['node_modules', 'package.json', 'pnpm-lock.yaml']);
try {
expect(
readFileSync(rootLockfile).equals(originalLockfileContent),
'the workspace root lockfile was modified by a fixture install'
).toBe(true);
} finally {
await del(['node_modules', 'package.json', 'pnpm-lock.yaml']);
}
});
17 changes: 15 additions & 2 deletions packages/auto-install/test/pnpm.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ const input = join(cwd, '../input.js');

process.chdir(cwd);

// pnpm would otherwise discover the monorepo workspace root above the fixture and regenerate
// its lockfile. The fixture pnpm-workspace.yaml anchors workspace detection to the fixture
// directory; ensure the fixture installs leave the workspace root lockfile untouched.
const rootLockfile = join(__dirname, '../../../pnpm-lock.yaml');
const originalLockfileContent = readFileSync(rootLockfile);

test('pnpm', async () => {
await rollup({
input,
Expand All @@ -29,6 +35,13 @@ test('pnpm', async () => {
}, 50000);

afterAll(async () => {
await del(['node_modules', 'package.json']);
writeFileSync('pnpm-lock.yaml', '');
try {
expect(
readFileSync(rootLockfile).equals(originalLockfileContent),
'the workspace root lockfile was modified by a fixture install'
).toBe(true);
} finally {
await del(['node_modules', 'package.json']);
writeFileSync('pnpm-lock.yaml', '');
}
});
4 changes: 3 additions & 1 deletion packages/babel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"pretest": "pnpm build",
"release": "pnpm --workspace-root package:release $(pwd)",
"test": "vitest --config ../../.config/vitest.config.mts run",
"test:earliest": "pnpm test -- --mode earliest",
"test:ts": "tsc types/index.d.ts test/types.ts --noEmit"
},
"files": [
Expand Down Expand Up @@ -80,7 +81,8 @@
"@rollup/plugin-json": "^5.0.0",
"@rollup/plugin-node-resolve": "^15.0.0",
"@types/babel__core": "^7.1.9",
"rollup": "^4.0.0-24",
"rollup": "catalog:",
"rollup-earliest": "npm:rollup@2.78.0",
Comment thread
lukastaegert marked this conversation as resolved.
Resolved
"source-map": "^0.7.4"
},
"types": "./types/index.d.ts",
Expand Down
Loading
Loading