[Vite]Vite-legacy插件原理了解

[Vite]Vite-legacy插件原理了解

作用

  1. 检测旧浏览器:插件需要能够检测到用户的浏览器是否需要转换代码。这通常是通过用户代理字符串来实现的。
  2. 代码转换:对于需要支持的旧浏览器,插件会使用Babel等工具将ES6+代码转换为ES5代码。
  3. Polyfills注入:为了支持旧浏览器中不存在的功能,插件会注入必要的polyfills。
  4. 配置调整:插件可能需要调整Vite的配置,例如修改入口文件,以便为旧浏览器生成额外的构建版本。
  5. 条件加载:在构建过程中,插件会生成多个版本的资源,包括一个为现代浏览器优化的版本和一个为旧浏览器优化的版本。在服务端,它将根据用户的浏览器类型来提供相应的资源。
  6. 服务端渲染:在某些情况下,插件可能还会涉及到服务端渲染(SSR),以进一步优化旧浏览器的兼容性。

为什么它会增加大量的打包时间?

  1. 额外的转换步骤:插件需要将现代JavaScript代码转换为ES5,这需要额外的处理时间。Babel等工具在转换过程中需要解析、转换和生成代码。
  2. 多版本构建:为了支持旧浏览器,插件需要生成额外的构建版本。这意味着同样的代码可能需要被打包多次,一次为现代浏览器,一次或多次为旧浏览器。
  3. Polyfills的加载:插件需要加载额外的polyfills来支持旧浏览器中缺失的功能,这会增加最终打包文件的大小,并且可能需要额外的时间来解析和应用这些polyfills。
  4. 条件加载逻辑:服务端需要根据用户的浏览器类型来决定加载哪个版本的资源,这可能涉及到额外的逻辑处理,从而增加服务端的响应时间。
  5. 资源分割:为了优化性能,现代前端构建工具通常会对代码进行分割。但是,如果plugin-legacy需要为旧浏览器生成额外的资源,这可能会导致更多的分割和更多的处理时间。
  6. 依赖管理:如果插件需要处理或解决依赖之间的兼容性问题,这也会增加处理时间。
  7. 构建缓存:Vite的快速开发体验部分依赖于其构建缓存。但是,如果plugin-legacy引入了额外的转换步骤,可能会影响缓存的效率。
  8. 服务端渲染(SSR):如果插件涉及到SSR,这可能会增加额外的计算和渲染时间。
  9. 测试和验证:为了保证转换后的代码在旧浏览器中正确运行,插件可能需要进行额外的测试和验证,这也会增加时间。

源码地址

https://github.com/vitejs/vite/blob/main/packages/plugin-legacy/src/index.ts

解读:

这段代码是 Vite 的 viteLegacyPlugin 插件的实现部分,它用于支持旧版浏览器。下面是代码的主要组成部分和功能的解释:

  1. 导入依赖:代码开始部分导入了所需的 Node.js 内置模块和第三方库,如pathcryptomoduleurlmagic-string等,以及 Vite 和 Rollup 的类型定义。

  2. 加载 BabelloadBabel 函数用于按需加载 Babel,以避免在开发过程中使用时的额外负担。

  3. Browserslist 配置:使用 browserslist 来确定目标浏览器,并据此决定需要包含哪些 polyfills。

  4. 插件选项Options 类型定义了插件的配置选项,如targetsmodernTargetspolyfills等。

  5. 生成插件数组viteLegacyPlugin 函数返回一个插件数组,这些插件在 Vite 构建过程中执行不同的任务。

  6. 配置插件legacyConfigPlugin 插件在 Vite 配置阶段运行,它可能修改 Vite 的构建目标和定义环境变量。

  7. 生成 Bundle 插件legacyGenerateBundlePlugin 在 Vite 生成 Bundle 时运行,负责处理 polyfills 的生成和注入。

  8. 后处理插件legacyPostPlugin 是一个 ‘post’ 插件,它在其他插件运行后执行,负责处理 JavaScript 代码的转换,注入 polyfills,修改 HTML 以适应旧版浏览器。

  9. 检测 PolyfillsdetectPolyfills 函数使用 Babel 来分析代码,并确定需要哪些 polyfills。

  10. 创建 Babel 预设选项createBabelPresetEnvOptions 函数创建了 Babel @babel/preset-env 插件的配置选项。

  11. 构建 Polyfill ChunkbuildPolyfillChunk 函数使用 Rollup 来构建包含所有所需 polyfills 的单独 chunk。

  12. Polyfills 插件polyfillsPlugin 提供了一个 Rollup 插件,用于在构建过程中包含 polyfills。

  13. 识别旧版 Chunk 和 BundleisLegacyChunkisLegacyBundle 函数用于识别是否正在处理旧版浏览器的代码。

  14. Babel 插件recordAndRemovePolyfillBabelPluginreplaceLegacyEnvBabelPluginwrapIIFEBabelPlugin 是用于处理代码的 Babel 插件,它们分别用于记录 polyfill 导入、替换环境变量和包裹 IIFE。

  15. CSP 哈希cspHashes 用于内容安全策略,包含了一些内联脚本的哈希值。

