mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-29 17:07:22 +00:00
Compare commits
7 Commits
df64cc4a87
...
485cf340f2
Author | SHA1 | Date | |
---|---|---|---|
|
485cf340f2 | ||
|
49545e6bdf | ||
|
f18278fe28 | ||
|
359aeeec88 | ||
|
980ebb0fbe | ||
|
0a0ec349a1 | ||
|
4a25e2bb50 |
@ -123,9 +123,6 @@ export async function updateTemplates (options?: { filter?: (template: ResolvedN
|
|||||||
return await tryUseNuxt()?.hooks.callHook('builder:generateApp', options)
|
return await tryUseNuxt()?.hooks.callHook('builder:generateApp', options)
|
||||||
}
|
}
|
||||||
|
|
||||||
const EXTENSION_RE = /\b\.\w+$/g
|
|
||||||
// Exclude bridge alias types to support Volar
|
|
||||||
const excludedAlias = [/^@vue\/.*$/, /^#internal\/nuxt/]
|
|
||||||
export async function _generateTypes (nuxt: Nuxt) {
|
export async function _generateTypes (nuxt: Nuxt) {
|
||||||
const rootDirWithSlash = withTrailingSlash(nuxt.options.rootDir)
|
const rootDirWithSlash = withTrailingSlash(nuxt.options.rootDir)
|
||||||
const relativeRootDir = relativeWithDot(nuxt.options.buildDir, nuxt.options.rootDir)
|
const relativeRootDir = relativeWithDot(nuxt.options.buildDir, nuxt.options.rootDir)
|
||||||
@ -227,7 +224,9 @@ export async function _generateTypes (nuxt: Nuxt) {
|
|||||||
} satisfies TSConfig)
|
} satisfies TSConfig)
|
||||||
|
|
||||||
const aliases: Record<string, string> = nuxt.options.alias
|
const aliases: Record<string, string> = nuxt.options.alias
|
||||||
|
const EXTENSION_RE = /\b\.\w+$/g
|
||||||
|
// Exclude bridge alias types to support Volar
|
||||||
|
const excludedAlias = [/^@vue\/.*$/, /^#internal\/nuxt/]
|
||||||
const basePath = tsConfig.compilerOptions!.baseUrl
|
const basePath = tsConfig.compilerOptions!.baseUrl
|
||||||
? resolve(nuxt.options.buildDir, tsConfig.compilerOptions!.baseUrl)
|
? resolve(nuxt.options.buildDir, tsConfig.compilerOptions!.baseUrl)
|
||||||
: nuxt.options.buildDir
|
: nuxt.options.buildDir
|
||||||
|
@ -21,9 +21,6 @@ function compareDirByPathLength ({ path: pathA }: { path: string }, { path: path
|
|||||||
return pathB.split(SLASH_SEPARATOR_RE).filter(Boolean).length - pathA.split(SLASH_SEPARATOR_RE).filter(Boolean).length
|
return pathB.split(SLASH_SEPARATOR_RE).filter(Boolean).length - pathA.split(SLASH_SEPARATOR_RE).filter(Boolean).length
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_COMPONENTS_DIRS_RE = /\/components(?:\/(?:global|islands))?$/
|
|
||||||
const STARTER_DOT_RE = /^\./g
|
|
||||||
|
|
||||||
export type getComponentsT = (mode?: 'client' | 'server' | 'all') => Component[]
|
export type getComponentsT = (mode?: 'client' | 'server' | 'all') => Component[]
|
||||||
|
|
||||||
export default defineNuxtModule<ComponentsOptions>({
|
export default defineNuxtModule<ComponentsOptions>({
|
||||||
@ -78,6 +75,8 @@ export default defineNuxtModule<ComponentsOptions>({
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const DEFAULT_COMPONENTS_DIRS_RE = /\/components(?:\/(?:global|islands))?$/
|
||||||
|
const STARTER_DOT_RE = /^\./g
|
||||||
// Resolve dirs
|
// Resolve dirs
|
||||||
nuxt.hook('app:resolve', async () => {
|
nuxt.hook('app:resolve', async () => {
|
||||||
// components/ dirs from all layers
|
// components/ dirs from all layers
|
||||||
|
@ -24,6 +24,8 @@ interface ComponentChunkOptions {
|
|||||||
buildDir: string
|
buildDir: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const IslandsTransformPlugin = (options: ServerOnlyComponentTransformPluginOptions) => createUnplugin((_options, meta) => {
|
||||||
|
const isVite = meta.framework === 'vite'
|
||||||
const SCRIPT_RE = /<script[^>]*>/gi
|
const SCRIPT_RE = /<script[^>]*>/gi
|
||||||
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 TEMPLATE_RE = /<template>([\s\S]*)<\/template>/
|
||||||
@ -36,8 +38,40 @@ function wrapWithVForDiv (code: string, vfor: string): string {
|
|||||||
return `<div v-for="${vfor}" style="display: contents;">${code}</div>`
|
return `<div v-for="${vfor}" style="display: contents;">${code}</div>`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const IslandsTransformPlugin = (options: ServerOnlyComponentTransformPluginOptions) => createUnplugin((_options, meta) => {
|
/**
|
||||||
const isVite = meta.framework === 'vite'
|
* extract attributes from a node
|
||||||
|
*/
|
||||||
|
function extractAttributes (attributes: Record<string, string>, names: string[]) {
|
||||||
|
const extracted: Record<string, string> = {}
|
||||||
|
for (const name of names) {
|
||||||
|
if (name in attributes) {
|
||||||
|
extracted[name] = attributes[name]!
|
||||||
|
delete attributes[name]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return extracted
|
||||||
|
}
|
||||||
|
|
||||||
|
function attributeToString (attributes: Record<string, string>) {
|
||||||
|
return Object.entries(attributes).map(([name, value]) => value ? ` ${name}="${value}"` : ` ${name}`).join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
function isBinding (attr: string): boolean {
|
||||||
|
return attr.startsWith(':')
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPropsToString (bindings: Record<string, string>): string {
|
||||||
|
const vfor = bindings['v-for']?.split(' in ').map((v: string) => v.trim()) as [string, string] | undefined
|
||||||
|
if (Object.keys(bindings).length === 0) { return 'undefined' }
|
||||||
|
const content = Object.entries(bindings).filter(b => b[0] && (b[0] !== '_bind' && b[0] !== 'v-for')).map(([name, value]) => isBinding(name) ? `[\`${name.slice(1)}\`]: ${value}` : `[\`${name}\`]: \`${value}\``).join(',')
|
||||||
|
const data = bindings._bind ? `__mergeProps(${bindings._bind}, { ${content} })` : `{ ${content} }`
|
||||||
|
if (!vfor) {
|
||||||
|
return `[${data}]`
|
||||||
|
} else {
|
||||||
|
return `__vforToArray(${vfor[1]}).map(${vfor[0]} => (${data}))`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'nuxt:server-only-component-transform',
|
name: 'nuxt:server-only-component-transform',
|
||||||
enforce: 'pre',
|
enforce: 'pre',
|
||||||
@ -145,40 +179,6 @@ export const IslandsTransformPlugin = (options: ServerOnlyComponentTransformPlug
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
|
||||||
* extract attributes from a node
|
|
||||||
*/
|
|
||||||
function extractAttributes (attributes: Record<string, string>, names: string[]) {
|
|
||||||
const extracted: Record<string, string> = {}
|
|
||||||
for (const name of names) {
|
|
||||||
if (name in attributes) {
|
|
||||||
extracted[name] = attributes[name]!
|
|
||||||
delete attributes[name]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return extracted
|
|
||||||
}
|
|
||||||
|
|
||||||
function attributeToString (attributes: Record<string, string>) {
|
|
||||||
return Object.entries(attributes).map(([name, value]) => value ? ` ${name}="${value}"` : ` ${name}`).join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
function isBinding (attr: string): boolean {
|
|
||||||
return attr.startsWith(':')
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPropsToString (bindings: Record<string, string>): string {
|
|
||||||
const vfor = bindings['v-for']?.split(' in ').map((v: string) => v.trim()) as [string, string] | undefined
|
|
||||||
if (Object.keys(bindings).length === 0) { return 'undefined' }
|
|
||||||
const content = Object.entries(bindings).filter(b => b[0] && (b[0] !== '_bind' && b[0] !== 'v-for')).map(([name, value]) => isBinding(name) ? `[\`${name.slice(1)}\`]: ${value}` : `[\`${name}\`]: \`${value}\``).join(',')
|
|
||||||
const data = bindings._bind ? `__mergeProps(${bindings._bind}, { ${content} })` : `{ ${content} }`
|
|
||||||
if (!vfor) {
|
|
||||||
return `[${data}]`
|
|
||||||
} else {
|
|
||||||
return `__vforToArray(${vfor[1]}).map(${vfor[0]} => (${data}))`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ComponentsChunkPlugin = createUnplugin((options: ComponentChunkOptions) => {
|
export const ComponentsChunkPlugin = createUnplugin((options: ComponentChunkOptions) => {
|
||||||
const { buildDir } = options
|
const { buildDir } = options
|
||||||
return {
|
return {
|
||||||
|
@ -15,108 +15,12 @@ interface TreeShakeTemplatePluginOptions {
|
|||||||
|
|
||||||
type AcornNode<N extends Node> = N & { start: number, end: number }
|
type AcornNode<N extends Node> = N & { start: number, end: number }
|
||||||
|
|
||||||
|
export const TreeShakeTemplatePlugin = (options: TreeShakeTemplatePluginOptions) => createUnplugin(() => {
|
||||||
const SSR_RENDER_RE = /ssrRenderComponent/
|
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 CLIENT_ONLY_NAME_RE = /^(?:_unref\()?(?:_component_)?(?:Lazy|lazy_)?(?:client_only|ClientOnly\)?)$/
|
||||||
const PARSER_OPTIONS = { sourceType: 'module', ecmaVersion: 'latest' }
|
const PARSER_OPTIONS = { sourceType: 'module', ecmaVersion: 'latest' }
|
||||||
|
|
||||||
export const TreeShakeTemplatePlugin = (options: TreeShakeTemplatePluginOptions) => createUnplugin(() => {
|
|
||||||
const regexpMap = new WeakMap<Component[], [RegExp, RegExp, string[]]>()
|
|
||||||
return {
|
|
||||||
name: 'nuxt:tree-shake-template',
|
|
||||||
enforce: 'post',
|
|
||||||
transformInclude (id) {
|
|
||||||
const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href))
|
|
||||||
return pathname.endsWith('.vue')
|
|
||||||
},
|
|
||||||
transform (code) {
|
|
||||||
const components = options.getComponents()
|
|
||||||
|
|
||||||
if (!regexpMap.has(components)) {
|
|
||||||
const serverPlaceholderPath = resolve(distDir, 'app/components/server-placeholder')
|
|
||||||
const clientOnlyComponents = components
|
|
||||||
.filter(c => c.mode === 'client' && !components.some(other => other.mode !== 'client' && other.pascalName === c.pascalName && !other.filePath.startsWith(serverPlaceholderPath)))
|
|
||||||
.flatMap(c => [c.pascalName, c.kebabName.replaceAll('-', '_')])
|
|
||||||
.concat(['ClientOnly', 'client_only'])
|
|
||||||
|
|
||||||
regexpMap.set(components, [new RegExp(`(${clientOnlyComponents.join('|')})`), new RegExp(`^(${clientOnlyComponents.map(c => `(?:(?:_unref\\()?(?:_component_)?(?:Lazy|lazy_)?${c}\\)?)`).join('|')})$`), clientOnlyComponents])
|
|
||||||
}
|
|
||||||
|
|
||||||
const s = new MagicString(code)
|
|
||||||
|
|
||||||
const [COMPONENTS_RE, COMPONENTS_IDENTIFIERS_RE] = regexpMap.get(components)!
|
|
||||||
if (!COMPONENTS_RE.test(code)) { return }
|
|
||||||
|
|
||||||
const codeAst = this.parse(code, PARSER_OPTIONS) as AcornNode<Program>
|
|
||||||
|
|
||||||
const componentsToRemoveSet = new Set<string>()
|
|
||||||
|
|
||||||
// remove client only components or components called in ClientOnly default slot
|
|
||||||
walk(codeAst, {
|
|
||||||
enter: (_node) => {
|
|
||||||
const node = _node as AcornNode<Node>
|
|
||||||
if (isSsrRender(node)) {
|
|
||||||
const [componentCall, _, children] = node.arguments
|
|
||||||
if (!componentCall) { return }
|
|
||||||
|
|
||||||
if (componentCall.type === 'Identifier' || componentCall.type === 'MemberExpression' || componentCall.type === 'CallExpression') {
|
|
||||||
const componentName = getComponentName(node)
|
|
||||||
const isClientComponent = COMPONENTS_IDENTIFIERS_RE.test(componentName)
|
|
||||||
const isClientOnlyComponent = CLIENT_ONLY_NAME_RE.test(componentName)
|
|
||||||
|
|
||||||
if (isClientComponent && children?.type === 'ObjectExpression') {
|
|
||||||
const slotsToRemove = isClientOnlyComponent ? children.properties.filter(prop => prop.type === 'Property' && prop.key.type === 'Identifier' && !PLACEHOLDER_EXACT_RE.test(prop.key.name)) as AcornNode<Property>[] : children.properties as AcornNode<Property>[]
|
|
||||||
|
|
||||||
for (const slot of slotsToRemove) {
|
|
||||||
s.remove(slot.start, slot.end + 1)
|
|
||||||
const removedCode = `({${code.slice(slot.start, slot.end + 1)}})`
|
|
||||||
const currentCodeAst = this.parse(s.toString(), PARSER_OPTIONS) as Node
|
|
||||||
|
|
||||||
walk(this.parse(removedCode, PARSER_OPTIONS) as Node, {
|
|
||||||
enter: (_node) => {
|
|
||||||
const node = _node as AcornNode<CallExpression>
|
|
||||||
if (isSsrRender(node)) {
|
|
||||||
const name = getComponentName(node)
|
|
||||||
|
|
||||||
// detect if the component is called else where
|
|
||||||
const nameToRemove = isComponentNotCalledInSetup(currentCodeAst, name)
|
|
||||||
if (nameToRemove) {
|
|
||||||
componentsToRemoveSet.add(nameToRemove)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const componentsToRemove = [...componentsToRemoveSet]
|
|
||||||
const removedNodes = new WeakSet<AcornNode<Node>>()
|
|
||||||
|
|
||||||
for (const componentName of componentsToRemove) {
|
|
||||||
// remove import declaration if it exists
|
|
||||||
removeImportDeclaration(codeAst, componentName, s)
|
|
||||||
// remove variable declaration
|
|
||||||
removeVariableDeclarator(codeAst, componentName, s, removedNodes)
|
|
||||||
// remove from setup return statement
|
|
||||||
removeFromSetupReturn(codeAst, componentName, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s.hasChanged()) {
|
|
||||||
return {
|
|
||||||
code: s.toString(),
|
|
||||||
map: options.sourcemap
|
|
||||||
? s.generateMap({ hires: true })
|
|
||||||
: undefined,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* find and remove all property with the name parameter from the setup return statement and the __returned__ object
|
* find and remove all property with the name parameter from the setup return statement and the __returned__ object
|
||||||
*/
|
*/
|
||||||
@ -296,3 +200,99 @@ function findMatchingPatternToRemove (node: AcornNode<Pattern>, toRemoveIfMatche
|
|||||||
if (matched) { return matched }
|
if (matched) { return matched }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const regexpMap = new WeakMap<Component[], [RegExp, RegExp, string[]]>()
|
||||||
|
return {
|
||||||
|
name: 'nuxt:tree-shake-template',
|
||||||
|
enforce: 'post',
|
||||||
|
transformInclude (id) {
|
||||||
|
const { pathname } = parseURL(decodeURIComponent(pathToFileURL(id).href))
|
||||||
|
return pathname.endsWith('.vue')
|
||||||
|
},
|
||||||
|
transform (code) {
|
||||||
|
const components = options.getComponents()
|
||||||
|
|
||||||
|
if (!regexpMap.has(components)) {
|
||||||
|
const serverPlaceholderPath = resolve(distDir, 'app/components/server-placeholder')
|
||||||
|
const clientOnlyComponents = components
|
||||||
|
.filter(c => c.mode === 'client' && !components.some(other => other.mode !== 'client' && other.pascalName === c.pascalName && !other.filePath.startsWith(serverPlaceholderPath)))
|
||||||
|
.flatMap(c => [c.pascalName, c.kebabName.replaceAll('-', '_')])
|
||||||
|
.concat(['ClientOnly', 'client_only'])
|
||||||
|
|
||||||
|
regexpMap.set(components, [new RegExp(`(${clientOnlyComponents.join('|')})`), new RegExp(`^(${clientOnlyComponents.map(c => `(?:(?:_unref\\()?(?:_component_)?(?:Lazy|lazy_)?${c}\\)?)`).join('|')})$`), clientOnlyComponents])
|
||||||
|
}
|
||||||
|
|
||||||
|
const s = new MagicString(code)
|
||||||
|
|
||||||
|
const [COMPONENTS_RE, COMPONENTS_IDENTIFIERS_RE] = regexpMap.get(components)!
|
||||||
|
if (!COMPONENTS_RE.test(code)) { return }
|
||||||
|
|
||||||
|
const codeAst = this.parse(code, PARSER_OPTIONS) as AcornNode<Program>
|
||||||
|
|
||||||
|
const componentsToRemoveSet = new Set<string>()
|
||||||
|
|
||||||
|
// remove client only components or components called in ClientOnly default slot
|
||||||
|
walk(codeAst, {
|
||||||
|
enter: (_node) => {
|
||||||
|
const node = _node as AcornNode<Node>
|
||||||
|
if (isSsrRender(node)) {
|
||||||
|
const [componentCall, _, children] = node.arguments
|
||||||
|
if (!componentCall) { return }
|
||||||
|
|
||||||
|
if (componentCall.type === 'Identifier' || componentCall.type === 'MemberExpression' || componentCall.type === 'CallExpression') {
|
||||||
|
const componentName = getComponentName(node)
|
||||||
|
const isClientComponent = COMPONENTS_IDENTIFIERS_RE.test(componentName)
|
||||||
|
const isClientOnlyComponent = CLIENT_ONLY_NAME_RE.test(componentName)
|
||||||
|
|
||||||
|
if (isClientComponent && children?.type === 'ObjectExpression') {
|
||||||
|
const slotsToRemove = isClientOnlyComponent ? children.properties.filter(prop => prop.type === 'Property' && prop.key.type === 'Identifier' && !PLACEHOLDER_EXACT_RE.test(prop.key.name)) as AcornNode<Property>[] : children.properties as AcornNode<Property>[]
|
||||||
|
|
||||||
|
for (const slot of slotsToRemove) {
|
||||||
|
s.remove(slot.start, slot.end + 1)
|
||||||
|
const removedCode = `({${code.slice(slot.start, slot.end + 1)}})`
|
||||||
|
const currentCodeAst = this.parse(s.toString(), PARSER_OPTIONS) as Node
|
||||||
|
|
||||||
|
walk(this.parse(removedCode, PARSER_OPTIONS) as Node, {
|
||||||
|
enter: (_node) => {
|
||||||
|
const node = _node as AcornNode<CallExpression>
|
||||||
|
if (isSsrRender(node)) {
|
||||||
|
const name = getComponentName(node)
|
||||||
|
|
||||||
|
// detect if the component is called else where
|
||||||
|
const nameToRemove = isComponentNotCalledInSetup(currentCodeAst, name)
|
||||||
|
if (nameToRemove) {
|
||||||
|
componentsToRemoveSet.add(nameToRemove)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const componentsToRemove = [...componentsToRemoveSet]
|
||||||
|
const removedNodes = new WeakSet<AcornNode<Node>>()
|
||||||
|
|
||||||
|
for (const componentName of componentsToRemove) {
|
||||||
|
// remove import declaration if it exists
|
||||||
|
removeImportDeclaration(codeAst, componentName, s)
|
||||||
|
// remove variable declaration
|
||||||
|
removeVariableDeclarator(codeAst, componentName, s, removedNodes)
|
||||||
|
// remove from setup return statement
|
||||||
|
removeFromSetupReturn(codeAst, componentName, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s.hasChanged()) {
|
||||||
|
return {
|
||||||
|
code: s.toString(),
|
||||||
|
map: options.sourcemap
|
||||||
|
? s.generateMap({ hires: true })
|
||||||
|
: undefined,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
@ -8,10 +8,6 @@ import type { Component, ComponentsDir } from 'nuxt/schema'
|
|||||||
|
|
||||||
import { QUOTE_RE, resolveComponentNameSegments } from '../core/utils'
|
import { QUOTE_RE, resolveComponentNameSegments } from '../core/utils'
|
||||||
|
|
||||||
const ISLAND_RE = /\.island(?:\.global)?$/
|
|
||||||
const GLOBAL_RE = /\.global(?:\.island)?$/
|
|
||||||
const COMPONENT_MODE_RE = /(?<=\.)(client|server)(\.global|\.island)*$/
|
|
||||||
const MODE_REPLACEMENT_RE = /(\.(client|server))?(\.global|\.island)*$/
|
|
||||||
/**
|
/**
|
||||||
* Scan the components inside different components folders
|
* Scan the components inside different components folders
|
||||||
* and return a unique list of components
|
* and return a unique list of components
|
||||||
@ -29,6 +25,11 @@ export async function scanComponents (dirs: ComponentsDir[], srcDir: string): Pr
|
|||||||
// All scanned paths
|
// All scanned paths
|
||||||
const scannedPaths: string[] = []
|
const scannedPaths: string[] = []
|
||||||
|
|
||||||
|
const ISLAND_RE = /\.island(?:\.global)?$/
|
||||||
|
const GLOBAL_RE = /\.global(?:\.island)?$/
|
||||||
|
const COMPONENT_MODE_RE = /(?<=\.)(client|server)(\.global|\.island)*$/
|
||||||
|
const MODE_REPLACEMENT_RE = /(\.(client|server))?(\.global|\.island)*$/
|
||||||
|
|
||||||
for (const dir of dirs) {
|
for (const dir of dirs) {
|
||||||
if (dir.enabled === false) {
|
if (dir.enabled === false) {
|
||||||
continue
|
continue
|
||||||
|
@ -102,10 +102,10 @@ export const componentsIslandsTemplate: NuxtTemplate = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const NON_VUE_RE = /\b\.(?!vue)\w+$/g
|
|
||||||
export const componentsTypeTemplate = {
|
export const componentsTypeTemplate = {
|
||||||
filename: 'components.d.ts' as const,
|
filename: 'components.d.ts' as const,
|
||||||
getContents: ({ app, nuxt }) => {
|
getContents: ({ app, nuxt }) => {
|
||||||
|
const NON_VUE_RE = /\b\.(?!vue)\w+$/g
|
||||||
const buildDir = nuxt.options.buildDir
|
const buildDir = nuxt.options.buildDir
|
||||||
const componentTypes = app.components.filter(c => !c.island).map((c) => {
|
const componentTypes = app.components.filter(c => !c.island).map((c) => {
|
||||||
const type = `typeof ${genDynamicImport(isAbsolute(c.filePath)
|
const type = `typeof ${genDynamicImport(isAbsolute(c.filePath)
|
||||||
|
@ -11,6 +11,7 @@ import { checkForExternalConfigurationFiles } from './external-config-files'
|
|||||||
import { cleanupCaches, getVueHash } from './cache'
|
import { cleanupCaches, getVueHash } from './cache'
|
||||||
|
|
||||||
export async function build (nuxt: Nuxt) {
|
export async function build (nuxt: Nuxt) {
|
||||||
|
const IS_RESTART_PATH_RE = /^(?:app\.|error\.|plugins\/|middleware\/|layouts\/)/i
|
||||||
const app = createApp(nuxt)
|
const app = createApp(nuxt)
|
||||||
nuxt.apps.default = app
|
nuxt.apps.default = app
|
||||||
|
|
||||||
|
@ -26,9 +26,9 @@ const logLevelMapReverse = {
|
|||||||
verbose: 3,
|
verbose: 3,
|
||||||
} satisfies Record<NuxtOptions['logLevel'], NitroConfig['logLevel']>
|
} satisfies Record<NuxtOptions['logLevel'], NitroConfig['logLevel']>
|
||||||
|
|
||||||
|
export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
||||||
const NODE_MODULES_RE = /(?<=\/)node_modules\/(.+)$/
|
const NODE_MODULES_RE = /(?<=\/)node_modules\/(.+)$/
|
||||||
const PNPM_NODE_MODULES_RE = /\.pnpm\/.+\/node_modules\/(.+)$/
|
const PNPM_NODE_MODULES_RE = /\.pnpm\/.+\/node_modules\/(.+)$/
|
||||||
export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
|
|
||||||
// Resolve config
|
// Resolve config
|
||||||
const excludePaths = nuxt.options._layers
|
const excludePaths = nuxt.options._layers
|
||||||
.flatMap(l => [
|
.flatMap(l => [
|
||||||
|
@ -632,7 +632,7 @@ export default defineNuxtPlugin({
|
|||||||
})`,
|
})`,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
const RESTART_RE = /^(?:app|error|app\.config)\.(?:js|ts|mjs|jsx|tsx|vue)$/i
|
||||||
nuxt.hooks.hook('builder:watch', (event, relativePath) => {
|
nuxt.hooks.hook('builder:watch', (event, relativePath) => {
|
||||||
const path = resolve(nuxt.options.srcDir, relativePath)
|
const path = resolve(nuxt.options.srcDir, relativePath)
|
||||||
// Local module patterns
|
// Local module patterns
|
||||||
@ -810,9 +810,6 @@ async function checkDependencyVersion (name: string, nuxtVersion: string): Promi
|
|||||||
console.warn(`[nuxt] Expected \`${name}\` to be at least \`${nuxtVersion}\` but got \`${version}\`. This might lead to unexpected behavior. Check your package.json or refresh your lockfile.`)
|
console.warn(`[nuxt] Expected \`${name}\` to be at least \`${nuxtVersion}\` but got \`${version}\`. This might lead to unexpected behavior. Check your package.json or refresh your lockfile.`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const RESTART_RE = /^(?:app|error|app\.config)\.(?:js|ts|mjs|jsx|tsx|vue)$/i
|
|
||||||
|
|
||||||
function deduplicateArray<T = unknown> (maybeArray: T): T {
|
function deduplicateArray<T = unknown> (maybeArray: T): T {
|
||||||
if (!Array.isArray(maybeArray)) { return maybeArray }
|
if (!Array.isArray(maybeArray)) { return maybeArray }
|
||||||
|
|
||||||
|
@ -7,10 +7,9 @@ interface DevOnlyPluginOptions {
|
|||||||
sourcemap?: boolean
|
sourcemap?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const DevOnlyPlugin = (options: DevOnlyPluginOptions) => createUnplugin(() => {
|
||||||
const DEVONLY_COMP_SINGLE_RE = /<(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>[\s\S]*?<\/(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>/
|
const DEVONLY_COMP_SINGLE_RE = /<(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>[\s\S]*?<\/(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>/
|
||||||
const DEVONLY_COMP_RE = /<(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>[\s\S]*?<\/(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>/g
|
const DEVONLY_COMP_RE = /<(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>[\s\S]*?<\/(?:dev-only|DevOnly|lazy-dev-only|LazyDevOnly)>/g
|
||||||
|
|
||||||
export const DevOnlyPlugin = (options: DevOnlyPluginOptions) => createUnplugin(() => {
|
|
||||||
return {
|
return {
|
||||||
name: 'nuxt:server-devonly:transform',
|
name: 'nuxt:server-devonly:transform',
|
||||||
enforce: 'pre',
|
enforce: 'pre',
|
||||||
|
@ -11,9 +11,6 @@ interface LayerAliasingOptions {
|
|||||||
layers: NuxtConfigLayer[]
|
layers: NuxtConfigLayer[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const ALIAS_RE = /(?<=['"])[~@]{1,2}(?=\/)/g
|
|
||||||
const ALIAS_RE_SINGLE = /(?<=['"])[~@]{1,2}(?=\/)/
|
|
||||||
|
|
||||||
export const LayerAliasingPlugin = (options: LayerAliasingOptions) => createUnplugin((_options, meta) => {
|
export const LayerAliasingPlugin = (options: LayerAliasingOptions) => createUnplugin((_options, meta) => {
|
||||||
const aliases: Record<string, Record<string, string>> = {}
|
const aliases: Record<string, Record<string, string>> = {}
|
||||||
for (const layer of options.layers) {
|
for (const layer of options.layers) {
|
||||||
@ -27,6 +24,8 @@ export const LayerAliasingPlugin = (options: LayerAliasingOptions) => createUnpl
|
|||||||
'@@': layer.config?.alias?.['@@'] || rootDir,
|
'@@': layer.config?.alias?.['@@'] || rootDir,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const ALIAS_RE = /(?<=['"])[~@]{1,2}(?=\/)/g
|
||||||
|
const ALIAS_RE_SINGLE = /(?<=['"])[~@]{1,2}(?=\/)/
|
||||||
const layers = Object.keys(aliases).sort((a, b) => b.length - a.length)
|
const layers = Object.keys(aliases).sort((a, b) => b.length - a.length)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -99,13 +99,13 @@ export const serverPluginTemplate: NuxtTemplate = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const pluginsDeclaration: NuxtTemplate = {
|
||||||
|
filename: 'types/plugins.d.ts',
|
||||||
|
getContents: async ({ nuxt, app }) => {
|
||||||
const TS_RE = /\.[cm]?tsx?$/
|
const TS_RE = /\.[cm]?tsx?$/
|
||||||
const JS_LETTER_RE = /\.(?<letter>[cm])?jsx?$/
|
const JS_LETTER_RE = /\.(?<letter>[cm])?jsx?$/
|
||||||
const JS_RE = /\.[cm]jsx?$/
|
const JS_RE = /\.[cm]jsx?$/
|
||||||
const JS_CAPTURE_RE = /\.[cm](jsx?)$/
|
const JS_CAPTURE_RE = /\.[cm](jsx?)$/
|
||||||
export const pluginsDeclaration: NuxtTemplate = {
|
|
||||||
filename: 'types/plugins.d.ts',
|
|
||||||
getContents: async ({ nuxt, app }) => {
|
|
||||||
const EXTENSION_RE = new RegExp(`(?<=\\w)(${nuxt.options.extensions.map(e => escapeRE(e)).join('|')})$`, 'g')
|
const EXTENSION_RE = new RegExp(`(?<=\\w)(${nuxt.options.extensions.map(e => escapeRE(e)).join('|')})$`, 'g')
|
||||||
|
|
||||||
const typesDir = join(nuxt.options.buildDir, 'types')
|
const typesDir = join(nuxt.options.buildDir, 'types')
|
||||||
@ -177,12 +177,12 @@ export { }
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const adHocModules = ['router', 'pages', 'imports', 'meta', 'components', 'nuxt-config-schema']
|
|
||||||
const IMPORT_NAME_RE = /\.\w+$/
|
|
||||||
const GIT_RE = /^git\+/
|
|
||||||
export const schemaTemplate: NuxtTemplate = {
|
export const schemaTemplate: NuxtTemplate = {
|
||||||
filename: 'types/schema.d.ts',
|
filename: 'types/schema.d.ts',
|
||||||
getContents: async ({ nuxt }) => {
|
getContents: async ({ nuxt }) => {
|
||||||
|
const adHocModules = ['router', 'pages', 'imports', 'meta', 'components', 'nuxt-config-schema']
|
||||||
|
const IMPORT_NAME_RE = /\.\w+$/
|
||||||
|
const GIT_RE = /^git\+/
|
||||||
const relativeRoot = relative(resolve(nuxt.options.buildDir, 'types'), nuxt.options.rootDir)
|
const relativeRoot = relative(resolve(nuxt.options.buildDir, 'types'), nuxt.options.rootDir)
|
||||||
const getImportName = (name: string) => (name[0] === '.' ? './' + join(relativeRoot, name) : name).replace(IMPORT_NAME_RE, '')
|
const getImportName = (name: string) => (name[0] === '.' ? './' + join(relativeRoot, name) : name).replace(IMPORT_NAME_RE, '')
|
||||||
|
|
||||||
@ -529,13 +529,12 @@ export const nuxtConfigTemplate: NuxtTemplate = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const TYPE_FILENAME_RE = /\.([cm])?[jt]s$/
|
|
||||||
const DECLARATION_RE = /\.d\.[cm]?ts$/
|
|
||||||
export const buildTypeTemplate: NuxtTemplate = {
|
export const buildTypeTemplate: NuxtTemplate = {
|
||||||
filename: 'types/build.d.ts',
|
filename: 'types/build.d.ts',
|
||||||
getContents ({ app }) {
|
getContents ({ app }) {
|
||||||
|
const TYPE_FILENAME_RE = /\.([cm])?[jt]s$/
|
||||||
|
const DECLARATION_RE = /\.d\.[cm]?ts$/
|
||||||
let declarations = ''
|
let declarations = ''
|
||||||
|
|
||||||
for (const file of app.templates) {
|
for (const file of app.templates) {
|
||||||
if (file.write || !file.filename || DECLARATION_RE.test(file.filename)) {
|
if (file.write || !file.filename || DECLARATION_RE.test(file.filename)) {
|
||||||
continue
|
continue
|
||||||
|
@ -5,10 +5,9 @@ import { tryUseNuxt } from '@nuxt/kit'
|
|||||||
import type { ImportsOptions } from 'nuxt/schema'
|
import type { ImportsOptions } from 'nuxt/schema'
|
||||||
import { isJS, isVue } from '../core/utils'
|
import { isJS, isVue } from '../core/utils'
|
||||||
|
|
||||||
|
export const TransformPlugin = ({ ctx, options, sourcemap }: { ctx: Unimport, options: Partial<ImportsOptions>, sourcemap?: boolean }) => createUnplugin(() => {
|
||||||
const NODE_MODULES_RE = /[\\/]node_modules[\\/]/
|
const NODE_MODULES_RE = /[\\/]node_modules[\\/]/
|
||||||
const IMPORTS_RE = /(['"])#imports\1/
|
const IMPORTS_RE = /(['"])#imports\1/
|
||||||
|
|
||||||
export const TransformPlugin = ({ ctx, options, sourcemap }: { ctx: Unimport, options: Partial<ImportsOptions>, sourcemap?: boolean }) => createUnplugin(() => {
|
|
||||||
return {
|
return {
|
||||||
name: 'nuxt:imports-transform',
|
name: 'nuxt:imports-transform',
|
||||||
enforce: 'post',
|
enforce: 'post',
|
||||||
|
@ -19,8 +19,6 @@ import { extractRouteRules, getMappedPages } from './route-rules'
|
|||||||
import { PageMetaPlugin } from './plugins/page-meta'
|
import { PageMetaPlugin } from './plugins/page-meta'
|
||||||
import { RouteInjectionPlugin } from './plugins/route-injection'
|
import { RouteInjectionPlugin } from './plugins/route-injection'
|
||||||
|
|
||||||
const OPTIONAL_PARAM_RE = /^\/?:.*(?:\?|\(\.\*\)\*)$/
|
|
||||||
|
|
||||||
export default defineNuxtModule({
|
export default defineNuxtModule({
|
||||||
meta: {
|
meta: {
|
||||||
name: 'pages',
|
name: 'pages',
|
||||||
@ -291,6 +289,7 @@ export default defineNuxtModule({
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const OPTIONAL_PARAM_RE = /^\/?:.*(?:\?|\(\.\*\)\*)$/
|
||||||
// Record all pages for use in prerendering
|
// Record all pages for use in prerendering
|
||||||
const prerenderRoutes = new Set<string>()
|
const prerenderRoutes = new Set<string>()
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ interface PageMetaPluginOptions {
|
|||||||
sourcemap?: boolean
|
sourcemap?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const PageMetaPlugin = (options: PageMetaPluginOptions) => createUnplugin(() => {
|
||||||
const HAS_MACRO_RE = /\bdefinePageMeta\s*\(\s*/
|
const HAS_MACRO_RE = /\bdefinePageMeta\s*\(\s*/
|
||||||
|
|
||||||
const CODE_EMPTY = `
|
const CODE_EMPTY = `
|
||||||
@ -35,8 +36,28 @@ if (import.meta.webpackHot) {
|
|||||||
if (err) { window.location = window.location.href }
|
if (err) { window.location = window.location.href }
|
||||||
})
|
})
|
||||||
}`
|
}`
|
||||||
|
// https://github.com/vuejs/vue-loader/pull/1911
|
||||||
|
// https://github.com/vitejs/vite/issues/8473
|
||||||
|
const QUERY_START_RE = /^\?/
|
||||||
|
const MACRO_RE = /¯o=true/
|
||||||
|
function rewriteQuery (id: string) {
|
||||||
|
return id.replace(/\?.+$/, r => '?macro=true&' + r.replace(QUERY_START_RE, '').replace(MACRO_RE, ''))
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseMacroQuery (id: string) {
|
||||||
|
const { search } = parseURL(decodeURIComponent(isAbsolute(id) ? pathToFileURL(id).href : id).replace(/\?macro=true$/, ''))
|
||||||
|
const query = parseQuery(search)
|
||||||
|
if (id.includes('?macro=true')) {
|
||||||
|
return { macro: 'true', ...query }
|
||||||
|
}
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
const QUOTED_SPECIFIER_RE = /(["']).*\1/
|
||||||
|
function getQuotedSpecifier (id: string) {
|
||||||
|
return id.match(QUOTED_SPECIFIER_RE)?.[0]
|
||||||
|
}
|
||||||
|
|
||||||
export const PageMetaPlugin = (options: PageMetaPluginOptions) => createUnplugin(() => {
|
|
||||||
return {
|
return {
|
||||||
name: 'nuxt:pages-macros-transform',
|
name: 'nuxt:pages-macros-transform',
|
||||||
enforce: 'post',
|
enforce: 'post',
|
||||||
@ -173,25 +194,3 @@ export const PageMetaPlugin = (options: PageMetaPluginOptions) => createUnplugin
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// https://github.com/vuejs/vue-loader/pull/1911
|
|
||||||
// https://github.com/vitejs/vite/issues/8473
|
|
||||||
const QUERY_START_RE = /^\?/
|
|
||||||
const MACRO_RE = /¯o=true/
|
|
||||||
function rewriteQuery (id: string) {
|
|
||||||
return id.replace(/\?.+$/, r => '?macro=true&' + r.replace(QUERY_START_RE, '').replace(MACRO_RE, ''))
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseMacroQuery (id: string) {
|
|
||||||
const { search } = parseURL(decodeURIComponent(isAbsolute(id) ? pathToFileURL(id).href : id).replace(/\?macro=true$/, ''))
|
|
||||||
const query = parseQuery(search)
|
|
||||||
if (id.includes('?macro=true')) {
|
|
||||||
return { macro: 'true', ...query }
|
|
||||||
}
|
|
||||||
return query
|
|
||||||
}
|
|
||||||
|
|
||||||
const QUOTED_SPECIFIER_RE = /(["']).*\1/
|
|
||||||
function getQuotedSpecifier (id: string) {
|
|
||||||
return id.match(QUOTED_SPECIFIER_RE)?.[0]
|
|
||||||
}
|
|
||||||
|
@ -4,12 +4,11 @@ import type { Nuxt } from '@nuxt/schema'
|
|||||||
import { stripLiteral } from 'strip-literal'
|
import { stripLiteral } from 'strip-literal'
|
||||||
import { isVue } from '../../core/utils'
|
import { isVue } from '../../core/utils'
|
||||||
|
|
||||||
|
export const RouteInjectionPlugin = (nuxt: Nuxt) => createUnplugin(() => {
|
||||||
const INJECTION_RE_TEMPLATE = /\b_ctx\.\$route\b/g
|
const INJECTION_RE_TEMPLATE = /\b_ctx\.\$route\b/g
|
||||||
const INJECTION_RE_SCRIPT = /\bthis\.\$route\b/g
|
const INJECTION_RE_SCRIPT = /\bthis\.\$route\b/g
|
||||||
|
|
||||||
const INJECTION_SINGLE_RE = /\bthis\.\$route\b|\b_ctx\.\$route\b/
|
const INJECTION_SINGLE_RE = /\bthis\.\$route\b|\b_ctx\.\$route\b/
|
||||||
|
|
||||||
export const RouteInjectionPlugin = (nuxt: Nuxt) => createUnplugin(() => {
|
|
||||||
return {
|
return {
|
||||||
name: 'nuxt:route-injection-plugin',
|
name: 'nuxt:route-injection-plugin',
|
||||||
enforce: 'post',
|
enforce: 'post',
|
||||||
|
@ -17,11 +17,6 @@ interface ComposableKeysOptions {
|
|||||||
composables: Array<{ name: string, source?: string | RegExp, argumentLength: number }>
|
composables: Array<{ name: string, source?: string | RegExp, argumentLength: number }>
|
||||||
}
|
}
|
||||||
|
|
||||||
const stringTypes: Array<string | undefined> = ['Literal', 'TemplateLiteral']
|
|
||||||
const NUXT_LIB_RE = /node_modules\/(?:nuxt|nuxt3|nuxt-nightly)\//
|
|
||||||
const SUPPORTED_EXT_RE = /\.(?:m?[jt]sx?|vue)/
|
|
||||||
const SCRIPT_RE = /(?<=<script[^>]*>)[\s\S]*?(?=<\/script>)/i
|
|
||||||
|
|
||||||
export const composableKeysPlugin = createUnplugin((options: ComposableKeysOptions) => {
|
export const composableKeysPlugin = createUnplugin((options: ComposableKeysOptions) => {
|
||||||
const composableMeta: Record<string, any> = {}
|
const composableMeta: Record<string, any> = {}
|
||||||
const composableLengths = new Set<number>()
|
const composableLengths = new Set<number>()
|
||||||
@ -35,6 +30,10 @@ export const composableKeysPlugin = createUnplugin((options: ComposableKeysOptio
|
|||||||
const maxLength = Math.max(...composableLengths)
|
const maxLength = Math.max(...composableLengths)
|
||||||
const KEYED_FUNCTIONS_RE = new RegExp(`\\b(${[...keyedFunctions].map(f => escapeRE(f)).join('|')})\\b`)
|
const KEYED_FUNCTIONS_RE = new RegExp(`\\b(${[...keyedFunctions].map(f => escapeRE(f)).join('|')})\\b`)
|
||||||
|
|
||||||
|
const stringTypes: Array<string | undefined> = ['Literal', 'TemplateLiteral']
|
||||||
|
const NUXT_LIB_RE = /node_modules\/(?:nuxt|nuxt3|nuxt-nightly)\//
|
||||||
|
const SUPPORTED_EXT_RE = /\.(?:m?[jt]sx?|vue)/
|
||||||
|
const SCRIPT_RE = /(?<=<script[^>]*>)[\s\S]*?(?=<\/script>)/i
|
||||||
return {
|
return {
|
||||||
name: 'nuxt:composable-keys',
|
name: 'nuxt:composable-keys',
|
||||||
enforce: 'post',
|
enforce: 'post',
|
||||||
|
@ -7,11 +7,6 @@ import { dirname, relative } from 'pathe'
|
|||||||
import MagicString from 'magic-string'
|
import MagicString from 'magic-string'
|
||||||
import { isCSSRequest } from 'vite'
|
import { isCSSRequest } from 'vite'
|
||||||
|
|
||||||
const PREFIX = 'virtual:public?'
|
|
||||||
const CSS_URL_RE = /url\((\/[^)]+)\)/g
|
|
||||||
const CSS_URL_SINGLE_RE = /url\(\/[^)]+\)/
|
|
||||||
const RENDER_CHUNK_RE = /(?<= = )['"`]/
|
|
||||||
|
|
||||||
interface VitePublicDirsPluginOptions {
|
interface VitePublicDirsPluginOptions {
|
||||||
dev?: boolean
|
dev?: boolean
|
||||||
sourcemap?: boolean
|
sourcemap?: boolean
|
||||||
@ -21,6 +16,11 @@ interface VitePublicDirsPluginOptions {
|
|||||||
export const VitePublicDirsPlugin = createUnplugin((options: VitePublicDirsPluginOptions) => {
|
export const VitePublicDirsPlugin = createUnplugin((options: VitePublicDirsPluginOptions) => {
|
||||||
const { resolveFromPublicAssets } = useResolveFromPublicAssets()
|
const { resolveFromPublicAssets } = useResolveFromPublicAssets()
|
||||||
|
|
||||||
|
const PREFIX = 'virtual:public?'
|
||||||
|
const CSS_URL_RE = /url\((\/[^)]+)\)/g
|
||||||
|
const CSS_URL_SINGLE_RE = /url\(\/[^)]+\)/
|
||||||
|
const RENDER_CHUNK_RE = /(?<= = )['"`]/
|
||||||
|
|
||||||
const devTransformPlugin: UnpluginOptions = {
|
const devTransformPlugin: UnpluginOptions = {
|
||||||
name: 'nuxt:vite-public-dir-resolution-dev',
|
name: 'nuxt:vite-public-dir-resolution-dev',
|
||||||
vite: {
|
vite: {
|
||||||
|
Loading…
Reference in New Issue
Block a user