diff --git a/__tests__/fixtures/cdn-plugin/index.html b/__tests__/fixtures/cdn-plugin/index.html new file mode 100644 index 0000000..44299c3 --- /dev/null +++ b/__tests__/fixtures/cdn-plugin/index.html @@ -0,0 +1,12 @@ + + + + + + Document + + +
+ + + \ No newline at end of file diff --git a/__tests__/fixtures/cdn-plugin/main.js b/__tests__/fixtures/cdn-plugin/main.js new file mode 100644 index 0000000..fc76027 --- /dev/null +++ b/__tests__/fixtures/cdn-plugin/main.js @@ -0,0 +1,15 @@ +/* eslint-disable */ +import { createApp, createVNode, defineComponent } from 'vue' + +const App = defineComponent({ + name: 'Application', + setup() { + return () => createVNode('div', null, 'hello world') + } +}) + +createApp(App).mount('#app') + +export * from 'vue' +const version = 'cdn-plugin-test-version' +export { version } diff --git a/__tests__/fixtures/external-plugin/button.js b/__tests__/fixtures/external-plugin/button.js new file mode 100644 index 0000000..a4ccdee --- /dev/null +++ b/__tests__/fixtures/external-plugin/button.js @@ -0,0 +1,8 @@ +import { createVNode, defineComponent } from 'vue' + +export default defineComponent({ + name: 'Button', + setup(_, { slots }) { + return () => createVNode('button', null, slots) + } +}) diff --git a/__tests__/fixtures/external-plugin/main.js b/__tests__/fixtures/external-plugin/main.js new file mode 100644 index 0000000..ac1bebf --- /dev/null +++ b/__tests__/fixtures/external-plugin/main.js @@ -0,0 +1,11 @@ +import _Button from './button' + +function withInstall(component) { + component.install = (app) => { + app.component(component.name, component) + } +} + +const Button = withInstall(_Button) + +export { Button } diff --git a/__tests__/plugin.spec.ts b/__tests__/plugin.spec.ts new file mode 100644 index 0000000..fbc78e3 --- /dev/null +++ b/__tests__/plugin.spec.ts @@ -0,0 +1,46 @@ +import path from 'path' +import test from 'ava' +import { build } from 'vite' +import { cdn, external } from '../dist' + +const defaultWd = __dirname + +const fixturePath = path.join(defaultWd, 'fixtures') + +test('exteranl plugin', async (t) => { + const bundle = await build({ + plugins: [external({ include: /\.(mjs|js|ts|vue|jsx|tsx)(\?.*|)$/, modules: [{ name: 'vue', global: 'Vue' }] })], + logLevel: 'silent', + build: { + lib: { + entry: path.join(fixturePath, 'external-plugin', 'main.js'), + formats: ['es'] + }, + write: false + } + }) + + const { code } = bundle[0].output[0] + const global = /Vue(.)\w+/g + const [s, s1] = code.match(global) + t.is(s, 'Vue.defineComponent') + t.is(s1, 'Vue.createVNode') +}) + +test('cdn plugin', async (t) => { + const bundle = await build({ + plugins: [cdn({ modules: [{ name: 'vue' }] })], + logLevel: 'silent', + root: path.join(fixturePath, 'cdn-plugin'), + build: { + write: false + } + }) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignored + const { output } = bundle + const chunk = output[0] + const html = output[1] + t.is(/Vue/.test(chunk.code), true) + t.is(/https:\/\/cdn\.jsdelivr\.net\/npm\/vue/.test(html.source), true) +}) diff --git a/package.json b/package.json index 0b22c6a..699b140 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ }, "devDependencies": { "@esbuild-kit/cjs-loader": "^2.4.2", + "@nolyfill/es-aggregate-error": "^1.0.20", "@rollup/plugin-json": "^6.0.0", "@types/babel__core": "^7.20.1", "@types/debug": "^4.1.8", @@ -74,8 +75,7 @@ "tsup": "^7.1.0", "typescript": "^4.8.3", "vite": "^3.1.3", - "vue": "^3.3.4", - "@nolyfill/es-aggregate-error": "^1.0.20" + "vue": "^3.3.4" }, "dependencies": { "@babel/core": "^7.22.5", diff --git a/src/code-gen.ts b/src/code-gen.ts index bced1a6..941fae5 100644 --- a/src/code-gen.ts +++ b/src/code-gen.ts @@ -17,7 +17,7 @@ export class CodeGen { this.aliasesToDependencies = new Map() this.dependencies.forEach(({ name, aliases }) => { this.aliasesToDependencies.set(name, name) - if (len(aliases)) { + if (Array.isArray(aliases) && len(aliases)) { aliases.forEach((aliase) => this.aliasesToDependencies.set(aliase, name)) } }) @@ -316,7 +316,6 @@ export async function tryScanGlobalName(code: string) { const ast = await babelParse(code, { babelrc: false, configFile: false }) const { body } = ast.program if (!len(body)) return - // It's enough to extract only the first node const node = body[0] // iife only return the first // eslint-disable-next-line @typescript-eslint/ban-ts-comment diff --git a/src/index.ts b/src/index.ts index d569cba..7246835 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import { createFilter } from '@rollup/pluginutils' import type { Plugin } from 'vite' import _debug from 'debug' -import { createScanner, getPackageExports } from './scanner' +import { createScanner, getPackageExports, serializationExportsFields } from './scanner' import { createInjectScript } from './inject' import { createCodeGenerator } from './code-gen' import { isSupportThreads, transformCJSRequire } from './shared' @@ -102,7 +102,7 @@ function external(opts: ExternalPluginOptions = {}): Plugin { const defaultWd = process.cwd() const dependencies = await Promise.all(modules.map(async (module) => { const exports = await getPackageExports(module, defaultWd) - return { bindings: exports, ...module } + return { bindings: exports, ...module, aliases: serializationExportsFields(module.name, module.aliases) } })) as ModuleInfo[] const deps = new Map(dependencies.map(dep => [dep.name, dep])) debug('scanning done', deps) @@ -131,4 +131,4 @@ export { cdn, external } export default cdn -export type { InjectVisitor, TrackModule, CDNPluginOptions, ExternalPluginOptions } from './interface' +export type { InjectVisitor, TrackModule, CDNPluginOptions, ExternalPluginOptions, ExternalModule } from './interface' diff --git a/src/interface.ts b/src/interface.ts index de0cef5..77bb392 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -29,6 +29,10 @@ export interface IModule extends TrackModule { resolve?: string | ResolverFunction } +export type ExternalModule = Required & { + aliases?: Array +} + export interface Serialization { url?: Set type?: string @@ -97,7 +101,7 @@ export type CDNPluginOptions = Pretty<{ }> export type ExternalPluginOptions = Pretty<{ - modules?: Array> + modules?: Array include?: FilterPattern exclude?: FilterPattern }> diff --git a/src/scanner.ts b/src/scanner.ts index 1efa44f..1547fb4 100644 --- a/src/scanner.ts +++ b/src/scanner.ts @@ -79,7 +79,7 @@ function createWorkerThreads(scannerModule: ScannerModule, defaultWd: string) { } -function serializationExportsFields(moduleName: string, aliases = []) { +export function serializationExportsFields(moduleName: string, aliases = []) { return aliases.filter(v => v !== '.').map(v => path.posix.join(moduleName, v)) }