Merge branch 'main' into patch-21

This commit is contained in:
Michael Brevard 2024-04-05 12:36:56 +03:00 committed by GitHub
commit 444e5be6e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 969 additions and 734 deletions

224
.eslintrc
View File

@ -1,224 +0,0 @@
{
"$schema": "https://json.schemastore.org/eslintrc",
"ignorePatterns": [
"dist",
"public",
"node_modules",
"packages/schema/schema"
],
"globals": {
"NodeJS": true,
"$fetch": true
},
"plugins": ["jsdoc", "import", "unicorn", "no-only-tests"],
"extends": [
"standard",
"plugin:jsdoc/recommended",
"@nuxt/eslint-config",
"plugin:import/typescript"
],
"rules": {
// Imports should come first
"import/first": "error",
// Other import rules
"import/no-mutable-exports": "error",
// Allow unresolved imports
"import/no-unresolved": "off",
// Allow paren-less arrow functions only when there's no braces
"arrow-parens": ["error", "as-needed", { "requireForBlockBody": true }],
// Allow async-await
"generator-star-spacing": "off",
// Prefer const over let
"prefer-const": ["error", { "destructuring": "any", "ignoreReadBeforeAssign": false }],
// No single if in an "else" block
"no-lonely-if": "error",
// Force curly braces for control flow,
// including if blocks with a single statement
"curly": ["error", "all"
],
// No async function without await
"require-await": "error",
// Force dot notation when possible
"dot-notation": "error",
"no-var": "error",
// Force object shorthand where possible
"object-shorthand": "error",
// No useless destructuring/importing/exporting renames
"no-useless-rename": "error",
/**********************/
/* Unicorn Rules */
/**********************/
// Pass error message when throwing errors
"unicorn/error-message": "error",
// Uppercase regex escapes
"unicorn/escape-case": "error",
// Array.isArray instead of instanceof
"unicorn/no-array-instanceof": "error",
// Prevent deprecated `new Buffer()`
"unicorn/no-new-buffer": "error",
// Keep regex literals safe!
"unicorn/no-unsafe-regex": "off",
// Lowercase number formatting for octal, hex, binary (0x12 instead of 0X12)
"unicorn/number-literal-case": "error",
// ** instead of Math.pow()
"unicorn/prefer-exponentiation-operator": "error",
// includes over indexOf when checking for existence
"unicorn/prefer-includes": "error",
// String methods startsWith/endsWith instead of more complicated stuff
"unicorn/prefer-starts-ends-with": "error",
// textContent instead of innerText
"unicorn/prefer-text-content": "error",
// Enforce throwing type error when throwing error while checking typeof
"unicorn/prefer-type-error": "error",
// Use new when throwing error
"unicorn/throw-new-error": "error",
"sort-imports": [
"error",
{
"ignoreDeclarationSort": true
}
],
"no-only-tests/no-only-tests": "error",
"unicorn/prefer-node-protocol": "error",
"no-console": "warn",
"vue/one-component-per-file": "off",
"vue/require-default-prop": "off",
// Vue stylistic rules from `@antfu/eslint-config`
"vue/array-bracket-spacing": ["error", "never"],
"vue/arrow-spacing": ["error", { "after": true, "before": true }],
"vue/block-spacing": ["error", "always"],
"vue/block-tag-newline": [
"error",
{
"multiline": "always",
"singleline": "always"
}
],
"vue/brace-style": ["error", "stroustrup", { "allowSingleLine": true }],
"vue/comma-dangle": ["error", "always-multiline"],
"vue/comma-spacing": ["error", { "after": true, "before": false }],
"vue/comma-style": ["error", "last"],
"vue/html-comment-content-spacing": [
"error",
"always",
{
"exceptions": ["-"]
}
],
"vue/key-spacing": ["error", { "afterColon": true, "beforeColon": false }],
"vue/keyword-spacing": ["error", { "after": true, "before": true }],
"vue/object-curly-newline": "off",
"vue/object-curly-spacing": ["error", "always"],
"vue/object-property-newline": [
"error",
{ "allowMultiplePropertiesPerLine": true }
],
"vue/operator-linebreak": ["error", "before"],
"vue/padding-line-between-blocks": ["error", "always"],
"vue/quote-props": ["error", "consistent-as-needed"],
"vue/space-in-parens": ["error", "never"],
"vue/template-curly-spacing": "error",
"jsdoc/require-jsdoc": "off",
"jsdoc/require-param": "off",
"jsdoc/require-returns": "off",
"jsdoc/require-param-type": "off",
"import/order": [
"error",
{
"pathGroups": [
{
"pattern": "#vue-router",
"group": "external"
}
]
}
],
"import/no-restricted-paths": [
"error",
{
"zones": [
{
"from": "packages/nuxt/src/!(core)/**/*",
"target": "packages/nuxt/src/core",
"message": "core should not directly import from modules."
},
{
"from": "packages/nuxt/src/!(app)/**/*",
"target": "packages/nuxt/src/app",
"message": "app should not directly import from modules."
},
{
"from": "packages/nuxt/src/app/**/index.ts",
"target": "packages/nuxt/src",
"message": "should not import from barrel/index files"
},
{
"from": "packages/nitro",
"target": "packages/!(nitro)/**/*",
"message": "nitro should not directly import other packages."
}
]
}
],
"@typescript-eslint/consistent-type-imports": [
"error",
{
"disallowTypeAnnotations": false
}
],
"@typescript-eslint/ban-ts-comment": [
"error",
{
"ts-expect-error": "allow-with-description",
"ts-ignore": true
}
],
"@typescript-eslint/prefer-ts-expect-error": "error",
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"ignoreRestSiblings": true
}
],
"jsdoc/check-tag-names": [
"error",
{
"definedTags": ["__NO_SIDE_EFFECTS__"]
}
]
},
"overrides": [
{
"files": ["packages/schema/**"],
"rules": {
"jsdoc/valid-types": "off",
"jsdoc/check-tag-names": [
"error",
{
"definedTags": ["experimental"]
}
]
}
},
{
"files": ["packages/nuxt/src/app/**", "test/**", "**/runtime/**"],
"rules": {
"no-console": "off"
}
}
],
"settings": {
"jsdoc": {
"ignoreInternal": true,
"tagNamePreference": {
"warning": "warning",
"note": "note"
}
}
}
}

