diff --git a/package.json b/package.json index a6a6caac..fad87588 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,8 @@ "release": "pnpm build && changeset publish", "check": "package-check", "test": "node scripts/test-examples.ts", - "test:unit": "vitest run" + "test:unit": "vitest run", + "test:ecosystem": "node scripts/test-ecosystem.mjs" }, "repository": { "type": "git", diff --git a/scripts/test-ecosystem.mjs b/scripts/test-ecosystem.mjs new file mode 100644 index 00000000..d50cc117 --- /dev/null +++ b/scripts/test-ecosystem.mjs @@ -0,0 +1,172 @@ +// Headless checks of the built plugin against the Vite version installed in +// this repository. vite-ecosystem-ci runs this script (`pnpm test:ecosystem`) +// after it replaces Vite with the build under test. It needs no browser and +// no network, so it only depends on Vite and the plugin. +import { mkdir, mkdtemp, readFile, readdir, realpath, rm, symlink, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import path from 'node:path'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { build, createLogger, createServer, version } from 'vite'; +import solid from '../dist/esm/index.mjs'; + +const repoRoot = fileURLToPath(new URL('..', import.meta.url)); +const failures = []; +const warnings = []; + +function check(name, ok, detail = '') { + console.log(`${ok ? 'PASS' : 'FAIL'} ${name}${ok || !detail ? '' : `: ${detail}`}`); + if (!ok) failures.push(name); +} + +function logger() { + const base = createLogger('warn', { allowClearScreen: false }); + return { + ...base, + warn(message, options) { + warnings.push(message); + base.warn(message, options); + }, + warnOnce(message, options) { + warnings.push(message); + base.warnOnce(message, options); + }, + }; +} + +async function write(file, content) { + await mkdir(path.dirname(file), { recursive: true }); + await writeFile(file, content); +} + +async function createFixture() { + const root = await realpath(await mkdtemp(path.join(tmpdir(), 'vite-plugin-solid-ecosystem-'))); + await write( + path.join(root, 'package.json'), + JSON.stringify({ + name: 'ecosystem-fixture', + private: true, + type: 'module', + dependencies: { 'solid-js': '*', '@solidjs/web': '*', 'fixture-solid-lib': '*' }, + }), + ); + await mkdir(path.join(root, 'node_modules')); + await mkdir(path.join(root, 'node_modules/@solidjs')); + for (const name of ['solid-js', '@solidjs/web']) { + await symlink( + await realpath(path.join(repoRoot, 'node_modules', name)), + path.join(root, 'node_modules', name), + 'dir', + ); + } + // A library that ships JSX source under the `solid` export condition. + const lib = path.join(root, 'node_modules/fixture-solid-lib'); + await write( + path.join(lib, 'package.json'), + JSON.stringify({ + name: 'fixture-solid-lib', + type: 'module', + exports: { '.': { solid: './index.jsx', default: './index.js' } }, + peerDependencies: { 'solid-js': '*', '@solidjs/web': '*' }, + }), + ); + await write(path.join(lib, 'index.jsx'), 'export const Badge = (props) => {props.children};\n'); + await write(path.join(lib, 'index.js'), 'throw new Error("the solid condition was not used");\n'); + + await write( + path.join(root, 'index.html'), + '
\n', + ); + await write( + path.join(root, 'src/App.tsx'), + [ + "import { createSignal } from 'solid-js';", + "import { Badge } from 'fixture-solid-lib';", + 'export default function App() {', + ' const [count, setCount] = createSignal(0);', + ' return