chore: improve type safety with indexed access (#27626)

This commit is contained in:
Daniel Roe 2024-06-27 16:27:08 +02:00 committed by GitHub
parent ef1cfa0508
commit 53df20ef2b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 75 additions and 69 deletions

View File

@ -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

View File

@ -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('')}`)
}

View File

@ -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}\`.`,
)

View File

@ -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 }

View File

@ -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))

View File

@ -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}"` : ''
}

View File

@ -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),

View File

@ -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 }]),

View File

@ -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)

View File

@ -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

View File

@ -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)
}
},
}

View File

@ -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
}
},

View File

@ -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,
}
}

View File

@ -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') {

View File

@ -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

View File

@ -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": [

View File

@ -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(

View File

@ -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 || ''),