diff --git a/docs/en/plugins/vite-plugin-external/usage.md b/docs/en/plugins/vite-plugin-external/usage.md index f1f1942..b9764ec 100644 --- a/docs/en/plugins/vite-plugin-external/usage.md +++ b/docs/en/plugins/vite-plugin-external/usage.md @@ -57,6 +57,34 @@ export default defineConfig({ }); ``` +## Dynamic Configuration of ESM format CDN + +> Replace specific CDN links with dynamic configuration, and inject `modulepreload` links into the HTML header. + +```js +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vite'; +import pluginExternal from 'vite-plugin-external'; + +export default defineConfig({ + plugins: [ + react({ + jsxRuntime: 'classic' + }), + pluginExternal({ + externals(libName) { + if (libName === 'react') { + return 'https://esm.sh/react@18.3.1'; + } + if (libName === 'react-dom/client') { + return 'https://esm.sh/react-dom@18.3.1'; + } + } + }) + ] +}); +``` + ## Multi-Environment Configuration > Sometimes development and production environments use different CDNs. You can configure `development` and `production` modes for respective external dependencies. diff --git a/docs/zh/plugins/vite-plugin-external/usage.md b/docs/zh/plugins/vite-plugin-external/usage.md index e605d44..12565fc 100644 --- a/docs/zh/plugins/vite-plugin-external/usage.md +++ b/docs/zh/plugins/vite-plugin-external/usage.md @@ -59,6 +59,34 @@ export default defineConfig({ }); ``` +## 动态配置 ESM 格式 CDN + +> 将指定的依赖替换为 CDN 资源, 并在 `index.html` 中添加 `modulepreload` 链接标签 + +```js +import react from '@vitejs/plugin-react'; +import { defineConfig } from 'vite'; +import pluginExternal from 'vite-plugin-external'; + +export default defineConfig({ + plugins: [ + react({ + jsxRuntime: 'classic' + }), + pluginExternal({ + externals(libName) { + if (libName === 'react') { + return 'https://esm.sh/react@18.3.1'; + } + if (libName === 'react-dom/client') { + return 'https://esm.sh/react-dom@18.3.1'; + } + } + }) + ] +}); +``` + ## 多模式场景配置 > 有时候可能开发环境和生产环境用到的 cdn 不一致。针对这种情况,可以配置 `development` 和 `production` 两个模式,分别对应开发环境和生产环境的外部依赖。 diff --git a/examples/config/vite.external.10.mts b/examples/config/vite.external.10.mts index f2a35a8..5c765be 100644 --- a/examples/config/vite.external.10.mts +++ b/examples/config/vite.external.10.mts @@ -10,15 +10,18 @@ export default defineConfig({ }) as unknown as Plugin, vitePluginExternal({ logLevel: 'TRACE', - // interop: 'auto', - externals(libName) { - if (libName === 'react') { - return 'https://esm.sh/react@18.3.1'; - } - if (libName === 'react-dom/client') { - return 'https://esm.sh/react-dom@18.3.1'; - } + externals: { + react: 'https://esm.sh/react@18.3.1', + 'react-dom/client': 'https://esm.sh/react-dom@18.3.1' } + // externals(libName) { + // if (libName === 'react') { + // return 'https://esm.sh/react@18.3.1'; + // } + // if (libName === 'react-dom/client') { + // return 'https://esm.sh/react-dom@18.3.1'; + // } + // } }) ], // resolve: { diff --git a/examples/vite3-demo/index5.html b/examples/vite3-demo/index5.html new file mode 100644 index 0000000..cca8bfe --- /dev/null +++ b/examples/vite3-demo/index5.html @@ -0,0 +1,13 @@ + + + + + + Vite + React + + + +
+ + + diff --git a/examples/vite3-demo/package.json b/examples/vite3-demo/package.json index 9670a54..b9f8dab 100644 --- a/examples/vite3-demo/package.json +++ b/examples/vite3-demo/package.json @@ -20,6 +20,7 @@ "build:external.7": "vite build --config=../config/vite.external.7.mts", "build:external.8": "vite build --config=../config/vite.external.8.mts", "build:external.9": "vite build --config=../config/vite.external.9.mts", + "build:external.10": "vite build --config=../config/vite.external.10.mts", "build:hook-use.1": "vite build --config=../config/vite.hook-use.1.mts", "build:include-css.1": "vite build --config=../config/vite.include-css.1.mts", "build:mock-data.1": "vite build --config=../config/vite.mock-data.1.mts", @@ -34,6 +35,7 @@ "dev:external.6": "vite dev --config=../config/vite.external.6.mts", "dev:external.7": "vite dev --config=../config/vite.external.7.mts", "dev:external.8": "vite dev --config=../config/vite.external.8.mts", + "dev:external.10": "vite dev --config=../config/vite.external.10.mts", "dev:mock-data.1": "vite dev --config=../config/vite.mock-data.1.mts", "dev:mock-data.2": "vite dev --config=../config/vite.mock-data.2.mts", "dev:mock-data.3": "vite dev --config=../config/vite.mock-data.3.mts", diff --git a/examples/vite4-demo/index5.html b/examples/vite4-demo/index5.html new file mode 100644 index 0000000..cca8bfe --- /dev/null +++ b/examples/vite4-demo/index5.html @@ -0,0 +1,13 @@ + + + + + + Vite + React + + + +
+ + + diff --git a/examples/vite4-demo/package.json b/examples/vite4-demo/package.json index 9f7641d..aed65e8 100644 --- a/examples/vite4-demo/package.json +++ b/examples/vite4-demo/package.json @@ -20,6 +20,7 @@ "build:external.7": "vite build --config=../config/vite.external.7.mts", "build:external.8": "vite build --config=../config/vite.external.8.mts", "build:external.9": "vite build --config=../config/vite.external.9.mts", + "build:external.10": "vite build --config=../config/vite.external.10.mts", "build:hook-use.1": "vite build --config=../config/vite.hook-use.1.mts", "build:include-css.1": "vite build --config=../config/vite.include-css.1.mts", "build:mock-data.1": "vite build --config=../config/vite.mock-data.1.mts", diff --git a/examples/vite5-demo/index5.html b/examples/vite5-demo/index5.html new file mode 100644 index 0000000..cca8bfe --- /dev/null +++ b/examples/vite5-demo/index5.html @@ -0,0 +1,13 @@ + + + + + + Vite + React + + + +
+ + + diff --git a/examples/vite5-demo/package.json b/examples/vite5-demo/package.json index e2d308b..ddb74dd 100644 --- a/examples/vite5-demo/package.json +++ b/examples/vite5-demo/package.json @@ -20,6 +20,7 @@ "build:external.7": "vite build --config=../config/vite.external.7.mts", "build:external.8": "vite build --config=../config/vite.external.8.mts", "build:external.9": "vite build --config=../config/vite.external.9.mts", + "build:external.10": "vite build --config=../config/vite.external.10.mts", "build:hook-use.1": "vite build --config=../config/vite.hook-use.1.mts", "build:include-css.1": "vite build --config=../config/vite.include-css.1.mts", "build:mock-data.1": "vite build --config=../config/vite.mock-data.1.mts", diff --git a/packages/vite-plugin-external/README.md b/packages/vite-plugin-external/README.md index ba44da9..5ebbdde 100644 --- a/packages/vite-plugin-external/README.md +++ b/packages/vite-plugin-external/README.md @@ -100,6 +100,27 @@ export default defineConfig({ }); ``` +## Changelog + +* **6.2.0** + * Support links to external resources + +* **6.1.0** + * Reimplemented external plugin logic for Vite 6.x compatibility + * Added optional `rollback` parameter to revert to previous implementation + * Added optional `logLevel` parameter to control logging level (values: "TRACE" | "DEBUG" | "INFO" | "WARN" | "ERROR" | "FATAL" | "OFF") + * Support to set `externals` as a function + +* **6.0.0** + * Added optional `externalGlobals` parameter to fix issue [rollup#3188](https://github.com/rollup/rollup/issues/3188) + +* **4.3.1** + * `externalizeDeps` configuration supports regex patterns + +* **4.3.0** + * Previous `mode: false` logic replaced with `interop: 'auto'` + * Added `nodeBuiltins` and `externalizeDeps` configurations for Node module bundling + ## Q&A * Q: Page cannot load after modifying `externals` diff --git a/packages/vite-plugin-external/package.json b/packages/vite-plugin-external/package.json index 07d3e7e..47b4bd3 100644 --- a/packages/vite-plugin-external/package.json +++ b/packages/vite-plugin-external/package.json @@ -1,6 +1,6 @@ { "name": "vite-plugin-external", - "version": "6.2.0", + "version": "6.2.1", "description": "Excludes specified module dependencies from runtime code and built bundles.", "types": "./dist/index.d.ts", "module": "./dist/index.mjs", diff --git a/packages/vite-plugin-external/src/lib/handleExternals.ts b/packages/vite-plugin-external/src/lib/handleExternals.ts index 07cbe1d..55e1269 100644 --- a/packages/vite-plugin-external/src/lib/handleExternals.ts +++ b/packages/vite-plugin-external/src/lib/handleExternals.ts @@ -69,26 +69,28 @@ export function setExternals( const rollupOptions: RollupOptions = getValue(config, 'build.rollupOptions', {}); // externalize dependencies for build command - const { nodeBuiltins, externalizeDeps } = opts; + const { nodeBuiltins, externalizeDeps, command } = opts; - // handle nodejs built-in modules - if (nodeBuiltins) { - let builtinModuleArray; - externalHook.use(builtinModuleArray = builtinModules.map((builtinModule) => { - return new RegExp(`^(?:node:)?${escapeRegex(builtinModule)}(?:/.+)*$`); - })); + if (command === 'build') { + // handle nodejs built-in modules + if (nodeBuiltins) { + let builtinModuleArray; + externalHook.use(builtinModuleArray = builtinModules.map((builtinModule) => { + return new RegExp(`^(?:node:)?${escapeRegex(builtinModule)}(?:/.+)*$`); + })); - logger.debug('Externalize nodejs built-in modules:', builtinModuleArray); - } + logger.debug('Externalize nodejs built-in modules:', builtinModuleArray); + } - // externalize given dependencies - if (externalizeDeps) { - let deps; - externalHook.use(deps = externalizeDeps.map((dep) => { - return types.isRegExp(dep) ? dep : new RegExp(`^${escapeRegex(dep)}(?:/.+)*$`); - })); + // externalize given dependencies + if (externalizeDeps) { + let deps; + externalHook.use(deps = externalizeDeps.map((dep) => { + return types.isRegExp(dep) ? dep : new RegExp(`^${escapeRegex(dep)}(?:/.+)*$`); + })); - logger.debug('Externalize given dependencies:', deps); + logger.debug('Externalize given dependencies:', deps); + } } // Merge external dependencies diff --git a/packages/vp-runtime-helper/package.json b/packages/vp-runtime-helper/package.json index acb0e63..ec4ab25 100644 --- a/packages/vp-runtime-helper/package.json +++ b/packages/vp-runtime-helper/package.json @@ -1,6 +1,6 @@ { "name": "vp-runtime-helper", - "version": "1.0.7", + "version": "1.0.8", "description": "Vite plugin runtime helper.", "types": "./dist/index.d.ts", "module": "./dist/index.mjs", diff --git a/packages/vp-runtime-helper/src/getDepsCacheDir.ts b/packages/vp-runtime-helper/src/depsCache.ts similarity index 53% rename from packages/vp-runtime-helper/src/getDepsCacheDir.ts rename to packages/vp-runtime-helper/src/depsCache.ts index 9a9272c..d302e3c 100644 --- a/packages/vp-runtime-helper/src/getDepsCacheDir.ts +++ b/packages/vp-runtime-helper/src/depsCache.ts @@ -3,7 +3,7 @@ import { resolve as pathResolve } from 'node:path'; import type { ResolvedConfig } from 'vite'; import { normalizePath } from 'vite'; -import { getHash } from './getHash'; +import { getHash } from './hash'; function getDepsCacheSuffix(config: ResolvedConfig, ssr: boolean): string { let suffix = ''; @@ -27,3 +27,30 @@ function getDepsCacheDirPrefix(config: ResolvedConfig): string { export function getDepsCacheDir(config: ResolvedConfig, ssr: boolean): string { return getDepsCacheDirPrefix(config) + getDepsCacheSuffix(config, ssr); } + +const FLATTEN_ID_HASH_LENGTH = 8; +const FLATTEN_ID_MAX_FILE_LENGTH = 170; +const limitFlattenIdLength = ( + id: string, + limit: number = FLATTEN_ID_MAX_FILE_LENGTH, +): string => { + if (id.length <= limit) { + return id; + } + return `${id.slice(0, limit - (FLATTEN_ID_HASH_LENGTH + 1))}_${getHash(id)}`; +}; + +const replaceSlashOrColonRE = /[/:]/g; +const replaceDotRE = /\./g; +const replaceNestedIdRE = /\s*>\s*/g; +const replaceHashRE = /#/g; +export function flattenId(id: string): string { + const flatId = limitFlattenIdLength( + id + .replace(replaceSlashOrColonRE, '_') + .replace(replaceDotRE, '__') + .replace(replaceNestedIdRE, '___') + .replace(replaceHashRE, '____'), + ); + return flatId; +} diff --git a/packages/vp-runtime-helper/src/devServer.ts b/packages/vp-runtime-helper/src/devServer.ts new file mode 100644 index 0000000..e2cc1cb --- /dev/null +++ b/packages/vp-runtime-helper/src/devServer.ts @@ -0,0 +1,21 @@ +import type { PreviewServer, ViteDevServer } from 'vite'; +import { normalizePath } from 'vite'; +export function isDevServer( + server: ViteDevServer | PreviewServer, +): server is ViteDevServer { + return 'pluginContainer' in server; +} + +const VOLUME_RE = /^[A-Z]:/i; +export const FS_PREFIX = '/@fs/'; +export function fsPathFromId(id: string): string { + const fsPath = normalizePath( + id.startsWith(FS_PREFIX) ? id.slice(FS_PREFIX.length) : id, + ); + return fsPath.startsWith('/') || VOLUME_RE.test(fsPath) ? fsPath : `/${fsPath}`; +} + +const postfixRE = /[?#].*$/; +export function cleanUrl(url: string): string { + return url.replace(postfixRE, ''); +} diff --git a/packages/vp-runtime-helper/src/flattenId.ts b/packages/vp-runtime-helper/src/flattenId.ts deleted file mode 100644 index 0f2d026..0000000 --- a/packages/vp-runtime-helper/src/flattenId.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { getHash } from './getHash'; - - -const FLATTEN_ID_HASH_LENGTH = 8; -const FLATTEN_ID_MAX_FILE_LENGTH = 170; -const limitFlattenIdLength = ( - id: string, - limit: number = FLATTEN_ID_MAX_FILE_LENGTH, -): string => { - if (id.length <= limit) { - return id; - } - return `${id.slice(0, limit - (FLATTEN_ID_HASH_LENGTH + 1))}_${getHash(id)}`; -}; - - -const replaceSlashOrColonRE = /[/:]/g; -const replaceDotRE = /\./g; -const replaceNestedIdRE = /\s*>\s*/g; -const replaceHashRE = /#/g; -export function flattenId(id: string): string { - const flatId = limitFlattenIdLength( - id - .replace(replaceSlashOrColonRE, '_') - .replace(replaceDotRE, '__') - .replace(replaceNestedIdRE, '___') - .replace(replaceHashRE, '____'), - ); - return flatId; -} diff --git a/packages/vp-runtime-helper/src/getHash.ts b/packages/vp-runtime-helper/src/hash.ts similarity index 100% rename from packages/vp-runtime-helper/src/getHash.ts rename to packages/vp-runtime-helper/src/hash.ts diff --git a/packages/vp-runtime-helper/src/index.ts b/packages/vp-runtime-helper/src/index.ts index 75f0af6..b6d0432 100644 --- a/packages/vp-runtime-helper/src/index.ts +++ b/packages/vp-runtime-helper/src/index.ts @@ -1,7 +1,7 @@ import { isAbsolute, join } from 'node:path'; import figlet from 'figlet'; -import { normalizePath, PreviewServer, ViteDevServer } from 'vite'; +import { normalizePath } from 'vite'; const escapeRegexRE = /[-/\\^$*+?.()|[\]{}]/g; export function escapeRegex(str: string): string { @@ -21,26 +21,6 @@ export function toAbsolutePath(pth: string, cwd: string): string { return normalizePath(pth); } -const postfixRE = /[?#].*$/; -export function cleanUrl(url: string): string { - return url.replace(postfixRE, ''); -} - -export function isDevServer( - server: ViteDevServer | PreviewServer, -): server is ViteDevServer { - return 'pluginContainer' in server; -} - -const VOLUME_RE = /^[A-Z]:/i; -export const FS_PREFIX = '/@fs/'; -export function fsPathFromId(id: string): string { - const fsPath = normalizePath( - id.startsWith(FS_PREFIX) ? id.slice(FS_PREFIX.length) : id, - ); - return fsPath.startsWith('/') || VOLUME_RE.test(fsPath) ? fsPath : `/${fsPath}`; -} - export function sleep(ms: number): Promise { return new Promise((resolve) => { setTimeout(resolve, ms); @@ -48,11 +28,11 @@ export function sleep(ms: number): Promise { } export * from './colorful'; -export * from './flattenId'; +export * from './depsCache'; +export * from './devServer'; export * from './generateStarter'; -export * from './getDepsCacheDir'; -export * from './getHash'; -export * from './getRuntimeVersion'; export * from './getValue'; +export * from './hash'; export * from './logger'; +export * from './version'; export type { LogLevel } from 'base-log-factory'; diff --git a/packages/vp-runtime-helper/src/getRuntimeVersion.ts b/packages/vp-runtime-helper/src/version.ts similarity index 100% rename from packages/vp-runtime-helper/src/getRuntimeVersion.ts rename to packages/vp-runtime-helper/src/version.ts