Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/dedupe-solid-in-builds.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'vite-plugin-solid': patch
---

Production builds now dedupe `solid-js` the same way dev does. A dependency with its own nested copy of `solid-js` no longer adds a second runtime to the bundle, which broke context lookups (#112).
3 changes: 3 additions & 0 deletions .github/workflows/vitest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ jobs:
- name: Install project dependencies
run: pnpm i

- name: Run unit tests
run: pnpm run test:unit

- name: Install playwright browser
working-directory: examples/vite-6
run: pnpm exec playwright install chromium
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"prepublishOnly": "pnpm build",
"release": "pnpm build && changeset publish",
"check": "package-check",
"test": "node scripts/test-examples.ts"
"test": "node scripts/test-examples.ts",
"test:unit": "vitest run"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -68,7 +69,8 @@
"rollup-plugin-cleaner": "^1.0.0",
"solid-js": "^1.9.3",
"typescript": "^5.2.2",
"vite": "^6.0.0"
"vite": "^6.0.0",
"vitest": "^3.0.7"
},
"peerDependencies": {
"@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.0.0 || ^7.0.0",
Expand Down
37 changes: 36 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 67 additions & 0 deletions src/dedupe.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import path from 'node:path';
import { build, type Rollup } from 'vite';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import solidPlugin from './index.js';

let root: string;

async function writePackage(dir: string, pkg: Record<string, unknown>, files: Record<string, string>) {
await mkdir(dir, { recursive: true });
await writeFile(path.join(dir, 'package.json'), JSON.stringify(pkg));
for (const [name, code] of Object.entries(files)) {
await writeFile(path.join(dir, name), code);
}
}

beforeAll(async () => {
root = await mkdtemp(path.join(tmpdir(), 'solid-dedupe-'));
const fakeSolid = (copy: string) =>
[
{ name: 'solid-js', version: '1.9.0', type: 'module', exports: { '.': './index.js' } },
{ 'index.js': `export const copy = ${JSON.stringify(copy)};\n` },
] as const;
await writePackage(path.join(root, 'node_modules/solid-js'), ...fakeSolid('root'));
// A dependency that ships its own nested copy of solid-js.
const lib = path.join(root, 'node_modules/lib');
await writePackage(
lib,
{ name: 'lib', version: '1.0.0', type: 'module', exports: { '.': './index.js' } },
{ 'index.js': "export { copy as libCopy } from 'solid-js';\n" },
);
await writePackage(path.join(lib, 'node_modules/solid-js'), ...fakeSolid('nested'));
await writeFile(path.join(root, 'package.json'), JSON.stringify({ name: 'app', type: 'module' }));
await writeFile(
path.join(root, 'entry.js'),
"import { copy } from 'solid-js';\nimport { libCopy } from 'lib';\nexport const copies = [copy, libCopy];\n",
);
});

afterAll(async () => {
await rm(root, { recursive: true, force: true });
});

describe('resolve.dedupe', () => {
// #112: dev deduped solid-js, but production builds bundled the nested copy too.
it('bundles a single solid-js copy in production builds', async () => {
const result = (await build({
root,
configFile: false,
logLevel: 'silent',
plugins: [solidPlugin()],
build: {
write: false,
minify: false,
rollupOptions: { input: path.join(root, 'entry.js'), preserveEntrySignatures: 'strict' },
},
})) as Rollup.RollupOutput | Rollup.RollupOutput[];
const output = Array.isArray(result) ? result[0] : result;
const code = output.output
.filter((chunk): chunk is Rollup.OutputChunk => chunk.type === 'chunk')
.map((chunk) => chunk.code)
.join('\n');
expect(code).toContain('"root"');
expect(code).not.toContain('"nested"');
});
});
9 changes: 5 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,9 @@ export default function solidPlugin(options: Partial<Options> = {}): Plugin {
},
});

const solidDeps = ['solid-js', 'solid-js/web', 'solid-js/store', 'solid-js/html', 'solid-js/h'];
// fix for bundling dev in production
const nestedDeps = replaceDev
? ['solid-js', 'solid-js/web', 'solid-js/store', 'solid-js/html', 'solid-js/h']
: [];
const nestedDeps = replaceDev ? solidDeps : [];

const userTest = (userConfig as any).test ?? {};
const test = {} as any;
Expand Down Expand Up @@ -286,7 +285,9 @@ export default function solidPlugin(options: Partial<Options> = {}): Plugin {
...(replaceDev ? ['development'] : []),
...(userConfig.mode === 'test' && !options.ssr ? ['browser'] : []),
],
dedupe: nestedDeps,
// Dedupe in builds too. A dependency that ships its own nested
// solid-js would otherwise add a second runtime to the bundle. #112
dedupe: solidDeps,
alias: [{ find: /^solid-refresh$/, replacement: runtimePublicPath }],
},
optimizeDeps: {
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"src", "cypress"
],
"exclude": [
"**/*.spec.ts"
"**/*.spec.ts", "**/*.test.ts"
],
"compilerOptions": {
"target": "ESNext",
Expand Down
10 changes: 10 additions & 0 deletions vitest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { defineConfig } from 'vitest/config';

// The plugin's own unit tests (`pnpm test:unit`). The example apps under
// examples/* carry their own vitest configs and run through `pnpm test`.
export default defineConfig({
test: {
include: ['src/**/*.test.ts'],
environment: 'node',
},
});
Loading