diff --git a/package.json b/package.json index ab907a9..569b55e 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "@sxzz/prettier-config": "^2.0.2", "@types/node": "^22.8.1", "bumpp": "^9.7.1", + "esbuild": "^0.24.0", "eslint": "^9.13.0", "prettier": "^3.3.3", "rollup": "^4.24.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 63a8fa6..51d5f2d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,6 +36,9 @@ importers: bumpp: specifier: ^9.7.1 version: 9.7.1 + esbuild: + specifier: ^0.24.0 + version: 0.24.0 eslint: specifier: ^9.13.0 version: 9.13.0(jiti@1.21.6) diff --git a/src/index.ts b/src/index.ts index 07b9862..022e396 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ -import path from 'node:path' +import path, { dirname } from 'node:path' import { createFilter, normalizePath } from '@rollup/pluginutils' import glob from 'fast-glob' -import { createUnplugin } from 'unplugin' +import { createUnplugin, type UnpluginOptions } from 'unplugin' import { ID_PREFIX } from './core/constants' import { writeTypeDeclaration } from './core/dts' import { resolveOption, type Options } from './core/options' @@ -17,42 +17,48 @@ export default createUnplugin((options = {}) => { const filter = createFilter(opt.include, opt.exclude) const map: GlobMap = {} - return { - name, + const resolveId = (id: string, src: string | undefined) => { + if (!src || !filter(src)) return - resolveId(id, src) { - if (!id.startsWith(ID_PREFIX)) return - if (!src || !filter(src)) return + const [name, pattern] = id.replace(ID_PREFIX, '').split(':', 2) + return `${ID_PREFIX}${name}:${src.replaceAll( + ':', + DRIVER_DIVIDER, + )}:${pattern}` + } - const [name, pattern] = id.replace(ID_PREFIX, '').split(':', 2) - return `${ID_PREFIX}${name}:${src.replaceAll( - ':', - DRIVER_DIVIDER, - )}:${pattern}` - }, + const load = async (id: string) => { + const [name, src, pattern] = id.replace(ID_PREFIX, '').split(':', 3) + const filename = src.replaceAll(DRIVER_DIVIDER_REGEXP, ':') - async load(id) { - if (!id.startsWith(ID_PREFIX)) return + const files = ( + await glob(pattern, { + cwd: filename ? path.dirname(filename) : opt.root, + absolute: true, + }) + ) + .map((file) => normalizePath(file)) + .filter((file) => file !== normalizePath(filename)) + .sort() + map[`${name}:${pattern}`] = files - const [name, src, pattern] = id.replace(ID_PREFIX, '').split(':', 3) - const filename = src.replaceAll(DRIVER_DIVIDER_REGEXP, ':') + const contents = files.map((file) => `export * from '${file}'`).join('\n') - const files = ( - await glob(pattern, { - cwd: filename ? path.dirname(filename) : opt.root, - absolute: true, - }) - ) - .map((file) => normalizePath(file)) - .filter((file) => file !== normalizePath(filename)) - .sort() - map[`${name}:${pattern}`] = files + if (opt.dts) await writeTypeDeclaration(map, opt.dts) - const contents = files.map((file) => `export * from '${file}'`).join('\n') + return `${contents}\n` + } - if (opt.dts) await writeTypeDeclaration(map, opt.dts) + const context: UnpluginOptions = { + name, - return `${contents}\n` + resolveId(id, importer) { + if (!id.startsWith(ID_PREFIX)) return + return resolveId(id, importer) + }, + load(id) { + if (!id.startsWith(ID_PREFIX)) return + return load(id) }, vite: { @@ -60,5 +66,32 @@ export default createUnplugin((options = {}) => { opt.root = config.root }, }, + + esbuild: { + setup(build) { + build.onResolve( + { filter: new RegExp(`^${ID_PREFIX}`) }, + ({ path, importer }) => { + const id = resolveId(path, importer) + if (id) return { path: id, namespace: name } + }, + ) + + build.onLoad( + { filter: new RegExp(`^${ID_PREFIX}`), namespace: name }, + async ({ path }) => { + let [, src] = path.replace(ID_PREFIX, '').split(':', 2) + src = src.replaceAll(DRIVER_DIVIDER_REGEXP, ':') + + return { + contents: await load(path), + resolveDir: dirname(src), + } + }, + ) + }, + }, } + + return context }) diff --git a/tests/__snapshots__/resolve.test.ts.snap b/tests/__snapshots__/resolve.test.ts.snap index e648d11..5fe289f 100644 --- a/tests/__snapshots__/resolve.test.ts.snap +++ b/tests/__snapshots__/resolve.test.ts.snap @@ -1,6 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`resolve > basic 1`] = ` +exports[`resolve > Rollup: basic 1`] = ` "const a = "a"; const foo = "bar"; @@ -11,7 +11,7 @@ export { a, foo }; " `; -exports[`resolve > exclude-self 1`] = ` +exports[`resolve > Rollup: exclude-self 1`] = ` "const a = "a"; const foo = "bar"; @@ -20,7 +20,7 @@ export { a, foo }; " `; -exports[`resolve > import-all 1`] = ` +exports[`resolve > Rollup: import-all 1`] = ` "const a = "a"; const foo = "bar"; @@ -32,7 +32,7 @@ export { a, foo }; " `; -exports[`resolve > ts 1`] = ` +exports[`resolve > Rollup: ts 1`] = ` "const a = "a"; const foo = "bar"; @@ -40,3 +40,51 @@ const foo = "bar"; export { a, foo }; " `; + +exports[`resolve > esbuild: basic 1`] = ` +"(() => { + // tests/fixtures/mod/a.ts + var a = "a"; + + // tests/fixtures/mod/b.ts + var foo = "bar"; +})(); +" +`; + +exports[`resolve > esbuild: exclude-self 1`] = ` +"(() => { + // tests/fixtures/mod/a.ts + var a = "a"; + + // tests/fixtures/mod/b.ts + var foo = "bar"; +})(); +" +`; + +exports[`resolve > esbuild: import-all 1`] = ` +"(() => { + // tests/fixtures/mod/a.ts + var a = "a"; + + // tests/fixtures/mod/b.ts + var foo = "bar"; + + // tests/fixtures/import-all.ts + console.log(a); + console.log(foo); +})(); +" +`; + +exports[`resolve > esbuild: ts 1`] = ` +"(() => { + // tests/fixtures/mod/a.ts + var a = "a"; + + // tests/fixtures/mod/b.ts + var foo = "bar"; +})(); +" +`; diff --git a/tests/resolve.test.ts b/tests/resolve.test.ts index 12fc158..a47583a 100644 --- a/tests/resolve.test.ts +++ b/tests/resolve.test.ts @@ -1,9 +1,11 @@ import path from 'node:path' +import { build } from 'esbuild' import glob from 'fast-glob' import { rollup } from 'rollup' import Esbuild from 'rollup-plugin-esbuild' import { describe, expect, test } from 'vitest' -import Plugin from '../src/rollup' +import EsBuildPlugin from '../src/esbuild' +import RollupPlugin from '../src/rollup' describe('resolve', async () => { const fixtures = await glob(['./fixtures/*.{js,ts}', '!**/*.d.ts'], { @@ -14,12 +16,12 @@ describe('resolve', async () => { const ext = path.extname(fixture) const filename = path.basename(fixture, ext) - test(filename, async () => { + test(`Rollup: ${filename}`, async () => { const bundle = await rollup({ input: fixture, treeshake: false, plugins: [ - Plugin({ + RollupPlugin({ dts: path.resolve(path.dirname(fixture), `${filename}-glob`), }), Esbuild(), @@ -28,5 +30,21 @@ describe('resolve', async () => { const { output } = await bundle.generate({}) expect(output[0].code).toMatchSnapshot() }) + + test(`esbuild: ${filename}`, async () => { + const bundle = await build({ + entryPoints: [fixture], + treeShaking: false, + bundle: true, + write: false, + plugins: [ + EsBuildPlugin({ + dts: path.resolve(path.dirname(fixture), `${filename}-glob`), + }), + ], + }) + const { outputFiles } = bundle + expect(outputFiles[0].text).toMatchSnapshot() + }) } })