mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-28 08:32:06 +00:00
refactor: improve regexp performance (#27207)
This commit is contained in:
parent
7189dafd26
commit
b96b62ecd2
@ -5,6 +5,7 @@ import noOnlyTests from 'eslint-plugin-no-only-tests'
|
||||
import typegen from 'eslint-typegen'
|
||||
// @ts-expect-error missing types
|
||||
import perfectionist from 'eslint-plugin-perfectionist'
|
||||
import regex from 'eslint-plugin-regexp'
|
||||
|
||||
export default createConfigForNuxt({
|
||||
features: {
|
||||
@ -216,6 +217,9 @@ export default createConfigForNuxt({
|
||||
'vue/multi-word-component-names': 'off',
|
||||
},
|
||||
},
|
||||
|
||||
// @ts-ignore types misaligned
|
||||
regex.configs['flat/recommended'],
|
||||
)
|
||||
|
||||
// Generate type definitions for the eslint config
|
||||
|
@ -64,6 +64,7 @@
|
||||
"eslint": "9.2.0",
|
||||
"eslint-plugin-no-only-tests": "3.1.0",
|
||||
"eslint-plugin-perfectionist": "2.10.0",
|
||||
"eslint-plugin-regexp": "^2.5.0",
|
||||
"eslint-typegen": "0.2.4",
|
||||
"execa": "9.1.0",
|
||||
"fs-extra": "11.2.0",
|
||||
|
@ -4,7 +4,7 @@ import type { Nuxt, NuxtCompatibility, NuxtCompatibilityIssues } from '@nuxt/sch
|
||||
import { useNuxt } from './context'
|
||||
|
||||
export function normalizeSemanticVersion (version: string) {
|
||||
return version.replace(/-[0-9]+\.[0-9a-f]+/, '') // Remove edge prefix
|
||||
return version.replace(/-\d+\.[0-9a-f]+/, '') // Remove edge prefix
|
||||
}
|
||||
|
||||
const builderMap = {
|
||||
|
@ -27,7 +27,7 @@ export async function compileTemplate<T> (template: NuxtTemplate<T>, ctx: any) {
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"{(.+)}"(?=,?$)/gm, r => JSON.parse(r).replace(/^{(.*)}$/, '$1'))
|
||||
const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"\{(.+)\}"(?=,?$)/gm, r => JSON.parse(r).replace(/^\{(.*)\}$/, '$1'))
|
||||
|
||||
/** @deprecated */
|
||||
const importSources = (sources: string | string[], { lazy = false } = {}) => {
|
||||
|
@ -94,7 +94,7 @@ function applyEnv (
|
||||
return obj
|
||||
}
|
||||
|
||||
const envExpandRx = /{{(.*?)}}/g
|
||||
const envExpandRx = /\{\{(.*?)\}\}/g
|
||||
|
||||
function _expandFromEnv (value: string, env: Record<string, any> = process.env) {
|
||||
return value.replace(envExpandRx, (match, key) => {
|
||||
|
@ -202,7 +202,7 @@ export async function _generateTypes (nuxt: Nuxt) {
|
||||
} else {
|
||||
const path = stats?.isFile()
|
||||
// remove extension
|
||||
? relativePath.replace(/(?<=\w)\.\w+$/g, '')
|
||||
? relativePath.replace(/\b\.\w+$/g, '')
|
||||
// non-existent file probably shouldn't be resolved
|
||||
: aliases[alias]
|
||||
|
||||
@ -230,7 +230,7 @@ export async function _generateTypes (nuxt: Nuxt) {
|
||||
tsConfig.compilerOptions!.paths[alias] = await Promise.all(paths.map(async (path: string) => {
|
||||
if (!isAbsolute(path)) { return path }
|
||||
const stats = await fsp.stat(path).catch(() => null /* file does not exist */)
|
||||
return relativeWithDot(nuxt.options.buildDir, stats?.isFile() ? path.replace(/(?<=\w)\.\w+$/g, '') /* remove extension */ : path)
|
||||
return relativeWithDot(nuxt.options.buildDir, stats?.isFile() ? path.replace(/\b\.\w+$/g, '') /* remove extension */ : path)
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ interface LoaderOptions {
|
||||
transform?: ComponentsOptions['transform']
|
||||
rootDir: string
|
||||
}
|
||||
const CLIENT_FALLBACK_RE = /<(NuxtClientFallback|nuxt-client-fallback)( [^>]*)?>/
|
||||
const CLIENT_FALLBACK_RE = /<(?:NuxtClientFallback|nuxt-client-fallback)(?: [^>]*)?>/
|
||||
const CLIENT_FALLBACK_GLOBAL_RE = /<(NuxtClientFallback|nuxt-client-fallback)( [^>]*)?>/g
|
||||
export const clientFallbackAutoIdPlugin = createUnplugin((options: LoaderOptions) => {
|
||||
const exclude = options.transform?.exclude || []
|
||||
@ -37,7 +37,7 @@ export const clientFallbackAutoIdPlugin = createUnplugin((options: LoaderOptions
|
||||
|
||||
s.replace(CLIENT_FALLBACK_GLOBAL_RE, (full, name, attrs) => {
|
||||
count++
|
||||
if (/ :?uid=/g.test(attrs)) { return full }
|
||||
if (/ :?uid=/.test(attrs)) { return full }
|
||||
return `<${name} :uid="'${hash(relativeID)}' + JSON.stringify($props) + '${count}'" ${attrs ?? ''}>`
|
||||
})
|
||||
|
||||
|
@ -25,7 +25,7 @@ interface ComponentChunkOptions {
|
||||
}
|
||||
|
||||
const SCRIPT_RE = /<script[^>]*>/g
|
||||
const HAS_SLOT_OR_CLIENT_RE = /(<slot[^>]*>)|(nuxt-client)/
|
||||
const HAS_SLOT_OR_CLIENT_RE = /<slot[^>]*>|nuxt-client/
|
||||
const TEMPLATE_RE = /<template>([\s\S]*)<\/template>/
|
||||
const NUXTCLIENT_ATTR_RE = /\s:?nuxt-client(="[^"]*")?/g
|
||||
const IMPORT_CODE = '\nimport { vforToArray as __vforToArray } from \'#app/components/utils\'' + '\nimport NuxtTeleportIslandComponent from \'#app/components/nuxt-teleport-island-component\'' + '\nimport NuxtTeleportSsrSlot from \'#app/components/nuxt-teleport-island-slot\''
|
||||
|
@ -43,7 +43,7 @@ export const loaderPlugin = createUnplugin((options: LoaderOptions) => {
|
||||
const s = new MagicString(code)
|
||||
|
||||
// replace `_resolveComponent("...")` to direct import
|
||||
s.replace(/(?<=[ (])_?resolveComponent\(\s*["'](lazy-|Lazy)?([^'"]*?)["'][\s,]*[^)]*\)/g, (full: string, lazy: string, name: string) => {
|
||||
s.replace(/(?<=[ (])_?resolveComponent\(\s*["'](lazy-|Lazy)?([^'"]*)["'][^)]*\)/g, (full: string, lazy: string, name: string) => {
|
||||
const component = findComponent(components, name, options.mode)
|
||||
if (component) {
|
||||
// @ts-expect-error TODO: refactor to nuxi
|
||||
|
@ -18,7 +18,7 @@ function compareDirByPathLength ({ path: pathA }: { path: string }, { path: path
|
||||
return pathB.split(/[\\/]/).filter(Boolean).length - pathA.split(/[\\/]/).filter(Boolean).length
|
||||
}
|
||||
|
||||
const DEFAULT_COMPONENTS_DIRS_RE = /\/components(\/global|\/islands)?$/
|
||||
const DEFAULT_COMPONENTS_DIRS_RE = /\/components\/(?:global|islands)?$/
|
||||
|
||||
export type getComponentsT = (mode?: 'client' | 'server' | 'all') => Component[]
|
||||
|
||||
|
@ -83,8 +83,8 @@ export async function scanComponents (dirs: ComponentsDir[], srcDir: string): Pr
|
||||
*/
|
||||
let fileName = basename(filePath, extname(filePath))
|
||||
|
||||
const island = /\.(island)(\.global)?$/.test(fileName) || dir.island
|
||||
const global = /\.(global)(\.island)?$/.test(fileName) || dir.global
|
||||
const island = /\.island(?:\.global)?$/.test(fileName) || dir.island
|
||||
const global = /\.global(?:\.island)?$/.test(fileName) || dir.global
|
||||
const mode = island ? 'server' : (fileName.match(/(?<=\.)(client|server)(\.global|\.island)*$/)?.[1] || 'all') as 'client' | 'server' | 'all'
|
||||
fileName = fileName.replace(/(\.(client|server))?(\.global|\.island)*$/, '')
|
||||
|
||||
|
@ -104,8 +104,8 @@ export const componentsTypeTemplate = {
|
||||
const buildDir = nuxt.options.buildDir
|
||||
const componentTypes = app.components.filter(c => !c.island).map((c) => {
|
||||
const type = `typeof ${genDynamicImport(isAbsolute(c.filePath)
|
||||
? relative(buildDir, c.filePath).replace(/(?<=\w)\.(?!vue)\w+$/g, '')
|
||||
: c.filePath.replace(/(?<=\w)\.(?!vue)\w+$/g, ''), { wrapper: false })}['${c.export}']`
|
||||
? relative(buildDir, c.filePath).replace(/\b\.(?!vue)\w+$/g, '')
|
||||
: c.filePath.replace(/\b\.(?!vue)\w+$/g, ''), { wrapper: false })}['${c.export}']`
|
||||
return [
|
||||
c.pascalName,
|
||||
c.island || c.mode === 'server' ? `IslandComponent<${type}>` : type,
|
||||
|
@ -16,7 +16,7 @@ interface TreeShakeTemplatePluginOptions {
|
||||
type AcornNode<N extends Node> = N & { start: number, end: number }
|
||||
|
||||
const SSR_RENDER_RE = /ssrRenderComponent/
|
||||
const PLACEHOLDER_EXACT_RE = /^(fallback|placeholder)$/
|
||||
const PLACEHOLDER_EXACT_RE = /^(?:fallback|placeholder)$/
|
||||
const CLIENT_ONLY_NAME_RE = /^(?:_unref\()?(?:_component_)?(?:Lazy|lazy_)?(?:client_only|ClientOnly\)?)$/
|
||||
const PARSER_OPTIONS = { sourceType: 'module', ecmaVersion: 'latest' }
|
||||
|
||||
|
@ -24,7 +24,7 @@ export async function build (nuxt: Nuxt) {
|
||||
if (event === 'change') { return }
|
||||
const path = resolve(nuxt.options.srcDir, relativePath)
|
||||
const relativePaths = nuxt.options._layers.map(l => relative(l.config.srcDir || l.cwd, path))
|
||||
const restartPath = relativePaths.find(relativePath => /^(app\.|error\.|plugins\/|middleware\/|layouts\/)/i.test(relativePath))
|
||||
const restartPath = relativePaths.find(relativePath => /^(?:app\.|error\.|plugins\/|middleware\/|layouts\/)/i.test(relativePath))
|
||||
if (restartPath) {
|
||||
if (restartPath.startsWith('app')) {
|
||||
app.mainComponent = undefined
|
||||
|
@ -383,7 +383,7 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
||||
tsConfig.compilerOptions.paths[alias] = [absolutePath]
|
||||
tsConfig.compilerOptions.paths[`${alias}/*`] = [`${absolutePath}/*`]
|
||||
} else {
|
||||
tsConfig.compilerOptions.paths[alias] = [absolutePath.replace(/(?<=\w)\.\w+$/g, '')] /* remove extension */
|
||||
tsConfig.compilerOptions.paths[alias] = [absolutePath.replace(/\b\.\w+$/g, '')] /* remove extension */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -621,4 +621,4 @@ export async function loadNuxt (opts: LoadNuxtOptions): Promise<Nuxt> {
|
||||
return nuxt
|
||||
}
|
||||
|
||||
const RESTART_RE = /^(app|error|app\.config)\.(js|ts|mjs|jsx|tsx|vue)$/i
|
||||
const RESTART_RE = /^(?:app|error|app\.config)\.(?:js|ts|mjs|jsx|tsx|vue)$/i
|
||||
|
@ -22,7 +22,7 @@ export const nuxtImportProtections = (nuxt: { options: NuxtOptions }, options: {
|
||||
])
|
||||
|
||||
patterns.push([
|
||||
/^((|~|~~|@|@@)\/)?nuxt\.config(\.|$)/,
|
||||
/^((~|~~|@|@@)?\/)?nuxt\.config(\.|$)/,
|
||||
'Importing directly from a `nuxt.config` file is not allowed. Instead, use runtime config or a module.',
|
||||
])
|
||||
|
||||
|
@ -70,8 +70,8 @@ function getStack () {
|
||||
return stack.stack?.replace(EXCLUDE_TRACE_RE, '').replace(/^Error.*\n/, '') || ''
|
||||
}
|
||||
|
||||
const FILENAME_RE = /at.*\(([^:)]+)[):]/
|
||||
const FILENAME_RE_GLOBAL = /at.*\(([^)]+)\)/g
|
||||
const FILENAME_RE = /at[^(]*\(([^:)]+)[):]/
|
||||
const FILENAME_RE_GLOBAL = /at[^(]*\(([^)]+)\)/g
|
||||
function extractFilenameFromStack (stacktrace: string) {
|
||||
return stacktrace.match(FILENAME_RE)?.[1].replace(withTrailingSlash(rootDir), '')
|
||||
}
|
||||
|
@ -646,7 +646,7 @@ function getServerComponentHTML (body: string[]): string {
|
||||
|
||||
const SSR_SLOT_TELEPORT_MARKER = /^uid=([^;]*);slot=(.*)$/
|
||||
const SSR_CLIENT_TELEPORT_MARKER = /^uid=([^;]*);client=(.*)$/
|
||||
const SSR_CLIENT_SLOT_MARKER = /^island-slot=(?:[^;]*);(.*)$/
|
||||
const SSR_CLIENT_SLOT_MARKER = /^island-slot=[^;]*;(.*)$/
|
||||
|
||||
function getSlotIslandResponse (ssrContext: NuxtSSRContext): NuxtIslandResponse['slots'] {
|
||||
if (!ssrContext.islandContext || !Object.keys(ssrContext.islandContext.slots).length) { return undefined }
|
||||
|
@ -269,7 +269,7 @@ export const appConfigDeclarationTemplate: NuxtTemplate = {
|
||||
return `
|
||||
import type { CustomAppConfig } from 'nuxt/schema'
|
||||
import type { Defu } from 'defu'
|
||||
${app.configs.map((id: string, index: number) => `import ${`cfg${index}`} from ${JSON.stringify(id.replace(/(?<=\w)\.\w+$/g, ''))}`).join('\n')}
|
||||
${app.configs.map((id: string, index: number) => `import ${`cfg${index}`} from ${JSON.stringify(id.replace(/\b\.\w+$/g, ''))}`).join('\n')}
|
||||
|
||||
declare const inlineConfig = ${JSON.stringify(nuxt.options.appConfig, null, 2)}
|
||||
type ResolvedAppConfig = Defu<typeof inlineConfig, [${app.configs.map((_id: string, index: number) => `typeof cfg${index}`).join(', ')}]>
|
||||
|
@ -34,7 +34,7 @@ export function isVue (id: string, opts: { type?: Array<'template' | 'script' |
|
||||
return true
|
||||
}
|
||||
|
||||
const JS_RE = /\.((c|m)?j|t)sx?$/
|
||||
const JS_RE = /\.(?:[cm]?j|t)sx?$/
|
||||
|
||||
export function isJS (id: string) {
|
||||
// JavaScript files
|
||||
|
@ -23,7 +23,7 @@ export default defineNuxtPlugin(async () => {
|
||||
|
||||
// Implementation
|
||||
|
||||
const OPTIONAL_PARAM_RE = /^\/?:.*(\?|\(\.\*\)\*)$/
|
||||
const OPTIONAL_PARAM_RE = /^\/?:.*(?:\?|\(\.\*\)\*)$/
|
||||
|
||||
function processRoutes (routes: RouteRecordRaw[], currentPath = '/', routesToPrerender = new Set<string>()) {
|
||||
for (const route of routes) {
|
||||
|
@ -135,7 +135,7 @@ export async function generateRoutesFromFiles (files: ScannedFile[], options: Ge
|
||||
return prepareRoutes(routes)
|
||||
}
|
||||
|
||||
const SFC_SCRIPT_RE = /<script\s*[^>]*>([\s\S]*?)<\/script\s*[^>]*>/i
|
||||
const SFC_SCRIPT_RE = /<script[^>]*>([\s\S]*?)<\/script[^>]*>/i
|
||||
export function extractScriptContent (html: string) {
|
||||
const match = html.match(SFC_SCRIPT_RE)
|
||||
|
||||
@ -146,7 +146,7 @@ export function extractScriptContent (html: string) {
|
||||
return null
|
||||
}
|
||||
|
||||
const PAGE_META_RE = /(definePageMeta\([\s\S]*?\))/
|
||||
const PAGE_META_RE = /definePageMeta\([\s\S]*?\)/
|
||||
const DYNAMIC_META_KEY = '__nuxt_dynamic_meta_key' as const
|
||||
|
||||
const pageContentsCache: Record<string, string> = {}
|
||||
@ -261,7 +261,7 @@ function getRoutePath (tokens: SegmentToken[]): string {
|
||||
}, '/')
|
||||
}
|
||||
|
||||
const PARAM_CHAR_RE = /[\w\d_.]/
|
||||
const PARAM_CHAR_RE = /[\w.]/
|
||||
|
||||
function parseSegment (segment: string) {
|
||||
let state: SegmentParserState = SegmentParserState.initial
|
||||
@ -537,7 +537,7 @@ export function pathToNitroGlob (path: string) {
|
||||
return null
|
||||
}
|
||||
|
||||
return path.replace(/\/(?:[^:/]+)?:\w+.*$/, '/**')
|
||||
return path.replace(/\/[^:/]*:\w.*$/, '/**')
|
||||
}
|
||||
|
||||
export function resolveRoutePaths (page: NuxtPage, parent = '/'): string[] {
|
||||
|
@ -182,7 +182,7 @@ describe('treeshake client only in ssr', () => {
|
||||
expect(treeshaken).not.toContain('ssrRenderComponent(_unref(HelloWorld')
|
||||
expect(treeshaken).toContain('ssrRenderComponent(_unref(Glob')
|
||||
}
|
||||
expect(treeshaken.replace(/data-v-[\d\w]{8}/g, 'data-v-one-hash').replace(/scoped=[\d\w]{8}/g, 'scoped=one-hash')).toMatchSnapshot()
|
||||
expect(treeshaken.replace(/data-v-\w{8}/g, 'data-v-one-hash').replace(/scoped=\w{8}/g, 'scoped=one-hash')).toMatchSnapshot()
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ export const DevRenderingPlugin = () => {
|
||||
const messages = JSON.parse(await fsp.readFile(r(page, 'messages.json'), 'utf-8'))
|
||||
|
||||
return template(contents, {
|
||||
interpolate: /{{{?([\s\S]+?)}?}}/g,
|
||||
interpolate: /\{\{\{?([\s\S]+?)\}?\}\}/g,
|
||||
})({
|
||||
messages: { ...genericMessages, ...messages },
|
||||
})
|
||||
|
@ -58,7 +58,7 @@ export const RenderPlugin = () => {
|
||||
}
|
||||
|
||||
// Inline our scripts
|
||||
const scriptSources = Array.from(html.matchAll(/<script[^>]*src="(.*)"[^>]*>[\s\S]*?<\/script>/g))
|
||||
const scriptSources = Array.from(html.matchAll(/<script[^>]*src="([^"]*)"[^>]*>[\s\S]*?<\/script>/g))
|
||||
.filter(([_block, src]) => src?.match(/^\/.*\.js$/))
|
||||
|
||||
for (const [scriptBlock, src] of scriptSources) {
|
||||
@ -79,10 +79,10 @@ export const RenderPlugin = () => {
|
||||
const messages = JSON.parse(readFileSync(r(`templates/${templateName}/messages.json`), 'utf-8'))
|
||||
|
||||
// Serialize into a js function
|
||||
const chunks = html.split(/\{{2,3}\s*[^{}]+\s*\}{2,3}/g).map(chunk => JSON.stringify(chunk))
|
||||
const chunks = html.split(/\{{2,3}[^{}]+\}{2,3}/g).map(chunk => JSON.stringify(chunk))
|
||||
const hasMessages = chunks.length > 1
|
||||
let templateString = chunks.shift()
|
||||
for (const expression of html.matchAll(/\{{2,3}(\s*[^{}]+\s*)\}{2,3}/g)) {
|
||||
for (const expression of html.matchAll(/\{{2,3}([^{}]+)\}{2,3}/g)) {
|
||||
templateString += ` + (${expression[1].trim()}) + ${chunks.shift()}`
|
||||
}
|
||||
if (chunks.length > 0) {
|
||||
@ -98,28 +98,28 @@ export const RenderPlugin = () => {
|
||||
].join('\n')
|
||||
|
||||
const templateContent = html
|
||||
.match(/<body.*?>([\s\S]*)<\/body>/)?.[0]
|
||||
.replace(/(?<=<|<\/)body/g, 'div')
|
||||
.match(/<body[^>]*>([\s\S]*)<\/body>/)?.[0]
|
||||
.replace(/(?<=<\/|<)body/g, 'div')
|
||||
.replace(/messages\./g, '')
|
||||
.replace(/<script[^>]*>([\s\S]*?)<\/script>/g, '')
|
||||
.replace(/<a href="(\/[^"]*)"([^>]*)>([\s\S]*)<\/a>/g, '<NuxtLink to="$1"$2>\n$3\n</NuxtLink>')
|
||||
|
||||
.replace(/<([^>]+) ([a-z]+)="([^"]*)({{\s*(\w+?)\s*}})([^"]*)"([^>]*)>/g, '<$1 :$2="`$3${$5}$6`"$7>')
|
||||
.replace(/>{{\s*(\w+?)\s*}}<\/[\w-]*>/g, ' v-text="$1" />')
|
||||
.replace(/>{{{\s*(\w+?)\s*}}}<\/[\w-]*>/g, ' v-html="$1" />')
|
||||
.replace(/<([^>]+) ([a-z]+)="([^"]*)(\{\{\s*(\w+)\s*\}\})([^"]*)"([^>]*)>/g, '<$1 :$2="`$3${$5}$6`"$7>')
|
||||
.replace(/>\{\{\s*(\w+)\s*\}\}<\/[\w-]*>/g, ' v-text="$1" />')
|
||||
.replace(/>\{\{\{\s*(\w+)\s*\}\}\}<\/[\w-]*>/g, ' v-html="$1" />')
|
||||
// We are not matching <link> <script> and <meta> tags as these aren't used yet in nuxt/ui
|
||||
// and should be taken care of wherever this SFC is used
|
||||
const title = html.match(/<title.*?>([\s\S]*)<\/title>/)?.[1].replace(/{{([\s\S]+?)}}/g, (r) => {
|
||||
const title = html.match(/<title[^>]*>([\s\S]*)<\/title>/)?.[1].replace(/\{\{([\s\S]+?)\}\}/g, (r) => {
|
||||
return `\${${r.slice(2, -2)}}`.replace(/messages\./g, 'props.')
|
||||
})
|
||||
const styleContent = Array.from(html.matchAll(/<style[^>]*>([\s\S]*?)<\/style>/g)).map(block => block[1]).join('\n')
|
||||
const globalStyles = styleContent.replace(/(\.[^{\d][^{]*?\{[^}]*?\})+.?/g, (r) => {
|
||||
const globalStyles = styleContent.replace(/(\.[^{\d][^{]*\{[^}]*\})+.?/g, (r) => {
|
||||
const lastChar = r[r.length - 1]
|
||||
if (lastChar && !['}', '.', '@', '*', ':'].includes(lastChar)) {
|
||||
return ';' + lastChar
|
||||
}
|
||||
return lastChar
|
||||
}).replace(/@media[^{]*?\{\}/g, '')
|
||||
}).replace(/@media[^{]*\{\}/g, '')
|
||||
const inlineScripts = Array.from(html.matchAll(/<script>([\s\S]*?)<\/script>/g))
|
||||
.map(block => block[1])
|
||||
.filter(i => !i.includes('const t=document.createElement("link")'))
|
||||
|
@ -18,8 +18,8 @@ interface ComposableKeysOptions {
|
||||
}
|
||||
|
||||
const stringTypes = ['Literal', 'TemplateLiteral']
|
||||
const NUXT_LIB_RE = /node_modules\/(nuxt|nuxt3|nuxt-nightly)\//
|
||||
const SUPPORTED_EXT_RE = /\.(m?[jt]sx?|vue)/
|
||||
const NUXT_LIB_RE = /node_modules\/(?:nuxt|nuxt3|nuxt-nightly)\//
|
||||
const SUPPORTED_EXT_RE = /\.(?:m?[jt]sx?|vue)/
|
||||
|
||||
export const composableKeysPlugin = createUnplugin((options: ComposableKeysOptions) => {
|
||||
const composableMeta: Record<string, any> = {}
|
||||
@ -40,7 +40,7 @@ export const composableKeysPlugin = createUnplugin((options: ComposableKeysOptio
|
||||
},
|
||||
transform (code, id) {
|
||||
if (!KEYED_FUNCTIONS_RE.test(code)) { return }
|
||||
const { 0: script = code, index: codeIndex = 0 } = code.match(/(?<=<script[^>]*>)[\S\s.]*?(?=<\/script>)/) || { index: 0, 0: code }
|
||||
const { 0: script = code, index: codeIndex = 0 } = code.match(/(?<=<script[^>]*>)[\s\S]*?(?=<\/script>)/) || { index: 0, 0: code }
|
||||
const s = new MagicString(code)
|
||||
// https://github.com/unjs/unplugin/issues/90
|
||||
let imports: Set<string> | undefined
|
||||
|
@ -21,7 +21,7 @@ interface SSRStylePluginOptions {
|
||||
mode: 'server' | 'client'
|
||||
}
|
||||
|
||||
const SUPPORTED_FILES_RE = /\.(vue|((c|m)?j|t)sx?)$/
|
||||
const SUPPORTED_FILES_RE = /\.(?:vue|(?:[cm]?j|t)sx?)$/
|
||||
|
||||
export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
||||
const cssMap: Record<string, { files: string[], inBundle: boolean }> = {}
|
||||
|
@ -7,7 +7,7 @@ export function uniq<T> (arr: T[]): T[] {
|
||||
}
|
||||
|
||||
// Copied from vue-bundle-renderer utils
|
||||
const IS_CSS_RE = /\.(?:css|scss|sass|postcss|pcss|less|stylus|styl)(\?[^.]+)?$/
|
||||
const IS_CSS_RE = /\.(?:css|scss|sass|postcss|pcss|less|stylus|styl)(?:\?[^.]+)?$/
|
||||
|
||||
export function isCSS (file: string) {
|
||||
return IS_CSS_RE.test(file)
|
||||
|
@ -7,7 +7,7 @@ import { applyPresets } from '../utils/config'
|
||||
import { nuxt } from '../presets/nuxt'
|
||||
import { node } from '../presets/node'
|
||||
|
||||
const assetPattern = /\.(css|s[ca]ss|png|jpe?g|gif|svg|woff2?|eot|ttf|otf|webp|webm|mp4|ogv)(\?.*)?$/i
|
||||
const assetPattern = /\.(?:css|s[ca]ss|png|jpe?g|gif|svg|woff2?|eot|ttf|otf|webp|webm|mp4|ogv)(?:\?.*)?$/i
|
||||
|
||||
export function server (ctx: WebpackConfigContext) {
|
||||
ctx.name = 'server'
|
||||
|
@ -25,6 +25,6 @@ export const isJS = (file: string) => isJSRegExp.test(file)
|
||||
|
||||
export const extractQueryPartJS = (file: string) => isJSRegExp.exec(file)?.[1]
|
||||
|
||||
export const isCSS = (file: string) => /\.css(\?[^.]+)?$/.test(file)
|
||||
export const isCSS = (file: string) => /\.css(?:\?[^.]+)?$/.test(file)
|
||||
|
||||
export const isHotUpdate = (file: string) => file.includes('hot-update')
|
||||
|
@ -55,7 +55,7 @@ export function fileName (ctx: WebpackConfigContext, key: string) {
|
||||
}
|
||||
|
||||
if (typeof fileName === 'string' && ctx.options.dev) {
|
||||
const hash = /\[(chunkhash|contenthash|hash)(?::(\d+))?]/.exec(fileName)
|
||||
const hash = /\[(chunkhash|contenthash|hash)(?::\d+)?\]/.exec(fileName)
|
||||
if (hash) {
|
||||
logger.warn(`Notice: Please do not use ${hash[1]} in dev mode to prevent memory leak`)
|
||||
}
|
||||
|
@ -77,6 +77,9 @@ importers:
|
||||
eslint-plugin-perfectionist:
|
||||
specifier: 2.10.0
|
||||
version: 2.10.0(eslint@9.2.0)(typescript@5.4.5)(vue-eslint-parser@9.4.2(eslint@9.2.0))
|
||||
eslint-plugin-regexp:
|
||||
specifier: ^2.5.0
|
||||
version: 2.5.0(eslint@9.2.0)
|
||||
eslint-typegen:
|
||||
specifier: 0.2.4
|
||||
version: 0.2.4(eslint@9.2.0)
|
||||
@ -3962,6 +3965,12 @@ packages:
|
||||
vue-eslint-parser:
|
||||
optional: true
|
||||
|
||||
eslint-plugin-regexp@2.5.0:
|
||||
resolution: {integrity: sha512-I7vKcP0o75WS5SHiVNXN+Eshq49sbrweMQIuqSL3AId9AwDe9Dhbfug65vw64LxmOd4v+yf5l5Xt41y9puiq0g==}
|
||||
engines: {node: ^18 || >=20}
|
||||
peerDependencies:
|
||||
eslint: '>=8.44.0'
|
||||
|
||||
eslint-plugin-unicorn@52.0.0:
|
||||
resolution: {integrity: sha512-1Yzm7/m+0R4djH0tjDjfVei/ju2w3AzUGjG6q8JnuNIL5xIwsflyCooW5sfBvQp2pMYQFSWWCFONsjCax1EHng==}
|
||||
engines: {node: '>=16'}
|
||||
@ -6147,9 +6156,17 @@ packages:
|
||||
resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
refa@0.12.1:
|
||||
resolution: {integrity: sha512-J8rn6v4DBb2nnFqkqwy6/NnTYMcgLA+sLr0iIO41qpv0n+ngb7ksag2tMRl0inb1bbO/esUwzW1vbJi7K0sI0g==}
|
||||
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
|
||||
|
||||
regenerator-runtime@0.14.1:
|
||||
resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
|
||||
|
||||
regexp-ast-analysis@0.7.1:
|
||||
resolution: {integrity: sha512-sZuz1dYW/ZsfG17WSAG7eS85r5a0dDsvg+7BiiYR5o6lKCAtUrEwdmRmaGF6rwVj3LcmAeYkOWKEPlbPzN3Y3A==}
|
||||
engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
|
||||
|
||||
regexp-tree@0.1.27:
|
||||
resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==}
|
||||
hasBin: true
|
||||
@ -6306,6 +6323,10 @@ packages:
|
||||
resolution: {integrity: sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==}
|
||||
engines: {node: '>= 12.13.0'}
|
||||
|
||||
scslre@0.3.0:
|
||||
resolution: {integrity: sha512-3A6sD0WYP7+QrjbfNA2FN3FsOaGGFoekCVgTyypy53gPxhbkCIjtO6YWgdrfM+n/8sI8JeXZOIxsHjMTNxQ4nQ==}
|
||||
engines: {node: ^14.0.0 || >=16.0.0}
|
||||
|
||||
scule@1.3.0:
|
||||
resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==}
|
||||
|
||||
@ -10806,6 +10827,17 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
eslint-plugin-regexp@2.5.0(eslint@9.2.0):
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.0(eslint@9.2.0)
|
||||
'@eslint-community/regexpp': 4.10.0
|
||||
comment-parser: 1.4.1
|
||||
eslint: 9.2.0
|
||||
jsdoc-type-pratt-parser: 4.0.0
|
||||
refa: 0.12.1
|
||||
regexp-ast-analysis: 0.7.1
|
||||
scslre: 0.3.0
|
||||
|
||||
eslint-plugin-unicorn@52.0.0(eslint@9.2.0):
|
||||
dependencies:
|
||||
'@babel/helper-validator-identifier': 7.24.5
|
||||
@ -13437,8 +13469,17 @@ snapshots:
|
||||
dependencies:
|
||||
redis-errors: 1.2.0
|
||||
|
||||
refa@0.12.1:
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.10.0
|
||||
|
||||
regenerator-runtime@0.14.1: {}
|
||||
|
||||
regexp-ast-analysis@0.7.1:
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.10.0
|
||||
refa: 0.12.1
|
||||
|
||||
regexp-tree@0.1.27: {}
|
||||
|
||||
regexp.prototype.flags@1.5.1:
|
||||
@ -13666,6 +13707,12 @@ snapshots:
|
||||
ajv-formats: 2.1.1(ajv@8.12.0)
|
||||
ajv-keywords: 5.1.0(ajv@8.12.0)
|
||||
|
||||
scslre@0.3.0:
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.10.0
|
||||
refa: 0.12.1
|
||||
regexp-ast-analysis: 0.7.1
|
||||
|
||||
scule@1.3.0: {}
|
||||
|
||||
semver@5.7.2: {}
|
||||
|
@ -42,7 +42,7 @@ async function main () {
|
||||
currentPR?.body.replace(/## 👉 Changelog[\s\S]*$/, '') || `> ${newVersion} is the next ${bumpType} release.\n>\n> **Timetable**: to be announced.`,
|
||||
'## 👉 Changelog',
|
||||
changelog
|
||||
.replace(/^## v.*?\n/, '')
|
||||
.replace(/^## v.*\n/, '')
|
||||
.replace(`...${releaseBranch}`, `...v${newVersion}`)
|
||||
.replace(/### ❤️ Contributors[\s\S]*$/, ''),
|
||||
'### ❤️ Contributors',
|
||||
|
@ -600,7 +600,7 @@ describe('nuxt composables', () => {
|
||||
const { id1, id2 } = html.match(/<div[^>]* data-prehydrate-id=":(?<id1>[^:]+)::(?<id2>[^:]+):"> onPrehydrate testing <\/div>/)?.groups || {}
|
||||
expect(id1).toBeTruthy()
|
||||
const matches = [
|
||||
html.match(/<script[^>]*>\(\(\)=>{console.log\(window\)}\)\(\)<\/script>/),
|
||||
html.match(/<script[^>]*>\(\(\)=>\{console.log\(window\)\}\)\(\)<\/script>/),
|
||||
html.match(new RegExp(`<script[^>]*>document.querySelectorAll\\('\\[data-prehydrate-id\\*=":${id1}:"]'\\).forEach\\(o=>{console.log\\(o.outerHTML\\)}\\)</script>`)),
|
||||
html.match(new RegExp(`<script[^>]*>document.querySelectorAll\\('\\[data-prehydrate-id\\*=":${id2}:"]'\\).forEach\\(o=>{console.log\\("other",o.outerHTML\\)}\\)</script>`)),
|
||||
]
|
||||
@ -1911,7 +1911,7 @@ describe('public directories', () => {
|
||||
describe.skipIf(isDev())('dynamic paths', () => {
|
||||
it('should work with no overrides', async () => {
|
||||
const html: string = await $fetch('/assets')
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"|url\(([^)]*?)\)/g)) {
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"|url\(([^)]*)\)/g)) {
|
||||
const url = match[2] || match[3]
|
||||
expect(url.startsWith('/_nuxt/') || url === '/public.svg').toBeTruthy()
|
||||
}
|
||||
@ -1920,11 +1920,11 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
||||
// webpack injects CSS differently
|
||||
it.skipIf(isWebpack)('adds relative paths to CSS', async () => {
|
||||
const html: string = await $fetch('/assets')
|
||||
const urls = Array.from(html.matchAll(/(href|src)="(.*?)"|url\(([^)]*?)\)/g)).map(m => m[2] || m[3])
|
||||
const urls = Array.from(html.matchAll(/(href|src)="(.*?)"|url\(([^)]*)\)/g)).map(m => m[2] || m[3])
|
||||
const cssURL = urls.find(u => /_nuxt\/assets.*\.css$/.test(u))
|
||||
expect(cssURL).toBeDefined()
|
||||
const css: string = await $fetch(cssURL!)
|
||||
const imageUrls = Array.from(css.matchAll(/url\(([^)]*)\)/g)).map(m => m[1].replace(/[-.][\w]{8}\./g, '.'))
|
||||
const imageUrls = Array.from(css.matchAll(/url\(([^)]*)\)/g)).map(m => m[1].replace(/[-.]\w{8}\./g, '.'))
|
||||
expect(imageUrls).toMatchInlineSnapshot(`
|
||||
[
|
||||
"./logo.svg",
|
||||
@ -1944,7 +1944,7 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
||||
})
|
||||
|
||||
const html = await $fetch('/foo/assets')
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"|url\(([^)]*?)\)/g)) {
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"|url\(([^)]*)\)/g)) {
|
||||
const url = match[2] || match[3]
|
||||
expect(
|
||||
url.startsWith('/foo/_other/') ||
|
||||
@ -1965,7 +1965,7 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
||||
})
|
||||
|
||||
const html = await $fetch('/assets')
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"|url\(([^)]*?)\)/g)) {
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"|url\(([^)]*)\)/g)) {
|
||||
const url = match[2] || match[3]
|
||||
expect(
|
||||
url.startsWith('./_nuxt/') ||
|
||||
@ -1999,7 +1999,7 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
||||
})
|
||||
|
||||
const html = await $fetch('/foo/assets')
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"|url\(([^)]*?)\)/g)) {
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"|url\(([^)]*)\)/g)) {
|
||||
const url = match[2] || match[3]
|
||||
expect(
|
||||
url.startsWith('https://example.com/_cdn/') ||
|
||||
@ -2198,7 +2198,7 @@ describe('component islands', () => {
|
||||
const fixtureDir = normalize(fileURLToPath(new URL('./fixtures/basic', import.meta.url)))
|
||||
for (const link of result.head.link) {
|
||||
link.href = link.href.replace(fixtureDir, '/<rootDir>').replaceAll('//', '/')
|
||||
link.key = link.key.replace(/-[a-zA-Z0-9]+$/, '')
|
||||
link.key = link.key.replace(/-[a-z0-9]+$/i, '')
|
||||
}
|
||||
result.head.link.sort((a, b) => b.href.localeCompare(a.href))
|
||||
}
|
||||
@ -2580,7 +2580,7 @@ function normaliseIslandResult (result: NuxtIslandResponse) {
|
||||
style: result.head.style.map(s => ({
|
||||
...s,
|
||||
innerHTML: (s.innerHTML || '').replace(/data-v-[a-z0-9]+/, 'data-v-xxxxx').replace(/\.[a-zA-Z0-9]+\.svg/, '.svg'),
|
||||
key: s.key.replace(/-[a-zA-Z0-9]+$/, ''),
|
||||
key: s.key.replace(/-[a-z0-9]+$/i, ''),
|
||||
})),
|
||||
},
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ export function parseData (html: string) {
|
||||
}
|
||||
const { script, attrs } = html.match(/<script type="application\/json" id="__NUXT_DATA__"(?<attrs>[^>]+)>(?<script>.*?)<\/script>/)?.groups || {}
|
||||
const _attrs: Record<string, string> = {}
|
||||
for (const attr of attrs.matchAll(/( |^)(?<key>[\w-]+)+="(?<value>[^"]+)"/g)) {
|
||||
for (const attr of attrs.matchAll(/( |^)(?<key>[\w-]+)="(?<value>[^"]+)"/g)) {
|
||||
_attrs[attr!.groups!.key] = attr!.groups!.value
|
||||
}
|
||||
return {
|
||||
|
Loading…
Reference in New Issue
Block a user