这个插件的目的是创建一个兼容旧版浏览器的构建版本,同时保持对现代浏览器的优化。它通过条件加载 polyfills 和使用 SystemJS 作为模块加载器来实现这一点。代码中使用了一些技巧,如动态导入 polyfills、修改文件名来区分现代和旧版构建,以及在 HTML 中注入额外的脚本标签。

/* eslint-disable n/no-extraneous-import */
import path from 'node:path'
import { createHash } from 'node:crypto'
import { createRequire } from 'node:module'
import { fileURLToPath } from 'node:url'
import { build, normalizePath } from 'vite'
import MagicString from 'magic-string'
import type {BuildOptions,HtmlTagDescriptor,Plugin,ResolvedConfig,
} from 'vite'
import type {NormalizedOutputOptions,OutputBundle,OutputOptions,PreRenderedChunk,RenderedChunk,
} from 'rollup'
import type {PluginItem as BabelPlugin,types as BabelTypes,
} from '@babel/core'
import colors from 'picocolors'
import browserslist from 'browserslist'
import type { Options } from './types'
import {detectModernBrowserCode,dynamicFallbackInlineCode,legacyEntryId,legacyPolyfillId,modernChunkLegacyGuard,safari10NoModuleFix,systemJSInlineCode,
} from './snippets'// lazy load babel since it's not used during dev
let babel: typeof import('@babel/core') | undefined
async function loadBabel() {if (!babel) {babel = await import('@babel/core')}return babel
}// The requested module 'browserslist' is a CommonJS module
// which may not support all module.exports as named exports
const { loadConfig: browserslistLoadConfig } = browserslist// Duplicated from build.ts in Vite Core, at least while the feature is experimental
// We should later expose this helper for other plugins to use
function toOutputFilePathInHtml(filename: string,type: 'asset' | 'public',hostId: string,hostType: 'js' | 'css' | 'html',config: ResolvedConfig,toRelative: (filename: string, importer: string) => string,
): string {const { renderBuiltUrl } = config.experimentallet relative = config.base === '' || config.base === './'if (renderBuiltUrl) {const result = renderBuiltUrl(filename, {hostId,hostType,type,ssr: !!config.build.ssr,})if (typeof result === 'object') {if (result.runtime) {throw new Error(`{ runtime: "${result.runtime}" } is not supported for assets in ${hostType} files: ${filename}`,)}if (typeof result.relative === 'boolean') {relative = result.relative}} else if (result) {return result}}if (relative && !config.build.ssr) {return toRelative(filename, hostId)} else {return config.base + filename}
}
function getBaseInHTML(urlRelativePath: string, config: ResolvedConfig) {// Prefer explicit URL if defined for linking to assets and public files from HTML,// even when base relative is specifiedreturn config.base === './' || config.base === ''? path.posix.join(path.posix.relative(urlRelativePath, '').slice(0, -2),'./',): config.base
}function toAssetPathFromHtml(filename: string,htmlPath: string,config: ResolvedConfig,
): string {const relativeUrlPath = normalizePath(path.relative(config.root, htmlPath))const toRelative = (filename: string, hostId: string) =>getBaseInHTML(relativeUrlPath, config) + filenamereturn toOutputFilePathInHtml(filename,'asset',htmlPath,'html',config,toRelative,)
}const legacyEnvVarMarker = `__VITE_IS_LEGACY__`const _require = createRequire(import.meta.url)const nonLeadingHashInFileNameRE = /[^/]+\[hash(?::\d+)?\]/
const prefixedHashInFileNameRE = /\W?\[hash(:\d+)?\]/function viteLegacyPlugin(options: Options = {}): Plugin[] {let config: ResolvedConfiglet targets: Options['targets']let modernTargets: Options['modernTargets']// browsers supporting ESM + dynamic import + import.meta + async generatorconst modernTargetsEsbuild = ['es2020','edge79','firefox67','chrome64','safari12',]// same with above but by browserslist syntax// es2020 = chrome 80+, safari 13.1+, firefox 72+, edge 80+// https://github.com/evanw/esbuild/issues/121#issuecomment-646956379const modernTargetsBabel ='edge>=79, firefox>=67, chrome>=64, safari>=12, chromeAndroid>=64, iOS>=12'const genLegacy = options.renderLegacyChunks !== falseconst genModern = options.renderModernChunks !== falseif (!genLegacy && !genModern) {throw new Error('`renderLegacyChunks` and `renderModernChunks` cannot be both false',)}const debugFlags = (process.env.DEBUG || '').split(',')const isDebug =debugFlags.includes('vite:*') || debugFlags.includes('vite:legacy')const facadeToLegacyChunkMap = new Map()const facadeToLegacyPolyfillMap = new Map()const facadeToModernPolyfillMap = new Map()const modernPolyfills = new Set<string>()const legacyPolyfills = new Set<string>()// When discovering polyfills in `renderChunk`, the hook may be non-deterministic, so we group the// modern and legacy polyfills in a sorted chunks map for each rendered outputs before merging them.const outputToChunkFileNameToPolyfills = new WeakMap<NormalizedOutputOptions,Map<string, { modern: Set<string>; legacy: Set<string> }> | null>()if (Array.isArray(options.modernPolyfills) && genModern) {options.modernPolyfills.forEach((i) => {modernPolyfills.add(i.includes('/') ? `core-js/${i}` : `core-js/modules/${i}.js`,)})}if (Array.isArray(options.additionalModernPolyfills)) {options.additionalModernPolyfills.forEach((i) => {modernPolyfills.add(i)})}if (Array.isArray(options.polyfills)) {options.polyfills.forEach((i) => {if (i.startsWith(`regenerator`)) {legacyPolyfills.add(`regenerator-runtime/runtime.js`)} else {legacyPolyfills.add(i.includes('/') ? `core-js/${i}` : `core-js/modules/${i}.js`,)}})}if (Array.isArray(options.additionalLegacyPolyfills)) {options.additionalLegacyPolyfills.forEach((i) => {legacyPolyfills.add(i)})}let overriddenBuildTarget = falselet overriddenDefaultModernTargets = falseconst legacyConfigPlugin: Plugin = {name: 'vite:legacy-config',async config(config, env) {if (env.command === 'build' && !config.build?.ssr) {if (!config.build) {config.build = {}}if (!config.build.cssTarget) {// Hint for esbuild that we are targeting legacy browsers when minifying CSS.// Full CSS compat table available at https://github.com/evanw/esbuild/blob/78e04680228cf989bdd7d471e02bbc2c8d345dc9/internal/compat/css_table.go// But note that only the `HexRGBA` feature affects the minify outcome.// HSL & rebeccapurple values will be minified away regardless the target.// So targeting `chrome61` suffices to fix the compatibility issue.config.build.cssTarget = 'chrome61'}if (genLegacy) {// Vite's default target browsers are **not** the same.// See https://github.com/vitejs/vite/pull/10052#issuecomment-1242076461overriddenBuildTarget = config.build.target !== undefinedoverriddenDefaultModernTargets = options.modernTargets !== undefinedif (options.modernTargets) {// Package is ESM onlyconst { default: browserslistToEsbuild } = await import('browserslist-to-esbuild')config.build.target = browserslistToEsbuild(options.modernTargets)} else {config.build.target = modernTargetsEsbuild}}}return {define: {'import.meta.env.LEGACY':env.command === 'serve' || config.build?.ssr? false: legacyEnvVarMarker,},}},configResolved(config) {if (overriddenBuildTarget) {config.logger.warn(colors.yellow(`plugin-legacy overrode 'build.target'. You should pass 'targets' as an option to this plugin with the list of legacy browsers to support instead.`,),)}if (overriddenDefaultModernTargets) {config.logger.warn(colors.yellow(`plugin-legacy 'modernTargets' option overrode the builtin targets of modern chunks. Some versions of browsers between legacy and modern may not be supported.`,),)}},}const legacyGenerateBundlePlugin: Plugin = {name: 'vite:legacy-generate-polyfill-chunk',apply: 'build',async generateBundle(opts, bundle) {if (config.build.ssr) {return}const chunkFileNameToPolyfills =outputToChunkFileNameToPolyfills.get(opts)if (chunkFileNameToPolyfills == null) {throw new Error('Internal @vitejs/plugin-legacy error: discovered polyfills should exist',)}if (!isLegacyBundle(bundle, opts)) {// Merge discovered modern polyfills to `modernPolyfills`for (const { modern } of chunkFileNameToPolyfills.values()) {modern.forEach((p) => modernPolyfills.add(p))}if (!modernPolyfills.size) {return}isDebug &&console.log(`[@vitejs/plugin-legacy] modern polyfills:`,modernPolyfills,)const polyfillChunk = await buildPolyfillChunk(config.mode,modernPolyfills,bundle,facadeToModernPolyfillMap,config.build,'es',opts,true,)if (genLegacy && polyfillChunk) {polyfillChunk.code = modernChunkLegacyGuard + polyfillChunk.code}return}if (!genLegacy) {return}// Merge discovered legacy polyfills to `legacyPolyfills`for (const { legacy } of chunkFileNameToPolyfills.values()) {legacy.forEach((p) => legacyPolyfills.add(p))}// legacy bundleif (options.polyfills !== false) {// check if the target needs Promise polyfill because SystemJS relies on it// https://github.com/systemjs/systemjs#ie11-supportawait detectPolyfills(`Promise.resolve(); Promise.all();`,targets,legacyPolyfills,)}if (legacyPolyfills.size || !options.externalSystemJS) {isDebug &&console.log(`[@vitejs/plugin-legacy] legacy polyfills:`,legacyPolyfills,)await buildPolyfillChunk(config.mode,legacyPolyfills,bundle,facadeToLegacyPolyfillMap,// force using terser for legacy polyfill minification, since esbuild// isn't legacy-safeconfig.build,'iife',opts,options.externalSystemJS,)}},}const legacyPostPlugin: Plugin = {name: 'vite:legacy-post-process',enforce: 'post',apply: 'build',renderStart(opts) {// Empty the nested map for this outputoutputToChunkFileNameToPolyfills.set(opts, null)},configResolved(_config) {if (_config.build.lib) {throw new Error('@vitejs/plugin-legacy does not support library mode.')}config = _configmodernTargets = options.modernTargets || modernTargetsBabelisDebug &&console.log(`[@vitejs/plugin-legacy] modernTargets:`, modernTargets)if (!genLegacy || config.build.ssr) {return}targets =options.targets ||browserslistLoadConfig({ path: config.root }) ||'last 2 versions and not dead, > 0.3%, Firefox ESR'isDebug && console.log(`[@vitejs/plugin-legacy] targets:`, targets)const getLegacyOutputFileName = (fileNames:| string| ((chunkInfo: PreRenderedChunk) => string)| undefined,defaultFileName = '[name]-legacy-[hash].js',): string | ((chunkInfo: PreRenderedChunk) => string) => {if (!fileNames) {return path.posix.join(config.build.assetsDir, defaultFileName)}return (chunkInfo) => {let fileName =typeof fileNames === 'function' ? fileNames(chunkInfo) : fileNamesif (fileName.includes('[name]')) {// [name]-[hash].[format] -> [name]-legacy-[hash].[format]fileName = fileName.replace('[name]', '[name]-legacy')} else if (nonLeadingHashInFileNameRE.test(fileName)) {// custom[hash].[format] -> [name]-legacy[hash].[format]// custom-[hash].[format] -> [name]-legacy-[hash].[format]// custom.[hash].[format] -> [name]-legacy.[hash].[format]// custom.[hash:10].[format] -> custom-legacy.[hash:10].[format]fileName = fileName.replace(prefixedHashInFileNameRE, '-legacy$&')} else {// entry.js -> entry-legacy.js// entry.min.js -> entry-legacy.min.jsfileName = fileName.replace(/(.+?)\.(.+)/, '$1-legacy.$2')}return fileName}}const createLegacyOutput = (options: OutputOptions = {},): OutputOptions => {return {...options,format: 'system',entryFileNames: getLegacyOutputFileName(options.entryFileNames),chunkFileNames: getLegacyOutputFileName(options.chunkFileNames),}}const { rollupOptions } = config.buildconst { output } = rollupOptionsif (Array.isArray(output)) {rollupOptions.output = [...output.map(createLegacyOutput),...(genModern ? output : []),]} else {rollupOptions.output = [createLegacyOutput(output),...(genModern ? [output || {}] : []),]}},async renderChunk(raw, chunk, opts, { chunks }) {if (config.build.ssr) {return null}// On first run, intialize the map with sorted chunk file nameslet chunkFileNameToPolyfills = outputToChunkFileNameToPolyfills.get(opts)if (chunkFileNameToPolyfills == null) {chunkFileNameToPolyfills = new Map()for (const fileName in chunks) {chunkFileNameToPolyfills.set(fileName, {modern: new Set(),legacy: new Set(),})}outputToChunkFileNameToPolyfills.set(opts, chunkFileNameToPolyfills)}const polyfillsDiscovered = chunkFileNameToPolyfills.get(chunk.fileName)if (polyfillsDiscovered == null) {throw new Error(`Internal @vitejs/plugin-legacy error: discovered polyfills for ${chunk.fileName} should exist`,)}if (!isLegacyChunk(chunk, opts)) {if (options.modernPolyfills &&!Array.isArray(options.modernPolyfills) &&genModern) {// analyze and record modern polyfillsawait detectPolyfills(raw, modernTargets, polyfillsDiscovered.modern)}const ms = new MagicString(raw)if (genLegacy && chunk.isEntry) {// append this code to avoid modern chunks running on legacy targeted browsersms.prepend(modernChunkLegacyGuard)}if (raw.includes(legacyEnvVarMarker)) {const re = new RegExp(legacyEnvVarMarker, 'g')let matchwhile ((match = re.exec(raw))) {ms.overwrite(match.index,match.index + legacyEnvVarMarker.length,`false`,)}}if (config.build.sourcemap) {return {code: ms.toString(),map: ms.generateMap({ hires: 'boundary' }),}}return {code: ms.toString(),}}if (!genLegacy) {return null}// @ts-expect-error avoid esbuild transform on legacy chunks since it produces// legacy-unsafe code - e.g. rewriting object properties into shorthandsopts.__vite_skip_esbuild__ = true// @ts-expect-error force terser for legacy chunks. This only takes effect if// minification isn't disabled, because that leaves out the terser plugin// entirely.opts.__vite_force_terser__ = true// @ts-expect-error In the `generateBundle` hook,// we'll delete the assets from the legacy bundle to avoid emitting duplicate assets.// But that's still a waste of computing resource.// So we add this flag to avoid emitting the asset in the first place whenever possible.opts.__vite_skip_asset_emit__ = true// avoid emitting assets for legacy bundleconst needPolyfills =options.polyfills !== false && !Array.isArray(options.polyfills)// transform the legacy chunk with @babel/preset-envconst sourceMaps = !!config.build.sourcemapconst babel = await loadBabel()const result = babel.transform(raw, {babelrc: false,configFile: false,compact: !!config.build.minify,sourceMaps,inputSourceMap: undefined, // sourceMaps ? chunk.map : undefined, `.map` TODO: moved to OutputChunk?presets: [// forcing our plugin to run before preset-env by wrapping it in a// preset so we can catch the injected import statements...[() => ({plugins: [recordAndRemovePolyfillBabelPlugin(polyfillsDiscovered.legacy),replaceLegacyEnvBabelPlugin(),wrapIIFEBabelPlugin(),],}),],[(await import('@babel/preset-env')).default,createBabelPresetEnvOptions(targets, { needPolyfills }),],],})if (result) return { code: result.code!, map: result.map }return null},transformIndexHtml(html, { chunk }) {if (config.build.ssr) returnif (!chunk) returnif (chunk.fileName.includes('-legacy')) {// The legacy bundle is built first, and its index.html isn't actually emitted if// modern bundle will be generated. Here we simply record its corresponding legacy chunk.facadeToLegacyChunkMap.set(chunk.facadeModuleId, chunk.fileName)if (genModern) {return}}if (!genModern) {html = html.replace(/<script type="module".*?<\/script>/g, '')}const tags: HtmlTagDescriptor[] = []const htmlFilename = chunk.facadeModuleId?.replace(/\?.*$/, '')// 1. inject modern polyfillsif (genModern) {const modernPolyfillFilename = facadeToModernPolyfillMap.get(chunk.facadeModuleId,)if (modernPolyfillFilename) {tags.push({tag: 'script',attrs: {type: 'module',crossorigin: true,src: toAssetPathFromHtml(modernPolyfillFilename,chunk.facadeModuleId!,config,),},})} else if (modernPolyfills.size) {throw new Error(`No corresponding modern polyfill chunk found for ${htmlFilename}`,)}}if (!genLegacy) {return { html, tags }}// 2. inject Safari 10 nomodule fixif (genModern) {tags.push({tag: 'script',attrs: { nomodule: genModern },children: safari10NoModuleFix,injectTo: 'body',})}// 3. inject legacy polyfillsconst legacyPolyfillFilename = facadeToLegacyPolyfillMap.get(chunk.facadeModuleId,)if (legacyPolyfillFilename) {tags.push({tag: 'script',attrs: {nomodule: genModern,crossorigin: true,id: legacyPolyfillId,src: toAssetPathFromHtml(legacyPolyfillFilename,chunk.facadeModuleId!,config,),},injectTo: 'body',})} else if (legacyPolyfills.size) {throw new Error(`No corresponding legacy polyfill chunk found for ${htmlFilename}`,)}// 4. inject legacy entryconst legacyEntryFilename = facadeToLegacyChunkMap.get(chunk.facadeModuleId,)if (legacyEntryFilename) {// `assets/foo.js` means importing "named register" in SystemJStags.push({tag: 'script',attrs: {nomodule: genModern,crossorigin: true,// we set the entry path on the element as an attribute so that the// script content will stay consistent - which allows using a constant// hash value for CSP.id: legacyEntryId,'data-src': toAssetPathFromHtml(legacyEntryFilename,chunk.facadeModuleId!,config,),},children: systemJSInlineCode,injectTo: 'body',})} else {throw new Error(`No corresponding legacy entry chunk found for ${htmlFilename}`,)}// 5. inject dynamic import fallback entryif (legacyPolyfillFilename && legacyEntryFilename && genModern) {tags.push({tag: 'script',attrs: { type: 'module' },children: detectModernBrowserCode,injectTo: 'head',})tags.push({tag: 'script',attrs: { type: 'module' },children: dynamicFallbackInlineCode,injectTo: 'head',})}return {html,tags,}},generateBundle(opts, bundle) {if (config.build.ssr) {return}if (isLegacyBundle(bundle, opts) && genModern) {// avoid emitting duplicate assetsfor (const name in bundle) {if (bundle[name].type === 'asset' && !/.+\.map$/.test(name)) {delete bundle[name]}}}},}return [legacyConfigPlugin, legacyGenerateBundlePlugin, legacyPostPlugin]
}export async function detectPolyfills(code: string,targets: any,list: Set<string>,
): Promise<void> {const babel = await loadBabel()const result = babel.transform(code, {ast: true,babelrc: false,configFile: false,compact: false,presets: [[(await import('@babel/preset-env')).default,createBabelPresetEnvOptions(targets, {}),],],})for (const node of result!.ast!.program.body) {if (node.type === 'ImportDeclaration') {const source = node.source.valueif (source.startsWith('core-js/') ||source.startsWith('regenerator-runtime/')) {list.add(source)}}}
}function createBabelPresetEnvOptions(targets: any,{ needPolyfills = true }: { needPolyfills?: boolean },
) {return {targets,bugfixes: true,loose: false,modules: false,useBuiltIns: needPolyfills ? 'usage' : false,corejs: needPolyfills? {version: _require('core-js/package.json').version,proposals: false,}: undefined,shippedProposals: true,ignoreBrowserslistConfig: true,}
}async function buildPolyfillChunk(mode: string,imports: Set<string>,bundle: OutputBundle,facadeToChunkMap: Map<string, string>,buildOptions: BuildOptions,format: 'iife' | 'es',rollupOutputOptions: NormalizedOutputOptions,excludeSystemJS?: boolean,
) {let { minify, assetsDir } = buildOptionsminify = minify ? 'terser' : falseconst res = await build({mode,// so that everything is resolved from hereroot: path.dirname(fileURLToPath(import.meta.url)),configFile: false,logLevel: 'error',plugins: [polyfillsPlugin(imports, excludeSystemJS)],build: {write: false,minify,assetsDir,rollupOptions: {input: {polyfills: polyfillId,},output: {format,entryFileNames: rollupOutputOptions.entryFileNames,},},},// Don't run esbuild for transpilation or minification// because we don't want to transpile code.esbuild: false,optimizeDeps: {esbuildOptions: {// If a value above 'es5' is set, esbuild injects helper functions which uses es2015 features.// This limits the input code not to include es2015+ codes.// But core-js is the only dependency which includes commonjs code// and core-js doesn't include es2015+ codes.target: 'es5',},},})const _polyfillChunk = Array.isArray(res) ? res[0] : resif (!('output' in _polyfillChunk)) returnconst polyfillChunk = _polyfillChunk.output[0]// associate the polyfill chunk to every entry chunk so that we can retrieve// the polyfill filename in index html transformfor (const key in bundle) {const chunk = bundle[key]if (chunk.type === 'chunk' && chunk.facadeModuleId) {facadeToChunkMap.set(chunk.facadeModuleId, polyfillChunk.fileName)}}// add the chunk to the bundlebundle[polyfillChunk.fileName] = polyfillChunkreturn polyfillChunk
}const polyfillId = '\0vite/legacy-polyfills'function polyfillsPlugin(imports: Set<string>,excludeSystemJS?: boolean,
): Plugin {return {name: 'vite:legacy-polyfills',resolveId(id) {if (id === polyfillId) {return id}},load(id) {if (id === polyfillId) {return ([...imports].map((i) => `import ${JSON.stringify(i)};`).join('') +(excludeSystemJS ? '' : `import "systemjs/dist/s.min.js";`))}},}
}function isLegacyChunk(chunk: RenderedChunk, options: NormalizedOutputOptions) {return options.format === 'system' && chunk.fileName.includes('-legacy')
}function isLegacyBundle(bundle: OutputBundle,options: NormalizedOutputOptions,
) {if (options.format === 'system') {const entryChunk = Object.values(bundle).find((output) => output.type === 'chunk' && output.isEntry,)return !!entryChunk && entryChunk.fileName.includes('-legacy')}return false
}function recordAndRemovePolyfillBabelPlugin(polyfills: Set<string>,
): BabelPlugin {return ({ types: t }: { types: typeof BabelTypes }): BabelPlugin => ({name: 'vite-remove-polyfill-import',post({ path }) {path.get('body').forEach((p) => {if (t.isImportDeclaration(p.node)) {polyfills.add(p.node.source.value)p.remove()}})},})
}function replaceLegacyEnvBabelPlugin(): BabelPlugin {return ({ types: t }): BabelPlugin => ({name: 'vite-replace-env-legacy',visitor: {Identifier(path) {if (path.node.name === legacyEnvVarMarker) {path.replaceWith(t.booleanLiteral(true))}},},})
}function wrapIIFEBabelPlugin(): BabelPlugin {return ({ types: t, template }): BabelPlugin => {const buildIIFE = template(';(function(){%%body%%})();')return {name: 'vite-wrap-iife',post({ path }) {if (!this.isWrapped) {this.isWrapped = truepath.replaceWith(t.program(buildIIFE({ body: path.node.body })))}},}}
}export const cspHashes = [safari10NoModuleFix,systemJSInlineCode,detectModernBrowserCode,dynamicFallbackInlineCode,
].map((i) => createHash('sha256').update(i).digest('base64'))export type { Options }export default viteLegacyPlugin

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/868031.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android进程调度,线程调度的使用总结

关于android进程调度&#xff0c;线程调度的使用总结 一 java层的调用流程 上层关于调度主要用到的接口(ams,pms,SchedulingPolicyService等)&#xff1a; setThreadScheduler setThreadGroupAndCpuset setThreadGroup setProcessGroup setProcessFrozen 调用到native对应的实…

【项目管理】常见的敏捷实践:Scrum框架

【项目管理】常见的敏捷实践&#xff1a;Scrum框架 精益、敏捷与Scrum框架Scrum框架实践Sprint&#xff08;冲刺&#xff09;Scrum角色Scrum工件Scrum会议 精益、敏捷与Scrum框架 敏捷与精益思想、看板、Scrum等概念的关系如下图所示&#xff1a; Lean 精益 Kanban 看板 Ag…

文件存储的方法一

文章目录 概念介绍实现方法示例代码 我们在上一章回中介绍了"如何实现本地存储"相关的内容&#xff0c;本章回中将介绍如何实现文件存储.闲话休提&#xff0c;让我们一起Talk Flutter吧。 概念介绍 我们在上一章回中介绍的本地存储只能存储dart语言中基本类型的数值…

机器学习训练之使用静态图加速

前言 MindSpore有两种运行模式&#xff1a;动态图模式和静态图模式。默认情况下是动态图模式&#xff0c;也可以手工切换为静态图模式。 动态图模式 动态图的特点是计算图的构建和计算同时发生&#xff0c;符合Python的解释执行方式。在调试模型时较为方便&#xff0c;能够实…

开发者评测|操作系统智能助手OS Copilot

操作系统智能助手OS Copilot 文章目录 操作系统智能助手OS CopilotOS Copilot 是什么优势功能 操作步骤创建实验重置密码创建Access Key配置安全组安装 os-copilot环境变量配置功能评测命令行模式多轮交互模式 OS Copilot 产品体验评测反馈OS Copilot 产品功能评测反馈 参考文档…

做测试/爬虫 selenium 元素定位 谷歌浏览器 插件推荐,提高元素定位效率

注:插件均在谷歌应用商店 下载 1.XPath Helper 插件 作用&#xff1a;用于Html中对目标字段或者属性值进行匹配 快捷启动&#xff1a;ctrl shift x 示例图如下&#xff1a; 2. ChroPath 插件 作用&#xff1a; 提高元素定位效率 启动&#xff1a;谷歌浏览器 按 F12 -&g…

【电商系统开发实用接口指南】包含国内国外多电商平台商品数据对接(附文档)

关于电商数据接口 开发电商系统的朋友对于电商平台API肯定不陌生&#xff0c;API接口即应用程序编程接口&#xff0c;电商平台开放部分API接口&#xff0c;供商家和服务商调用&#xff0c;以满足电商业务管理需求。随着电商市场需求的日益增长以及技术手段的不断成熟&#xf…

【C++第十课 - stack_queue】stack、queue的使用、适配器模型stack、queue和priority_queue的底层实现、deque

目录 一、stack使用1、push2、pop3、empty4、top题目1、最小栈2、栈的压入、弹出序3、逆波兰表达式求值 二、queue的使用priority_queue习题 三、适配器stack的底层实现queue的底层实现priority_queue的底层实现仿函数/函数对象函数指针 四、deque 一、stack使用 stack是个容器…

聚焦大模型应用落地,2024全球数字经济大会人工智能专题论坛在京举办

7月1日下午&#xff0c;2024全球数字经济大会人工智能专题论坛在中关村国家自主创新示范区会议中心举办。论坛紧扣大模型应用落地这一热点&#xff0c;以“应用即未来——大模型赋能千行百业&#xff0c;新生态拥抱产业未来”为主题&#xff0c;备受社会各界关注。 一、北京已…

软考高级第四版备考--第12天(规划质量管理)Plan Quanlity Management

定义&#xff1a;识别项目及其可交付成果的质量要求、标准&#xff0c;并书面描述项目将如何证明符合质量要求、标准的过程。 作用&#xff1a;为整个项目期间如何管理和核实质量提供指南和方向 输入&#xff1a;项目章程&#xff08;项目章程中包含对项目和产品特征的高层级…

b4a开发安卓app

用免费云服务器&#xff0c;三丰云记录安卓开发过程 B4A&#xff08;Basic4Android&#xff09;是一种可用于开发安卓应用的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它允许开发者使用 Basic 语言创建安卓应用。以下是使用 B4A 开发安卓 app 的一般步骤&#xff1a…

Windows中Git的使用(2024最新版)

Windows中Git的使用 获取ssh keys本地绑定邮箱初始化本地仓库添加到本地缓存区提交到本地缓存区切换本地分支为main关联远程分支推送到GitHub查看推送日志 Git 2020年发布了新的默认分支名称"main"&#xff0c;取代了"master"作为主分支的名称。操作有了些…

Python3 OS 文件/目录方法

os 模块提供了非常丰富的方法用来处理文件和目录。常用的方法如下表所示&#xff1a; 序号方法及描述1 os.access(path, mode) 检验权限模式2 os.chdir(path) 改变当前工作目录3 os.chflags(path, flags) 设置路径的标记为数字标记。4 os.chmod(path, mode) 更改权限5 os.…

【已解决】: fatal error: cuda_runtime_api.h: No such file or directory

既然他找不到&#xff0c;我们就把路径给他写清楚&#xff01; 检查自己是不是有这个文件&#xff1a; 去路径/usr/local下&#xff0c;使用命令查询是否拥有该文件&#xff1a; find . -name cuda_runtime_api.h结果&#xff1a; 因为我要使用的是cuda-11.3&#xff0c;因…

ChatGPT:aka是什么意思

ChatGPT&#xff1a;aka是什么意思 /*** The default initial capacity - MUST be a power of two.*/static final int DEFAULT_INITIAL_CAPACITY 1 << 4; // aka 16在这里&#xff0c;“aka” 是英语缩写“also known as”的意思。所以 1 << 4 也就是“也被称为1…

Spring Boot与HashiCorp Vault的集成

Spring Boot与HashiCorp Vault的集成 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01;今天我们将探讨如何在Spring Boot应用中集成HashiCorp Vault&#xff0c;实…

【Spring Cloud】一个例程快速了解网关Gateway的使用

Spring Cloud Gateway提供了一个在Spring生态系统之上构建的API网关&#xff0c;包括&#xff1a;Spring 5&#xff0c;Spring Boot 2和Project Reactor。Spring Cloud Gateway旨在提供一种简单而有效的路由方式&#xff0c;并为它们提供一些网关基本功能&#xff0c;例如&…

自用款 复制粘贴工具 Paste macOS电脑适配

Paste是一款专为Mac和iOS用户设计的剪贴板管理工具&#xff0c;它提供了强大的剪贴板增强功能。Paste能够实时记录用户复制和剪切的内容&#xff0c;包括文本、图片、链接等多种数据类型&#xff0c;并形成一个可视化的剪贴板历史记录&#xff0c;方便用户随时访问和检索。此外…

unity Batch Breaking Cause 合批失败原因

Additional Vertex Streams — the object has additional vertex streams set using MeshRenderer.additionalVertexStreams. Deferred Objects on Different Lighting Layers — the object is on a different light layer.Deferred Objects Split by Shadow Distance — one…

【论文通读】RuleR: Improving LLM Controllability by Rule-based Data Recycling

RuleR: Improving LLM Controllability by Rule-based Data Recycling 前言AbstractMotivationSolutionMethodExperimentsConclusion 前言 一篇关于提升LLMs输出可控性的短文&#xff0c;对SFT数据以规则的方式进行增强&#xff0c;从而提升SFT数据的质量&#xff0c;进而间接帮…