mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 23:22:02 +00:00
Merge branch 'main' into patch-21
This commit is contained in:
commit
444e5be6e8
224
.eslintrc
224
.eslintrc
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -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 }}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
})
|
||||
```
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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 */
|
||||
}
|
||||
```
|
||||
|
@ -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) {
|
||||
// ...
|
||||
}
|
||||
})
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
280
eslint.config.mjs
Normal 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'
|
||||
}
|
||||
}
|
||||
]
|
22
package.json
22
package.json
@ -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"
|
||||
},
|
||||
|
@ -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"
|
||||
},
|
||||
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
function defineNuxtConfig (config) {
|
||||
return config
|
||||
}
|
||||
|
@ -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": {
|
||||
|
@ -1,3 +1,4 @@
|
||||
// eslint-disable-next-line import/export
|
||||
export * from 'vue'
|
||||
|
||||
export const install = () => {}
|
||||
|
@ -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'
|
||||
@ -223,8 +223,8 @@ export function useAsyncData<
|
||||
|
||||
const promise = nuxtApp.runWithContext(_handler)
|
||||
|
||||
nuxtApp.ssrContext!._sharedPrerenderCache!.set(key, promise)
|
||||
return promise
|
||||
nuxtApp.ssrContext!._sharedPrerenderCache!.set(key, promise)
|
||||
return promise
|
||||
}
|
||||
|
||||
// Used to get default values
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,8 +85,7 @@ const isProcessingMiddleware = () => {
|
||||
return true
|
||||
}
|
||||
} catch {
|
||||
// Within an async middleware
|
||||
return true
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -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 {
|
||||
@ -146,7 +147,7 @@ export const islandsTransform = createUnplugin((options: ServerOnlyComponentTran
|
||||
* extract attributes from a node
|
||||
*/
|
||||
function extractAttributes (attributes: Record<string, string>, names: string[]) {
|
||||
const extracted:Record<string, string> = {}
|
||||
const extracted: Record<string, string> = {}
|
||||
for (const name of names) {
|
||||
if (name in attributes) {
|
||||
extracted[name] = attributes[name]
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
})
|
||||
|
@ -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
|
||||
|
112
packages/nuxt/test/components-transform.test.ts
Normal file
112
packages/nuxt/test/components-transform.test.ts
Normal 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
|
||||
}
|
@ -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
|
||||
|
@ -56,6 +56,7 @@ export default defineBuildConfig({
|
||||
'@vue/language-core',
|
||||
// Implicit
|
||||
'@vue/compiler-core',
|
||||
'@vue/compiler-sfc',
|
||||
'@vue/shared',
|
||||
'untyped'
|
||||
]
|
||||
|
@ -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",
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -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,
|
||||
|
@ -70,7 +70,7 @@ export type NuxtConfigLayer = ConfigLayer<NuxtConfig & {
|
||||
}>
|
||||
|
||||
export interface NuxtBuilder {
|
||||
bundle: (nuxt: Nuxt) => Promise<void>
|
||||
bundle: (nuxt: Nuxt) => Promise<void>
|
||||
}
|
||||
|
||||
// Normalized Nuxt options available as `nuxt.options.*`
|
||||
|
@ -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"
|
||||
|
@ -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: {
|
||||
|
@ -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"
|
||||
|
826
pnpm-lock.yaml
826
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -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'))
|
||||
|
2
test/fixtures/basic/pages/preview/index.vue
vendored
2
test/fixtures/basic/pages/preview/index.vue
vendored
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user