diff --git a/packages/vite/LICENSE.md b/packages/vite/LICENSE.md index 477c9f826dda9b..86dfd162e42b67 100644 --- a/packages/vite/LICENSE.md +++ b/packages/vite/LICENSE.md @@ -360,6 +360,38 @@ Repository: lukeed/polka --------------------------------------- +## @rolldown/pluginutils +License: MIT +Repository: git+https://github.com/rolldown/rolldown.git + +> MIT License +> +> Copyright (c) 2024-present VoidZero Inc. & Contributors +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in all +> copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +> SOFTWARE. +> +> end of terms and conditions +> +> The licenses of externally maintained libraries from which parts of the Software is derived are listed [here](https://github.com/rolldown/rolldown/blob/main/THIRD-PARTY-LICENSE). + +--------------------------------------- + ## @rollup/plugin-alias, @rollup/plugin-commonjs, @rollup/plugin-dynamic-import-vars, @rollup/pluginutils License: MIT By: Johannes Stein diff --git a/packages/vite/package.json b/packages/vite/package.json index f83522bc1a860e..a1295b3019a277 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -97,6 +97,7 @@ "@jridgewell/trace-mapping": "^0.3.29", "@oxc-project/types": "0.77.0", "@polka/compression": "^1.0.0-next.25", + "@rolldown/pluginutils": "^1.0.0-beta.21", "@rollup/plugin-alias": "^5.1.1", "@rollup/plugin-commonjs": "^28.0.6", "@rollup/plugin-dynamic-import-vars": "2.1.4", diff --git a/packages/vite/src/node/plugins/asset.ts b/packages/vite/src/node/plugins/asset.ts index e710bbb2ba4cda..74f0220a4e9c19 100644 --- a/packages/vite/src/node/plugins/asset.ts +++ b/packages/vite/src/node/plugins/asset.ts @@ -166,13 +166,14 @@ export function assetPlugin(config: ResolvedConfig): Plugin { }, load: { - async handler(id) { - if (id[0] === '\0') { + filter: { + id: { // Rollup convention, this id should be handled by the // plugin that marked it with \0 - return - } - + exclude: /^\0/, + }, + }, + async handler(id) { // raw requests, read from disk if (rawRE.test(id)) { const file = checkPublicFile(id, config) || cleanUrl(id) diff --git a/packages/vite/src/node/plugins/assetImportMetaUrl.ts b/packages/vite/src/node/plugins/assetImportMetaUrl.ts index fe52cfb61fc34e..37aef68c303ad5 100644 --- a/packages/vite/src/node/plugins/assetImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/assetImportMetaUrl.ts @@ -1,6 +1,7 @@ import path from 'node:path' import MagicString from 'magic-string' import { stripLiteral } from 'strip-literal' +import { exactRegex } from '@rolldown/pluginutils' import type { Plugin } from '../plugin' import type { ResolvedConfig } from '../config' import { @@ -50,123 +51,121 @@ export function assetImportMetaUrlPlugin(config: ResolvedConfig): Plugin { }, transform: { + filter: { + id: { + exclude: [exactRegex(preloadHelperId), exactRegex(CLIENT_ENTRY)], + }, + code: /new\s+URL.+import\.meta\.url/, + }, async handler(code, id) { - if ( - id !== preloadHelperId && - id !== CLIENT_ENTRY && - code.includes('new URL') && - code.includes(`import.meta.url`) - ) { - let s: MagicString | undefined - const assetImportMetaUrlRE = - /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\)/dg - const cleanString = stripLiteral(code) - - let match: RegExpExecArray | null - while ((match = assetImportMetaUrlRE.exec(cleanString))) { - const [[startIndex, endIndex], [urlStart, urlEnd]] = match.indices! - if (hasViteIgnoreRE.test(code.slice(startIndex, urlStart))) continue + let s: MagicString | undefined + const assetImportMetaUrlRE = + /\bnew\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*(?:,\s*)?\)/dg + const cleanString = stripLiteral(code) - const rawUrl = code.slice(urlStart, urlEnd) + let match: RegExpExecArray | null + while ((match = assetImportMetaUrlRE.exec(cleanString))) { + const [[startIndex, endIndex], [urlStart, urlEnd]] = match.indices! + if (hasViteIgnoreRE.test(code.slice(startIndex, urlStart))) continue - if (!s) s = new MagicString(code) + const rawUrl = code.slice(urlStart, urlEnd) - // potential dynamic template string - if (rawUrl[0] === '`' && rawUrl.includes('${')) { - const queryDelimiterIndex = getQueryDelimiterIndex(rawUrl) - const hasQueryDelimiter = queryDelimiterIndex !== -1 - const pureUrl = hasQueryDelimiter - ? rawUrl.slice(0, queryDelimiterIndex) + '`' - : rawUrl - const queryString = hasQueryDelimiter - ? rawUrl.slice(queryDelimiterIndex, -1) - : '' - const ast = this.parse(pureUrl) - const templateLiteral = (ast as any).body[0].expression - if (templateLiteral.expressions.length) { - const pattern = buildGlobPattern(templateLiteral) - if (pattern.startsWith('*')) { - // don't transform for patterns like this - // because users won't intend to do that in most cases - continue - } + if (!s) s = new MagicString(code) - const globOptions = { - eager: true, - import: 'default', - // A hack to allow 'as' & 'query' exist at the same time - query: injectQuery(queryString, 'url'), - } - s.update( - startIndex, - endIndex, - `new URL((import.meta.glob(${JSON.stringify( - pattern, - )}, ${JSON.stringify( - globOptions, - )}))[${pureUrl}], import.meta.url)`, - ) + // potential dynamic template string + if (rawUrl[0] === '`' && rawUrl.includes('${')) { + const queryDelimiterIndex = getQueryDelimiterIndex(rawUrl) + const hasQueryDelimiter = queryDelimiterIndex !== -1 + const pureUrl = hasQueryDelimiter + ? rawUrl.slice(0, queryDelimiterIndex) + '`' + : rawUrl + const queryString = hasQueryDelimiter + ? rawUrl.slice(queryDelimiterIndex, -1) + : '' + const ast = this.parse(pureUrl) + const templateLiteral = (ast as any).body[0].expression + if (templateLiteral.expressions.length) { + const pattern = buildGlobPattern(templateLiteral) + if (pattern.startsWith('*')) { + // don't transform for patterns like this + // because users won't intend to do that in most cases continue } - } - const url = rawUrl.slice(1, -1) - if (isDataUrl(url)) { + const globOptions = { + eager: true, + import: 'default', + // A hack to allow 'as' & 'query' exist at the same time + query: injectQuery(queryString, 'url'), + } + s.update( + startIndex, + endIndex, + `new URL((import.meta.glob(${JSON.stringify( + pattern, + )}, ${JSON.stringify( + globOptions, + )}))[${pureUrl}], import.meta.url)`, + ) continue } - let file: string | undefined - if (url[0] === '.') { - file = slash(path.resolve(path.dirname(id), url)) - file = tryFsResolve(file, fsResolveOptions) ?? file - } else { - assetResolver ??= createBackCompatIdResolver(config, { - extensions: [], - mainFields: [], - tryIndex: false, - preferRelative: true, - }) - file = await assetResolver(this.environment, url, id) - file ??= - url[0] === '/' - ? slash(path.join(publicDir, url)) - : slash(path.resolve(path.dirname(id), url)) - } + } - // Get final asset URL. If the file does not exist, - // we fall back to the initial URL and let it resolve in runtime - let builtUrl: string | undefined - if (file) { - try { - if (publicDir && isParentDirectory(publicDir, file)) { - const publicPath = '/' + path.posix.relative(publicDir, file) - builtUrl = await fileToUrl(this, publicPath) - } else { - this.addWatchFile(file) - builtUrl = await fileToUrl(this, file) - } - } catch { - // do nothing, we'll log a warning after this + const url = rawUrl.slice(1, -1) + if (isDataUrl(url)) { + continue + } + let file: string | undefined + if (url[0] === '.') { + file = slash(path.resolve(path.dirname(id), url)) + file = tryFsResolve(file, fsResolveOptions) ?? file + } else { + assetResolver ??= createBackCompatIdResolver(config, { + extensions: [], + mainFields: [], + tryIndex: false, + preferRelative: true, + }) + file = await assetResolver(this.environment, url, id) + file ??= + url[0] === '/' + ? slash(path.join(publicDir, url)) + : slash(path.resolve(path.dirname(id), url)) + } + + // Get final asset URL. If the file does not exist, + // we fall back to the initial URL and let it resolve in runtime + let builtUrl: string | undefined + if (file) { + try { + if (publicDir && isParentDirectory(publicDir, file)) { + const publicPath = '/' + path.posix.relative(publicDir, file) + builtUrl = await fileToUrl(this, publicPath) + } else { + this.addWatchFile(file) + builtUrl = await fileToUrl(this, file) } + } catch { + // do nothing, we'll log a warning after this } - if (!builtUrl) { - const rawExp = code.slice(startIndex, endIndex) - config.logger.warnOnce( - `\n${rawExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime. ` + - `If this is intended, you can use the /* @vite-ignore */ comment to suppress this warning.`, - ) - builtUrl = url - } - s.update( - startIndex, - endIndex, - `new URL(${JSON.stringify(builtUrl)}, import.meta.url)`, - ) } - if (s) { - return transformStableResult(s, id, config) + if (!builtUrl) { + const rawExp = code.slice(startIndex, endIndex) + config.logger.warnOnce( + `\n${rawExp} doesn't exist at build time, it will remain unchanged to be resolved at runtime. ` + + `If this is intended, you can use the /* @vite-ignore */ comment to suppress this warning.`, + ) + builtUrl = url } + s.update( + startIndex, + endIndex, + `new URL(${JSON.stringify(builtUrl)}, import.meta.url)`, + ) + } + if (s) { + return transformStableResult(s, id, config) } - return null }, }, } diff --git a/packages/vite/src/node/plugins/css.ts b/packages/vite/src/node/plugins/css.ts index ffe4c4bf41b2bc..467bb6a84ef66c 100644 --- a/packages/vite/src/node/plugins/css.ts +++ b/packages/vite/src/node/plugins/css.ts @@ -334,9 +334,10 @@ export function cssPlugin(config: ResolvedConfig): Plugin { }, load: { + filter: { + id: CSS_LANGS_RE, + }, async handler(id) { - if (!isCSSRequest(id)) return - if (urlRE.test(id)) { if (isModuleCSSRequest(id)) { throw new Error( @@ -361,15 +362,13 @@ export function cssPlugin(config: ResolvedConfig): Plugin { }, }, transform: { + filter: { + id: { + include: CSS_LANGS_RE, + exclude: [commonjsProxyRE, SPECIAL_QUERY_RE], + }, + }, async handler(raw, id) { - if ( - !isCSSRequest(id) || - commonjsProxyRE.test(id) || - SPECIAL_QUERY_RE.test(id) - ) { - return - } - const { environment } = this const resolveUrl = (url: string, importer?: string) => idResolver(environment, url, importer) @@ -509,15 +508,13 @@ export function cssPostPlugin(config: ResolvedConfig): Plugin { }, transform: { + filter: { + id: { + include: CSS_LANGS_RE, + exclude: [commonjsProxyRE, SPECIAL_QUERY_RE], + }, + }, async handler(css, id) { - if ( - !isCSSRequest(id) || - commonjsProxyRE.test(id) || - SPECIAL_QUERY_RE.test(id) - ) { - return - } - css = stripBomTag(css) // cache css compile result to map @@ -1073,15 +1070,13 @@ export function cssAnalysisPlugin(config: ResolvedConfig): Plugin { name: 'vite:css-analysis', transform: { + filter: { + id: { + include: CSS_LANGS_RE, + exclude: [commonjsProxyRE, SPECIAL_QUERY_RE], + }, + }, async handler(_, id) { - if ( - !isCSSRequest(id) || - commonjsProxyRE.test(id) || - SPECIAL_QUERY_RE.test(id) - ) { - return - } - const { moduleGraph } = this.environment as DevEnvironment const thisModule = moduleGraph.getModuleById(id) diff --git a/packages/vite/src/node/plugins/dynamicImportVars.ts b/packages/vite/src/node/plugins/dynamicImportVars.ts index 1c16e7c7697218..e993bd2aa33bec 100644 --- a/packages/vite/src/node/plugins/dynamicImportVars.ts +++ b/packages/vite/src/node/plugins/dynamicImportVars.ts @@ -4,6 +4,7 @@ import { init, parse as parseImports } from 'es-module-lexer' import type { ImportSpecifier } from 'es-module-lexer' import { parseAst } from 'rollup/parseAst' import { dynamicImportToGlob } from '@rollup/plugin-dynamic-import-vars' +import { exactRegex } from '@rolldown/pluginutils' import type { Plugin } from '../plugin' import type { ResolvedConfig } from '../config' import { CLIENT_ENTRY } from '../constants' @@ -181,29 +182,27 @@ export function dynamicImportVarsPlugin(config: ResolvedConfig): Plugin { name: 'vite:dynamic-import-vars', resolveId: { + filter: { id: exactRegex(dynamicImportHelperId) }, handler(id) { - if (id === dynamicImportHelperId) { - return id - } + return id }, }, load: { - handler(id) { - if (id === dynamicImportHelperId) { - return `export default ${dynamicImportHelper.toString()}` - } + filter: { id: exactRegex(dynamicImportHelperId) }, + handler(_id) { + return `export default ${dynamicImportHelper.toString()}` }, }, transform: { + filter: { + id: { exclude: exactRegex(CLIENT_ENTRY) }, + code: hasDynamicImportRE, + }, async handler(source, importer) { const { environment } = this - if ( - !getFilter(this)(importer) || - importer === CLIENT_ENTRY || - !hasDynamicImportRE.test(source) - ) { + if (!getFilter(this)(importer)) { return } diff --git a/packages/vite/src/node/plugins/html.ts b/packages/vite/src/node/plugins/html.ts index 56e328974cd708..a11060bbdfdb32 100644 --- a/packages/vite/src/node/plugins/html.ts +++ b/packages/vite/src/node/plugins/html.ts @@ -96,14 +96,14 @@ export function htmlInlineProxyPlugin(config: ResolvedConfig): Plugin { name: 'vite:html-inline-proxy', resolveId: { + filter: { id: isHtmlProxyRE }, handler(id) { - if (isHTMLProxy(id)) { - return id - } + return id }, }, load: { + filter: { id: isHtmlProxyRE }, handler(id) { const proxyMatch = htmlProxyRE.exec(id) if (proxyMatch) { @@ -349,393 +349,379 @@ export function buildHtmlPlugin(config: ResolvedConfig): Plugin { name: 'vite:build-html', transform: { + filter: { id: /\.html$/ }, async handler(html, id) { - if (id.endsWith('.html')) { - id = normalizePath(id) - const relativeUrlPath = normalizePath(path.relative(config.root, id)) - const publicPath = `/${relativeUrlPath}` - const publicBase = getBaseInHTML(relativeUrlPath, config) - - const publicToRelative = (filename: string) => publicBase + filename - const toOutputPublicFilePath = (url: string) => - toOutputFilePathInHtml( - url.slice(1), - 'public', - relativeUrlPath, - 'html', - config, - publicToRelative, - ) - // Determines true start position for the node, either the < character - // position, or the newline at the end of the previous line's node. - const nodeStartWithLeadingWhitespace = ( - node: DefaultTreeAdapterMap['node'], - ) => { - const startOffset = node.sourceCodeLocation!.startOffset - if (startOffset === 0) return 0 - - // Gets the offset for the start of the line including the - // newline trailing the previous node - const lineStartOffset = - startOffset - node.sourceCodeLocation!.startCol - - // - // - // - // Here we want to target the newline at the end of the previous line - // as the start position for our target. - // - // - // - // - // However, if there is content between our target node start and the - // previous newline, we cannot strip it out without risking content deletion. - let isLineEmpty = false - try { - const line = s.slice(Math.max(0, lineStartOffset), startOffset) - isLineEmpty = !line.trim() - } catch { - // magic-string may throw if there's some content removed in the sliced string, - // which we ignore and assume the line is not empty - } - - return isLineEmpty ? lineStartOffset : startOffset + id = normalizePath(id) + const relativeUrlPath = normalizePath(path.relative(config.root, id)) + const publicPath = `/${relativeUrlPath}` + const publicBase = getBaseInHTML(relativeUrlPath, config) + + const publicToRelative = (filename: string) => publicBase + filename + const toOutputPublicFilePath = (url: string) => + toOutputFilePathInHtml( + url.slice(1), + 'public', + relativeUrlPath, + 'html', + config, + publicToRelative, + ) + // Determines true start position for the node, either the < character + // position, or the newline at the end of the previous line's node. + const nodeStartWithLeadingWhitespace = ( + node: DefaultTreeAdapterMap['node'], + ) => { + const startOffset = node.sourceCodeLocation!.startOffset + if (startOffset === 0) return 0 + + // Gets the offset for the start of the line including the + // newline trailing the previous node + const lineStartOffset = + startOffset - node.sourceCodeLocation!.startCol + + // + // + // + // Here we want to target the newline at the end of the previous line + // as the start position for our target. + // + // + // + // + // However, if there is content between our target node start and the + // previous newline, we cannot strip it out without risking content deletion. + let isLineEmpty = false + try { + const line = s.slice(Math.max(0, lineStartOffset), startOffset) + isLineEmpty = !line.trim() + } catch { + // magic-string may throw if there's some content removed in the sliced string, + // which we ignore and assume the line is not empty } - // pre-transform - html = await applyHtmlTransforms(html, preHooks, this, { - path: publicPath, - filename: id, - }) - - let js = '' - const s = new MagicString(html) - const scriptUrls: ScriptAssetsUrl[] = [] - const styleUrls: ScriptAssetsUrl[] = [] - let inlineModuleIndex = -1 - - let everyScriptIsAsync = true - let someScriptsAreAsync = false - let someScriptsAreDefer = false - - const assetUrlsPromises: Promise[] = [] - - // for each encountered asset url, rewrite original html so that it - // references the post-build location, ignoring empty attributes and - // attributes that directly reference named output. - const namedOutput = Object.keys( - config.build.rollupOptions.input || {}, - ) - const processAssetUrl = async ( - url: string, - shouldInline?: boolean, - ) => { - if ( - url !== '' && // Empty attribute - !namedOutput.includes(url) && // Direct reference to named output - !namedOutput.includes(removeLeadingSlash(url)) // Allow for absolute references as named output can't be an absolute path - ) { - try { - return await urlToBuiltUrl(this, url, id, shouldInline) - } catch (e) { - if (e.code !== 'ENOENT') { - throw e - } + return isLineEmpty ? lineStartOffset : startOffset + } + + // pre-transform + html = await applyHtmlTransforms(html, preHooks, this, { + path: publicPath, + filename: id, + }) + + let js = '' + const s = new MagicString(html) + const scriptUrls: ScriptAssetsUrl[] = [] + const styleUrls: ScriptAssetsUrl[] = [] + let inlineModuleIndex = -1 + + let everyScriptIsAsync = true + let someScriptsAreAsync = false + let someScriptsAreDefer = false + + const assetUrlsPromises: Promise[] = [] + + // for each encountered asset url, rewrite original html so that it + // references the post-build location, ignoring empty attributes and + // attributes that directly reference named output. + const namedOutput = Object.keys(config.build.rollupOptions.input || {}) + const processAssetUrl = async (url: string, shouldInline?: boolean) => { + if ( + url !== '' && // Empty attribute + !namedOutput.includes(url) && // Direct reference to named output + !namedOutput.includes(removeLeadingSlash(url)) // Allow for absolute references as named output can't be an absolute path + ) { + try { + return await urlToBuiltUrl(this, url, id, shouldInline) + } catch (e) { + if (e.code !== 'ENOENT') { + throw e } } - return url } + return url + } - const setModuleSideEffectPromises: Promise[] = [] - await traverseHtml(html, id, (node) => { - if (!nodeIsElement(node)) { - return - } + const setModuleSideEffectPromises: Promise[] = [] + await traverseHtml(html, id, (node) => { + if (!nodeIsElement(node)) { + return + } - let shouldRemove = false - - // script tags - if (node.nodeName === 'script') { - const { - src, - srcSourceCodeLocation, - isModule, - isAsync, - isIgnored, - } = getScriptInfo(node) - - if (isIgnored) { - removeViteIgnoreAttr(s, node.sourceCodeLocation!) - } else { - const url = src && src.value - const isPublicFile = !!(url && checkPublicFile(url, config)) - if (isPublicFile) { - // referencing public dir url, prefix with base - overwriteAttrValue( - s, - srcSourceCodeLocation!, - partialEncodeURIPath(toOutputPublicFilePath(url)), - ) - } + let shouldRemove = false - if (isModule) { - inlineModuleIndex++ - if (url && !isExcludedUrl(url) && !isPublicFile) { - setModuleSideEffectPromises.push( - this.resolve(url, id).then((resolved) => { - if (!resolved) { - return Promise.reject( - new Error(`Failed to resolve ${url} from ${id}`), - ) - } - // set moduleSideEffects to keep the module even if `treeshake.moduleSideEffects=false` is set - const moduleInfo = this.getModuleInfo(resolved.id) - if (moduleInfo) { - moduleInfo.moduleSideEffects = true - } else if (!resolved.external) { - return this.load(resolved).then((mod) => { - mod.moduleSideEffects = true - }) - } - }), - ) - // - const filePath = id.replace(normalizePath(config.root), '') - addToHTMLProxyCache(config, filePath, inlineModuleIndex, { - code: contents, - }) - js += `\nimport "${id}?html-proxy&index=${inlineModuleIndex}.js"` - shouldRemove = true - } + // script tags + if (node.nodeName === 'script') { + const { src, srcSourceCodeLocation, isModule, isAsync, isIgnored } = + getScriptInfo(node) - everyScriptIsAsync &&= isAsync - someScriptsAreAsync ||= isAsync - someScriptsAreDefer ||= !isAsync - } else if (url && !isPublicFile) { - if (!isExcludedUrl(url)) { - config.logger.warn( - ` + const filePath = id.replace(normalizePath(config.root), '') + addToHTMLProxyCache(config, filePath, inlineModuleIndex, { + code: contents, + }) + js += `\nimport "${id}?html-proxy&index=${inlineModuleIndex}.js"` + shouldRemove = true + } + + everyScriptIsAsync &&= isAsync + someScriptsAreAsync ||= isAsync + someScriptsAreDefer ||= !isAsync + } else if (url && !isPublicFile) { + if (!isExcludedUrl(url)) { + config.logger.warn( + ` asset - for (const { start, end, url } of scriptUrls) { - if (checkPublicFile(url, config)) { - s.update( - start, - end, - partialEncodeURIPath(toOutputPublicFilePath(url)), - ) - } else if (!isExcludedUrl(url)) { - s.update( - start, - end, - partialEncodeURIPath(await urlToBuiltUrl(this, url, id)), - ) - } + if (shouldRemove) { + // remove the script tag from the html. we are going to inject new + // ones in the end. + s.remove( + nodeStartWithLeadingWhitespace(node), + node.sourceCodeLocation!.endOffset, + ) } + }) + + isAsyncScriptMap.get(config)!.set(id, everyScriptIsAsync) - // ignore if its url can't be resolved - const resolvedStyleUrls = await Promise.all( - styleUrls.map(async (styleUrl) => ({ - ...styleUrl, - resolved: await this.resolve(styleUrl.url, id), - })), + if (someScriptsAreAsync && someScriptsAreDefer) { + config.logger.warn( + `\nMixed async and defer script modules in ${id}, output script will fallback to defer. Every script, including inline ones, need to be marked as async for your output script to be async.`, ) - for (const { start, end, url, resolved } of resolvedStyleUrls) { - if (resolved == null) { - config.logger.warnOnce( - `\n${url} doesn't exist at build time, it will remain unchanged to be resolved at runtime`, - ) - const importExpression = `\nimport ${JSON.stringify(url)}` - js = js.replace(importExpression, '') - } else { - s.remove(start, end) - } - } + } - processedHtml(this).set(id, s.toString()) + await Promise.all(assetUrlsPromises) - // inject module preload polyfill only when configured and needed - const { modulePreload } = this.environment.config.build - if ( - modulePreload !== false && - modulePreload.polyfill && - (someScriptsAreAsync || someScriptsAreDefer) - ) { - js = `import "${modulePreloadPolyfillId}";\n${js}` + // emit asset + for (const { start, end, url } of scriptUrls) { + if (checkPublicFile(url, config)) { + s.update( + start, + end, + partialEncodeURIPath(toOutputPublicFilePath(url)), + ) + } else if (!isExcludedUrl(url)) { + s.update( + start, + end, + partialEncodeURIPath(await urlToBuiltUrl(this, url, id)), + ) } + } - await Promise.all(setModuleSideEffectPromises) + // ignore if its url can't be resolved + const resolvedStyleUrls = await Promise.all( + styleUrls.map(async (styleUrl) => ({ + ...styleUrl, + resolved: await this.resolve(styleUrl.url, id), + })), + ) + for (const { start, end, url, resolved } of resolvedStyleUrls) { + if (resolved == null) { + config.logger.warnOnce( + `\n${url} doesn't exist at build time, it will remain unchanged to be resolved at runtime`, + ) + const importExpression = `\nimport ${JSON.stringify(url)}` + js = js.replace(importExpression, '') + } else { + s.remove(start, end) + } + } + + processedHtml(this).set(id, s.toString()) - // Force rollup to keep this module from being shared between other entry points. - // If the resulting chunk is empty, it will be removed in generateBundle. - return { code: js, moduleSideEffects: 'no-treeshake' } + // inject module preload polyfill only when configured and needed + const { modulePreload } = this.environment.config.build + if ( + modulePreload !== false && + modulePreload.polyfill && + (someScriptsAreAsync || someScriptsAreDefer) + ) { + js = `import "${modulePreloadPolyfillId}";\n${js}` } + + await Promise.all(setModuleSideEffectPromises) + + // Force rollup to keep this module from being shared between other entry points. + // If the resulting chunk is empty, it will be removed in generateBundle. + return { code: js, moduleSideEffects: 'no-treeshake' } }, }, diff --git a/packages/vite/src/node/plugins/importAnalysisBuild.ts b/packages/vite/src/node/plugins/importAnalysisBuild.ts index 284f32f5c14380..277b609fa22e1d 100644 --- a/packages/vite/src/node/plugins/importAnalysisBuild.ts +++ b/packages/vite/src/node/plugins/importAnalysisBuild.ts @@ -8,6 +8,7 @@ import { init, parse as parseImports } from 'es-module-lexer' import type { SourceMap } from 'rollup' import type { RawSourceMap } from '@ampproject/remapping' import convertSourceMap from 'convert-source-map' +import { exactRegex } from '@rolldown/pluginutils' import { combineSourcemaps, generateCodeFrame, numberToPos } from '../utils' import type { Plugin } from '../plugin' import type { ResolvedConfig } from '../config' @@ -222,32 +223,27 @@ export function buildImportAnalysisPlugin(config: ResolvedConfig): Plugin { return { name: 'vite:build-import-analysis', resolveId: { + filter: { id: exactRegex(preloadHelperId) }, handler(id) { - if (id === preloadHelperId) { - return id - } + return id }, }, load: { - handler(id) { - if (id === preloadHelperId) { - const preloadCode = getPreloadCode( - this.environment, - !!renderBuiltUrl, - isRelativeBase, - ) - return { code: preloadCode, moduleSideEffects: false } - } + filter: { id: exactRegex(preloadHelperId) }, + handler(_id) { + const preloadCode = getPreloadCode( + this.environment, + !!renderBuiltUrl, + isRelativeBase, + ) + return { code: preloadCode, moduleSideEffects: false } }, }, transform: { + filter: { code: dynamicImportPrefixRE }, async handler(source, importer) { - if (!dynamicImportPrefixRE.test(source)) { - return - } - await init let imports: readonly ImportSpecifier[] = [] diff --git a/packages/vite/src/node/plugins/importMetaGlob.ts b/packages/vite/src/node/plugins/importMetaGlob.ts index e89aeb450363a7..774b4f56c4536b 100644 --- a/packages/vite/src/node/plugins/importMetaGlob.ts +++ b/packages/vite/src/node/plugins/importMetaGlob.ts @@ -52,8 +52,8 @@ export function importGlobPlugin(config: ResolvedConfig): Plugin { importGlobMaps.clear() }, transform: { + filter: { code: 'import.meta.glob' }, async handler(code, id) { - if (!code.includes('import.meta.glob')) return const result = await transformGlobImport( code, id, diff --git a/packages/vite/src/node/plugins/json.ts b/packages/vite/src/node/plugins/json.ts index a516851cb03ace..7127eba2ed43fe 100644 --- a/packages/vite/src/node/plugins/json.ts +++ b/packages/vite/src/node/plugins/json.ts @@ -45,10 +45,10 @@ export function jsonPlugin( name: 'vite:json', transform: { + filter: { + id: { include: jsonExtRE, exclude: SPECIAL_QUERY_RE }, + }, handler(json, id) { - if (!jsonExtRE.test(id)) return null - if (SPECIAL_QUERY_RE.test(id)) return null - if (inlineRE.test(id) || noInlineRE.test(id)) { this.warn( `\n` + diff --git a/packages/vite/src/node/plugins/modulePreloadPolyfill.ts b/packages/vite/src/node/plugins/modulePreloadPolyfill.ts index e662ddf7dd0857..d0558fd44f6ff4 100644 --- a/packages/vite/src/node/plugins/modulePreloadPolyfill.ts +++ b/packages/vite/src/node/plugins/modulePreloadPolyfill.ts @@ -1,3 +1,4 @@ +import { exactRegex } from '@rolldown/pluginutils' import type { ResolvedConfig } from '..' import type { Plugin } from '../plugin' import { isModernFlag } from './importAnalysisBuild' @@ -11,27 +12,25 @@ export function modulePreloadPolyfillPlugin(config: ResolvedConfig): Plugin { return { name: 'vite:modulepreload-polyfill', resolveId: { - handler(id) { - if (id === modulePreloadPolyfillId) { - return resolvedModulePreloadPolyfillId - } + filter: { id: exactRegex(modulePreloadPolyfillId) }, + handler(_id) { + return resolvedModulePreloadPolyfillId }, }, load: { - handler(id) { - if (id === resolvedModulePreloadPolyfillId) { - // `isModernFlag` is only available during build since it is resolved by `vite:build-import-analysis` - if ( - config.command !== 'build' || - this.environment.config.consumer !== 'client' - ) { - return '' - } - if (!polyfillString) { - polyfillString = `${isModernFlag}&&(${polyfill.toString()}());` - } - return { code: polyfillString, moduleSideEffects: true } + filter: { id: exactRegex(resolvedModulePreloadPolyfillId) }, + handler(_id) { + // `isModernFlag` is only available during build since it is resolved by `vite:build-import-analysis` + if ( + config.command !== 'build' || + this.environment.config.consumer !== 'client' + ) { + return '' + } + if (!polyfillString) { + polyfillString = `${isModernFlag}&&(${polyfill.toString()}());` } + return { code: polyfillString, moduleSideEffects: true } }, }, } diff --git a/packages/vite/src/node/plugins/wasm.ts b/packages/vite/src/node/plugins/wasm.ts index 9b47ca9d302d5f..98cd50309aed59 100644 --- a/packages/vite/src/node/plugins/wasm.ts +++ b/packages/vite/src/node/plugins/wasm.ts @@ -1,3 +1,4 @@ +import { exactRegex } from '@rolldown/pluginutils' import type { Plugin } from '../plugin' import { fileToUrl } from './asset' @@ -52,23 +53,19 @@ export const wasmHelperPlugin = (): Plugin => { name: 'vite:wasm-helper', resolveId: { + filter: { id: exactRegex(wasmHelperId) }, handler(id) { - if (id === wasmHelperId) { - return id - } + return id }, }, load: { + filter: { id: [exactRegex(wasmHelperId), wasmInitRE] }, async handler(id) { if (id === wasmHelperId) { return `export default ${wasmHelperCode}` } - if (!wasmInitRE.test(id)) { - return - } - const url = await fileToUrl(this, id) return ` @@ -85,11 +82,8 @@ export const wasmFallbackPlugin = (): Plugin => { name: 'vite:wasm-fallback', load: { - handler(id) { - if (!id.endsWith('.wasm')) { - return - } - + filter: { id: /\.wasm$/ }, + handler(_id) { throw new Error( '"ESM integration proposal for Wasm" is not supported currently. ' + 'Use vite-plugin-wasm or other community plugins to handle this. ' + diff --git a/packages/vite/src/node/plugins/workerImportMetaUrl.ts b/packages/vite/src/node/plugins/workerImportMetaUrl.ts index c8c98520b0987d..8eb97cdd0d1e14 100644 --- a/packages/vite/src/node/plugins/workerImportMetaUrl.ts +++ b/packages/vite/src/node/plugins/workerImportMetaUrl.ts @@ -181,16 +181,8 @@ async function getWorkerType( return 'classic' } -function isIncludeWorkerImportMetaUrl(code: string): boolean { - if ( - (code.includes('new Worker') || code.includes('new SharedWorker')) && - code.includes('new URL') && - code.includes(`import.meta.url`) - ) { - return true - } - return false -} +const workerImportMetaUrlRE = + /new\s+(?:Worker|SharedWorker)\s*\(\s*new\s+URL.+?import\.meta\.url/s export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { const isBuild = config.command === 'build' @@ -213,85 +205,84 @@ export function workerImportMetaUrlPlugin(config: ResolvedConfig): Plugin { }, shouldTransformCachedModule({ code }) { - if (isBuild && config.build.watch && isIncludeWorkerImportMetaUrl(code)) { + if (isBuild && config.build.watch && workerImportMetaUrlRE.test(code)) { return true } }, transform: { + filter: { code: workerImportMetaUrlRE }, async handler(code, id) { - if (isIncludeWorkerImportMetaUrl(code)) { - let s: MagicString | undefined - const cleanString = stripLiteral(code) - const workerImportMetaUrlRE = - /\bnew\s+(?:Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/dg - - let match: RegExpExecArray | null - while ((match = workerImportMetaUrlRE.exec(cleanString))) { - const [[, endIndex], [expStart, expEnd], [urlStart, urlEnd]] = - match.indices! - - const rawUrl = code.slice(urlStart, urlEnd) - - // potential dynamic template string - if (rawUrl[0] === '`' && rawUrl.includes('${')) { - this.error( - `\`new URL(url, import.meta.url)\` is not supported in dynamic template string.`, - expStart, - ) - } + let s: MagicString | undefined + const cleanString = stripLiteral(code) + const workerImportMetaUrlRE = + /\bnew\s+(?:Worker|SharedWorker)\s*\(\s*(new\s+URL\s*\(\s*('[^']+'|"[^"]+"|`[^`]+`)\s*,\s*import\.meta\.url\s*\))/dg + + let match: RegExpExecArray | null + while ((match = workerImportMetaUrlRE.exec(cleanString))) { + const [[, endIndex], [expStart, expEnd], [urlStart, urlEnd]] = + match.indices! + + const rawUrl = code.slice(urlStart, urlEnd) + + // potential dynamic template string + if (rawUrl[0] === '`' && rawUrl.includes('${')) { + this.error( + `\`new URL(url, import.meta.url)\` is not supported in dynamic template string.`, + expStart, + ) + } - s ||= new MagicString(code) - const workerType = await getWorkerType(code, cleanString, endIndex) - const url = rawUrl.slice(1, -1) - let file: string | undefined - if (url[0] === '.') { - file = path.resolve(path.dirname(id), url) - file = slash(tryFsResolve(file, fsResolveOptions) ?? file) - } else { - workerResolver ??= createBackCompatIdResolver(config, { - extensions: [], - tryIndex: false, - preferRelative: true, - }) - file = await workerResolver(this.environment, url, id) - file ??= - url[0] === '/' - ? slash(path.join(config.publicDir, url)) - : slash(path.resolve(path.dirname(id), url)) - } + s ||= new MagicString(code) + const workerType = await getWorkerType(code, cleanString, endIndex) + const url = rawUrl.slice(1, -1) + let file: string | undefined + if (url[0] === '.') { + file = path.resolve(path.dirname(id), url) + file = slash(tryFsResolve(file, fsResolveOptions) ?? file) + } else { + workerResolver ??= createBackCompatIdResolver(config, { + extensions: [], + tryIndex: false, + preferRelative: true, + }) + file = await workerResolver(this.environment, url, id) + file ??= + url[0] === '/' + ? slash(path.join(config.publicDir, url)) + : slash(path.resolve(path.dirname(id), url)) + } - if ( - isBuild && - config.isWorker && - config.bundleChain.at(-1) === cleanUrl(file) - ) { - s.update(expStart, expEnd, 'self.location.href') + if ( + isBuild && + config.isWorker && + config.bundleChain.at(-1) === cleanUrl(file) + ) { + s.update(expStart, expEnd, 'self.location.href') + } else { + let builtUrl: string + if (isBuild) { + builtUrl = await workerFileToUrl(config, file) } else { - let builtUrl: string - if (isBuild) { - builtUrl = await workerFileToUrl(config, file) - } else { - builtUrl = await fileToUrl(this, cleanUrl(file)) - builtUrl = injectQuery( - builtUrl, - `${WORKER_FILE_ID}&type=${workerType}`, - ) - } - s.update( - expStart, - expEnd, - `new URL(/* @vite-ignore */ ${JSON.stringify(builtUrl)}, import.meta.url)`, + builtUrl = await fileToUrl(this, cleanUrl(file)) + builtUrl = injectQuery( + builtUrl, + `${WORKER_FILE_ID}&type=${workerType}`, ) } + s.update( + expStart, + expEnd, + `new URL(/* @vite-ignore */ ${JSON.stringify(builtUrl)}, import.meta.url)`, + ) } + } - if (s) { - return transformStableResult(s, id, config) - } - - return null + if (s) { + return transformStableResult(s, id, config) } + + return null }, }, } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9750fc3dbf74c1..c9823bfbb4722f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -263,6 +263,9 @@ importers: '@polka/compression': specifier: ^1.0.0-next.25 version: 1.0.0-next.25 + '@rolldown/pluginutils': + specifier: ^1.0.0-beta.21 + version: 1.0.0-beta.21 '@rollup/plugin-alias': specifier: ^5.1.1 version: 5.1.1(rollup@4.40.1) @@ -1827,6 +1830,10 @@ packages: resolution: {integrity: sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==} engines: {node: '>=6.9.0'} + '@babel/generator@7.27.5': + resolution: {integrity: sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==} + engines: {node: '>=6.9.0'} + '@babel/generator@7.28.0': resolution: {integrity: sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==} engines: {node: '>=6.9.0'} @@ -2220,8 +2227,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-regenerator@7.28.0': - resolution: {integrity: sha512-LOAozRVbqxEVjSKfhGnuLoE4Kz4Oc5UJzuvFUhSsQzdCdaAQu06mG8zDv2GFSerM62nImUZ7K92vxnQcLSDlCQ==} + '@babel/plugin-transform-regenerator@7.28.1': + resolution: {integrity: sha512-P0QiV/taaa3kXpLY+sXla5zec4E+4t4Aqc9ggHlfZ7a2cp8/x/Gv08jfwEtn9gnnYIMvHx6aoOZ8XJL8eU71Dg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -2323,10 +2330,6 @@ packages: resolution: {integrity: sha512-8OLQgDScAOHXnAz2cV+RfzzNMipuLVBz2biuAJFMV9bfkNf393je3VM8CLkjQodW5+iWsSJdSgSWT6rsZoXHPw==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.0': - resolution: {integrity: sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==} - engines: {node: '>=6.9.0'} - '@babel/types@7.28.1': resolution: {integrity: sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==} engines: {node: '>=6.9.0'} @@ -2651,8 +2654,8 @@ packages: '@iconify-json/logos@1.2.4': resolution: {integrity: sha512-XC4If5D/hbaZvUkTV8iaZuGlQCyG6CNOlaAaJaGa13V5QMYwYjgtKk3vPP8wz3wtTVNVEVk3LRx1fOJz+YnSMw==} - '@iconify-json/simple-icons@1.2.43': - resolution: {integrity: sha512-JERgKGFRfZdyjGyTvVBVW5rftahy9tNUX+P+0QUnbaAEWvEMexXHE9863YVMVrIRhoj/HybGsibg8ZWieo/NDg==} + '@iconify-json/simple-icons@1.2.44': + resolution: {integrity: sha512-CdWgSPygwDlDbKtDWjvi3NtUefnkoepXv90n3dQxJerqzD9kI+nEJOiWUBM+eOyMYQKtxBpLWFBrgeotF0IZKw==} '@iconify-json/vscode-icons@1.2.23': resolution: {integrity: sha512-gFTcKecKra2/b5SbGDgHGI/l8CuikHyBPmqGlK+YCmS8AK72dtDQbUekdoACsju/3TYS37QvdPoOQwnyx2LdYg==} @@ -3112,6 +3115,9 @@ packages: '@rolldown/pluginutils@1.0.0-beta.19': resolution: {integrity: sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA==} + '@rolldown/pluginutils@1.0.0-beta.21': + resolution: {integrity: sha512-OTjWr7XYqRZaSzi6dTe0fP25EEsYEQ2H04xIedXG3D0Hrs+Bpe3V5L48R6y+R5ohTygp1ijC09mbrd7vlslpzA==} + '@rolldown/pluginutils@1.0.0-beta.29': resolution: {integrity: sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==} @@ -4039,11 +4045,11 @@ packages: '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} - '@volar/language-core@2.4.17': - resolution: {integrity: sha512-chmRZMbKmcGpKMoO7Reb70uiLrzo0KWC2CkFttKUuKvrE+VYgi+fL9vWMJ07Fv5ulX0V1TAyyacN9q3nc5/ecA==} + '@volar/language-core@2.4.20': + resolution: {integrity: sha512-dRDF1G33xaAIDqR6+mXUIjXYdu9vzSxlMGfMEwBxQsfY/JMUEXSpLTR057oTKlUQ2nIvCmP9k94A8h8z2VrNSA==} - '@volar/source-map@2.4.17': - resolution: {integrity: sha512-QDybtQyO3Ms/NjFqNHTC5tbDN2oK5VH7ZaKrcubtfHBDj63n2pizHC3wlMQ+iT55kQXZUUAbmBX5L1C8CHFeBw==} + '@volar/source-map@2.4.20': + resolution: {integrity: sha512-mVjmFQH8mC+nUaVwmbxoYUy8cww+abaO8dWzqPUjilsavjxH0jCJ3Mp8HFuHsdewZs2c+SP+EO7hCd8Z92whJg==} '@vue/compiler-core@3.4.38': resolution: {integrity: sha512-8IQOTCWnLFqfHzOGm9+P8OPSEDukgg3Huc92qSG49if/xI2SAwLHQO2qaPQbjCWPBcQoO1WYfXfTACUrWV3c5A==} @@ -4084,8 +4090,8 @@ packages: '@vue/devtools-shared@7.7.7': resolution: {integrity: sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==} - '@vue/language-core@3.0.1': - resolution: {integrity: sha512-sq+/Mc1IqIexWEQ+Q2XPiDb5SxSvY5JPqHnMOl/PlF5BekslzduX8dglSkpC17VeiAQB6dpS+4aiwNLJRduCNw==} + '@vue/language-core@3.0.3': + resolution: {integrity: sha512-I9wY0ULMN9tMSua+2C7g+ez1cIziVMUzIHlDYGSl2rtru3Eh4sXj95vZ+4GBuXwwPnEmYfzSApVbXiVbI8V5Gg==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -4349,11 +4355,11 @@ packages: resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} engines: {node: '>=18'} - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -4599,8 +4605,8 @@ packages: resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} engines: {node: '>=12.13'} - core-js-compat@3.43.0: - resolution: {integrity: sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==} + core-js-compat@3.44.0: + resolution: {integrity: sha512-JepmAj2zfl6ogy34qfWtcE7nHKAJnKsQFRn++scjVS2bZFllwptzw61BZcZFYBPpUznLfAvh0LGhxKppk04ClA==} core-js@3.44.0: resolution: {integrity: sha512-aFCtd4l6GvAXwVEh3XbbVqJGHDJt0OZRa+5ePGx3LLwi12WfexqQxcsohb2wgsa/92xtl19Hd66G/L+TaAxDMw==} @@ -7038,16 +7044,16 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - twoslash-protocol@0.3.2: - resolution: {integrity: sha512-lWIL1dGcMr7cywSLSn8ufCoeyPab3bIwPE6DmAlQYQSMjJUgzzRvSz/LsQ179eNJafRghYDlIgF2v7pmsjV3Ww==} + twoslash-protocol@0.3.3: + resolution: {integrity: sha512-26NXjXEj+2NgytwOjjhFtNpQI7Zgct6PTyLg6JO7fIbO7MIm+kx89IxvBustgBOSKxoWjNBN2LXjNHHMD7+k0g==} - twoslash-vue@0.3.2: - resolution: {integrity: sha512-PzhlfoJBVSNx2H/fA/7vROIsr3s0EMoE4mxaP6k/4kj03woQ8vU0CqEhSfvCojYv4v6xYQJDyHFNhqzQuWT6Vg==} + twoslash-vue@0.3.3: + resolution: {integrity: sha512-CoIvzNK9QRJUUTTEzT0bFcf5kJc9wgBQHRcj5VcmhzFvcSdk/hevExeahcSHClpz1eE4EHJtma2+1wbK+GOjeg==} peerDependencies: typescript: ^5.5.0 - twoslash@0.3.2: - resolution: {integrity: sha512-TB+ja888uMKhbng8HzpTHm+JfxIWbngIHPy4nKEt2N93MFjpqmkqn8ppnPhIKj4kDnrohEsiogMF7T1gMY06rw==} + twoslash@0.3.3: + resolution: {integrity: sha512-Yen1RBSYh/NUR6tfK6xT7DsXkBYby7lfp078Q7XoJR5dtAeG9jB5PVL1oF2sZwHapLjaqcZNlw7GSB8himZNsQ==} peerDependencies: typescript: ^5.5.0 @@ -7537,7 +7543,7 @@ snapshots: '@babel/parser': 7.28.0 '@babel/template': 7.27.2 '@babel/traverse': 7.28.0 - '@babel/types': 7.28.0 + '@babel/types': 7.28.1 convert-source-map: 2.0.0 debug: 4.4.1 gensync: 1.0.0-beta.2 @@ -7546,21 +7552,29 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/generator@7.27.5': + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.27.7 + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.29 + jsesc: 3.1.0 + '@babel/generator@7.28.0': dependencies: '@babel/parser': 7.28.0 - '@babel/types': 7.28.0 + '@babel/types': 7.28.1 '@jridgewell/gen-mapping': 0.3.12 '@jridgewell/trace-mapping': 0.3.29 jsesc: 3.1.0 '@babel/helper-annotate-as-pure@7.27.1': dependencies: - '@babel/types': 7.28.0 + '@babel/types': 7.27.7 '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.28.0 + '@babel/types': 7.27.7 '@babel/helper-compilation-targets@7.27.2': dependencies: @@ -7578,7 +7592,7 @@ snapshots: '@babel/helper-optimise-call-expression': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.0) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.27.7 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -7605,15 +7619,15 @@ snapshots: '@babel/helper-member-expression-to-functions@7.27.1': dependencies: - '@babel/traverse': 7.28.0 - '@babel/types': 7.28.0 + '@babel/traverse': 7.27.7 + '@babel/types': 7.27.7 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.27.1': dependencies: '@babel/traverse': 7.28.0 - '@babel/types': 7.28.0 + '@babel/types': 7.28.1 transitivePeerDependencies: - supports-color @@ -7628,7 +7642,7 @@ snapshots: '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.28.0 + '@babel/types': 7.27.7 '@babel/helper-plugin-utils@7.27.1': {} @@ -7637,7 +7651,7 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-annotate-as-pure': 7.27.1 '@babel/helper-wrap-function': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.27.7 transitivePeerDependencies: - supports-color @@ -7646,14 +7660,14 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-member-expression-to-functions': 7.27.1 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.27.7 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.28.0 - '@babel/types': 7.28.0 + '@babel/traverse': 7.27.7 + '@babel/types': 7.27.7 transitivePeerDependencies: - supports-color @@ -7666,15 +7680,15 @@ snapshots: '@babel/helper-wrap-function@7.27.1': dependencies: '@babel/template': 7.27.2 - '@babel/traverse': 7.28.0 - '@babel/types': 7.28.0 + '@babel/traverse': 7.27.7 + '@babel/types': 7.27.7 transitivePeerDependencies: - supports-color '@babel/helpers@7.27.6': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.28.0 + '@babel/types': 7.28.1 '@babel/parser@7.27.5': dependencies: @@ -7682,7 +7696,7 @@ snapshots: '@babel/parser@7.28.0': dependencies: - '@babel/types': 7.28.0 + '@babel/types': 7.28.1 '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.28.0)': dependencies: @@ -7913,7 +7927,7 @@ snapshots: '@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.0) '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.0 + '@babel/traverse': 7.27.7 transitivePeerDependencies: - supports-color @@ -8005,7 +8019,7 @@ snapshots: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-regenerator@7.28.0(@babel/core@7.28.0)': + '@babel/plugin-transform-regenerator@7.28.1(@babel/core@7.28.0)': dependencies: '@babel/core': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 @@ -8127,7 +8141,7 @@ snapshots: '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.0) '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.0) '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.0) - '@babel/plugin-transform-regenerator': 7.28.0(@babel/core@7.28.0) + '@babel/plugin-transform-regenerator': 7.28.1(@babel/core@7.28.0) '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.28.0) '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.0) '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.0) @@ -8143,7 +8157,7 @@ snapshots: babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.0) babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.0) babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.0) - core-js-compat: 3.43.0 + core-js-compat: 3.44.0 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -8161,15 +8175,15 @@ snapshots: dependencies: '@babel/code-frame': 7.27.1 '@babel/parser': 7.28.0 - '@babel/types': 7.28.0 + '@babel/types': 7.28.1 '@babel/traverse@7.27.7': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.0 + '@babel/generator': 7.27.5 '@babel/parser': 7.28.0 '@babel/template': 7.27.2 - '@babel/types': 7.28.0 + '@babel/types': 7.27.7 debug: 4.4.1 globals: 11.12.0 transitivePeerDependencies: @@ -8182,7 +8196,7 @@ snapshots: '@babel/helper-globals': 7.28.0 '@babel/parser': 7.28.0 '@babel/template': 7.27.2 - '@babel/types': 7.28.0 + '@babel/types': 7.28.1 debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -8192,11 +8206,6 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/types@7.28.0': - dependencies: - '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/types@7.28.1': dependencies: '@babel/helper-string-parser': 7.27.1 @@ -8451,7 +8460,7 @@ snapshots: dependencies: '@iconify/types': 2.0.0 - '@iconify-json/simple-icons@1.2.43': + '@iconify-json/simple-icons@1.2.44': dependencies: '@iconify/types': 2.0.0 @@ -8540,7 +8549,7 @@ snapshots: '@img/sharp-wasm32@0.33.5': dependencies: - '@emnapi/runtime': 1.4.3 + '@emnapi/runtime': 1.4.5 optional: true '@img/sharp-win32-ia32@0.33.5': @@ -8832,6 +8841,8 @@ snapshots: '@rolldown/pluginutils@1.0.0-beta.19': {} + '@rolldown/pluginutils@1.0.0-beta.21': {} + '@rolldown/pluginutils@1.0.0-beta.29': {} '@rollup/plugin-alias@5.1.1(rollup@4.40.1)': @@ -8991,7 +9002,7 @@ snapshots: dependencies: '@shikijs/core': 3.8.1 '@shikijs/types': 3.8.1 - twoslash: 0.3.2(typescript@5.7.3) + twoslash: 0.3.3(typescript@5.7.3) typescript: 5.7.3 transitivePeerDependencies: - supports-color @@ -9014,8 +9025,8 @@ snapshots: mdast-util-gfm: 3.1.0 mdast-util-to-hast: 13.2.0 shiki: 3.8.1 - twoslash: 0.3.2(typescript@5.7.3) - twoslash-vue: 0.3.2(typescript@5.7.3) + twoslash: 0.3.3(typescript@5.7.3) + twoslash-vue: 0.3.3(typescript@5.7.3) vue: 3.5.17(typescript@5.7.3) transitivePeerDependencies: - '@nuxt/kit' @@ -9724,11 +9735,11 @@ snapshots: loupe: 3.1.4 tinyrainbow: 2.0.0 - '@volar/language-core@2.4.17': + '@volar/language-core@2.4.20': dependencies: - '@volar/source-map': 2.4.17 + '@volar/source-map': 2.4.20 - '@volar/source-map@2.4.17': {} + '@volar/source-map@2.4.20': {} '@vue/compiler-core@3.4.38': dependencies: @@ -9815,16 +9826,16 @@ snapshots: dependencies: rfdc: 1.4.1 - '@vue/language-core@3.0.1(typescript@5.7.3)': + '@vue/language-core@3.0.3(typescript@5.7.3)': dependencies: - '@volar/language-core': 2.4.17 + '@volar/language-core': 2.4.20 '@vue/compiler-dom': 3.5.17 '@vue/compiler-vue2': 2.7.16 '@vue/shared': 3.5.17 alien-signals: 2.0.5 - minimatch: 10.0.3 muggle-string: 0.4.1 path-browserify: 1.0.1 + picomatch: 4.0.3 optionalDependencies: typescript: 5.7.3 @@ -10023,7 +10034,7 @@ snapshots: dependencies: '@babel/core': 7.28.0 '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.0) - core-js-compat: 3.43.0 + core-js-compat: 3.44.0 transitivePeerDependencies: - supports-color @@ -10036,7 +10047,7 @@ snapshots: babel-walk@3.0.0-canary-5: dependencies: - '@babel/types': 7.28.0 + '@babel/types': 7.27.7 bail@2.0.2: {} @@ -10068,12 +10079,12 @@ snapshots: transitivePeerDependencies: - supports-color - brace-expansion@1.1.12: + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.2: + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 @@ -10252,7 +10263,7 @@ snapshots: constantinople@4.0.1: dependencies: '@babel/parser': 7.28.0 - '@babel/types': 7.28.0 + '@babel/types': 7.27.7 content-disposition@1.0.0: dependencies: @@ -10308,7 +10319,7 @@ snapshots: dependencies: is-what: 4.1.16 - core-js-compat@3.43.0: + core-js-compat@3.44.0: dependencies: browserslist: 4.25.1 @@ -11692,7 +11703,7 @@ snapshots: minimatch@10.0.1: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 2.0.1 minimatch@10.0.3: dependencies: @@ -11700,11 +11711,11 @@ snapshots: minimatch@3.1.2: dependencies: - brace-expansion: 1.1.12 + brace-expansion: 1.1.11 minimatch@9.0.5: dependencies: - brace-expansion: 2.0.2 + brace-expansion: 2.0.1 minimist@1.2.8: {} @@ -12937,21 +12948,21 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - twoslash-protocol@0.3.2: {} + twoslash-protocol@0.3.3: {} - twoslash-vue@0.3.2(typescript@5.7.3): + twoslash-vue@0.3.3(typescript@5.7.3): dependencies: - '@vue/language-core': 3.0.1(typescript@5.7.3) - twoslash: 0.3.2(typescript@5.7.3) - twoslash-protocol: 0.3.2 + '@vue/language-core': 3.0.3(typescript@5.7.3) + twoslash: 0.3.3(typescript@5.7.3) + twoslash-protocol: 0.3.3 typescript: 5.7.3 transitivePeerDependencies: - supports-color - twoslash@0.3.2(typescript@5.7.3): + twoslash@0.3.3(typescript@5.7.3): dependencies: '@typescript/vfs': 1.6.1(typescript@5.7.3) - twoslash-protocol: 0.3.2 + twoslash-protocol: 0.3.3 typescript: 5.7.3 transitivePeerDependencies: - supports-color @@ -13158,7 +13169,7 @@ snapshots: dependencies: '@docsearch/css': 3.9.0 '@docsearch/js': 3.9.0(@algolia/client-search@5.20.3)(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) - '@iconify-json/simple-icons': 1.2.43 + '@iconify-json/simple-icons': 1.2.44 '@shikijs/core': 3.7.0 '@shikijs/transformers': 3.7.0 '@shikijs/types': 3.7.0 @@ -13271,7 +13282,7 @@ snapshots: with@7.0.2: dependencies: '@babel/parser': 7.28.0 - '@babel/types': 7.28.0 + '@babel/types': 7.27.7 assert-never: 1.4.0 babel-walk: 3.0.0-canary-5