View File

@ -236,7 +236,7 @@ jobs:
TEST_CONTEXT: ${{ matrix.context }}
SKIP_BUNDLE_SIZE: ${{ github.event_name != 'push' || matrix.env == 'dev' || matrix.builder == 'webpack' || matrix.context == 'default' || runner.os == 'Windows' }}
- uses: codecov/codecov-action@c16abc29c95fcf9174b58eb7e1abf4c866893bc8 # v4.1.1
- uses: codecov/codecov-action@7afa10ed9b269c561c2336fd862446844e0cbf71 # v4.2.0
if: github.event_name != 'push' && matrix.env == 'built' && matrix.builder == 'vite' && matrix.context == 'default' && matrix.os == 'ubuntu-latest' && matrix.manifest == 'manifest-on'
with:
token: ${{ secrets.CODECOV_TOKEN }}

View File

@ -130,12 +130,12 @@ export const useLocale = () => {
export const useDefaultLocale = (fallback = 'en-US') => {
const locale = ref(fallback)
if (process.server) {
if (import.meta.server) {
const reqLocale = useRequestHeaders()['accept-language']?.split(',')[0]
if (reqLocale) {
locale.value = reqLocale
}
} else if (process.client) {
} else if (import.meta.client) {
const navLang = navigator.language
if (navLang) {
locale.value = navLang

View File

@ -125,12 +125,12 @@ However, if you want to avoid this behaviour you can do so:
```ts twoslash [middleware/example.ts]
export default defineNuxtRouteMiddleware(to => {
// skip middleware on server
if (process.server) return
if (import.meta.server) return
// skip middleware on client side entirely
if (process.client) return
if (import.meta.client) return
// or only skip middleware on initial client load
const nuxtApp = useNuxtApp()
if (process.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) return
if (import.meta.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) return
})
```

View File

@ -78,7 +78,7 @@ export default defineNuxtPlugin({
::note
If you are using the object-syntax, the properties may be statically analyzed in future to produce a more optimized build. So you should not define them at runtime. :br
For example, setting `enforce: process.server ? 'pre' : 'post'` would defeat any future optimization Nuxt is able to do for your plugins.
For example, setting `enforce: import.meta.server ? 'pre' : 'post'` would defeat any future optimization Nuxt is able to do for your plugins.
::
## Registration Order

View File

@ -98,7 +98,7 @@ The behavior is different between the client-side and server-side:
const config = useRuntimeConfig()
console.log('Runtime config:', config)
if (process.server) {
if (import.meta.server) {
console.log('API secret:', config.apiSecret)
}
</script>

View File

@ -105,7 +105,7 @@ export default defineNuxtConfig({
const resolver = createResolver(import.meta.url)
// add a route
files.push({
path: resolver.resolve('./runtime/app/router-options')
path: resolver.resolve('./runtime/app/router-options'),
optional: true
})
}
@ -172,6 +172,6 @@ import { createMemoryHistory } from 'vue-router'
export default <RouterConfig> {
// https://router.vuejs.org/api/interfaces/routeroptions.html
history: base => process.client ? createMemoryHistory(base) : null /* default */
history: base => import.meta.client ? createMemoryHistory(base) : null /* default */
}
```

View File

@ -51,7 +51,7 @@ export default defineNuxtPlugin((nuxtApp) => {
})
nuxtApp.hook('vue:error', (..._args) => {
console.log('vue:error')
// if (process.client) {
// if (import.meta.client) {
// console.log(..._args)
// }
})
@ -120,7 +120,7 @@ Nuxt exposes the following properties through `ssrContext`:
export const useColor = () => useState<string>('color', () => 'pink')
export default defineNuxtPlugin((nuxtApp) => {
if (process.server) {
if (import.meta.server) {
const color = useColor()
}
})
@ -159,7 +159,7 @@ export default defineComponent({
setup (_props, { slots, emit }) {
const nuxtApp = useNuxtApp()
onErrorCaptured((err) => {
if (process.client && !nuxtApp.isHydrating) {
if (import.meta.client && !nuxtApp.isHydrating) {
// ...
}
})

View File

@ -117,7 +117,7 @@ import { defineNuxtPlugin } from '#imports'
import metaConfig from '#build/meta.config.mjs'
export default defineNuxtPlugin((nuxtApp) => {
const createHead = process.server ? createServerHead : createClientHead
const createHead = import.meta.server ? createServerHead : createClientHead
const head = createHead()
head.push(metaConfig.globalMeta)

View File

@ -251,7 +251,7 @@ export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(VueFire, { firebaseApp })
<% if(options.ssr) { %>
if (process.server) {
if (import.meta.server) {
nuxtApp.payload.vuefire = useSSRInitialState(undefined, firebaseApp)
} else if (nuxtApp.payload?.vuefire) {
useSSRInitialState(nuxtApp.payload.vuefire, firebaseApp)

280
eslint.config.mjs Normal file
View File

@ -0,0 +1,280 @@
// Configs
// import standard from "eslint-config-standard"
// import nuxt from '@nuxt/eslint-config'
// Plugins
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import jsdoc from 'eslint-plugin-jsdoc'
import esImport from 'eslint-plugin-import'
import unicorn from 'eslint-plugin-unicorn'
import noOnlyTests from 'eslint-plugin-no-only-tests'
/**
* eslintrc compatibility
* @see https://eslint.org/docs/latest/use/configure/migration-guide#using-eslintrc-configs-in-flat-config
* @see https://github.com/eslint/eslintrc#usage-esm
*/
import { FlatCompat } from '@eslint/eslintrc'
import js from '@eslint/js'
const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended
})
// TODO: Type definition?
export default [
{
ignores: [
'**/dist/**',
'**/.nuxt/**',
'**/.nuxt-*/**',
'**/.output/**',
'**/.output-*/**',
'**/public/**',
'**/node_modules/**',
'packages/schema/schema',
// TODO: remove when fully migrated to flat config
'**/*.d.mts'
]
},
// standard,
...compat.extends('eslint-config-standard'),
jsdoc.configs['flat/recommended'],
// nuxt,
...compat.extends('@nuxt/eslint-config'),
esImport.configs.typescript,
{
rules: {
'import/export': 'off'
}
},
{
files: ['**/*.vue', '**/*.ts', '**/*.mts', '**/*.js', '**/*.cjs', '**/*.mjs'],
languageOptions: {
globals: {
NodeJS: 'readonly',
$fetch: 'readonly'
}
},
plugins: {
jsdoc,
import: esImport,
unicorn,
'no-only-tests': noOnlyTests
},
rules: {
// Imports should come first
'import/first': 'error',
// Other import rules
'import/no-mutable-exports': 'error',
// Allow unresolved imports
'import/no-unresolved': 'off',
// Allow paren-less arrow functions only when there's no braces
'arrow-parens': ['error', 'as-needed', { requireForBlockBody: true }],
// Allow async-await
'generator-star-spacing': 'off',
// Prefer const over let
'prefer-const': ['error', { destructuring: 'any', ignoreReadBeforeAssign: false }],
// No single if in an "else" block
'no-lonely-if': 'error',
// Force curly braces for control flow,
// including if blocks with a single statement
curly: ['error', 'all'],
// No async function without await
'require-await': 'error',
// Force dot notation when possible
'dot-notation': 'error',
'no-var': 'error',
// Force object shorthand where possible
'object-shorthand': 'error',
// No useless destructuring/importing/exporting renames
'no-useless-rename': 'error',
/**********************/
/* Unicorn Rules */
/**********************/
// Pass error message when throwing errors
'unicorn/error-message': 'error',
// Uppercase regex escapes
'unicorn/escape-case': 'error',
// Array.isArray instead of instanceof
'unicorn/no-array-instanceof': 'error',
// Prevent deprecated `new Buffer()`
'unicorn/no-new-buffer': 'error',
// Keep regex literals safe!
'unicorn/no-unsafe-regex': 'off',
// Lowercase number formatting for octal, hex, binary (0x12 instead of 0X12)
'unicorn/number-literal-case': 'error',
// ** instead of Math.pow()
'unicorn/prefer-exponentiation-operator': 'error',
// includes over indexOf when checking for existence
'unicorn/prefer-includes': 'error',
// String methods startsWith/endsWith instead of more complicated stuff
'unicorn/prefer-starts-ends-with': 'error',
// textContent instead of innerText
'unicorn/prefer-text-content': 'error',
// Enforce throwing type error when throwing error while checking typeof
'unicorn/prefer-type-error': 'error',
// Use new when throwing error
'unicorn/throw-new-error': 'error',
'sort-imports': [
'error',
{
ignoreDeclarationSort: true
}
],
'no-only-tests/no-only-tests': 'error',
'unicorn/prefer-node-protocol': 'error',
'no-console': ['warn', { allow: ['warn', 'error', 'debug'] }],
'vue/one-component-per-file': 'off',
'vue/require-default-prop': 'off',
// Vue stylistic rules from `@antfu/eslint-config`
'vue/array-bracket-spacing': ['error', 'never'],
'vue/arrow-spacing': ['error', { after: true, before: true }],
'vue/block-spacing': ['error', 'always'],
'vue/block-tag-newline': [
'error',
{
multiline: 'always',
singleline: 'always'
}
],
'vue/brace-style': ['error', 'stroustrup', { allowSingleLine: true }],
'vue/comma-dangle': ['error', 'always-multiline'],
'vue/comma-spacing': ['error', { after: true, before: false }],
'vue/comma-style': ['error', 'last'],
'vue/html-comment-content-spacing': [
'error',
'always',
{
exceptions: ['-']
}
],
'vue/key-spacing': ['error', { afterColon: true, beforeColon: false }],
'vue/keyword-spacing': ['error', { after: true, before: true }],
'vue/object-curly-newline': 'off',
'vue/object-curly-spacing': ['error', 'always'],
'vue/object-property-newline': [
'error',
{ allowMultiplePropertiesPerLine: true }
],
'vue/operator-linebreak': ['error', 'before'],
'vue/padding-line-between-blocks': ['error', 'always'],
'vue/quote-props': ['error', 'consistent-as-needed'],
'vue/space-in-parens': ['error', 'never'],
'vue/template-curly-spacing': 'error',
'jsdoc/require-jsdoc': 'off',
'jsdoc/require-param': 'off',
'jsdoc/require-returns': 'off',
'jsdoc/require-param-type': 'off',
'import/order': [
'error',
{
pathGroups: [
{
pattern: '#vue-router',
group: 'external'
}
]
}
],
'import/no-restricted-paths': [
'error',
{
zones: [
{
from: 'packages/nuxt/src/!(core)/**/*',
target: 'packages/nuxt/src/core',
message: 'core should not directly import from modules.'
},
{
from: 'packages/nuxt/src/!(app)/**/*',
target: 'packages/nuxt/src/app',
message: 'app should not directly import from modules.'
},
{
from: 'packages/nuxt/src/app/**/index.ts',
target: 'packages/nuxt/src',
message: 'should not import from barrel/index files'
},
{
from: 'packages/nitro',
target: 'packages/!(nitro)/**/*',
message: 'nitro should not directly import other packages.'
}
]
}
],
'@typescript-eslint/consistent-type-imports': [
'error',
{
disallowTypeAnnotations: false
}
],
'@typescript-eslint/ban-ts-comment': [
'error',
{
'ts-expect-error': 'allow-with-description',
'ts-ignore': true
}
],
'@typescript-eslint/prefer-ts-expect-error': 'error',
'@typescript-eslint/no-unused-vars': [
'error',
{
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
ignoreRestSiblings: true
}
],
'jsdoc/check-tag-names': [
'error',
{
definedTags: ['__NO_SIDE_EFFECTS__']
}
]
},
settings: {
jsdoc: {
ignoreInternal: true,
tagNamePreference: {
warning: 'warning',
note: 'note'
}
}
}
},
{
files: ['packages/schema/**'],
rules: {
'jsdoc/valid-types': 'off',
'jsdoc/check-tag-names': [
'error',
{
definedTags: ['experimental']
}
]
}
},
{
files: ['packages/nuxt/src/app/**', 'test/**', '**/runtime/**', '**/*.test.ts'],
rules: {
'no-console': 'off'
}
},
{
files: ['test/fixtures/**'],
rules: {
'@typescript-eslint/no-unused-vars': 'off',
'vue/valid-v-for': 'off'
}
}
]

View File

@ -13,8 +13,8 @@
"cleanup": "rimraf 'packages/**/node_modules' 'playground/node_modules' 'node_modules'",
"dev": "pnpm play",
"dev:prepare": "pnpm --filter './packages/**' prepack --stub",
"lint": "eslint --ext .vue,.ts,.js,.mjs .",
"lint:fix": "eslint --ext .vue,.ts,.js,.mjs . --fix",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"lint:docs": "markdownlint ./docs && case-police 'docs/**/*.md' *.md",
"lint:docs:fix": "markdownlint ./docs --fix && case-police 'docs/**/*.md' *.md --fix",
"lint:knip": "pnpx knip",
@ -37,20 +37,22 @@
"@nuxt/schema": "workspace:*",
"@nuxt/vite-builder": "workspace:*",
"@nuxt/webpack-builder": "workspace:*",
"rollup": "^4.13.2",
"rollup": "^4.14.0",
"nuxt": "workspace:*",
"vite": "5.2.7",
"vite": "5.2.8",
"vue": "3.4.21",
"magic-string": "^0.30.8"
"magic-string": "^0.30.9"
},
"devDependencies": {
"@eslint/eslintrc": "3.0.2",
"@eslint/js": "8.57.0",
"@nuxt/eslint-config": "0.2.0",
"@nuxt/kit": "workspace:*",
"@nuxt/test-utils": "3.12.0",
"@nuxt/webpack-builder": "workspace:*",
"@testing-library/vue": "8.0.3",
"@types/fs-extra": "11.0.4",
"@types/node": "20.12.2",
"@types/node": "20.12.4",
"@types/semver": "7.5.8",
"@vitest/coverage-v8": "1.4.0",
"@vue/test-utils": "2.4.5",
@ -63,15 +65,15 @@
"eslint-plugin-import": "2.29.1",
"eslint-plugin-jsdoc": "48.2.2",
"eslint-plugin-no-only-tests": "3.1.0",
"eslint-plugin-unicorn": "51.0.1",
"eslint-plugin-unicorn": "52.0.0",
"execa": "8.0.1",
"fs-extra": "11.2.0",
"globby": "14.0.1",
"h3": "1.11.1",
"happy-dom": "14.3.9",
"happy-dom": "14.4.0",
"jiti": "1.21.0",
"markdownlint-cli": "0.39.0",
"nitropack": "2.9.5",
"nitropack": "2.9.6",
"nuxi": "3.11.1",
"nuxt": "workspace:*",
"nuxt-content-twoslash": "0.0.10",
@ -89,7 +91,7 @@
"vue-router": "4.3.0",
"vue-tsc": "2.0.7"
},
"packageManager": "pnpm@8.15.5",
"packageManager": "pnpm@8.15.6",
"engines": {
"node": "^14.18.0 || >=16.10.0"
},

View File

@ -1,6 +1,6 @@
{
"name": "@nuxt/kit",
"version": "3.11.1",
"version": "3.11.2",
"repository": {
"type": "git",
"url": "git+https://github.com/nuxt/nuxt.git",
@ -50,9 +50,9 @@
"@types/lodash-es": "4.17.12",
"@types/semver": "7.5.8",
"lodash-es": "4.17.21",
"nitropack": "2.9.5",
"nitropack": "2.9.6",
"unbuild": "latest",
"vite": "5.2.7",
"vite": "5.2.8",
"vitest": "1.4.0",
"webpack": "5.91.0"
},

View File

@ -1,3 +1,4 @@
/* eslint-disable jsdoc/require-jsdoc */
function defineNuxtConfig (config) {
return config
}

View File

@ -1,6 +1,6 @@
{
"name": "nuxt",
"version": "3.11.1",
"version": "3.11.2",
"repository": {
"type": "git",
"url": "git+https://github.com/nuxt/nuxt.git",
@ -64,11 +64,11 @@
"@nuxt/kit": "workspace:*",
"@nuxt/schema": "workspace:*",
"@nuxt/telemetry": "^2.5.3",
"@nuxt/ui-templates": "^1.3.1",
"@nuxt/ui-templates": "^1.3.2",
"@nuxt/vite-builder": "workspace:*",
"@unhead/dom": "^1.9.3",
"@unhead/ssr": "^1.9.3",
"@unhead/vue": "^1.9.3",
"@unhead/dom": "^1.9.4",
"@unhead/ssr": "^1.9.4",
"@unhead/vue": "^1.9.4",
"@vue/shared": "^3.4.21",
"acorn": "8.11.3",
"c12": "^1.10.0",
@ -87,9 +87,9 @@
"jiti": "^1.21.0",
"klona": "^2.0.6",
"knitwork": "^1.1.0",
"magic-string": "^0.30.8",
"magic-string": "^0.30.9",
"mlly": "^1.6.1",
"nitropack": "^2.9.5",
"nitropack": "^2.9.6",
"nuxi": "^3.11.1",
"nypm": "^0.3.8",
"ofetch": "^1.3.4",
@ -122,7 +122,7 @@
"@types/fs-extra": "11.0.4",
"@vitejs/plugin-vue": "5.0.4",
"unbuild": "latest",
"vite": "5.2.7",
"vite": "5.2.8",
"vitest": "1.4.0"
},
"peerDependencies": {

View File

@ -1,3 +1,4 @@
// eslint-disable-next-line import/export
export * from 'vue'
export const install = () => {}

View File

@ -1,4 +1,4 @@
import { computed, getCurrentInstance, onBeforeMount, onServerPrefetch, onUnmounted, ref, shallowRef, toRef, unref, watch } from 'vue'
import { computed, getCurrentInstance, getCurrentScope, onBeforeMount, onScopeDispose, onServerPrefetch, onUnmounted, ref, shallowRef, toRef, unref, watch } from 'vue'
import type { Ref, WatchSource } from 'vue'
import type { NuxtApp } from '../nuxt'
import { useNuxtApp } from '../nuxt'
@ -370,16 +370,20 @@ export function useAsyncData<
// 4. Navigation (lazy: false) - or plugin usage: await fetch
initialFetch()
}
const hasScope = getCurrentScope()
if (options.watch) {
watch(options.watch, () => asyncData.refresh())
const unsub = watch(options.watch, () => asyncData.refresh())
if (hasScope) {
onScopeDispose(unsub)
}
}
const off = nuxtApp.hook('app:data:refresh', async (keys) => {
if (!keys || keys.includes(key)) {
await asyncData.refresh()
}
})
if (instance) {
onUnmounted(off)
if (hasScope) {
onScopeDispose(off)
}
}

View File

@ -85,8 +85,7 @@ const isProcessingMiddleware = () => {
return true
}
} catch {
// Within an async middleware
return true
return false
}
return false
}

View File

@ -8,6 +8,7 @@ import MagicString from 'magic-string'
import { ELEMENT_NODE, parse, walk } from 'ultrahtml'
import { hash } from 'ohash'
import { resolvePath } from '@nuxt/kit'
import defu from 'defu'
import { isVue } from '../core/utils'
interface ServerOnlyComponentTransformPluginOptions {
@ -182,15 +183,27 @@ export const componentsChunkPlugin = createUnplugin((options: ComponentChunkOpti
vite: {
async config (config) {
const components = options.getComponents()
config.build = config.build || {}
config.build.rollupOptions = config.build.rollupOptions || {}
config.build.rollupOptions.output = config.build.rollupOptions.output || {}
config.build.rollupOptions.input = config.build.rollupOptions.input || {}
config.build = defu(config.build, {
rollupOptions: {
input: {},
output: {}
}
})
const rollupOptions = config.build.rollupOptions!
if (typeof rollupOptions.input === 'string') {
rollupOptions.input = { entry: rollupOptions.input }
} else if (typeof rollupOptions.input === 'object' && Array.isArray(rollupOptions.input)) {
rollupOptions.input = rollupOptions.input.reduce<{ [key: string]: string }>((acc, input) => { acc[input] = input; return acc }, {})
}
// don't use 'strict', this would create another "facade" chunk for the entry file, causing the ssr styles to not detect everything
config.build.rollupOptions.preserveEntrySignatures = 'allow-extension'
rollupOptions.preserveEntrySignatures = 'allow-extension'
for (const component of components) {
if (component.mode === 'client' || component.mode === 'all') {
(config.build.rollupOptions.input as Record<string, string>)[component.pascalName] = await resolvePath(component.filePath)
rollupOptions.input![component.pascalName] = await resolvePath(component.filePath)
}
}
},

View File

@ -6,6 +6,7 @@ import { createUnplugin } from 'unplugin'
import { parseURL } from 'ufo'
import { parseQuery } from 'vue-router'
import { normalize, resolve } from 'pathe'
import { genImport } from 'knitwork'
import { distDir } from '../dirs'
import type { getComponentsT } from './module'
@ -27,7 +28,7 @@ export function createTransformPlugin (nuxt: Nuxt, getComponents: getComponentsT
const components = getComponents(mode)
return components.flatMap((c): Import[] => {
const withMode = (mode: string | undefined) => mode
? `${c.filePath}${c.filePath.includes('?') ? '&' : '?'}nuxt_component=${mode}&nuxt_component_name=${c.pascalName}`
? `${c.filePath}${c.filePath.includes('?') ? '&' : '?'}nuxt_component=${mode}&nuxt_component_name=${c.pascalName}&nuxt_component_export=${c.export || 'default'}`
: c.filePath
const mode = !c._raw && c.mode && ['client', 'server'].includes(c.mode) ? c.mode : undefined
@ -60,20 +61,22 @@ export function createTransformPlugin (nuxt: Nuxt, getComponents: getComponentsT
const query = parseQuery(search)
const mode = query.nuxt_component
const bare = id.replace(/\?.*/, '')
const componentExport = query.nuxt_component_export as string || 'default'
const exportWording = componentExport === 'default' ? 'export default' : `export const ${componentExport} =`
if (mode === 'async') {
return {
code: [
'import { defineAsyncComponent } from "vue"',
`export default defineAsyncComponent(() => import(${JSON.stringify(bare)}).then(r => r.default))`
`${exportWording} defineAsyncComponent(() => import(${JSON.stringify(bare)}).then(r => r[${JSON.stringify(componentExport)}] || r.default || r))`
].join('\n'),
map: null
}
} else if (mode === 'client') {
return {
code: [
`import __component from ${JSON.stringify(bare)}`,
genImport(bare, [{ name: componentExport, as: '__component' }]),
'import { createClientOnly } from "#app/components/client-only"',
'export default createClientOnly(__component)'
`${exportWording} createClientOnly(__component)`
].join('\n'),
map: null
}
@ -82,7 +85,7 @@ export function createTransformPlugin (nuxt: Nuxt, getComponents: getComponentsT
code: [
'import { defineAsyncComponent } from "vue"',
'import { createClientOnly } from "#app/components/client-only"',
`export default defineAsyncComponent(() => import(${JSON.stringify(bare)}).then(r => createClientOnly(r.default)))`
`${exportWording} defineAsyncComponent(() => import(${JSON.stringify(bare)}).then(r => createClientOnly(r[${JSON.stringify(componentExport)}] || r.default || r)))`
].join('\n'),
map: null
}
@ -91,7 +94,7 @@ export function createTransformPlugin (nuxt: Nuxt, getComponents: getComponentsT
return {
code: [
`import { createServerComponent } from ${JSON.stringify(serverComponentRuntime)}`,
`export default createServerComponent(${JSON.stringify(name)})`
`${exportWording} createServerComponent(${JSON.stringify(name)})`
].join('\n'),
map: null
}

View File

@ -220,7 +220,7 @@ function isComponentNotCalledInSetup (codeAst: Node, name: string): string | voi
/**
* retrieve the component identifier being used on ssrRender callExpression
* @param {CallExpression} ssrRenderNode - ssrRender callExpression
* @param ssrRenderNode - ssrRender callExpression
*/
function getComponentName (ssrRenderNode: AcornNode<CallExpression>): string {
const componentCall = ssrRenderNode.arguments[0] as Identifier | MemberExpression | CallExpression

View File

@ -12,6 +12,7 @@ import escapeRE from 'escape-string-regexp'
import { defu } from 'defu'
import fsExtra from 'fs-extra'
import { dynamicEventHandler } from 'h3'
import { isWindows } from 'std-env'
import type { Nuxt, NuxtOptions, RuntimeConfig } from 'nuxt/schema'
// @ts-expect-error TODO: add legacy type support for subpath imports
import { template as defaultSpaLoadingTemplate } from '@nuxt/ui-templates/templates/spa-loading-icon.mjs'
@ -416,10 +417,12 @@ export async function initNitro (nuxt: Nuxt & { _nitro?: Nitro }) {
})
const cacheDir = resolve(nuxt.options.buildDir, 'cache/nitro/prerender')
const cacheDriverPath = await resolvePath(join(distDir, 'core/runtime/nitro/cache-driver'))
await fsp.rm(cacheDir, { recursive: true, force: true }).catch(() => {})
nitro.options._config.storage = defu(nitro.options._config.storage, {
'internal:nuxt:prerender': {
driver: pathToFileURL(await resolvePath(join(distDir, 'core/runtime/nitro/cache-driver'))).href,
// TODO: resolve upstream where file URLs are not being resolved/inlined correctly
driver: isWindows ? pathToFileURL(cacheDriverPath).href : cacheDriverPath,
base: cacheDir
}
})

View File

@ -9,7 +9,7 @@ import {
createWebHistory
} from '#vue-router'
import { createError } from 'h3'
import { isEqual, withoutBase } from 'ufo'
import { isEqual, isSamePath, withoutBase } from 'ufo'
import type { PageMeta } from '../composables'
@ -70,9 +70,6 @@ const plugin: Plugin<{ router: Router }> = defineNuxtPlugin({
const routes = routerOptions.routes?.(_routes) ?? _routes
let startPosition: Parameters<RouterScrollBehavior>[2] | null
const initialURL = import.meta.server
? nuxtApp.ssrContext!.url
: createCurrentLocation(routerBase, window.location, nuxtApp.payload.path)
const router = createRouter({
...routerOptions,
@ -111,8 +108,12 @@ const plugin: Plugin<{ router: Router }> = defineNuxtPlugin({
get: () => previousRoute.value
})
const initialURL = import.meta.server
? nuxtApp.ssrContext!.url
: createCurrentLocation(routerBase, window.location, nuxtApp.payload.path)
// Allows suspending the route object until page navigation completes
const _route = shallowRef(router.resolve(initialURL) as RouteLocation)
const _route = shallowRef(router.currentRoute.value)
const syncCurrentRoute = () => { _route.value = router.currentRoute.value }
nuxtApp.hook('page:finish', syncCurrentRoute)
router.afterEach((to, from) => {
@ -138,19 +139,21 @@ const plugin: Plugin<{ router: Router }> = defineNuxtPlugin({
named: {}
}
const error = useError()
try {
if (import.meta.server) {
await router.push(initialURL)
}
await router.isReady()
} catch (error: any) {
// We'll catch 404s here
await nuxtApp.runWithContext(() => showError(error))
}
const resolvedInitialRoute = import.meta.client && initialURL !== router.currentRoute.value.fullPath
? router.resolve(initialURL)
: router.currentRoute.value
syncCurrentRoute()
if (import.meta.server && nuxtApp.ssrContext?.islandContext) {
// We're in an island context, and don't need to handle middleware or redirections
return { provide: { router } }
@ -225,6 +228,7 @@ const plugin: Plugin<{ router: Router }> = defineNuxtPlugin({
await nuxtApp.callHook('page:loading:end')
})
const error = useError()
router.afterEach(async (to, _from, failure) => {
delete nuxtApp._processingMiddleware
@ -247,18 +251,19 @@ const plugin: Plugin<{ router: Router }> = defineNuxtPlugin({
path: to.fullPath
}
})))
} else if (import.meta.server && to.redirectedFrom && to.fullPath !== initialURL) {
} else if (import.meta.server && to.fullPath !== initialURL && (to.redirectedFrom || !isSamePath(to.fullPath, initialURL))) {
await nuxtApp.runWithContext(() => navigateTo(to.fullPath || '/'))
}
})
nuxtApp.hooks.hookOnce('app:created', async () => {
try {
const to = router.resolve(initialURL)
// #4920, #4982
if ('name' in to) { to.name = undefined }
if ('name' in resolvedInitialRoute) {
resolvedInitialRoute.name = undefined
}
await router.replace({
...to,
...resolvedInitialRoute,
force: true
})
// reset scroll behavior to initial value

View File

@ -0,0 +1,112 @@
import { fileURLToPath } from 'node:url'
import { describe, expect, it } from 'vitest'
import type { Component, Nuxt } from '@nuxt/schema'
import { kebabCase } from 'scule'
import { createTransformPlugin } from '../src/components/transform'
describe('components:transform', () => {
it('should transform #components imports', async () => {
const transform = createTransformer([
createComponent('Foo'),
createComponent('Bar', { export: 'Bar' })
])
const code = await transform('import { Foo, Bar } from \'#components\'', '/app.vue')
expect(code).toMatchInlineSnapshot(`
"import Foo from '/Foo.vue';
import { Bar } from '/Bar.vue';
"
`)
})
it('should correctly resolve server-only components', async () => {
const transform = createTransformer([
createComponent('Foo', { mode: 'server' })
])
const code = await transform('import { Foo, LazyFoo } from \'#components\'', '/app.vue')
expect(code).toMatchInlineSnapshot(`
"import Foo from '/Foo.vue?nuxt_component=server&nuxt_component_name=Foo&nuxt_component_export=default';
import LazyFoo from '/Foo.vue?nuxt_component=server,async&nuxt_component_name=Foo&nuxt_component_export=default';
"
`)
expect(await transform('', '/Foo.vue?nuxt_component=server&nuxt_component_name=Foo&nuxt_component_export=default')).toMatchInlineSnapshot(`
"import { createServerComponent } from "<repo>/nuxt/src/components/runtime/server-component"
export default createServerComponent("Foo")"
`)
expect(await transform('', '/Foo.vue?nuxt_component=server,async&nuxt_component_name=Foo&nuxt_component_export=default')).toMatchInlineSnapshot(`
"import { createServerComponent } from "<repo>/nuxt/src/components/runtime/server-component"
export default createServerComponent("Foo")"
`)
expect(await transform('', '/Foo.vue?nuxt_component=server&nuxt_component_name=Foo&nuxt_component_export=Foo')).toMatchInlineSnapshot(`
"import { createServerComponent } from "<repo>/nuxt/src/components/runtime/server-component"
export const Foo = createServerComponent("Foo")"
`)
})
it('should correctly resolve client-only components', async () => {
const transform = createTransformer([
createComponent('Foo', { mode: 'client' })
])
const code = await transform('import { Foo, LazyFoo } from \'#components\'', '/app.vue')
expect(code).toMatchInlineSnapshot(`
"import Foo from '/Foo.vue?nuxt_component=client&nuxt_component_name=Foo&nuxt_component_export=default';
import LazyFoo from '/Foo.vue?nuxt_component=client,async&nuxt_component_name=Foo&nuxt_component_export=default';
"
`)
expect(await transform('', '/Foo.vue?nuxt_component=client&nuxt_component_name=Foo&nuxt_component_export=default')).toMatchInlineSnapshot(`
"import { default as __component } from "/Foo.vue";
import { createClientOnly } from "#app/components/client-only"
export default createClientOnly(__component)"
`)
expect(await transform('', '/Foo.vue?nuxt_component=client,async&nuxt_component_name=Foo&nuxt_component_export=default')).toMatchInlineSnapshot(`
"import { defineAsyncComponent } from "vue"
import { createClientOnly } from "#app/components/client-only"
export default defineAsyncComponent(() => import("/Foo.vue").then(r => createClientOnly(r["default"] || r.default || r)))"
`)
expect(await transform('', '/Foo.vue?nuxt_component=client,async&nuxt_component_name=Foo&nuxt_component_export=Foo')).toMatchInlineSnapshot(`
"import { defineAsyncComponent } from "vue"
import { createClientOnly } from "#app/components/client-only"
export const Foo = defineAsyncComponent(() => import("/Foo.vue").then(r => createClientOnly(r["Foo"] || r.default || r)))"
`)
})
})
const rootDir = fileURLToPath(new URL('../..', import.meta.url))
function createTransformer (components: Component[], mode: 'client' | 'server' | 'all' = 'all') {
const stubNuxt = {
options: {
buildDir: '/',
sourcemap: {
server: false,
client: false
}
}
} as Nuxt
const plugin = createTransformPlugin(stubNuxt, () => components, mode).vite()
return async (code: string, id: string) => {
const result = await (plugin as any).transform!(code, id)
return (typeof result === 'string' ? result : result?.code)?.replaceAll(rootDir, '<repo>/')
}
}
function createComponent (pascalName: string, options: Partial<Component> = {}) {
return {
filePath: `/${pascalName}.vue`,
pascalName,
export: 'default',
chunkName: `components/${pascalName.toLowerCase()}`,
kebabName: kebabCase(pascalName),
mode: 'all',
prefetch: false,
preload: false,
shortPath: `components/${pascalName}.vue`,
...options
} satisfies Component
}

View File

@ -1,5 +1,4 @@
/// <reference types="nitropack" />
export * from './dist/index.js'
import type { DefineNuxtConfig } from 'nuxt/config'
import type { RuntimeConfig, SchemaDefinition } from 'nuxt/schema'
@ -7,6 +6,8 @@ import type { H3Event } from 'h3'
import type { NuxtIslandContext, NuxtIslandResponse, NuxtRenderHTMLContext } from './dist/app/types.js'
import type { LogObject } from 'consola'
export * from './dist/index.js'
declare global {
const defineNuxtConfig: DefineNuxtConfig
const defineNuxtSchema: (schema: SchemaDefinition) => SchemaDefinition

View File

@ -56,6 +56,7 @@ export default defineBuildConfig({
'@vue/language-core',
// Implicit
'@vue/compiler-core',
'@vue/compiler-sfc',
'@vue/shared',
'untyped'
]

View File

@ -1,6 +1,6 @@
{
"name": "@nuxt/schema",
"version": "3.11.1",
"version": "3.11.2",
"repository": {
"type": "git",
"url": "git+https://github.com/nuxt/nuxt.git",
@ -38,30 +38,31 @@
"@types/file-loader": "5.0.4",
"@types/pug": "2.0.10",
"@types/sass-loader": "8.0.8",
"@unhead/schema": "1.9.3",
"@unhead/schema": "1.9.4",
"@vitejs/plugin-vue": "5.0.4",
"@vitejs/plugin-vue-jsx": "3.1.0",
"@vue/compiler-core": "3.4.21",
"@vue/compiler-sfc": "3.4.21",
"@vue/language-core": "2.0.7",
"c12": "1.10.0",
"esbuild-loader": "4.1.0",
"h3": "1.11.1",
"ignore": "5.3.1",
"nitropack": "2.9.5",
"nitropack": "2.9.6",
"ofetch": "1.3.4",
"unbuild": "latest",
"unctx": "2.3.1",
"unenv": "1.9.0",
"vite": "5.2.7",
"vite": "5.2.8",
"vue": "3.4.21",
"vue-bundle-renderer": "2.0.0",
"vue-loader": "17.4.2",
"vue-router": "4.3.0",
"webpack": "5.91.0",
"webpack-dev-middleware": "7.2.0"
"webpack-dev-middleware": "7.2.1"
},
"dependencies": {
"@nuxt/ui-templates": "^1.3.1",
"@nuxt/ui-templates": "^1.3.2",
"consola": "^3.2.3",
"defu": "^6.1.4",
"hookable": "^5.5.3",

View File

@ -8,6 +8,14 @@ export default defineUntypedSchema({
* Vue.js config
*/
vue: {
/** @type {typeof import('@vue/compiler-sfc').AssetURLTagConfig} */
transformAssetUrls: {
video: ['src', 'poster'],
source: ['src'],
img: ['src'],
image: ['xlink:href', 'href'],
use: ['xlink:href', 'href']
},
/**
* Options for the Vue compiler that will be passed at build time.
* @see [documentation](https://vuejs.org/api/application.html#app-config-compileroptions)

View File

@ -43,6 +43,7 @@ export default defineUntypedSchema({
'@unhead/vue',
'vue',
'@vue/runtime-core',
'@vue/compiler-sfc',
'@vue/runtime-dom',
'vue-router',
'@nuxt/schema',
@ -78,6 +79,9 @@ export default defineUntypedSchema({
*
* We recommend instead letting the [official Vue extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
* generate accurate types for your components.
*
* Note that you may wish to set this to `true` if you are using other libraries, such as ESLint,
* that are unable to understand the type of `.vue` files.
*/
shim: false
}

View File

@ -49,11 +49,17 @@ export default defineUntypedSchema({
template: {
compilerOptions: {
$resolve: async (val, get) => val ?? (await get('vue') as Record<string, any>).compilerOptions
},
transformAssetUrls: {
$resolve: async (val, get) => val ?? (await get('vue') as Record<string, any>).transformAssetUrls
}
},
script: {
propsDestructure: {
$resolve: async (val, get) => val ?? Boolean((await get('vue') as Record<string, any>).propsDestructure)
},
hoistStatic: {
$resolve: async (val, get) => val ?? (await get('vue') as Record<string, any>).compilerOptions?.hoistStatic
}
}
},

View File

@ -1,5 +1,6 @@
import { defu } from 'defu'
import { defineUntypedSchema } from 'untyped'
import type { VueLoaderOptions } from 'vue-loader'
export default defineUntypedSchema({
webpack: {
@ -200,14 +201,15 @@ export default defineUntypedSchema({
*/
vue: {
transformAssetUrls: {
video: 'src',
source: 'src',
object: 'src',
embed: 'src'
$resolve: async (val, get) => (val ?? (await get('vue.transformAssetUrls'))) as VueLoaderOptions['transformAssetUrls']
},
compilerOptions: { $resolve: async (val, get) => val ?? (await get('vue.compilerOptions')) },
propsDestructure: { $resolve: async (val, get) => val ?? Boolean(await get('vue.propsDestructure')) }
compilerOptions: {
$resolve: async (val, get) => (val ?? (await get('vue.compilerOptions'))) as VueLoaderOptions['compilerOptions']
},
propsDestructure: {
$resolve: async (val, get) => Boolean(val ?? await get('vue.propsDestructure'))
}
} satisfies { [K in keyof VueLoaderOptions]: { $resolve: (val: unknown, get: (id: string) => Promise<unknown>) => Promise<VueLoaderOptions[K]> } },
css: {
importLoaders: 0,

View File

@ -1,6 +1,6 @@
{
"name": "@nuxt/vite-builder",
"version": "3.11.1",
"version": "3.11.2",
"repository": {
"type": "git",
"url": "git+https://github.com/nuxt/nuxt.git",
@ -49,7 +49,7 @@
"get-port-please": "^3.1.2",
"h3": "^1.11.1",
"knitwork": "^1.1.0",
"magic-string": "^0.30.8",
"magic-string": "^0.30.9",
"mlly": "^1.6.1",
"ohash": "^1.1.3",
"pathe": "^1.1.2",
@ -62,7 +62,7 @@
"ufo": "^1.5.3",
"unenv": "^1.9.0",
"unplugin": "^1.10.1",
"vite": "^5.2.7",
"vite": "^5.2.8",
"vite-node": "^1.4.0",
"vite-plugin-checker": "^0.6.4",
"vue-bundle-renderer": "^2.0.0"

View File

@ -112,17 +112,6 @@ export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
}),
virtual(nuxt.vfs)
],
vue: {
template: {
transformAssetUrls: {
video: ['src', 'poster'],
source: ['src'],
img: ['src'],
image: ['xlink:href', 'href'],
use: ['xlink:href', 'href']
}
}
},
server: {
watch: { ignored: isIgnored },
fs: {

View File

@ -1,6 +1,6 @@
{
"name": "@nuxt/webpack-builder",
"version": "3.11.1",
"version": "3.11.2",
"repository": {
"type": "git",
"url": "git+https://github.com/nuxt/nuxt.git",
@ -28,7 +28,7 @@
"@nuxt/friendly-errors-webpack-plugin": "^2.6.0",
"@nuxt/kit": "workspace:*",
"autoprefixer": "^10.4.19",
"css-loader": "^6.10.0",
"css-loader": "^6.11.0",
"css-minimizer-webpack-plugin": "^6.0.0",
"cssnano": "^6.1.2",
"defu": "^6.1.4",
@ -41,8 +41,8 @@
"h3": "^1.11.1",
"hash-sum": "^2.0.0",
"lodash-es": "4.17.21",
"magic-string": "^0.30.8",
"memfs": "^4.8.0",
"magic-string": "^0.30.9",
"memfs": "^4.8.1",
"mini-css-extract-plugin": "^2.8.1",
"mlly": "^1.6.1",
"ohash": "^1.1.3",
@ -64,7 +64,7 @@
"vue-loader": "^17.4.2",
"webpack": "^5.91.0",
"webpack-bundle-analyzer": "^4.10.1",
"webpack-dev-middleware": "^7.2.0",
"webpack-dev-middleware": "^7.2.1",
"webpack-hot-middleware": "^2.26.1",
"webpack-virtual-modules": "^0.6.1",
"webpackbar": "^6.0.1"

File diff suppressed because it is too large Load Diff

View File

@ -32,7 +32,7 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
const serverDir = join(rootDir, '.output/server')
const serverStats = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot('"208k"')
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot('"209k"')
const modules = await analyzeSizes('node_modules/**/*', serverDir)
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"1335k"')
@ -72,10 +72,10 @@ describe.skipIf(process.env.SKIP_BUNDLE_SIZE === 'true' || process.env.ECOSYSTEM
const serverDir = join(rootDir, '.output-inline/server')
const serverStats = await analyzeSizes(['**/*.mjs', '!node_modules'], serverDir)
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot('"526k"')
expect.soft(roundToKilobytes(serverStats.totalBytes)).toMatchInlineSnapshot('"527k"')
const modules = await analyzeSizes('node_modules/**/*', serverDir)
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"75.5k"')
expect.soft(roundToKilobytes(modules.totalBytes)).toMatchInlineSnapshot('"75.6k"')
const packages = modules.files
.filter(m => m.endsWith('package.json'))

View File

@ -4,7 +4,7 @@ const { enabled: isPreview } = usePreviewMode()
const { data } = await useAsyncData(async () => {
await new Promise(resolve => setTimeout(resolve, 200))
const fetchedOnClient = process.client
const fetchedOnClient = import.meta.client
console.log(fetchedOnClient)