mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-21 21:25:11 +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[]) => {
|
||||
const existingComponentIndex = components.findIndex(c => (c.pascalName === component.pascalName || c.kebabName === component.kebabName) && c.mode === component.mode)
|
||||
if (existingComponentIndex !== -1) {
|
||||
const existingComponent = components[existingComponentIndex]
|
||||
const existingComponent = components[existingComponentIndex]!
|
||||
const existingPriority = existingComponent.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 !*
|
||||
return ignorePatterns.map((p) => {
|
||||
const [_, negation = '', pattern] = p.match(NEGATION_RE) || []
|
||||
if (pattern[0] === '*') {
|
||||
if (pattern && pattern[0] === '*') {
|
||||
return 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) => {
|
||||
const [head, ...tail] = group.split('{')
|
||||
if (tail.length) {
|
||||
const [body, ...rest] = tail.join('{').split('}')
|
||||
const [body = '', ...rest] = tail.join('{').split('}')
|
||||
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.hook('app:templates', (app) => {
|
||||
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(
|
||||
`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) {
|
||||
const find = app.middleware.findIndex(item => item.name === middleware.name)
|
||||
if (find >= 0) {
|
||||
const foundPath = app.middleware[find].path
|
||||
const foundPath = app.middleware[find]!.path
|
||||
if (foundPath === middleware.path) { continue }
|
||||
if (options.override === true) {
|
||||
app.middleware[find] = { ...middleware }
|
||||
|
@ -174,7 +174,7 @@ export async function resolveNuxtModule (base: string, paths: string[]): Promise
|
||||
|
||||
for (const path of paths) {
|
||||
if (path.startsWith(base)) {
|
||||
resolved.push(path.split('/index.ts')[0])
|
||||
resolved.push(path.split('/index.ts')[0]!)
|
||||
} else {
|
||||
const resolvedPath = await resolver.resolvePath(path)
|
||||
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))) {
|
||||
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 */)
|
||||
if (!stats) {
|
||||
const resolvedModule = await tryResolveModule(aliases[alias], nuxt.options.modulesDir)
|
||||
const resolvedModule = await tryResolveModule(aliases[alias]!, nuxt.options.modulesDir)
|
||||
if (resolvedModule) {
|
||||
absolutePath = resolvedModule
|
||||
stats = await fsp.stat(resolvedModule).catch(() => null)
|
||||
@ -251,7 +251,7 @@ export async function _generateTypes (nuxt: Nuxt) {
|
||||
// remove extension
|
||||
? relativePath.replace(/\b\.\w+$/g, '')
|
||||
// non-existent file probably shouldn't be resolved
|
||||
: aliases[alias]
|
||||
: aliases[alias]!
|
||||
|
||||
tsConfig.compilerOptions.paths[alias] = [path]
|
||||
|
||||
@ -334,7 +334,7 @@ function renderAttrs (obj: Record<string, string>) {
|
||||
return attrs.join(' ')
|
||||
}
|
||||
|
||||
function renderAttr (key: string, value: string) {
|
||||
function renderAttr (key: string, value?: string) {
|
||||
return value ? `${key}="${value}"` : ''
|
||||
}
|
||||
|
||||
|
@ -139,9 +139,14 @@ export const RenderPlugin = () => {
|
||||
}
|
||||
return lastChar || ''
|
||||
}).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")'))
|
||||
|
||||
const inlineScripts: string[] = []
|
||||
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, {
|
||||
type: typeof value === 'string' ? 'String' : typeof value === 'number' ? 'Number' : typeof value === 'boolean' ? 'Boolean' : 'undefined',
|
||||
default: JSON.stringify(value),
|
||||
|
@ -16,10 +16,10 @@ export function analyzePlugin (ctx: ViteBuildContext): Plugin[] {
|
||||
async generateBundle (_opts, outputBundle) {
|
||||
for (const _bundleId in outputBundle) {
|
||||
const bundle = outputBundle[_bundleId]
|
||||
if (bundle.type !== 'chunk') { continue }
|
||||
if (!bundle || bundle.type !== 'chunk') { continue }
|
||||
const minifiedModuleEntryPromises: Array<Promise<[string, RenderedModule]>> = []
|
||||
for (const moduleId in bundle.modules) {
|
||||
const module = bundle.modules[moduleId]
|
||||
const module = bundle.modules[moduleId]!
|
||||
minifiedModuleEntryPromises.push(
|
||||
transform(module.code || '', { minify: true })
|
||||
.then(result => [moduleId, { ...module, code: result.code }]),
|
||||
|
@ -17,7 +17,7 @@ interface ComposableKeysOptions {
|
||||
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 SUPPORTED_EXT_RE = /\.(?:m?[jt]sx?|vue)/
|
||||
|
||||
@ -182,7 +182,7 @@ class ScopeTracker {
|
||||
leaveScope () {
|
||||
this.scopeIndexStack.pop()
|
||||
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)) {
|
||||
if (NUXT_IMPORT_RE.test(i.specifier)) { continue }
|
||||
|
||||
const { namedImports, defaultImport, namespacedImport } = parseStaticImport(i)
|
||||
for (const name in namedImports || {}) {
|
||||
addName(namedImports![name], i.specifier)
|
||||
const { namedImports = {}, defaultImport, namespacedImport } = parseStaticImport(i)
|
||||
for (const name in namedImports) {
|
||||
addName(namedImports[name]!, i.specifier)
|
||||
}
|
||||
if (defaultImport) {
|
||||
addName(defaultImport, i.specifier)
|
||||
|
@ -38,7 +38,7 @@ export const VitePublicDirsPlugin = createUnplugin((options: { sourcemap?: boole
|
||||
const s = new MagicString(code)
|
||||
const q = code.match(/(?<= = )['"`]/)?.[0] || '"'
|
||||
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})`)
|
||||
}
|
||||
}
|
||||
@ -53,13 +53,13 @@ export const VitePublicDirsPlugin = createUnplugin((options: { sourcemap?: boole
|
||||
},
|
||||
generateBundle (_outputOptions, bundle) {
|
||||
for (const file in bundle) {
|
||||
const chunk = bundle[file]
|
||||
const chunk = bundle[file]!
|
||||
if (!file.endsWith('.css') || chunk.type !== 'asset') { continue }
|
||||
|
||||
let css = chunk.source.toString()
|
||||
let wasReplaced = false
|
||||
for (const [full, url] of css.matchAll(CSS_URL_RE)) {
|
||||
if (resolveFromPublicAssets(url)) {
|
||||
if (url && resolveFromPublicAssets(url)) {
|
||||
const relativeURL = relative(withLeadingSlash(dirname(file)), url)
|
||||
css = css.replace(full, `url(${relativeURL})`)
|
||||
wasReplaced = true
|
||||
|
@ -24,7 +24,7 @@ interface SSRStylePluginOptions {
|
||||
const SUPPORTED_FILES_RE = /\.(?:vue|(?:[cm]?j|t)sx?)$/
|
||||
|
||||
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 relativeToSrcDir = (path: string) => relative(options.srcDir, path)
|
||||
@ -63,7 +63,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
||||
|
||||
const emitted: Record<string, string> = {}
|
||||
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)
|
||||
if (!files.length || !inBundle) { continue }
|
||||
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
|
||||
// to be inlined on server rendering
|
||||
if (options.mode === 'client') {
|
||||
options.clientCSSMap[moduleId] ||= new Set()
|
||||
const moduleMap = options.clientCSSMap[moduleId] ||= new Set()
|
||||
if (isCSS(moduleId)) {
|
||||
// Vue files can (also) be their own entrypoints as they are tracked separately
|
||||
if (isVue(moduleId)) {
|
||||
options.clientCSSMap[moduleId].add(moduleId)
|
||||
moduleMap.add(moduleId)
|
||||
const parent = moduleId.replace(/\?.+$/, '')
|
||||
options.clientCSSMap[parent] ||= new Set()
|
||||
options.clientCSSMap[parent].add(moduleId)
|
||||
const parentMap = options.clientCSSMap[parent] ||= new Set()
|
||||
parentMap.add(moduleId)
|
||||
}
|
||||
// This is required to track CSS in entry chunk
|
||||
if (isEntry) {
|
||||
options.clientCSSMap[chunk.facadeModuleId!].add(moduleId)
|
||||
if (isEntry && chunk.facadeModuleId) {
|
||||
const facadeMap = options.clientCSSMap[chunk.facadeModuleId] ||= new Set()
|
||||
facadeMap.add(moduleId)
|
||||
}
|
||||
}
|
||||
continue
|
||||
@ -134,7 +135,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
||||
|
||||
const relativePath = relativeToSrcDir(moduleId)
|
||||
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.
|
||||
if (id === options.entry && (options.shouldInline === true || (typeof options.shouldInline === 'function' && options.shouldInline(id)))) {
|
||||
const s = new MagicString(code)
|
||||
options.clientCSSMap[id] ||= new Set()
|
||||
const idClientCSSMap = options.clientCSSMap[id] ||= new Set()
|
||||
if (!options.globalCSS.length) { return }
|
||||
|
||||
for (const file of options.globalCSS) {
|
||||
@ -160,7 +161,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
||||
s.prepend(`${genImport(file)}\n`)
|
||||
continue
|
||||
}
|
||||
options.clientCSSMap[id].add(resolved.id)
|
||||
idClientCSSMap.add(resolved.id)
|
||||
}
|
||||
if (s.hasChanged()) {
|
||||
return {
|
||||
@ -184,7 +185,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
||||
}
|
||||
|
||||
const relativeId = relativeToSrcDir(id)
|
||||
cssMap[relativeId] = cssMap[relativeId] || { files: [] }
|
||||
const idMap = cssMap[relativeId] ||= { files: [] }
|
||||
|
||||
const emittedIds = new Set<string>()
|
||||
|
||||
@ -208,7 +209,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
||||
})
|
||||
|
||||
idRefMap[relativeToSrcDir(file)] = ref
|
||||
cssMap[relativeId].files.push(ref)
|
||||
idMap.files.push(ref)
|
||||
}
|
||||
|
||||
if (!SUPPORTED_FILES_RE.test(pathname)) { return }
|
||||
@ -235,7 +236,7 @@ export function ssrStylesPlugin (options: SSRStylePluginOptions): Plugin {
|
||||
})
|
||||
|
||||
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',
|
||||
configResolved (config) {
|
||||
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
|
||||
}
|
||||
},
|
||||
|
@ -39,7 +39,7 @@ export default function virtual (vfs: Record<string, string>): Plugin {
|
||||
const idNoPrefix = id.slice(PREFIX.length)
|
||||
if (idNoPrefix in vfs) {
|
||||
return {
|
||||
code: vfs[idNoPrefix],
|
||||
code: vfs[idNoPrefix] || '',
|
||||
map: null,
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ interface Envs {
|
||||
|
||||
export function transpile (envs: Envs): Array<string | RegExp> {
|
||||
const nuxt = useNuxt()
|
||||
const transpile: Array<string | RegExp> = []
|
||||
const transpile: RegExp[] = []
|
||||
|
||||
for (let pattern of nuxt.options.build.transpile) {
|
||||
if (typeof pattern === 'function') {
|
||||
|
@ -41,8 +41,8 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
|
||||
...app.plugins.map(p => dirname(p.src)),
|
||||
...app.middleware.map(m => dirname(m.path)),
|
||||
...Object.values(app.layouts || {}).map(l => dirname(l.file)),
|
||||
dirname(nuxt.apps.default.rootComponent!),
|
||||
dirname(nuxt.apps.default.errorComponent!),
|
||||
dirname(nuxt.apps.default!.rootComponent!),
|
||||
dirname(nuxt.apps.default!.errorComponent!),
|
||||
]),
|
||||
].filter(d => d && existsSync(d))
|
||||
|
||||
@ -183,7 +183,7 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
|
||||
clientCSSMap,
|
||||
chunksWithInlinedCSS,
|
||||
shouldInline: ctx.nuxt.options.features.inlineStyles,
|
||||
components: ctx.nuxt.apps.default.components,
|
||||
components: ctx.nuxt.apps.default!.components || [],
|
||||
globalCSS: ctx.nuxt.options.css,
|
||||
mode: isServer ? 'server' : 'client',
|
||||
entry: ctx.entry,
|
||||
@ -193,7 +193,7 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
|
||||
// Remove CSS entries for files that will have inlined styles
|
||||
ctx.nuxt.hook('build:manifest', (manifest) => {
|
||||
for (const key in manifest) {
|
||||
const entry = manifest[key]
|
||||
const entry = manifest[key]!
|
||||
const shouldRemoveCSS = chunksWithInlinedCSS.has(key) && !entry.isEntry
|
||||
if (entry.isEntry && chunksWithInlinedCSS.has(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']) {
|
||||
data[selector] = []
|
||||
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(`
|
||||
@ -1410,7 +1410,7 @@ describe('extends support', () => {
|
||||
describe('app', () => {
|
||||
it('extends foo/app/router.options & bar/app/router.options', async () => {
|
||||
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('bar-exact-active-class')
|
||||
})
|
||||
@ -1453,7 +1453,7 @@ describe('nested suspense', () => {
|
||||
['/suspense/sync-1/sync-1/', '/suspense/sync-2/async-1/'],
|
||||
['/suspense/async-1/async-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 + '?layout=custom'],
|
||||
[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 () => {
|
||||
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 = ''
|
||||
for (const file of cssFiles || []) {
|
||||
css += await $fetch<string>(file)
|
||||
@ -1970,7 +1970,7 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
||||
it('should work with no overrides', async () => {
|
||||
const html: string = await $fetch<string>('/assets')
|
||||
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()
|
||||
}
|
||||
})
|
||||
@ -1979,10 +1979,10 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
||||
it.skipIf(isWebpack)('adds relative paths to CSS', async () => {
|
||||
const html: string = await $fetch<string>('/assets')
|
||||
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()
|
||||
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(`
|
||||
[
|
||||
"./logo.svg",
|
||||
@ -2001,7 +2001,7 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
||||
|
||||
const html = await $fetch<string>('/foo/assets')
|
||||
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()
|
||||
}
|
||||
|
||||
@ -2017,7 +2017,7 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
||||
|
||||
const html = await $fetch<string>('/assets')
|
||||
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/_nuxt')).toBeFalsy()
|
||||
}
|
||||
@ -2046,7 +2046,7 @@ describe.skipIf(isDev())('dynamic paths', () => {
|
||||
|
||||
const html = await $fetch<string>('/foo/assets')
|
||||
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()
|
||||
}
|
||||
})
|
||||
@ -2082,7 +2082,7 @@ describe('component islands', () => {
|
||||
|
||||
result.html = result.html.replace(/ data-island-uid="[^"]*"/g, '')
|
||||
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(`
|
||||
@ -2104,7 +2104,7 @@ describe('component islands', () => {
|
||||
}),
|
||||
}))
|
||||
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, '')
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
@ -2162,7 +2162,7 @@ describe('component islands', () => {
|
||||
}),
|
||||
}))
|
||||
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.components = {}
|
||||
@ -2187,7 +2187,7 @@ describe('component islands', () => {
|
||||
it('render server component with selective client hydration', async () => {
|
||||
const result = await $fetch<NuxtIslandResponse>('/__nuxt_island/ServerWithClient')
|
||||
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
|
||||
result.components = {}
|
||||
@ -2208,13 +2208,13 @@ describe('component islands', () => {
|
||||
}
|
||||
`)
|
||||
expect(teleportsEntries).toHaveLength(1)
|
||||
expect(teleportsEntries[0][0].startsWith('Counter-')).toBeTruthy()
|
||||
expect(teleportsEntries[0][1].props).toMatchInlineSnapshot(`
|
||||
expect(teleportsEntries[0]![0].startsWith('Counter-')).toBeTruthy()
|
||||
expect(teleportsEntries[0]![1].props).toMatchInlineSnapshot(`
|
||||
{
|
||||
"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()) {
|
||||
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-z0-9]+$/i, '')
|
||||
link.href = link.href!.replace(fixtureDir, '/<rootDir>').replaceAll('//', '/')
|
||||
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
|
||||
@ -2254,7 +2254,7 @@ describe('component islands', () => {
|
||||
} else if (isDev() && !isWebpack) {
|
||||
// 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
|
||||
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(`
|
||||
{
|
||||
"link": [
|
||||
|
@ -111,7 +111,7 @@ if (process.env.TEST_ENV !== 'built' && !isWindows) {
|
||||
const resolveHmrId = async () => {
|
||||
const node = await page.$('#hmr-id')
|
||||
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 triggerHmr = async () => fsp.writeFile(
|
||||
|
@ -107,17 +107,17 @@ export function parsePayload (payload: string) {
|
||||
}
|
||||
export function parseData (html: string) {
|
||||
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)
|
||||
return {
|
||||
script: _script.runInContext(createContext({ window: {} })),
|
||||
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> = {}
|
||||
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 {
|
||||
script: parsePayload(script || ''),
|
||||
|
Loading…
Reference in New Issue
Block a user