mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
chore: improve type safety with indexed access (#27626)
This commit is contained in:
parent
ef1cfa0508
commit
53df20ef2b
@ -55,7 +55,7 @@ export async function addComponent (opts: AddComponentOptions) {
|
|||||||
nuxt.hook('components:extend', (components: Component[]) => {
|
nuxt.hook('components:extend', (components: Component[]) => {
|
||||||
const existingComponentIndex = components.findIndex(c => (c.pascalName === component.pascalName || c.kebabName === component.kebabName) && c.mode === component.mode)
|
const existingComponentIndex = components.findIndex(c => (c.pascalName === component.pascalName || c.kebabName === component.kebabName) && c.mode === component.mode)
|
||||||
if (existingComponentIndex !== -1) {
|
if (existingComponentIndex !== -1) {
|
||||||
const existingComponent = components[existingComponentIndex]
|
const existingComponent = components[existingComponentIndex]!
|
||||||
const existingPriority = existingComponent.priority ?? 0
|
const existingPriority = existingComponent.priority ?? 0
|
||||||
const newPriority = component.priority ?? 0
|
const newPriority = component.priority ?? 0
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ export function resolveIgnorePatterns (relativePath?: string): string[] {
|
|||||||
// Map ignore patterns based on if they start with * or !*
|
// Map ignore patterns based on if they start with * or !*
|
||||||
return ignorePatterns.map((p) => {
|
return ignorePatterns.map((p) => {
|
||||||
const [_, negation = '', pattern] = p.match(NEGATION_RE) || []
|
const [_, negation = '', pattern] = p.match(NEGATION_RE) || []
|
||||||
if (pattern[0] === '*') {
|
if (pattern && pattern[0] === '*') {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
return negation + relative(relativePath, resolve(nuxt.options.rootDir, pattern || p))
|
return negation + relative(relativePath, resolve(nuxt.options.rootDir, pattern || p))
|
||||||
@ -73,7 +73,7 @@ export function resolveGroupSyntax (group: string): string[] {
|
|||||||
groups = groups.flatMap((group) => {
|
groups = groups.flatMap((group) => {
|
||||||
const [head, ...tail] = group.split('{')
|
const [head, ...tail] = group.split('{')
|
||||||
if (tail.length) {
|
if (tail.length) {
|
||||||
const [body, ...rest] = tail.join('{').split('}')
|
const [body = '', ...rest] = tail.join('{').split('}')
|
||||||
return body.split(',').map(part => `${head}${part}${rest.join('')}`)
|
return body.split(',').map(part => `${head}${part}${rest.join('')}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ export function addLayout (this: any, template: NuxtTemplate | string, name?: st
|
|||||||
// Nuxt 3 adds layouts on app
|
// Nuxt 3 adds layouts on app
|
||||||
nuxt.hook('app:templates', (app) => {
|
nuxt.hook('app:templates', (app) => {
|
||||||
if (layoutName in app.layouts) {
|
if (layoutName in app.layouts) {
|
||||||
const relativePath = relative(nuxt.options.srcDir, app.layouts[layoutName].file)
|
const relativePath = relative(nuxt.options.srcDir, app.layouts[layoutName]!.file)
|
||||||
return logger.warn(
|
return logger.warn(
|
||||||
`Not overriding \`${layoutName}\` (provided by \`~/${relativePath}\`) with \`${src || filename}\`.`,
|
`Not overriding \`${layoutName}\` (provided by \`~/${relativePath}\`) with \`${src || filename}\`.`,
|
||||||
)
|
)
|
||||||
|
@ -44,7 +44,7 @@ export function addRouteMiddleware (input: NuxtMiddleware | NuxtMiddleware[], op
|
|||||||
for (const middleware of middlewares) {
|
for (const middleware of middlewares) {
|
||||||
const find = app.middleware.findIndex(item => item.name === middleware.name)
|
const find = app.middleware.findIndex(item => item.name === middleware.name)
|
||||||
if (find >= 0) {
|
if (find >= 0) {
|
||||||
const foundPath = app.middleware[find].path
|
const foundPath = app.middleware[find]!.path
|
||||||
if (foundPath === middleware.path) { continue }
|
if (foundPath === middleware.path) { continue }
|
||||||
if (options.override === true) {
|
if (options.override === true) {
|
||||||
app.middleware[find] = { ...middleware }
|
app.middleware[find] = { ...middleware }
|
||||||
|
@ -174,7 +174,7 @@ export async function resolveNuxtModule (base: string, paths: string[]): Promise
|
|||||||
|
|
||||||
for (const path of paths) {
|
for (const path of paths) {
|
||||||
if (path.startsWith(base)) {
|
if (path.startsWith(base)) {
|
||||||
resolved.push(path.split('/index.ts')[0])
|
resolved.push(path.split('/index.ts')[0]!)
|
||||||
} else {
|
} else {
|
||||||
const resolvedPath = await resolver.resolvePath(path)
|
const resolvedPath = await resolver.resolvePath(path)
|
||||||
resolved.push(resolvedPath.slice(0, resolvedPath.lastIndexOf(path) + path.length))
|
resolved.push(resolvedPath.slice(0, resolvedPath.lastIndexOf(path) + path.length))
|
||||||
|
@ -228,10 +228,10 @@ export async function _generateTypes (nuxt: Nuxt) {
|
|||||||
if (excludedAlias.some(re => re.test(alias))) {
|
if (excludedAlias.some(re => re.test(alias))) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
let absolutePath = resolve(basePath, aliases[alias])
|
let absolutePath = resolve(basePath, aliases[alias]!)
|
||||||
let stats = await fsp.stat(absolutePath).catch(() => null /* file does not exist */)
|
let stats = await fsp.stat(absolutePath).catch(() => null /* file does not exist */)
|
||||||
if (!stats) {
|
if (!stats) {
|
||||||
const resolvedModule = await tryResolveModule(aliases[alias], nuxt.options.modulesDir)
|
const resolvedModule = await tryResolveModule(aliases[alias]!, nuxt.options.modulesDir)
|
||||||
if (resolvedModule) {
|
if (resolvedModule) {
|
||||||
absolutePath = resolvedModule
|
absolutePath = resolvedModule
|
||||||
stats = await fsp.stat(resolvedModule).catch(() => null)
|
stats = await fsp.stat(resolvedModule).catch(() => null)
|
||||||
@ -251,7 +251,7 @@ export async function _generateTypes (nuxt: Nuxt) {
|
|||||||
// remove extension
|
// remove extension
|
||||||
? relativePath.replace(/\b\.\w+$/g, '')
|
? relativePath.replace(/\b\.\w+$/g, '')
|
||||||
// non-existent file probably shouldn't be resolved
|
// non-existent file probably shouldn't be resolved
|
||||||
: aliases[alias]
|
: aliases[alias]!
|
||||||
|
|
||||||
tsConfig.compilerOptions.paths[alias] = [path]
|
tsConfig.compilerOptions.paths[alias] = [path]
|
||||||
|
|
||||||
@ -334,7 +334,7 @@ function renderAttrs (obj: Record<string, string>) {
|
|||||||
return attrs.join(' ')
|
return attrs.join(' ')
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderAttr (key: string, value: string) {
|
function renderAttr (key: string, value?: string) {
|
||||||
return value ? `${key}="${value}"` : ''
|
return value ? `${key}="${value}"` : ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,9 +139,14 @@ export const RenderPlugin = () => {
|
|||||||
}
|
}
|
||||||
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])
|
const inlineScripts: string[] = []
|
||||||
.filter(i => !i.includes('const t=document.createElement("link")'))
|
for (const [_, i] of html.matchAll(/<script>([\s\S]*?)<\/script>/g)) {
|
||||||
|
if (i && !i.includes('const t=document.createElement("link")')) {
|
||||||
|
inlineScripts.push(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const props = genObjectFromRawEntries(Object.entries({ ...genericMessages, ...messages }).map(([key, value]) => [key, {
|
const props = genObjectFromRawEntries(Object.entries({ ...genericMessages, ...messages }).map(([key, value]) => [key, {
|
||||||
type: typeof value === 'string' ? 'String' : typeof value === 'number' ? 'Number' : typeof value === 'boolean' ? 'Boolean' : 'undefined',
|
type: typeof value === 'string' ? 'String' : typeof value === 'number' ? 'Number' : typeof value === 'boolean' ? 'Boolean' : 'undefined',
|
||||||
default: JSON.stringify(value),
|
default: JSON.stringify(value),
|
||||||
|
@ -16,10 +16,10 @@ export function analyzePlugin (ctx: ViteBuildContext): Plugin[] {
|
|||||||
async generateBundle (_opts, outputBundle) {
|
async generateBundle (_opts, outputBundle) {
|
||||||
for (const _bundleId in outputBundle) {
|
for (const _bundleId in outputBundle) {
|
||||||
const bundle = outputBundle[_bundleId]
|
const bundle = outputBundle[_bundleId]
|
||||||
if (bundle.type !== 'chunk') { continue }
|
if (!bundle || bundle.type !== 'chunk') { continue }
|
||||||
const minifiedModuleEntryPromises: Array<Promise<[string, RenderedModule]>> = []
|
const minifiedModuleEntryPromises: Array<Promise<[string, RenderedModule]>> = []
|
||||||
for (const moduleId in bundle.modules) {
|
for (const moduleId in bundle.modules) {
|
||||||
const module = bundle.modules[moduleId]
|
const module = bundle.modules[moduleId]!
|
||||||
minifiedModuleEntryPromises.push(
|
minifiedModuleEntryPromises.push(
|
||||||
transform(module.code || '', { minify: true })
|
transform(module.code || '', { minify: true })
|
||||||
.then(result => [moduleId, { ...module, code: result.code }]),
|
.then(result => [moduleId, { ...module, code: result.code }]),
|
||||||
|
@ -17,7 +17,7 @@ interface ComposableKeysOptions {
|
|||||||
composables: Array<{ name: string, source?: string | RegExp, argumentLength: number }>
|
composables: Array<{ name: string, source?: string | RegExp, argumentLength: number }>
|
||||||
}
|
}
|
||||||
|
|
||||||
const stringTypes = ['Literal', 'TemplateLiteral']
|
const stringTypes: Array<string | undefined> = ['Literal', 'TemplateLiteral']
|
||||||
const NUXT_LIB_RE = /node_modules\/(?:nuxt|nuxt3|nuxt-nightly)\//
|
const NUXT_LIB_RE = /node_modules\/(?:nuxt|nuxt3|nuxt-nightly)\//
|
||||||
const SUPPORTED_EXT_RE = /\.(?:m?[jt]sx?|vue)/
|
const SUPPORTED_EXT_RE = /\.(?:m?[jt]sx?|vue)/
|
||||||
|
|
||||||
@ -182,7 +182,7 @@ class ScopeTracker {
|
|||||||
leaveScope () {
|
leaveScope () {
|
||||||
this.scopeIndexStack.pop()
|
this.scopeIndexStack.pop()
|
||||||
this.curScopeKey = this.getKey()
|
this.curScopeKey = this.getKey()
|
||||||
this.scopeIndexStack[this.scopeIndexStack.length - 1]++
|
this.scopeIndexStack[this.scopeIndexStack.length - 1]!++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,9 +255,9 @@ export function detectImportNames (code: string, composableMeta: Record<string,
|
|||||||
for (const i of findStaticImports(code)) {
|
for (const i of findStaticImports(code)) {
|
||||||
if (NUXT_IMPORT_RE.test(i.specifier)) { continue }
|
if (NUXT_IMPORT_RE.test(i.specifier)) { continue }
|
||||||
|
|
||||||
const { namedImports, defaultImport, namespacedImport } = parseStaticImport(i)
|
const { namedImports = {}, defaultImport, namespacedImport } = parseStaticImport(i)
|
||||||
for (const name in namedImports || {}) {
|
for (const name in namedImports) {
|
||||||
addName(namedImports![name], i.specifier)
|
addName(namedImports[name]!, i.specifier)
|
||||||
}
|
}
|
||||||
if (defaultImport) {
|
if (defaultImport) {
|
||||||
addName(defaultImport, i.specifier)
|
addName(defaultImport, i.specifier)
|
||||||
|
@ -38,7 +38,7 @@ export const VitePublicDirsPlugin = createUnplugin((options: { sourcemap?: boole
|
|||||||
const s = new MagicString(code)
|
const s = new MagicString(code)
|
||||||
const q = code.match(/(?<= = )['"`]/)?.[0] || '"'
|
const q = code.match(/(?<= = )['"`]/)?.[0] || '"'
|
||||||
for (const [full, url] of code.matchAll(CSS_URL_RE)) {
|
for (const [full, url] of code.matchAll(CSS_URL_RE)) {
|
||||||
if (resolveFromPublicAssets(url)) {
|
if (url && resolveFromPublicAssets(url)) {
|
||||||
s.replace(full, `url(${q} + publicAssetsURL(${q}${url}${q}) + ${q})`)
|
s.replace(full, `url(${q} + publicAssetsURL(${q}${url}${q}) + ${q})`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,13 +53,13 @@ export const VitePublicDirsPlugin = createUnplugin((options: { sourcemap?: boole
|
|||||||
},
|
},
|
||||||
generateBundle (_outputOptions, bundle) {
|
generateBundle (_outputOptions, bundle) {
|
||||||
for (const file in bundle) {
|
for (const file in bundle) {
|
||||||
const chunk = bundle[file]
|
const chunk = bundle[file]!
|
||||||
if (!file.endsWith('.css') || chunk.type !== 'asset') { continue }
|
if (!file.endsWith('.css') || chunk.type !== 'asset') { continue }
|
||||||
|
|
||||||
let css = chunk.source.toString()
|
let css = chunk.source.toString()
|
||||||
let wasReplaced = false
|
let wasReplaced = false
|
||||||
for (const [full, url] of css.matchAll(CSS_URL_RE)) {
|
for (const [full, url] of css.matchAll(CSS_URL_RE)) {
|
||||||
if (resolveFromPublicAssets(url)) {
|
if (url && resolveFromPublicAssets(url)) {
|
||||||
const relativeURL = relative(withLeadingSlash(dirname(file)), url)
|
const relativeURL = relative(withLeadingSlash(dirname(file)), url)
|
||||||
css = css.replace(full, `url(${relativeURL})`)
|
css = css.replace(full, `url(${relativeURL})`)
|
||||||
wasReplaced = true
|
wasReplaced = true
|
||||||
|
@ -24,7 +24,7 @@ interface SSRStylePluginOptions {
|
|||||||
const SUPPORTED_FILES_RE = /\.(?:vue|(?:[cm]?j|t)sx?)$/
|
const SUPPORTED_FILES_RE = /\.(?:vue|(?:[cm]?j|t)sx?)$/
|
||||||
|
|
||||||
export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
||||||
const cssMap: Record<string, { files: string[], inBundle: boolean }> = {}
|
const cssMap: Record<string, { files: string[], inBundle?: boolean }> = {}
|
||||||
const idRefMap: Record<string, string> = {}
|
const idRefMap: Record<string, string> = {}
|
||||||
|
|
||||||
const relativeToSrcDir = (path: string) => relative(options.srcDir, path)
|
const relativeToSrcDir = (path: string) => relative(options.srcDir, path)
|
||||||
@ -63,7 +63,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
|||||||
|
|
||||||
const emitted: Record<string, string> = {}
|
const emitted: Record<string, string> = {}
|
||||||
for (const file in cssMap) {
|
for (const file in cssMap) {
|
||||||
const { files, inBundle } = cssMap[file]
|
const { files, inBundle } = cssMap[file]!
|
||||||
// File has been tree-shaken out of build (or there are no styles to inline)
|
// File has been tree-shaken out of build (or there are no styles to inline)
|
||||||
if (!files.length || !inBundle) { continue }
|
if (!files.length || !inBundle) { continue }
|
||||||
const fileName = filename(file)
|
const fileName = filename(file)
|
||||||
@ -115,18 +115,19 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
|||||||
// 'Teleport' CSS chunks that made it into the bundle on the client side
|
// 'Teleport' CSS chunks that made it into the bundle on the client side
|
||||||
// to be inlined on server rendering
|
// to be inlined on server rendering
|
||||||
if (options.mode === 'client') {
|
if (options.mode === 'client') {
|
||||||
options.clientCSSMap[moduleId] ||= new Set()
|
const moduleMap = options.clientCSSMap[moduleId] ||= new Set()
|
||||||
if (isCSS(moduleId)) {
|
if (isCSS(moduleId)) {
|
||||||
// Vue files can (also) be their own entrypoints as they are tracked separately
|
// Vue files can (also) be their own entrypoints as they are tracked separately
|
||||||
if (isVue(moduleId)) {
|
if (isVue(moduleId)) {
|
||||||
options.clientCSSMap[moduleId].add(moduleId)
|
moduleMap.add(moduleId)
|
||||||
const parent = moduleId.replace(/\?.+$/, '')
|
const parent = moduleId.replace(/\?.+$/, '')
|
||||||
options.clientCSSMap[parent] ||= new Set()
|
const parentMap = options.clientCSSMap[parent] ||= new Set()
|
||||||
options.clientCSSMap[parent].add(moduleId)
|
parentMap.add(moduleId)
|
||||||
}
|
}
|
||||||
// This is required to track CSS in entry chunk
|
// This is required to track CSS in entry chunk
|
||||||
if (isEntry) {
|
if (isEntry && chunk.facadeModuleId) {
|
||||||
options.clientCSSMap[chunk.facadeModuleId!].add(moduleId)
|
const facadeMap = options.clientCSSMap[chunk.facadeModuleId] ||= new Set()
|
||||||
|
facadeMap.add(moduleId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@ -134,7 +135,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
|||||||
|
|
||||||
const relativePath = relativeToSrcDir(moduleId)
|
const relativePath = relativeToSrcDir(moduleId)
|
||||||
if (relativePath in cssMap) {
|
if (relativePath in cssMap) {
|
||||||
cssMap[relativePath].inBundle = cssMap[relativePath].inBundle ?? ((isVue(moduleId) && relativeToSrcDir(moduleId)) || isEntry)
|
cssMap[relativePath]!.inBundle = cssMap[relativePath]!.inBundle ?? ((isVue(moduleId) && !!relativeToSrcDir(moduleId)) || isEntry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +147,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
|||||||
// or include it here in the client build so it is emitted in the CSS.
|
// or include it here in the client build so it is emitted in the CSS.
|
||||||
if (id === options.entry && (options.shouldInline === true || (typeof options.shouldInline === 'function' && options.shouldInline(id)))) {
|
if (id === options.entry && (options.shouldInline === true || (typeof options.shouldInline === 'function' && options.shouldInline(id)))) {
|
||||||
const s = new MagicString(code)
|
const s = new MagicString(code)
|
||||||
options.clientCSSMap[id] ||= new Set()
|
const idClientCSSMap = options.clientCSSMap[id] ||= new Set()
|
||||||
if (!options.globalCSS.length) { return }
|
if (!options.globalCSS.length) { return }
|
||||||
|
|
||||||
for (const file of options.globalCSS) {
|
for (const file of options.globalCSS) {
|
||||||
@ -160,7 +161,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
|||||||
s.prepend(`${genImport(file)}\n`)
|
s.prepend(`${genImport(file)}\n`)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
options.clientCSSMap[id].add(resolved.id)
|
idClientCSSMap.add(resolved.id)
|
||||||
}
|
}
|
||||||
if (s.hasChanged()) {
|
if (s.hasChanged()) {
|
||||||
return {
|
return {
|
||||||
@ -184,7 +185,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const relativeId = relativeToSrcDir(id)
|
const relativeId = relativeToSrcDir(id)
|
||||||
cssMap[relativeId] = cssMap[relativeId] || { files: [] }
|
const idMap = cssMap[relativeId] ||= { files: [] }
|
||||||
|
|
||||||
const emittedIds = new Set<string>()
|
const emittedIds = new Set<string>()
|
||||||
|
|
||||||
@ -208,7 +209,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
|||||||
})
|
})
|
||||||
|
|
||||||
idRefMap[relativeToSrcDir(file)] = ref
|
idRefMap[relativeToSrcDir(file)] = ref
|
||||||
cssMap[relativeId].files.push(ref)
|
idMap.files.push(ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SUPPORTED_FILES_RE.test(pathname)) { return }
|
if (!SUPPORTED_FILES_RE.test(pathname)) { return }
|
||||||
@ -235,7 +236,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
|||||||
})
|
})
|
||||||
|
|
||||||
idRefMap[relativeToSrcDir(resolved.id)] = ref
|
idRefMap[relativeToSrcDir(resolved.id)] = ref
|
||||||
cssMap[relativeId].files.push(ref)
|
idMap.files.push(ref)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ export function typeCheckPlugin (options: { sourcemap?: boolean } = {}): Plugin
|
|||||||
name: 'nuxt:type-check',
|
name: 'nuxt:type-check',
|
||||||
configResolved (config) {
|
configResolved (config) {
|
||||||
const input = config.build.rollupOptions.input
|
const input = config.build.rollupOptions.input
|
||||||
if (input && typeof input !== 'string' && !Array.isArray(input)) {
|
if (input && typeof input !== 'string' && !Array.isArray(input) && input.entry) {
|
||||||
entry = input.entry
|
entry = input.entry
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -39,7 +39,7 @@ export default function virtual (vfs: Record<string, string>): Plugin {
|
|||||||
const idNoPrefix = id.slice(PREFIX.length)
|
const idNoPrefix = id.slice(PREFIX.length)
|
||||||
if (idNoPrefix in vfs) {
|
if (idNoPrefix in vfs) {
|
||||||
return {
|
return {
|
||||||
code: vfs[idNoPrefix],
|
code: vfs[idNoPrefix] || '',
|
||||||
map: null,
|
map: null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ interface Envs {
|
|||||||
|
|
||||||
export function transpile (envs: Envs): Array<string | RegExp> {
|
export function transpile (envs: Envs): Array<string | RegExp> {
|
||||||
const nuxt = useNuxt()
|
const nuxt = useNuxt()
|
||||||
const transpile: Array<string | RegExp> = []
|
const transpile: RegExp[] = []
|
||||||
|
|
||||||
for (let pattern of nuxt.options.build.transpile) {
|
for (let pattern of nuxt.options.build.transpile) {
|
||||||
if (typeof pattern === 'function') {
|
if (typeof pattern === 'function') {
|
||||||
|
@ -41,8 +41,8 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
|
|||||||
...app.plugins.map(p => dirname(p.src)),
|
...app.plugins.map(p => dirname(p.src)),
|
||||||
...app.middleware.map(m => dirname(m.path)),
|
...app.middleware.map(m => dirname(m.path)),
|
||||||
...Object.values(app.layouts || {}).map(l => dirname(l.file)),
|
...Object.values(app.layouts || {}).map(l => dirname(l.file)),
|
||||||
dirname(nuxt.apps.default.rootComponent!),
|
dirname(nuxt.apps.default!.rootComponent!),
|
||||||
dirname(nuxt.apps.default.errorComponent!),
|
dirname(nuxt.apps.default!.errorComponent!),
|
||||||
]),
|
]),
|
||||||
].filter(d => d && existsSync(d))
|
].filter(d => d && existsSync(d))
|
||||||
|
|
||||||
@ -183,7 +183,7 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
|
|||||||
clientCSSMap,
|
clientCSSMap,
|
||||||
chunksWithInlinedCSS,
|
chunksWithInlinedCSS,
|
||||||
shouldInline: ctx.nuxt.options.features.inlineStyles,
|
shouldInline: ctx.nuxt.options.features.inlineStyles,
|
||||||
components: ctx.nuxt.apps.default.components,
|
components: ctx.nuxt.apps.default!.components || [],
|
||||||
globalCSS: ctx.nuxt.options.css,
|
globalCSS: ctx.nuxt.options.css,
|
||||||
mode: isServer ? 'server' : 'client',
|
mode: isServer ? 'server' : 'client',
|
||||||
entry: ctx.entry,
|
entry: ctx.entry,
|
||||||
@ -193,7 +193,7 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
|
|||||||
// Remove CSS entries for files that will have inlined styles
|
// Remove CSS entries for files that will have inlined styles
|
||||||
ctx.nuxt.hook('build:manifest', (manifest) => {
|
ctx.nuxt.hook('build:manifest', (manifest) => {
|
||||||
for (const key in manifest) {
|
for (const key in manifest) {
|
||||||
const entry = manifest[key]
|
const entry = manifest[key]!
|
||||||
const shouldRemoveCSS = chunksWithInlinedCSS.has(key) && !entry.isEntry
|
const shouldRemoveCSS = chunksWithInlinedCSS.has(key) && !entry.isEntry
|
||||||
if (entry.isEntry && chunksWithInlinedCSS.has(key)) {
|
if (entry.isEntry && chunksWithInlinedCSS.has(key)) {
|
||||||
// @ts-expect-error internal key
|
// @ts-expect-error internal key
|
||||||
|
@ -716,7 +716,7 @@ describe('nuxt links', () => {
|
|||||||
for (const selector of ['nuxt-link', 'router-link', 'link-with-trailing-slash', 'link-without-trailing-slash']) {
|
for (const selector of ['nuxt-link', 'router-link', 'link-with-trailing-slash', 'link-without-trailing-slash']) {
|
||||||
data[selector] = []
|
data[selector] = []
|
||||||
for (const match of html.matchAll(new RegExp(`href="([^"]*)"[^>]*class="[^"]*\\b${selector}\\b`, 'g'))) {
|
for (const match of html.matchAll(new RegExp(`href="([^"]*)"[^>]*class="[^"]*\\b${selector}\\b`, 'g'))) {
|
||||||
data[selector].push(match[1])
|
data[selector]!.push(match[1]!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expect(data).toMatchInlineSnapshot(`
|
expect(data).toMatchInlineSnapshot(`
|
||||||
@ -1410,7 +1410,7 @@ describe('extends support', () => {
|
|||||||
describe('app', () => {
|
describe('app', () => {
|
||||||
it('extends foo/app/router.options & bar/app/router.options', async () => {
|
it('extends foo/app/router.options & bar/app/router.options', async () => {
|
||||||
const html: string = await $fetch<string>('/')
|
const html: string = await $fetch<string>('/')
|
||||||
const routerLinkClasses = html.match(/href="\/" class="([^"]*)"/)?.[1].split(' ')
|
const routerLinkClasses = html.match(/href="\/" class="([^"]*)"/)![1]!.split(' ')
|
||||||
expect(routerLinkClasses).toContain('foo-active-class')
|
expect(routerLinkClasses).toContain('foo-active-class')
|
||||||
expect(routerLinkClasses).toContain('bar-exact-active-class')
|
expect(routerLinkClasses).toContain('bar-exact-active-class')
|
||||||
})
|
})
|
||||||
@ -1453,7 +1453,7 @@ describe('nested suspense', () => {
|
|||||||
['/suspense/sync-1/sync-1/', '/suspense/sync-2/async-1/'],
|
['/suspense/sync-1/sync-1/', '/suspense/sync-2/async-1/'],
|
||||||
['/suspense/async-1/async-1/', '/suspense/async-2/async-1/'],
|
['/suspense/async-1/async-1/', '/suspense/async-2/async-1/'],
|
||||||
['/suspense/async-1/sync-1/', '/suspense/async-2/async-1/'],
|
['/suspense/async-1/sync-1/', '/suspense/async-2/async-1/'],
|
||||||
]).flatMap(([start, end]) => [
|
] as const).flatMap(([start, end]) => [
|
||||||
[start, end],
|
[start, end],
|
||||||
[start, end + '?layout=custom'],
|
[start, end + '?layout=custom'],
|
||||||
[start + '?layout=custom', end],
|
[start + '?layout=custom', end],
|
||||||
@ -1719,7 +1719,7 @@ describe.skipIf(isDev() || isWebpack)('inlining component styles', () => {
|
|||||||
|
|
||||||
it('should not include inlined CSS in generated CSS file', async () => {
|
it('should not include inlined CSS in generated CSS file', async () => {
|
||||||
const html: string = await $fetch<string>('/styles')
|
const html: string = await $fetch<string>('/styles')
|
||||||
const cssFiles = new Set([...html.matchAll(/<link [^>]*href="([^"]*\.css)">/g)].map(m => m[1]))
|
const cssFiles = new Set([...html.matchAll(/<link [^>]*href="([^"]*\.css)">/g)].map(m => m[1]!))
|
||||||
let css = ''
|
let css = ''
|
||||||
for (const file of cssFiles || []) {
|
for (const file of cssFiles || []) {
|
||||||
css += await $fetch<string>(file)
|
css += await $fetch<string>(file)
|
||||||
@ -1970,7 +1970,7 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
|||||||
it('should work with no overrides', async () => {
|
it('should work with no overrides', async () => {
|
||||||
const html: string = await $fetch<string>('/assets')
|
const html: string = await $fetch<string>('/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]
|
const url = match[2] || match[3]!
|
||||||
expect(url.startsWith('/_nuxt/') || isPublicFile('/', url)).toBeTruthy()
|
expect(url.startsWith('/_nuxt/') || isPublicFile('/', url)).toBeTruthy()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -1979,10 +1979,10 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
|||||||
it.skipIf(isWebpack)('adds relative paths to CSS', async () => {
|
it.skipIf(isWebpack)('adds relative paths to CSS', async () => {
|
||||||
const html: string = await $fetch<string>('/assets')
|
const html: string = await $fetch<string>('/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))
|
const cssURL = urls.find(u => /_nuxt\/assets.*\.css$/.test(u!))
|
||||||
expect(cssURL).toBeDefined()
|
expect(cssURL).toBeDefined()
|
||||||
const css = await $fetch<string>(cssURL!)
|
const css = await $fetch<string>(cssURL!)
|
||||||
const imageUrls = new Set(Array.from(css.matchAll(/url\(([^)]*)\)/g)).map(m => m[1].replace(/[-.]\w{8}\./g, '.')))
|
const imageUrls = new Set(Array.from(css.matchAll(/url\(([^)]*)\)/g)).map(m => m[1]!.replace(/[-.]\w{8}\./g, '.')))
|
||||||
expect([...imageUrls]).toMatchInlineSnapshot(`
|
expect([...imageUrls]).toMatchInlineSnapshot(`
|
||||||
[
|
[
|
||||||
"./logo.svg",
|
"./logo.svg",
|
||||||
@ -2001,7 +2001,7 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
|||||||
|
|
||||||
const html = await $fetch<string>('/foo/assets')
|
const html = await $fetch<string>('/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]
|
const url = match[2] || match[3]!
|
||||||
expect(url.startsWith('/foo/_other/') || isPublicFile('/foo/', url)).toBeTruthy()
|
expect(url.startsWith('/foo/_other/') || isPublicFile('/foo/', url)).toBeTruthy()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2017,7 +2017,7 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
|||||||
|
|
||||||
const html = await $fetch<string>('/assets')
|
const html = await $fetch<string>('/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]
|
const url = match[2] || match[3]!
|
||||||
expect(url.startsWith('./_nuxt/') || isPublicFile('./', url)).toBeTruthy()
|
expect(url.startsWith('./_nuxt/') || isPublicFile('./', url)).toBeTruthy()
|
||||||
expect(url.startsWith('./_nuxt/_nuxt')).toBeFalsy()
|
expect(url.startsWith('./_nuxt/_nuxt')).toBeFalsy()
|
||||||
}
|
}
|
||||||
@ -2046,7 +2046,7 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
|||||||
|
|
||||||
const html = await $fetch<string>('/foo/assets')
|
const html = await $fetch<string>('/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]
|
const url = match[2] || match[3]!
|
||||||
expect(url.startsWith('https://example.com/_cdn/') || isPublicFile('https://example.com/', url)).toBeTruthy()
|
expect(url.startsWith('https://example.com/_cdn/') || isPublicFile('https://example.com/', url)).toBeTruthy()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -2082,7 +2082,7 @@ describe('component islands', () => {
|
|||||||
|
|
||||||
result.html = result.html.replace(/ data-island-uid="[^"]*"/g, '')
|
result.html = result.html.replace(/ data-island-uid="[^"]*"/g, '')
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
result.head.link = result.head.link.filter(l => !l.href.includes('@nuxt+ui-templates') && (l.href.startsWith('_nuxt/components/islands/') && l.href.includes('_nuxt/components/islands/RouteComponent')))
|
result.head.link = result.head.link.filter(l => !l.href!.includes('@nuxt+ui-templates') && (l.href!.startsWith('_nuxt/components/islands/') && l.href!.includes('_nuxt/components/islands/RouteComponent')))
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
@ -2104,7 +2104,7 @@ describe('component islands', () => {
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
result.head.link = result.head.link.filter(l => !l.href.includes('@nuxt+ui-templates') && (l.href.startsWith('_nuxt/components/islands/') && l.href.includes('_nuxt/components/islands/LongAsyncComponent')))
|
result.head.link = result.head.link.filter(l => !l.href!.includes('@nuxt+ui-templates') && (l.href!.startsWith('_nuxt/components/islands/') && l.href!.includes('_nuxt/components/islands/LongAsyncComponent')))
|
||||||
}
|
}
|
||||||
result.html = result.html.replaceAll(/ (data-island-uid|data-island-component)="([^"]*)"/g, '')
|
result.html = result.html.replaceAll(/ (data-island-uid|data-island-component)="([^"]*)"/g, '')
|
||||||
expect(result).toMatchInlineSnapshot(`
|
expect(result).toMatchInlineSnapshot(`
|
||||||
@ -2162,7 +2162,7 @@ describe('component islands', () => {
|
|||||||
}),
|
}),
|
||||||
}))
|
}))
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
result.head.link = result.head.link.filter(l => !l.href.includes('@nuxt+ui-templates') && (l.href.startsWith('_nuxt/components/islands/') && l.href.includes('_nuxt/components/islands/AsyncServerComponent')))
|
result.head.link = result.head.link.filter(l => !l.href!.includes('@nuxt+ui-templates') && (l.href!.startsWith('_nuxt/components/islands/') && l.href!.includes('_nuxt/components/islands/AsyncServerComponent')))
|
||||||
}
|
}
|
||||||
result.props = {}
|
result.props = {}
|
||||||
result.components = {}
|
result.components = {}
|
||||||
@ -2187,7 +2187,7 @@ describe('component islands', () => {
|
|||||||
it('render server component with selective client hydration', async () => {
|
it('render server component with selective client hydration', async () => {
|
||||||
const result = await $fetch<NuxtIslandResponse>('/__nuxt_island/ServerWithClient')
|
const result = await $fetch<NuxtIslandResponse>('/__nuxt_island/ServerWithClient')
|
||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
result.head.link = result.head.link.filter(l => !l.href.includes('@nuxt+ui-templates') && (l.href.startsWith('_nuxt/components/islands/') && l.href.includes('_nuxt/components/islands/AsyncServerComponent')))
|
result.head.link = result.head.link.filter(l => !l.href!.includes('@nuxt+ui-templates') && (l.href!.startsWith('_nuxt/components/islands/') && l.href!.includes('_nuxt/components/islands/AsyncServerComponent')))
|
||||||
}
|
}
|
||||||
const { components } = result
|
const { components } = result
|
||||||
result.components = {}
|
result.components = {}
|
||||||
@ -2208,13 +2208,13 @@ describe('component islands', () => {
|
|||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
expect(teleportsEntries).toHaveLength(1)
|
expect(teleportsEntries).toHaveLength(1)
|
||||||
expect(teleportsEntries[0][0].startsWith('Counter-')).toBeTruthy()
|
expect(teleportsEntries[0]![0].startsWith('Counter-')).toBeTruthy()
|
||||||
expect(teleportsEntries[0][1].props).toMatchInlineSnapshot(`
|
expect(teleportsEntries[0]![1].props).toMatchInlineSnapshot(`
|
||||||
{
|
{
|
||||||
"multiplier": 1,
|
"multiplier": 1,
|
||||||
}
|
}
|
||||||
`)
|
`)
|
||||||
expect(teleportsEntries[0][1].html).toMatchInlineSnapshot('"<div class="sugar-counter"> Sugar Counter 12 x 1 = 12 <button> Inc </button></div><!--teleport anchor-->"')
|
expect(teleportsEntries[0]![1].html).toMatchInlineSnapshot('"<div class="sugar-counter"> Sugar Counter 12 x 1 = 12 <button> Inc </button></div><!--teleport anchor-->"')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2232,10 +2232,10 @@ describe('component islands', () => {
|
|||||||
if (isDev()) {
|
if (isDev()) {
|
||||||
const fixtureDir = normalize(fileURLToPath(new URL('./fixtures/basic', import.meta.url)))
|
const fixtureDir = normalize(fileURLToPath(new URL('./fixtures/basic', import.meta.url)))
|
||||||
for (const link of result.head.link) {
|
for (const link of result.head.link) {
|
||||||
link.href = link.href.replace(fixtureDir, '/<rootDir>').replaceAll('//', '/')
|
link.href = link.href!.replace(fixtureDir, '/<rootDir>').replaceAll('//', '/')
|
||||||
link.key = link.key.replace(/-[a-z0-9]+$/i, '')
|
link.key = link.key!.replace(/-[a-z0-9]+$/i, '')
|
||||||
}
|
}
|
||||||
result.head.link.sort((a, b) => b.href.localeCompare(a.href))
|
result.head.link.sort((a, b) => b.href!.localeCompare(a.href!))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: fix rendering of styles in webpack
|
// TODO: fix rendering of styles in webpack
|
||||||
@ -2254,7 +2254,7 @@ describe('component islands', () => {
|
|||||||
} else if (isDev() && !isWebpack) {
|
} else if (isDev() && !isWebpack) {
|
||||||
// TODO: resolve dev bug triggered by earlier fetch of /vueuse-head page
|
// TODO: resolve dev bug triggered by earlier fetch of /vueuse-head page
|
||||||
// https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/core/runtime/nitro/renderer.ts#L139
|
// https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/core/runtime/nitro/renderer.ts#L139
|
||||||
result.head.link = result.head.link.filter(h => !h.href.includes('SharedComponent'))
|
result.head.link = result.head.link.filter(h => !h.href!.includes('SharedComponent'))
|
||||||
expect(result.head).toMatchInlineSnapshot(`
|
expect(result.head).toMatchInlineSnapshot(`
|
||||||
{
|
{
|
||||||
"link": [
|
"link": [
|
||||||
|
@ -111,7 +111,7 @@ if (process.env.TEST_ENV !== 'built' && !isWindows) {
|
|||||||
const resolveHmrId = async () => {
|
const resolveHmrId = async () => {
|
||||||
const node = await page.$('#hmr-id')
|
const node = await page.$('#hmr-id')
|
||||||
const text = await node?.innerText() || ''
|
const text = await node?.innerText() || ''
|
||||||
return Number(text?.trim().split(':')[1].trim())
|
return Number(text.trim().split(':')[1]?.trim() || '')
|
||||||
}
|
}
|
||||||
const componentPath = join(fixturePath, 'components/islands/HmrComponent.vue')
|
const componentPath = join(fixturePath, 'components/islands/HmrComponent.vue')
|
||||||
const triggerHmr = async () => fsp.writeFile(
|
const triggerHmr = async () => fsp.writeFile(
|
||||||
|
@ -107,17 +107,17 @@ export function parsePayload (payload: string) {
|
|||||||
}
|
}
|
||||||
export function parseData (html: string) {
|
export function parseData (html: string) {
|
||||||
if (!isRenderingJson) {
|
if (!isRenderingJson) {
|
||||||
const { script } = html.match(/<script>(?<script>window.__NUXT__.*?)<\/script>/)?.groups || {}
|
const { script = '' } = html.match(/<script>(?<script>window.__NUXT__.*?)<\/script>/)?.groups || {}
|
||||||
const _script = new Script(script)
|
const _script = new Script(script)
|
||||||
return {
|
return {
|
||||||
script: _script.runInContext(createContext({ window: {} })),
|
script: _script.runInContext(createContext({ window: {} })),
|
||||||
attrs: {},
|
attrs: {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const { script, attrs } = html.match(/<script type="application\/json" id="__NUXT_DATA__"(?<attrs>[^>]+)>(?<script>.*?)<\/script>/)?.groups || {}
|
const { script, attrs = '' } = html.match(/<script type="application\/json" id="__NUXT_DATA__"(?<attrs>[^>]+)>(?<script>.*?)<\/script>/)?.groups || {}
|
||||||
const _attrs: Record<string, string> = {}
|
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
|
_attrs[attr!.groups!.key!] = attr!.groups!.value!
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
script: parsePayload(script || ''),
|
script: parsePayload(script || ''),
|
||||||
|
Loading…
Reference in New Issue
Block a user