mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-25 07:05:11 +00:00
chore: move bridge to nuxt/bridge
(#4305)
This commit is contained in:
parent
70610d8858
commit
f91f987401
49
.github/ISSUE_TEMPLATE/bug-report-bridge.yml
vendored
49
.github/ISSUE_TEMPLATE/bug-report-bridge.yml
vendored
@ -1,49 +0,0 @@
|
||||
name: "\U0001F41E Bug report (Nuxt Bridge)"
|
||||
description: Create a report to help us improve Nuxt Bridge
|
||||
labels: ["pending triage", "bridge"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Please carefully read the contribution docs before creating a bug report
|
||||
👉 https://v3.nuxtjs.org/community/reporting-bugs
|
||||
|
||||
Please use the code sandbox template below to create a minimal reproduction
|
||||
👉 https://codesandbox.io/s/github/nuxt/starter/tree/v2-bridge-codesandbox
|
||||
- type: textarea
|
||||
id: bug-env
|
||||
attributes:
|
||||
label: Environment
|
||||
description: You can use `npx nuxi info` to fill this section
|
||||
placeholder: Environment
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Reproduction
|
||||
description: Please provide a link to a repo that can reproduce the problem you ran into. A [minimal reproduction](https://v3.nuxtjs.org/community/reporting-bugs#create-a-minimal-reproduction) is required unless you are absolutely sure that the issue is obvious and the provided information is enough to understand the problem. If a report is vague (e.g. just a generic error message) and has no reproduction, it will receive a "need reproduction" label. If no reproduction is provided after we might close it.
|
||||
placeholder: Reproduction
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: bug-description
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: A clear and concise description of what the bug is. If you intend to submit a PR for this issue, tell us in the description. Thanks!
|
||||
placeholder: Bug description
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: additonal
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: If applicable, add any other context about the problem here`
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Logs
|
||||
description: |
|
||||
Optional if provided reproduction. Please try not to insert an image but copy paste the log text.
|
||||
render: shell
|
||||
|
@ -1,6 +1,6 @@
|
||||
name: "\U0001F41E Bug report (Nuxt 3)"
|
||||
description: Create a report to help us improve Nuxt 3
|
||||
labels: ["pending triage", "nuxt3"]
|
||||
name: "\U0001F41E Bug report"
|
||||
description: Create a report to help us improve Nuxt
|
||||
labels: ["pending triage"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
@ -1,5 +0,0 @@
|
||||
# Nuxt Bridge
|
||||
|
||||
> Use backported Nuxt 3 features in Nuxt 2
|
||||
|
||||
Learn more about this package: <https://v3.nuxtjs.org/bridge/overview>
|
@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import 'nuxi/cli'
|
@ -1,14 +0,0 @@
|
||||
import { defineBuildConfig } from 'unbuild'
|
||||
|
||||
export default defineBuildConfig({
|
||||
declaration: true,
|
||||
entries: [
|
||||
'src/module',
|
||||
{ input: 'src/runtime/', outDir: 'dist/runtime', format: 'esm', declaration: true }
|
||||
],
|
||||
externals: [
|
||||
'webpack',
|
||||
'vite',
|
||||
'vue-meta'
|
||||
]
|
||||
})
|
@ -1,27 +0,0 @@
|
||||
// CommonJS proxy to bypass jiti transforms from nuxt 2
|
||||
module.exports = function (...args) {
|
||||
return import('./dist/module.mjs').then(m => m.default.call(this, ...args))
|
||||
}
|
||||
|
||||
const pkg = require('./package.json')
|
||||
|
||||
module.exports.defineNuxtConfig = (config = {}) => {
|
||||
if (config.bridge !== false) {
|
||||
config.bridge = config.bridge || {}
|
||||
config.bridge._version = pkg.version
|
||||
if (!config.buildModules) {
|
||||
config.buildModules = []
|
||||
}
|
||||
if (!config.buildModules.find(m => m === '@nuxt/bridge' || m === '@nuxt/bridge-edge')) {
|
||||
// Ensure other modules register their hooks before
|
||||
config.buildModules.push('@nuxt/bridge')
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
module.exports.meta = {
|
||||
pkg,
|
||||
name: pkg.name,
|
||||
version: pkg.version
|
||||
}
|
@ -1,87 +0,0 @@
|
||||
{
|
||||
"name": "@nuxt/bridge",
|
||||
"version": "3.0.0",
|
||||
"repository": "nuxt/framework",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"main": "./module.cjs",
|
||||
"types": "./types.d.ts",
|
||||
"files": [
|
||||
"module.cjs",
|
||||
"types.d.ts",
|
||||
"dist"
|
||||
],
|
||||
"bin": {
|
||||
"nuxi": "./bin/nuxt.mjs"
|
||||
},
|
||||
"scripts": {
|
||||
"prepack": "unbuild"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.16.7",
|
||||
"@babel/plugin-transform-typescript": "^7.16.8",
|
||||
"@nuxt/kit": "3.0.0",
|
||||
"@nuxt/postcss8": "^1.1.3",
|
||||
"@nuxt/schema": "3.0.0",
|
||||
"@nuxt/ui-templates": "npm:@nuxt/ui-templates-edge@latest",
|
||||
"@vitejs/plugin-legacy": "^1.8.0",
|
||||
"@vue/composition-api": "^1.4.9",
|
||||
"acorn": "^8.7.0",
|
||||
"cookie-es": "^0.5.0",
|
||||
"defu": "^6.0.0",
|
||||
"destr": "^1.1.1",
|
||||
"enhanced-resolve": "^5.9.3",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"estree-walker": "^3.0.1",
|
||||
"externality": "^0.2.1",
|
||||
"fs-extra": "^10.0.1",
|
||||
"get-port-please": "^2.5.0",
|
||||
"globby": "^13.1.1",
|
||||
"h3": "^0.7.3",
|
||||
"hash-sum": "^2.0.0",
|
||||
"knitwork": "^0.1.1",
|
||||
"magic-string": "^0.26.1",
|
||||
"mlly": "^0.5.1",
|
||||
"murmurhash-es": "^0.1.1",
|
||||
"nitropack": "^0.2.7",
|
||||
"node-fetch": "^3.2.3",
|
||||
"nuxi": "3.0.0",
|
||||
"ohash": "^0.1.0",
|
||||
"pathe": "^0.2.0",
|
||||
"perfect-debounce": "^0.1.3",
|
||||
"postcss": "^8",
|
||||
"postcss-import": "^14.1.0",
|
||||
"postcss-import-resolver": "^2.0.0",
|
||||
"postcss-preset-env": "^7.4.3",
|
||||
"postcss-url": "^10.1.3",
|
||||
"scule": "^0.2.1",
|
||||
"semver": "^7.3.7",
|
||||
"ufo": "^0.8.3",
|
||||
"unimport": "^0.1.4",
|
||||
"unplugin": "^0.6.1",
|
||||
"unplugin-vue2-script-setup": "^0.10.2",
|
||||
"untyped": "^0.4.4",
|
||||
"vite": "^2.9.1",
|
||||
"vite-plugin-vue2": "^1.9.3",
|
||||
"vue-bundle-renderer": "^0.3.5",
|
||||
"vue-template-compiler": "^2.6.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/types": "^2.15.8",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/hash-sum": "^1.0.0",
|
||||
"@types/node-fetch": "^3.0.2",
|
||||
"@vueuse/head": "^0.7.6",
|
||||
"nuxt": "^2",
|
||||
"unbuild": "latest",
|
||||
"vue": "^2",
|
||||
"vue-router": "^3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.16.0 || ^16.11.0 || ^17.0.0"
|
||||
},
|
||||
"installConfig": {
|
||||
"hoistingLimits": "workspaces"
|
||||
}
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
import { useNuxt, addTemplate, resolveAlias, addWebpackPlugin, addVitePlugin, addPlugin } from '@nuxt/kit'
|
||||
import { NuxtModule } from '@nuxt/schema'
|
||||
import { resolve } from 'pathe'
|
||||
import { componentsTypeTemplate } from '../../nuxt3/src/components/templates'
|
||||
import { schemaTemplate } from '../../nuxt3/src/core/templates'
|
||||
import { distDir } from './dirs'
|
||||
import { VueCompat } from './vue-compat'
|
||||
|
||||
export function setupAppBridge (_options: any) {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
// Setup aliases
|
||||
nuxt.options.alias['#app'] = resolve(distDir, 'runtime/index')
|
||||
nuxt.options.alias['nuxt3/app'] = nuxt.options.alias['#app']
|
||||
nuxt.options.alias['nuxt/app'] = nuxt.options.alias['#app']
|
||||
nuxt.options.alias['#build'] = nuxt.options.buildDir
|
||||
|
||||
// Mock `bundleBuilder.build` to support `nuxi prepare`
|
||||
if (nuxt.options._prepare) {
|
||||
nuxt.hook('builder:prepared', (builder) => {
|
||||
builder.bundleBuilder.build = () => Promise.resolve(builder.bundleBuilder)
|
||||
})
|
||||
}
|
||||
|
||||
// Transpile core vue libraries
|
||||
// TODO: resolve in vercel/nft
|
||||
nuxt.options.build.transpile.push('vuex')
|
||||
|
||||
// Transpile libs with modern syntax
|
||||
nuxt.options.build.transpile.push('h3')
|
||||
|
||||
// Disable legacy fetch polyfills
|
||||
nuxt.options.fetch.server = false
|
||||
nuxt.options.fetch.client = false
|
||||
|
||||
// Setup types for components
|
||||
const components = []
|
||||
nuxt.hook('components:extend', (registeredComponents) => {
|
||||
components.push(...registeredComponents)
|
||||
})
|
||||
addTemplate({
|
||||
...componentsTypeTemplate,
|
||||
options: { components, buildDir: nuxt.options.buildDir }
|
||||
})
|
||||
nuxt.hook('prepare:types', ({ references }) => {
|
||||
references.push({ path: resolve(nuxt.options.buildDir, 'types/components.d.ts') })
|
||||
})
|
||||
|
||||
// Augment schema with module types
|
||||
nuxt.hook('modules:done', async (container: any) => {
|
||||
nuxt.options._installedModules = await Promise.all(Object.values(container.requiredModules).map(async (m: { src: string, handler: NuxtModule }) => ({
|
||||
meta: await m.handler.getMeta?.(),
|
||||
entryPath: resolveAlias(m.src, nuxt.options.alias)
|
||||
})))
|
||||
addTemplate(schemaTemplate)
|
||||
})
|
||||
nuxt.hook('prepare:types', ({ references }) => {
|
||||
// Add module augmentations directly to NuxtConfig
|
||||
references.push({ path: resolve(nuxt.options.buildDir, 'types/schema.d.ts') })
|
||||
})
|
||||
|
||||
// Alias vue to have identical vue3 exports
|
||||
const { dst: vueCompat } = addTemplate({ src: resolve(distDir, 'runtime/vue2-bridge.mjs') })
|
||||
addWebpackPlugin(VueCompat.webpack({ src: vueCompat }))
|
||||
addVitePlugin(VueCompat.vite({ src: vueCompat }))
|
||||
|
||||
nuxt.hook('prepare:types', ({ tsConfig, references }) => {
|
||||
// Type 'vue' module with composition API exports
|
||||
references.push({ path: resolve(distDir, 'runtime/vue2-bridge.d.ts') })
|
||||
|
||||
// Enable Volar support with vue 2 compat mode
|
||||
// @ts-ignore
|
||||
tsConfig.vueCompilerOptions = {
|
||||
experimentalCompatMode: 2
|
||||
}
|
||||
})
|
||||
|
||||
// Deprecate various Nuxt options
|
||||
if (nuxt.options.globalName !== 'nuxt') {
|
||||
throw new Error('Custom global name is not supported by @nuxt/bridge.')
|
||||
}
|
||||
|
||||
// Fix wp4 esm
|
||||
nuxt.hook('webpack:config', (configs) => {
|
||||
for (const config of configs.filter(c => c.module)) {
|
||||
// @ts-ignore
|
||||
const jsRule: any = config.module.rules.find(rule => rule.test instanceof RegExp && rule.test.test('index.mjs'))
|
||||
jsRule.type = 'javascript/auto'
|
||||
|
||||
config.module.rules.unshift({
|
||||
test: /\.mjs$/,
|
||||
type: 'javascript/auto',
|
||||
include: [/node_modules/]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
addPlugin({
|
||||
src: resolve(distDir, 'runtime/error.plugin.server.mjs'),
|
||||
mode: 'server'
|
||||
})
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
// Based on https://github.com/webpack/webpack/blob/v4.46.0/lib/node/NodeMainTemplatePlugin.js#L81-L191
|
||||
|
||||
import { createRequire } from 'module'
|
||||
import type { Compiler } from 'webpack'
|
||||
export class AsyncLoadingPlugin {
|
||||
private opts: any
|
||||
private Template: any
|
||||
constructor (opts) {
|
||||
this.opts = opts
|
||||
const _require = createRequire(import.meta.url)
|
||||
const TemplatePath = _require.resolve('webpack/lib/Template', { paths: [...this.opts.modulesDir] })
|
||||
this.Template = _require(TemplatePath)
|
||||
}
|
||||
|
||||
apply (compiler: Compiler) {
|
||||
compiler.hooks.compilation.tap('AsyncLoading', (compilation) => {
|
||||
const mainTemplate = compilation.mainTemplate
|
||||
mainTemplate.hooks.requireEnsure.tap(
|
||||
'AsyncLoading',
|
||||
(_source, chunk, hash) => {
|
||||
const Template = this.Template
|
||||
const chunkFilename = mainTemplate.outputOptions.chunkFilename
|
||||
const chunkMaps = chunk.getChunkMaps()
|
||||
const insertMoreModules = [
|
||||
'var moreModules = chunk.modules, chunkIds = chunk.ids;',
|
||||
'for(var moduleId in moreModules) {',
|
||||
Template.indent(
|
||||
mainTemplate.renderAddModule(
|
||||
hash,
|
||||
chunk,
|
||||
'moduleId',
|
||||
'moreModules[moduleId]'
|
||||
)
|
||||
),
|
||||
'}'
|
||||
]
|
||||
return Template.asString([
|
||||
'// Async chunk loading for Nitro',
|
||||
'',
|
||||
'var installedChunkData = installedChunks[chunkId];',
|
||||
'if(installedChunkData !== 0) { // 0 means "already installed".',
|
||||
Template.indent([
|
||||
'// array of [resolve, reject, promise] means "currently loading"',
|
||||
'if(installedChunkData) {',
|
||||
Template.indent(['promises.push(installedChunkData[2]);']),
|
||||
'} else {',
|
||||
Template.indent([
|
||||
'// load the chunk and return promise to it',
|
||||
'var promise = new Promise(function(resolve, reject) {',
|
||||
Template.indent([
|
||||
'installedChunkData = installedChunks[chunkId] = [resolve, reject];',
|
||||
'import(' +
|
||||
mainTemplate.getAssetPath(
|
||||
JSON.stringify(`./${chunkFilename}`),
|
||||
{
|
||||
hash: `" + ${mainTemplate.renderCurrentHashCode(
|
||||
hash
|
||||
)} + "`,
|
||||
hashWithLength: length =>
|
||||
`" + ${mainTemplate.renderCurrentHashCode(
|
||||
hash,
|
||||
length
|
||||
)} + "`,
|
||||
chunk: {
|
||||
id: '" + chunkId + "',
|
||||
hash: `" + ${JSON.stringify(
|
||||
chunkMaps.hash
|
||||
)}[chunkId] + "`,
|
||||
hashWithLength: (length) => {
|
||||
const shortChunkHashMap = {}
|
||||
for (const chunkId of Object.keys(chunkMaps.hash)) {
|
||||
if (typeof chunkMaps.hash[chunkId] === 'string') {
|
||||
shortChunkHashMap[chunkId] = chunkMaps.hash[
|
||||
chunkId
|
||||
].substr(0, length)
|
||||
}
|
||||
}
|
||||
return `" + ${JSON.stringify(
|
||||
shortChunkHashMap
|
||||
)}[chunkId] + "`
|
||||
},
|
||||
contentHash: {
|
||||
javascript: `" + ${JSON.stringify(
|
||||
chunkMaps.contentHash.javascript
|
||||
)}[chunkId] + "`
|
||||
},
|
||||
contentHashWithLength: {
|
||||
javascript: (length) => {
|
||||
const shortContentHashMap = {}
|
||||
const contentHash =
|
||||
chunkMaps.contentHash.javascript
|
||||
for (const chunkId of Object.keys(contentHash)) {
|
||||
if (typeof contentHash[chunkId] === 'string') {
|
||||
shortContentHashMap[chunkId] = contentHash[
|
||||
chunkId
|
||||
].substr(0, length)
|
||||
}
|
||||
}
|
||||
return `" + ${JSON.stringify(
|
||||
shortContentHashMap
|
||||
)}[chunkId] + "`
|
||||
}
|
||||
},
|
||||
name: `" + (${JSON.stringify(
|
||||
chunkMaps.name
|
||||
)}[chunkId]||chunkId) + "`
|
||||
},
|
||||
contentHashType: 'javascript'
|
||||
}
|
||||
) +
|
||||
').then(chunk => {',
|
||||
Template.indent(
|
||||
insertMoreModules
|
||||
.concat([
|
||||
'var callbacks = [];',
|
||||
'for(var i = 0; i < chunkIds.length; i++) {',
|
||||
Template.indent([
|
||||
'if(installedChunks[chunkIds[i]])',
|
||||
Template.indent([
|
||||
'callbacks = callbacks.concat(installedChunks[chunkIds[i]][0]);'
|
||||
]),
|
||||
'installedChunks[chunkIds[i]] = 0;'
|
||||
]),
|
||||
'}',
|
||||
'for(i = 0; i < callbacks.length; i++)',
|
||||
Template.indent('callbacks[i]();')
|
||||
])
|
||||
),
|
||||
'});'
|
||||
]),
|
||||
'});',
|
||||
'promises.push(installedChunkData[2] = promise);'
|
||||
]),
|
||||
'}'
|
||||
]),
|
||||
'}'
|
||||
])
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
import { installModule, useNuxt } from '@nuxt/kit'
|
||||
import * as CompositionApi from '@vue/composition-api'
|
||||
import type { Preset } from 'unimport'
|
||||
import autoImports from '../../nuxt3/src/auto-imports/module'
|
||||
import { vuePreset } from '../../nuxt3/src/auto-imports/presets'
|
||||
|
||||
const UnsupportedImports = new Set(['useAsyncData', 'useFetch', 'useError', 'throwError', 'clearError', 'defineNuxtLink', 'useActiveRoute'])
|
||||
const CapiHelpers = new Set(Object.keys(CompositionApi))
|
||||
|
||||
export function setupAutoImports () {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
const bridgePresets: Preset[] = [{
|
||||
from: '@vue/composition-api',
|
||||
imports: vuePreset.imports.filter(i => CapiHelpers.has(i as string))
|
||||
}]
|
||||
|
||||
nuxt.hook('autoImports:sources', (presets) => {
|
||||
const vuePreset = presets.find(p => p.from === 'vue')
|
||||
if (vuePreset) { vuePreset.disabled = true }
|
||||
|
||||
const appPreset = presets.find(p => p.from === '#app')
|
||||
if (!appPreset) { return }
|
||||
|
||||
for (const [index, i] of Object.entries(appPreset.imports).reverse()) {
|
||||
if (typeof i === 'string' && UnsupportedImports.has(i)) {
|
||||
appPreset.imports.splice(Number(index), 1)
|
||||
}
|
||||
}
|
||||
|
||||
appPreset.imports.push('useNuxt2Meta')
|
||||
})
|
||||
|
||||
nuxt.hook('modules:done', () => installModule(autoImports, { presets: bridgePresets }))
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
import crypto from 'crypto'
|
||||
import { pathToFileURL } from 'url'
|
||||
import { createUnplugin } from 'unplugin'
|
||||
import { parse } from 'acorn'
|
||||
import MagicString from 'magic-string'
|
||||
import { walk } from 'estree-walker'
|
||||
import { parseQuery, parseURL } from 'ufo'
|
||||
|
||||
function createKey (
|
||||
source: string,
|
||||
method: crypto.BinaryToTextEncoding = 'base64'
|
||||
) {
|
||||
const hash = crypto.createHash('md5')
|
||||
hash.update(source)
|
||||
return hash.digest(method).toString()
|
||||
}
|
||||
|
||||
const keyedFunctions =
|
||||
/(useStatic|shallowSsrRef|ssrPromise|ssrRef|reqSsrRef|useAsync)/
|
||||
|
||||
export const KeyPlugin = createUnplugin(() => {
|
||||
return {
|
||||
name: 'nuxt-legacy-capi-key-transform',
|
||||
enforce: 'pre',
|
||||
transformInclude (id) {
|
||||
const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href))
|
||||
const query = parseQuery(search)
|
||||
|
||||
if (id.includes('node_modules')) {
|
||||
return false
|
||||
}
|
||||
|
||||
// vue files
|
||||
if (pathname.endsWith('.vue') && (query.type === 'script' || !search)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// js files
|
||||
if (pathname.match(/\.((c|m)?j|t)sx?/g)) {
|
||||
return true
|
||||
}
|
||||
},
|
||||
transform (code, id) {
|
||||
if (!keyedFunctions.test(code)) { return null }
|
||||
|
||||
try {
|
||||
const { 0: script = code, index: codeIndex = 0 } =
|
||||
code.match(/(?<=<script[^>]*>)[\S\s.]*?(?=<\/script>)/) || []
|
||||
const ast = parse(script, { ecmaVersion: 2020, sourceType: 'module' })
|
||||
const s = new MagicString(code)
|
||||
|
||||
walk(ast, {
|
||||
enter (node) {
|
||||
const { end } = node as unknown as {
|
||||
end: number
|
||||
}
|
||||
const { callee, arguments: args = [] } = node as {
|
||||
callee?: {
|
||||
type?: string
|
||||
name?: string
|
||||
property?: { type: string; name: string }
|
||||
}
|
||||
arguments?: any[]
|
||||
}
|
||||
if (
|
||||
callee?.type === 'Identifier' ||
|
||||
callee?.property?.type === 'Identifier'
|
||||
) {
|
||||
let method: crypto.BinaryToTextEncoding = 'base64'
|
||||
|
||||
switch (callee.name || callee.property?.name) {
|
||||
case 'useStatic':
|
||||
if (args.length > 2) { return }
|
||||
if (args.length === 2) {
|
||||
s.prependLeft(codeIndex + end - 1, ', undefined')
|
||||
}
|
||||
method = 'hex'
|
||||
break
|
||||
|
||||
case 'shallowSsrRef':
|
||||
case 'ssrPromise':
|
||||
case 'ssrRef':
|
||||
case 'reqSsrRef':
|
||||
case 'useAsync':
|
||||
if (args.length > 1) { return }
|
||||
break
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
s.appendLeft(
|
||||
codeIndex + end - 1,
|
||||
", '" + createKey(`${id}-${end}`, method) + "'"
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return s.toString()
|
||||
} catch { }
|
||||
}
|
||||
}
|
||||
})
|
@ -1,54 +0,0 @@
|
||||
import { createRequire } from 'module'
|
||||
import { useNuxt, addPluginTemplate, addVitePlugin, addWebpackPlugin } from '@nuxt/kit'
|
||||
import { resolve } from 'pathe'
|
||||
import { BridgeConfig } from '../types'
|
||||
import { distDir } from './dirs'
|
||||
import { KeyPlugin } from './capi-legacy-key-plugin'
|
||||
|
||||
export function setupCAPIBridge (options: Exclude<BridgeConfig['capi'], boolean>) {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
// Error if `@nuxtjs/composition-api` is added
|
||||
if (nuxt.options.buildModules.find(m => m === '@nuxtjs/composition-api' || m === '@nuxtjs/composition-api/module')) {
|
||||
throw new Error('Please remove `@nuxtjs/composition-api` from `buildModules` to avoid conflict with bridge.')
|
||||
}
|
||||
|
||||
// Add composition-api support
|
||||
const _require = createRequire(import.meta.url)
|
||||
const vueCapiEntry = _require.resolve('@vue/composition-api/dist/vue-composition-api.mjs')
|
||||
nuxt.options.alias['@vue/composition-api/dist/vue-composition-api.common.js'] = vueCapiEntry
|
||||
nuxt.options.alias['@vue/composition-api/dist/vue-composition-api.common.prod.js'] = vueCapiEntry
|
||||
nuxt.options.alias['@vue/composition-api/dist/vue-composition-api.esm.js'] = vueCapiEntry
|
||||
nuxt.options.alias['@vue/composition-api/dist/vue-composition-api.js'] = vueCapiEntry
|
||||
nuxt.options.alias['@vue/composition-api/dist/vue-composition-api.mjs'] = vueCapiEntry
|
||||
nuxt.options.alias['@vue/composition-api'] = vueCapiEntry
|
||||
const capiPluginPath = resolve(distDir, 'runtime/capi.plugin.mjs')
|
||||
addPluginTemplate({ filename: 'capi.plugin.mjs', src: capiPluginPath })
|
||||
|
||||
// Add support for useNuxtApp
|
||||
const appPlugin = addPluginTemplate(resolve(distDir, 'runtime/app.plugin.mjs'))
|
||||
nuxt.hook('modules:done', () => {
|
||||
nuxt.options.plugins.unshift(appPlugin)
|
||||
})
|
||||
|
||||
// Register Composition API before loading the rest of app
|
||||
nuxt.hook('webpack:config', (configs) => {
|
||||
// @ts-ignore
|
||||
configs.forEach(config => config.entry.app.unshift(capiPluginPath))
|
||||
})
|
||||
|
||||
if (options.legacy === false) {
|
||||
// Skip adding `@nuxtjs/composition-api` handlers if legacy support is disabled
|
||||
return
|
||||
}
|
||||
|
||||
// Handle legacy `@nuxtjs/composition-api`
|
||||
nuxt.options.alias['@nuxtjs/composition-api'] = resolve(distDir, 'runtime/capi.legacy.mjs')
|
||||
nuxt.options.build.transpile.push('@nuxtjs/composition-api', '@vue/composition-api')
|
||||
|
||||
// Enable automatic ssrRef key generation
|
||||
addVitePlugin(KeyPlugin.vite())
|
||||
addWebpackPlugin(KeyPlugin.webpack())
|
||||
|
||||
// TODO: Add @nuxtjs/composition-api shims
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
import { fileURLToPath } from 'url'
|
||||
import { existsSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
import { dirname } from 'pathe'
|
||||
|
||||
let dir = dirname(fileURLToPath(import.meta.url))
|
||||
while (dir !== '/' && !existsSync(join(dir, 'package.json'))) {
|
||||
dir = dirname(dir)
|
||||
}
|
||||
export const pkgDir = dir
|
||||
export const distDir = join(pkgDir, 'dist')
|
@ -1,33 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import { addTemplate, useNuxt, installModule } from '@nuxt/kit'
|
||||
import metaModule from '../../nuxt3/src/head/module'
|
||||
import { distDir } from './dirs'
|
||||
|
||||
const checkDocsMsg = 'Please see https://v3.nuxtjs.org for more information.'
|
||||
const msgPrefix = '[bridge] [meta]'
|
||||
|
||||
interface SetupMetaOptions {
|
||||
needsExplicitEnable?: boolean
|
||||
}
|
||||
|
||||
export const setupMeta = async (opts: SetupMetaOptions) => {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
if (opts.needsExplicitEnable) {
|
||||
const metaPath = addTemplate({
|
||||
filename: 'meta.mjs',
|
||||
getContents: () => `export const useHead = () => console.warn('${msgPrefix} To enable experimental \`useHead\` support, set \`bridge.meta\` to \`true\` in your \`nuxt.config\`. ${checkDocsMsg}')`
|
||||
})
|
||||
nuxt.options.alias['#head'] = metaPath.dst
|
||||
return
|
||||
}
|
||||
|
||||
if (nuxt.options.head && typeof nuxt.options.head === 'function') {
|
||||
throw new TypeError(`${msgPrefix} The head() function in \`nuxt.config\` has been deprecated and in Nuxt 3 will need to be moved to \`app.vue\`. ${checkDocsMsg}`)
|
||||
}
|
||||
|
||||
const runtimeDir = resolve(distDir, 'runtime/head')
|
||||
nuxt.options.alias['#head'] = runtimeDir
|
||||
|
||||
await installModule(metaModule)
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
import { createRequire } from 'module'
|
||||
import { defineNuxtModule, installModule, checkNuxtCompatibility, nuxtCtx } from '@nuxt/kit'
|
||||
import type { NuxtModule } from '@nuxt/schema'
|
||||
import { NuxtCompatibility } from '@nuxt/schema/src/types/compatibility'
|
||||
import type { BridgeConfig, ScriptSetupOptions } from '../types'
|
||||
import { setupNitroBridge } from './nitro'
|
||||
import { setupAppBridge } from './app'
|
||||
import { setupCAPIBridge } from './capi'
|
||||
import { setupBetterResolve } from './resolve'
|
||||
import { setupAutoImports } from './auto-imports'
|
||||
import { setupTypescript } from './typescript'
|
||||
import { setupMeta } from './meta'
|
||||
import { setupTranspile } from './transpile'
|
||||
import { setupScriptSetup } from './setup'
|
||||
|
||||
export default defineNuxtModule({
|
||||
meta: {
|
||||
name: 'nuxt-bridge',
|
||||
configKey: 'bridge'
|
||||
},
|
||||
defaults: {
|
||||
nitro: true,
|
||||
vite: false,
|
||||
app: {},
|
||||
capi: {},
|
||||
transpile: true,
|
||||
scriptSetup: true,
|
||||
autoImports: true,
|
||||
compatibility: true,
|
||||
meta: null,
|
||||
// TODO: Remove from 2.16
|
||||
postcss8: true,
|
||||
typescript: true,
|
||||
resolve: true
|
||||
} as BridgeConfig,
|
||||
async setup (opts, nuxt) {
|
||||
const _require = createRequire(import.meta.url)
|
||||
|
||||
// Allow using kit compasables in all modules
|
||||
if (!nuxtCtx.use()) {
|
||||
nuxtCtx.set(nuxt)
|
||||
}
|
||||
|
||||
// Mock _layers
|
||||
nuxt.options._layers = nuxt.options._layers || [{
|
||||
config: nuxt.options,
|
||||
cwd: nuxt.options.rootDir,
|
||||
configFile: nuxt.options._nuxtConfigFile
|
||||
}]
|
||||
|
||||
if (opts.nitro) {
|
||||
nuxt.hook('modules:done', async () => {
|
||||
await setupNitroBridge()
|
||||
})
|
||||
}
|
||||
if (opts.app) {
|
||||
await setupAppBridge(opts.app)
|
||||
}
|
||||
if (opts.capi) {
|
||||
if (!opts.app) {
|
||||
throw new Error('[bridge] Cannot enable composition-api with app disabled!')
|
||||
}
|
||||
await setupCAPIBridge(opts.capi === true ? {} : opts.capi)
|
||||
}
|
||||
if (opts.scriptSetup) {
|
||||
await setupScriptSetup(opts.scriptSetup as ScriptSetupOptions)
|
||||
}
|
||||
if (opts.autoImports) {
|
||||
await setupAutoImports()
|
||||
}
|
||||
if (opts.vite) {
|
||||
const viteModule = await import('./vite/module').then(r => r.default || r) as NuxtModule
|
||||
nuxt.hook('modules:done', () => installModule(viteModule))
|
||||
}
|
||||
if (opts.postcss8) {
|
||||
await installModule(_require.resolve('@nuxt/postcss8'))
|
||||
}
|
||||
if (opts.typescript) {
|
||||
await setupTypescript()
|
||||
}
|
||||
if (opts.resolve) {
|
||||
setupBetterResolve()
|
||||
}
|
||||
if (opts.transpile) {
|
||||
setupTranspile()
|
||||
}
|
||||
if (opts.compatibility) {
|
||||
nuxt.hook('modules:done', async (moduleContainer: any) => {
|
||||
for (const [name, m] of Object.entries(moduleContainer.requiredModules || {})) {
|
||||
const compat = ((m as any)?.handler?.meta?.compatibility || {}) as NuxtCompatibility
|
||||
if (compat) {
|
||||
const issues = await checkNuxtCompatibility(compat, nuxt)
|
||||
if (issues.length) {
|
||||
console.warn(`[bridge] Detected module incompatibility issues for \`${name}\`:\n` + issues.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (opts.meta !== false && opts.capi) {
|
||||
await setupMeta({ needsExplicitEnable: opts.meta === null })
|
||||
}
|
||||
}
|
||||
})
|
@ -1,396 +0,0 @@
|
||||
import { promises as fsp, existsSync } from 'fs'
|
||||
import fetch from 'node-fetch'
|
||||
import fsExtra from 'fs-extra'
|
||||
import { addPluginTemplate, resolvePath, useNuxt } from '@nuxt/kit'
|
||||
import { joinURL, stringifyQuery, withoutTrailingSlash } from 'ufo'
|
||||
import { resolve, join, dirname } from 'pathe'
|
||||
import { createNitro, createDevServer, build, writeTypes, prepare, copyPublicAssets, prerender } from 'nitropack'
|
||||
import { dynamicEventHandler, toEventHandler } from 'h3'
|
||||
import type { Nitro, NitroEventHandler, NitroDevEventHandler, NitroConfig } from 'nitropack'
|
||||
import { Nuxt } from '@nuxt/schema'
|
||||
import defu from 'defu'
|
||||
import { AsyncLoadingPlugin } from './async-loading'
|
||||
import { distDir } from './dirs'
|
||||
import { isDirectory, readDirRecursively } from './vite/utils/fs'
|
||||
|
||||
export async function setupNitroBridge () {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
// Ensure we're not just building with 'static' target
|
||||
if (!nuxt.options.dev && nuxt.options.target === 'static' && !nuxt.options._prepare && !(nuxt.options as any)._export && !nuxt.options._legacyGenerate) {
|
||||
throw new Error('[nitro] Please use `nuxt generate` for static target')
|
||||
}
|
||||
|
||||
// Handle legacy property name `assetsPath`
|
||||
nuxt.options.app.buildAssetsDir = nuxt.options.app.buildAssetsDir || nuxt.options.app.assetsPath
|
||||
nuxt.options.app.assetsPath = nuxt.options.app.buildAssetsDir
|
||||
nuxt.options.app.baseURL = nuxt.options.app.baseURL || (nuxt.options.app as any).basePath
|
||||
nuxt.options.app.cdnURL = nuxt.options.app.cdnURL || ''
|
||||
|
||||
// Extract publicConfig and app
|
||||
const publicConfig = nuxt.options.publicRuntimeConfig
|
||||
const appConfig = { ...publicConfig._app, ...publicConfig.app }
|
||||
delete publicConfig.app
|
||||
delete publicConfig._app
|
||||
|
||||
// Merge with new `runtimeConfig` format
|
||||
nuxt.options.runtimeConfig = defu(nuxt.options.runtimeConfig, {
|
||||
...publicConfig,
|
||||
...nuxt.options.privateRuntimeConfig,
|
||||
public: publicConfig,
|
||||
app: appConfig
|
||||
})
|
||||
|
||||
// Disable loading-screen
|
||||
// @ts-ignore
|
||||
nuxt.options.build.loadingScreen = false
|
||||
// @ts-ignore
|
||||
nuxt.options.build.indicator = false
|
||||
|
||||
if (nuxt.options.build.analyze === true) {
|
||||
const { rootDir } = nuxt.options
|
||||
nuxt.options.build.analyze = {
|
||||
template: 'treemap',
|
||||
projectRoot: rootDir,
|
||||
filename: join(rootDir, '.nuxt/stats', '{name}.html')
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve Handlers
|
||||
const { handlers, devHandlers } = await resolveHandlers(nuxt)
|
||||
|
||||
// Resolve config
|
||||
const _nitroConfig = (nuxt.options as any).nitro || {} as NitroConfig
|
||||
const nitroConfig: NitroConfig = defu(_nitroConfig, <NitroConfig>{
|
||||
rootDir: resolve(nuxt.options.rootDir),
|
||||
srcDir: resolve(nuxt.options.srcDir, 'server'),
|
||||
dev: nuxt.options.dev,
|
||||
preset: nuxt.options.dev ? 'nitro-dev' : undefined,
|
||||
buildDir: resolve(nuxt.options.buildDir),
|
||||
scanDirs: nuxt.options._layers.map(layer => join(layer.config.srcDir, 'server')),
|
||||
renderer: resolve(distDir, 'runtime/nitro/renderer'),
|
||||
errorHandler: resolve(distDir, 'runtime/nitro/error'),
|
||||
nodeModulesDirs: nuxt.options.modulesDir,
|
||||
handlers,
|
||||
devHandlers: [],
|
||||
runtimeConfig: {
|
||||
...nuxt.options.runtimeConfig,
|
||||
nitro: {
|
||||
envPrefix: 'NUXT_',
|
||||
...nuxt.options.runtimeConfig.nitro
|
||||
}
|
||||
},
|
||||
typescript: {
|
||||
generateTsConfig: false
|
||||
},
|
||||
publicAssets: [
|
||||
{
|
||||
baseURL: nuxt.options.app.buildAssetsDir,
|
||||
dir: resolve(nuxt.options.buildDir, 'dist/client')
|
||||
},
|
||||
...nuxt.options._layers
|
||||
.map(layer => join(layer.config.srcDir, 'public'))
|
||||
.filter(dir => existsSync(dir))
|
||||
.map(dir => ({ dir }))
|
||||
],
|
||||
prerender: {
|
||||
crawlLinks: nuxt.options.generate.crawler,
|
||||
routes: nuxt.options.generate.routes
|
||||
},
|
||||
externals: {
|
||||
inline: [
|
||||
...(nuxt.options.dev ? [] : ['vue', '@vue/', '@nuxt/', nuxt.options.buildDir]),
|
||||
'@nuxt/bridge/dist',
|
||||
'@nuxt/bridge-edge/dist'
|
||||
]
|
||||
},
|
||||
alias: {
|
||||
// Vue 2 mocks
|
||||
encoding: 'unenv/runtime/mock/proxy',
|
||||
he: 'unenv/runtime/mock/proxy',
|
||||
resolve: 'unenv/runtime/mock/proxy',
|
||||
'source-map': 'unenv/runtime/mock/proxy',
|
||||
'lodash.template': 'unenv/runtime/mock/proxy',
|
||||
'serialize-javascript': 'unenv/runtime/mock/proxy',
|
||||
|
||||
// Renderer
|
||||
'#vue-renderer': resolve(distDir, 'runtime/nitro/vue2'),
|
||||
'#vue2-server-renderer': 'vue-server-renderer/' + (nuxt.options.dev ? 'build.dev.js' : 'build.prod.js'),
|
||||
|
||||
// Paths
|
||||
'#paths': resolve(distDir, 'runtime/nitro/paths'),
|
||||
|
||||
// Nuxt aliases
|
||||
...nuxt.options.alias
|
||||
}
|
||||
})
|
||||
|
||||
// Let nitro handle #build for windows path normalization
|
||||
delete nitroConfig.alias['#build']
|
||||
|
||||
// Extend nitro config with hook
|
||||
await nuxt.callHook('nitro:config', nitroConfig)
|
||||
|
||||
// Initiate nitro
|
||||
const nitro = await createNitro(nitroConfig)
|
||||
|
||||
// Expose nitro to modules
|
||||
await nuxt.callHook('nitro:init', nitro)
|
||||
|
||||
// Shared vfs storage
|
||||
nitro.vfs = nuxt.vfs = nitro.vfs || nuxt.vfs || {}
|
||||
|
||||
// Connect hooks
|
||||
nuxt.hook('close', () => nitro.hooks.callHook('close'))
|
||||
|
||||
async function updateViteBase () {
|
||||
const clientDist = resolve(nuxt.options.buildDir, 'dist/client')
|
||||
|
||||
// Remove public files that have been duplicated into buildAssetsDir
|
||||
// TODO: Add option to configure this behaviour in vite
|
||||
const publicDir = join(nuxt.options.srcDir, nuxt.options.dir.static)
|
||||
let publicFiles: string[] = []
|
||||
if (await isDirectory(publicDir)) {
|
||||
publicFiles = readDirRecursively(publicDir).map(r => r.replace(publicDir, ''))
|
||||
for (const file of publicFiles) {
|
||||
try {
|
||||
fsExtra.rmSync(join(clientDist, file))
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy doubly-nested /_nuxt/_nuxt files into buildAssetsDir
|
||||
// TODO: Workaround vite issue
|
||||
if (await isDirectory(clientDist)) {
|
||||
const nestedAssetsPath = withoutTrailingSlash(join(clientDist, nuxt.options.app.buildAssetsDir))
|
||||
|
||||
if (await isDirectory(nestedAssetsPath)) {
|
||||
await fsExtra.copy(nestedAssetsPath, clientDist, { recursive: true })
|
||||
await fsExtra.remove(nestedAssetsPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
nuxt.hook('generate:before', updateViteBase)
|
||||
|
||||
// .ts is supported for serverMiddleware
|
||||
nuxt.options.extensions.push('ts')
|
||||
|
||||
// Disable server sourceMap, esbuild will generate for it.
|
||||
nuxt.hook('webpack:config', (webpackConfigs) => {
|
||||
const serverConfig = webpackConfigs.find(config => config.name === 'server')
|
||||
if (serverConfig) {
|
||||
serverConfig.devtool = false
|
||||
}
|
||||
})
|
||||
|
||||
// Set up webpack plugin for node async loading
|
||||
nuxt.hook('webpack:config', (webpackConfigs) => {
|
||||
const serverConfig = webpackConfigs.find(config => config.name === 'server')
|
||||
if (serverConfig) {
|
||||
serverConfig.plugins = serverConfig.plugins || []
|
||||
serverConfig.plugins.push(new AsyncLoadingPlugin({
|
||||
modulesDir: nuxt.options.modulesDir
|
||||
}) as any)
|
||||
}
|
||||
})
|
||||
|
||||
// Nitro client plugin
|
||||
addPluginTemplate({
|
||||
filename: 'nitro-bridge.client.mjs',
|
||||
src: resolve(distDir, 'runtime/nitro-bridge.client.mjs')
|
||||
})
|
||||
|
||||
// Nitro server plugin (for vue-meta)
|
||||
addPluginTemplate({
|
||||
filename: 'nitro-bridge.server.mjs',
|
||||
src: resolve(distDir, 'runtime/nitro-bridge.server.mjs')
|
||||
})
|
||||
|
||||
// Fix module resolution
|
||||
nuxt.hook('webpack:config', (configs) => {
|
||||
for (const config of configs) {
|
||||
// We use only object form of alias in base config
|
||||
if (Array.isArray(config.resolve.alias)) { return }
|
||||
config.resolve.alias.ufo = 'ufo/dist/index.mjs'
|
||||
config.resolve.alias.ohmyfetch = 'ohmyfetch/dist/index.mjs'
|
||||
}
|
||||
})
|
||||
|
||||
// Generate mjs resources
|
||||
nuxt.hook('build:compiled', async ({ name }) => {
|
||||
if (nuxt.options._prepare) { return }
|
||||
if (name === 'server') {
|
||||
const jsServerEntry = resolve(nuxt.options.buildDir, 'dist/server/server.js')
|
||||
await fsp.writeFile(jsServerEntry.replace(/.js$/, '.cjs'), 'module.exports = require("./server.js")', 'utf8')
|
||||
await fsp.writeFile(jsServerEntry.replace(/.js$/, '.mjs'), 'export { default } from "./server.cjs"', 'utf8')
|
||||
} else if (name === 'client') {
|
||||
const manifest = await fsp.readFile(resolve(nuxt.options.buildDir, 'dist/server/client.manifest.json'), 'utf8')
|
||||
await fsp.writeFile(resolve(nuxt.options.buildDir, 'dist/server/client.manifest.mjs'), 'export default ' + manifest, 'utf8')
|
||||
}
|
||||
})
|
||||
|
||||
// Setup handlers
|
||||
const devMidlewareHandler = dynamicEventHandler()
|
||||
nitro.options.devHandlers.unshift({ handler: devMidlewareHandler })
|
||||
nitro.options.devHandlers.push(...devHandlers)
|
||||
nitro.options.handlers.unshift({
|
||||
route: '/__nuxt_error',
|
||||
lazy: true,
|
||||
handler: resolve(distDir, 'runtime/nitro/renderer')
|
||||
})
|
||||
|
||||
// Create dev server
|
||||
if (nuxt.server) {
|
||||
nuxt.server.__closed = true
|
||||
nuxt.server = createNuxt2DevServer(nitro)
|
||||
nuxt.hook('build:resources', () => {
|
||||
nuxt.server.reload()
|
||||
})
|
||||
}
|
||||
|
||||
// Add typed route responses
|
||||
nuxt.hook('prepare:types', (opts) => {
|
||||
opts.references.push({ path: resolve(nuxt.options.buildDir, 'types/nitro.d.ts') })
|
||||
})
|
||||
|
||||
// nuxt prepare
|
||||
nuxt.hook('build:done', async () => {
|
||||
await writeTypes(nitro)
|
||||
})
|
||||
|
||||
// nuxt build/dev
|
||||
// @ts-ignore
|
||||
nuxt.options.build._minifyServer = false
|
||||
nuxt.options.build.standalone = false
|
||||
|
||||
const waitUntilCompile = new Promise<void>(resolve => nitro.hooks.hook('nitro:compiled', () => resolve()))
|
||||
nuxt.hook('build:done', async () => {
|
||||
if (nuxt.options._prepare) { return }
|
||||
await writeDocumentTemplate(nuxt)
|
||||
if (nuxt.options.dev) {
|
||||
await build(nitro)
|
||||
await waitUntilCompile
|
||||
// nitro.hooks.callHook('nitro:dev:reload')
|
||||
} else {
|
||||
await prepare(nitro)
|
||||
await copyPublicAssets(nitro)
|
||||
if (nuxt.options._generate || nuxt.options.target === 'static') {
|
||||
await prerender(nitro)
|
||||
}
|
||||
await build(nitro)
|
||||
}
|
||||
})
|
||||
|
||||
// nuxt dev
|
||||
if (nuxt.options.dev) {
|
||||
nuxt.hook('build:compile', ({ compiler }) => {
|
||||
compiler.outputFileSystem = { ...fsExtra, join } as any
|
||||
})
|
||||
nuxt.hook('server:devMiddleware', (m) => { devMidlewareHandler.set(toEventHandler(m)) })
|
||||
}
|
||||
|
||||
// nuxt generate
|
||||
nuxt.options.generate.dir = nitro.options.output.publicDir
|
||||
nuxt.options.generate.manifest = false
|
||||
nuxt.hook('generate:cache:ignore', (ignore: string[]) => {
|
||||
ignore.push(nitro.options.output.dir)
|
||||
ignore.push(nitro.options.output.serverDir)
|
||||
if (nitro.options.output.publicDir) {
|
||||
ignore.push(nitro.options.output.publicDir)
|
||||
}
|
||||
})
|
||||
nuxt.hook('generate:before', async () => {
|
||||
console.log('generate:before')
|
||||
await prepare(nitro)
|
||||
})
|
||||
nuxt.hook('generate:extendRoutes', async () => {
|
||||
console.log('generate:extendRoutes')
|
||||
await build(nitro)
|
||||
await nuxt.server.reload()
|
||||
})
|
||||
nuxt.hook('generate:done', async () => {
|
||||
console.log('generate:done')
|
||||
await nuxt.server.close()
|
||||
await build(nitro)
|
||||
})
|
||||
}
|
||||
|
||||
function createNuxt2DevServer (nitro: Nitro) {
|
||||
const server = createDevServer(nitro)
|
||||
|
||||
const listeners = []
|
||||
async function listen (port) {
|
||||
const listener = await server.listen(port, {
|
||||
showURL: false,
|
||||
isProd: true
|
||||
})
|
||||
listeners.push(listener)
|
||||
return listener
|
||||
}
|
||||
|
||||
async function renderRoute (route = '/', renderContext = {}) {
|
||||
const [listener] = listeners
|
||||
if (!listener) {
|
||||
throw new Error('There is no server listener to call `server.renderRoute()`')
|
||||
}
|
||||
const res = await fetch(joinURL(listener.url, route), {
|
||||
headers: { 'nuxt-render-context': stringifyQuery(renderContext) }
|
||||
})
|
||||
|
||||
const html = await res.text()
|
||||
|
||||
if (!res.ok) { return { html, error: res.statusText } }
|
||||
|
||||
return { html }
|
||||
}
|
||||
|
||||
return {
|
||||
...server,
|
||||
listeners,
|
||||
renderRoute,
|
||||
listen,
|
||||
serverMiddlewarePaths () { return [] },
|
||||
ready () { }
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveHandlers (nuxt: Nuxt) {
|
||||
const handlers: NitroEventHandler[] = []
|
||||
const devHandlers: NitroDevEventHandler[] = []
|
||||
|
||||
for (let m of nuxt.options.serverMiddleware) {
|
||||
if (typeof m === 'string' || typeof m === 'function' /* legacy middleware */) { m = { handler: m } }
|
||||
const route = m.path || m.route || '/'
|
||||
const handler = m.handler || m.handle
|
||||
if (typeof handler !== 'string' || typeof route !== 'string') {
|
||||
devHandlers.push({ route, handler })
|
||||
} else {
|
||||
delete m.handler
|
||||
delete m.path
|
||||
handlers.push({
|
||||
...m,
|
||||
route,
|
||||
handler: await resolvePath(handler)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
handlers,
|
||||
devHandlers
|
||||
}
|
||||
}
|
||||
|
||||
async function writeDocumentTemplate (nuxt: Nuxt) {
|
||||
// Compile html template
|
||||
const src = nuxt.options.appTemplatePath || resolve(nuxt.options.buildDir, 'views/app.template.html')
|
||||
const dst = src.replace(/.html$/, '.mjs').replace('app.template.mjs', 'document.template.mjs')
|
||||
const contents = nuxt.vfs[src] || await fsp.readFile(src, 'utf-8').catch(() => '')
|
||||
if (contents) {
|
||||
const compiled = 'export default ' +
|
||||
// eslint-disable-next-line no-template-curly-in-string
|
||||
`(params) => \`${contents.replace(/{{ (\w+) }}/g, '${params.$1}')}\``
|
||||
await fsp.mkdir(dirname(dst), { recursive: true })
|
||||
await fsp.writeFile(dst, compiled, 'utf8')
|
||||
}
|
||||
}
|
@ -1,82 +0,0 @@
|
||||
import fs from 'fs'
|
||||
import { promisify } from 'util'
|
||||
import defu from 'defu'
|
||||
import enhancedResolve from 'enhanced-resolve'
|
||||
import { ResolveOptions } from 'webpack/types'
|
||||
import { extendWebpackConfig, useNuxt } from '@nuxt/kit'
|
||||
|
||||
type UserResolveOptions = Parameters<typeof enhancedResolve.ResolverFactory['createResolver']>[0]
|
||||
type ResolverOptions = Omit<UserResolveOptions, 'fileSystem'> & { fileSystem?: enhancedResolve.CachedInputFileSystem }
|
||||
|
||||
const DEFAULTS: UserResolveOptions = {
|
||||
fileSystem: new enhancedResolve.CachedInputFileSystem(fs, 4000),
|
||||
extensions: ['.ts', '.tsx', '.mjs', '.cjs', '.js', '.jsx', '.json', '.vue']
|
||||
}
|
||||
|
||||
// Abstracted resolver factory which can be used in rollup, webpack, etc.
|
||||
const createResolver = (resolveOptions: ResolverOptions) => {
|
||||
const options = defu(resolveOptions, DEFAULTS) as UserResolveOptions
|
||||
const resolver = enhancedResolve.ResolverFactory.createResolver(options)
|
||||
|
||||
const root = options.roots?.[0] || '.'
|
||||
|
||||
const promisifiedResolve = promisify(resolver.resolve.bind(resolver)) as (context: object, path: string, request: string, resolveContext: enhancedResolve.ResolveContext) => Promise<string | false>
|
||||
|
||||
const resolve = (id: string, importer?: string) => promisifiedResolve({}, importer || root, id, {})
|
||||
|
||||
return { resolve, resolver }
|
||||
}
|
||||
|
||||
// Webpack plugin to add first-level support for subpath exports, etc.
|
||||
class EnhancedResolverPlugin {
|
||||
resolver: ReturnType<typeof createResolver>
|
||||
|
||||
constructor (options: ResolverOptions) {
|
||||
this.resolver = createResolver(options)
|
||||
}
|
||||
|
||||
apply (defaultResolver: any) {
|
||||
const enhancedResolver = this.resolver
|
||||
|
||||
defaultResolver.getHook('resolve').tapPromise('EnhancedResolverPlugin', async (request) => {
|
||||
const id = request.request
|
||||
// Fall back to default webpack4 resolver if not a node_modules import
|
||||
if (!id || !defaultResolver.isModule(id)) { return }
|
||||
|
||||
// Fall back to webpack4 resolver if resolving babel helpers
|
||||
// https://github.com/nuxt/nuxt.js/blob/714249740690569eedf74aa7bca7ed31c01953d4/packages/babel-preset-app/src/index.js#L169
|
||||
if (id.includes('@babel/')) { return }
|
||||
|
||||
const importer = request.context?.issuer
|
||||
try {
|
||||
const result = await enhancedResolver.resolve(id, importer)
|
||||
// Fall back to default webpack4 resolver if we can't resolve id
|
||||
if (!result) { return }
|
||||
request.path = result
|
||||
return request
|
||||
} catch {
|
||||
// Fall back to default webpack4 resolver in the event of error
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function setupBetterResolve () {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
extendWebpackConfig((config) => {
|
||||
const isServer = config.name === 'server'
|
||||
|
||||
config.resolve = config.resolve || {}
|
||||
config.resolve.plugins = config.resolve.plugins || []
|
||||
|
||||
config.resolve.plugins.push(new EnhancedResolverPlugin({
|
||||
conditionNames: ['import', ...isServer ? ['node'] : []],
|
||||
mainFields: ['module', ...isServer ? [] : ['browser'], 'main'],
|
||||
alias: config.resolve.alias,
|
||||
modules: config.resolve.modules,
|
||||
plugins: config.resolve.plugins as Array<Exclude<ResolveOptions['plugins'][number], string>>,
|
||||
roots: config.resolve.roots || [nuxt.options.rootDir]
|
||||
}))
|
||||
})
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
import Vue from 'vue'
|
||||
import { createHooks } from 'hookable'
|
||||
import { callWithNuxt, setNuxtAppInstance } from '#app'
|
||||
|
||||
// Reshape payload to match key `useLazyAsyncData` expects
|
||||
function proxiedState (state) {
|
||||
state._asyncData = state._asyncData || {}
|
||||
state._errors = state._errors || {}
|
||||
return new Proxy(state, {
|
||||
get (target, prop) {
|
||||
if (prop === 'data') {
|
||||
return target._asyncData
|
||||
}
|
||||
if (prop === '_data') {
|
||||
return target.state
|
||||
}
|
||||
return Reflect.get(target, prop)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const runOnceWith = (obj, fn) => {
|
||||
if (!obj || !['function', 'object'].includes(typeof obj)) {
|
||||
return fn()
|
||||
}
|
||||
if (obj.__nuxt_installed) { return }
|
||||
obj.__nuxt_installed = true
|
||||
return fn()
|
||||
}
|
||||
|
||||
export default async (ctx, inject) => {
|
||||
const nuxtApp = {
|
||||
vueApp: {
|
||||
component: (id, definition) => runOnceWith(definition, () => Vue.component(id, definition)),
|
||||
config: {
|
||||
globalProperties: {}
|
||||
},
|
||||
directive: (id, definition) => runOnceWith(definition, () => Vue.directive(id, definition)),
|
||||
mixin: mixin => runOnceWith(mixin, () => Vue.mixin(mixin)),
|
||||
mount: () => { },
|
||||
provide: inject,
|
||||
unmount: () => { },
|
||||
use (vuePlugin) {
|
||||
runOnceWith(vuePlugin, () => vuePlugin.install(this))
|
||||
},
|
||||
version: Vue.version
|
||||
},
|
||||
provide: inject,
|
||||
globalName: 'nuxt',
|
||||
payload: proxiedState(process.client ? ctx.nuxtState : ctx.ssrContext.nuxt),
|
||||
_asyncDataPromises: [],
|
||||
isHydrating: true,
|
||||
nuxt2Context: ctx
|
||||
}
|
||||
|
||||
nuxtApp.hooks = createHooks()
|
||||
nuxtApp.hook = nuxtApp.hooks.hook
|
||||
nuxtApp.callHook = nuxtApp.hooks.callHook
|
||||
|
||||
const middleware = await import('#build/middleware').then(r => r.default)
|
||||
nuxtApp._middleware = nuxtApp._middleware || {
|
||||
global: [],
|
||||
named: middleware
|
||||
}
|
||||
|
||||
ctx.app.router.beforeEach(async (to, from, next) => {
|
||||
nuxtApp._processingMiddleware = true
|
||||
|
||||
for (const middleware of nuxtApp._middleware.global) {
|
||||
const result = await callWithNuxt(nuxtApp, middleware, [to, from])
|
||||
if (result || result === false) { return next(result) }
|
||||
}
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
ctx.app.router.afterEach(() => {
|
||||
delete nuxtApp._processingMiddleware
|
||||
})
|
||||
|
||||
if (!Array.isArray(ctx.app.created)) {
|
||||
ctx.app.created = [ctx.app.created].filter(Boolean)
|
||||
}
|
||||
|
||||
if (!Array.isArray(ctx.app.mounted)) {
|
||||
ctx.app.mounted = [ctx.app.mounted].filter(Boolean)
|
||||
}
|
||||
|
||||
if (process.server) {
|
||||
nuxtApp.ssrContext = ctx.ssrContext
|
||||
}
|
||||
|
||||
ctx.app.created.push(function () {
|
||||
nuxtApp.vue2App = this
|
||||
})
|
||||
|
||||
ctx.app.mounted.push(() => { nuxtApp.isHydrating = false })
|
||||
|
||||
const proxiedApp = new Proxy(nuxtApp, {
|
||||
get (target, prop) {
|
||||
if (prop[0] === '$') {
|
||||
return target.nuxt2Context[prop] || target.vue2App?.[prop]
|
||||
}
|
||||
return Reflect.get(target, prop)
|
||||
}
|
||||
})
|
||||
|
||||
setNuxtAppInstance(proxiedApp)
|
||||
|
||||
if (process.client) {
|
||||
window.onNuxtReady(() => {
|
||||
nuxtApp.hooks.callHook('app:mounted', nuxtApp.vueApp)
|
||||
})
|
||||
}
|
||||
|
||||
inject('_nuxtApp', proxiedApp)
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
import type { Hookable } from 'hookable'
|
||||
// @ts-ignore
|
||||
import type { Vue } from 'vue/types/vue'
|
||||
import type { ComponentOptions } from 'vue'
|
||||
import { defineComponent, getCurrentInstance } from './composables'
|
||||
|
||||
export const isVue2 = true
|
||||
export const isVue3 = false
|
||||
|
||||
export const defineNuxtComponent = defineComponent
|
||||
|
||||
export interface VueAppCompat {
|
||||
component: Vue['component'],
|
||||
config: {
|
||||
globalProperties: any
|
||||
[key: string]: any
|
||||
},
|
||||
directive: Vue['directive'],
|
||||
mixin: Vue['mixin'],
|
||||
mount: Vue['mount'],
|
||||
provide: (name: string, value: any) => void,
|
||||
unmount: Vue['unmount'],
|
||||
use: Vue['use']
|
||||
version: string
|
||||
}
|
||||
|
||||
export interface RuntimeNuxtHooks {
|
||||
'vue:setup': () => void
|
||||
'app:mounted': (app: VueAppCompat) => void | Promise<void>
|
||||
'meta:register': (metaRenderers: any[]) => void | Promise<void>
|
||||
}
|
||||
|
||||
export interface NuxtAppCompat {
|
||||
nuxt2Context: Vue
|
||||
vue2App: ComponentOptions<Vue>
|
||||
|
||||
vueApp: VueAppCompat
|
||||
|
||||
globalName: string
|
||||
|
||||
hooks: Hookable<RuntimeNuxtHooks>
|
||||
hook: NuxtAppCompat['hooks']['hook']
|
||||
callHook: NuxtAppCompat['hooks']['callHook']
|
||||
|
||||
[key: string]: any
|
||||
|
||||
ssrContext?: Record<string, any>
|
||||
payload: {
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
provide: (name: string, value: any) => void
|
||||
}
|
||||
|
||||
export interface Context {
|
||||
$_nuxtApp: NuxtAppCompat
|
||||
}
|
||||
|
||||
let currentNuxtAppInstance: NuxtAppCompat | null
|
||||
|
||||
export const setNuxtAppInstance = (nuxt: NuxtAppCompat | null) => {
|
||||
currentNuxtAppInstance = nuxt
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the setup function passed in has access to the Nuxt instance via `useNuxt`.
|
||||
*
|
||||
* @param nuxt A Nuxt instance
|
||||
* @param setup The function to call
|
||||
*/
|
||||
export function callWithNuxt<T extends (...args: any[]) => any> (nuxt: NuxtAppCompat, setup: T, args?: Parameters<T>) {
|
||||
setNuxtAppInstance(nuxt)
|
||||
const p: ReturnType<T> = args ? setup(...args as Parameters<T>) : setup()
|
||||
if (process.server) {
|
||||
// Unset nuxt instance to prevent context-sharing in server-side
|
||||
setNuxtAppInstance(null)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
interface Plugin {
|
||||
(nuxt: NuxtAppCompat): Promise<void> | Promise<{ provide?: Record<string, any> }> | void | { provide?: Record<string, any> }
|
||||
}
|
||||
|
||||
export function defineNuxtPlugin (plugin: Plugin): (ctx: Context, inject: (id: string, value: any) => void) => void {
|
||||
return async (ctx, inject) => {
|
||||
const result = await callWithNuxt(ctx.$_nuxtApp, plugin, [ctx.$_nuxtApp])
|
||||
if (result && result.provide) {
|
||||
for (const key in result.provide) {
|
||||
inject(key, result.provide[key])
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
export const useNuxtApp = (): NuxtAppCompat => {
|
||||
const vm = getCurrentInstance()
|
||||
|
||||
if (!vm) {
|
||||
if (!currentNuxtAppInstance) {
|
||||
throw new Error('nuxt app instance unavailable')
|
||||
}
|
||||
return currentNuxtAppInstance
|
||||
}
|
||||
|
||||
return vm.proxy.$_nuxtApp
|
||||
}
|
@ -1 +0,0 @@
|
||||
../../../nuxt3/src/app/composables/asyncData.ts
|
@ -1,559 +0,0 @@
|
||||
import defu from 'defu'
|
||||
import { computed, getCurrentInstance as getVM, isReactive, isRef, onBeforeMount, onServerPrefetch, reactive, ref, set, shallowRef, toRaw, toRefs, watch } from '@vue/composition-api'
|
||||
import { useNuxtApp } from './app'
|
||||
import { useRouter as _useRouter, useState } from './composables'
|
||||
|
||||
// Vue composition API export
|
||||
export {
|
||||
computed,
|
||||
createApp,
|
||||
createRef,
|
||||
customRef,
|
||||
defineAsyncComponent,
|
||||
del,
|
||||
effectScope,
|
||||
getCurrentInstance,
|
||||
getCurrentScope,
|
||||
h,
|
||||
inject,
|
||||
isRaw,
|
||||
isReactive,
|
||||
isReadonly,
|
||||
isRef,
|
||||
markRaw,
|
||||
nextTick,
|
||||
onActivated,
|
||||
onBeforeMount,
|
||||
onBeforeUnmount,
|
||||
onBeforeUpdate,
|
||||
onDeactivated,
|
||||
onErrorCaptured,
|
||||
onMounted,
|
||||
onScopeDispose,
|
||||
onServerPrefetch,
|
||||
onUnmounted,
|
||||
onUpdated,
|
||||
provide,
|
||||
proxyRefs,
|
||||
reactive,
|
||||
readonly,
|
||||
set,
|
||||
shallowReactive,
|
||||
shallowReadonly,
|
||||
shallowRef,
|
||||
toRaw,
|
||||
toRef,
|
||||
toRefs,
|
||||
triggerRef,
|
||||
unref,
|
||||
useAttrs,
|
||||
useCssModule,
|
||||
useCSSModule,
|
||||
useSlots,
|
||||
version,
|
||||
warn,
|
||||
watch,
|
||||
watchEffect,
|
||||
watchPostEffect,
|
||||
watchSyncEffect
|
||||
} from '@vue/composition-api'
|
||||
|
||||
export { ref }
|
||||
|
||||
// Common deprecation utils
|
||||
// TODO: Add migration guide docs to @nuxtjs/composition-api
|
||||
const checkDocsMsg = 'Please see https://v3.nuxtjs.org/bridge/bridge-composition-api for more information.'
|
||||
const msgPrefix = '[bridge] [legacy capi]'
|
||||
const unsupported = message => () => { throw new Error(`${msgPrefix} ${message} ${checkDocsMsg}`) }
|
||||
const _warned = {}
|
||||
const warnOnce = (id, message) => {
|
||||
if (!_warned[id]) {
|
||||
console.warn(msgPrefix, message, checkDocsMsg)
|
||||
_warned[id] = true
|
||||
}
|
||||
}
|
||||
|
||||
// Warn in case of having any imports from `@nuxtjs/composition-api`
|
||||
warnOnce('import', '`@nuxtjs/composition-api` is deprecated.')
|
||||
|
||||
// Stub functions that provided type support
|
||||
export const defineNuxtMiddleware = unsupported('You are using `defineNuxtMiddleware`, which is not supported.')
|
||||
export const defineNuxtPlugin = unsupported('You are using `defineNuxtPlugin`, which has a Nuxt 3-compatible replacement.')
|
||||
|
||||
// Internal exports
|
||||
export const setMetaPlugin = unsupported('`setMetaPlugin` is an internal function that is no longer used.')
|
||||
export const setSSRContext = unsupported('`setSSRContext` is an internal function that is no longer used.')
|
||||
export const globalPlugin = unsupported('`globalPlugin` is an internal function that is no longer used.')
|
||||
|
||||
// Deprecated functions
|
||||
export const withContext = unsupported('`withContext` is a deprecated method that is no longer provided, but which has a Nuxt 3-compatible replacement.')
|
||||
export const useStatic = unsupported('`useStatic` is a deprecated method that is no longer provided.')
|
||||
export const reqRef = unsupported('`reqRef` is a deprecated method that is no longer provided, but which has a Nuxt 3-compatible replacement.')
|
||||
export const reqSsrRef = unsupported('`reqSsrRef` is no longer provided, but has a Nuxt 3-compatible replacement.')
|
||||
|
||||
// ssrRef helpers
|
||||
const sanitise = val => (val && JSON.parse(JSON.stringify(val))) || val
|
||||
const getValue = val => val instanceof Function ? val() : val
|
||||
|
||||
export const ssrRef = (value, key) => {
|
||||
const vm = getVM()
|
||||
if (!vm) { throw new Error('ssrRef no longer supports global/ambient context and must be called within a setup() function') }
|
||||
|
||||
warnOnce('ssrRef', '`ssrRef` is deprecated and has a Nuxt 3-compatible replacement.')
|
||||
|
||||
return useState(key, value instanceof Function ? value : () => value)
|
||||
}
|
||||
|
||||
export const shallowSsrRef = (value, key) => {
|
||||
warnOnce('shallowSsrRef', '`shallowSsrRef` is deprecated and has a Nuxt 3-compatible replacement.')
|
||||
|
||||
const ref = ssrRef(value, key)
|
||||
|
||||
if (process.client) {
|
||||
return shallowRef(ref.value)
|
||||
}
|
||||
|
||||
return ref
|
||||
}
|
||||
|
||||
export const ssrPromise = (value, key) => {
|
||||
warnOnce('ssrPromise', 'ssrPromise is deprecated.')
|
||||
|
||||
const ssrRefs = useSSRRefs()
|
||||
const promise = Promise.resolve(isHMR() ? getValue(value) : ssrRefs[key] ?? getValue(value))
|
||||
|
||||
onServerPrefetch(async () => { ssrRefs[key] = sanitise(await promise) })
|
||||
return promise
|
||||
}
|
||||
|
||||
// Composition API functions
|
||||
export const onGlobalSetup = (fn) => {
|
||||
warnOnce('onGlobalSetup', '`onGlobalSetup` is deprecated and has a Nuxt 3-compatible replacement.')
|
||||
useNuxtApp().hook('vue:setup', fn)
|
||||
}
|
||||
|
||||
export const useAsync = (cb, key) => {
|
||||
warnOnce('useAsync', 'You are using `useAsync`, which has a Nuxt 3-compatible replacement.')
|
||||
|
||||
const _ref = isRef(key) ? key : ssrRef(null, key)
|
||||
|
||||
if (!_ref.value || isHMR()) {
|
||||
const p = Promise.resolve(cb()).then(res => (_ref.value = res))
|
||||
onServerPrefetch(() => p)
|
||||
}
|
||||
|
||||
return _ref
|
||||
}
|
||||
|
||||
export const useContext = () => {
|
||||
warnOnce('useContext', 'You are using `useContext`, which has a Nuxt 3-compatible replacement.')
|
||||
|
||||
const route = useRoute()
|
||||
const nuxt = useNuxtApp()
|
||||
|
||||
return {
|
||||
...nuxt.nuxt2Context,
|
||||
route: computed(() => route),
|
||||
query: computed(() => route.value.query),
|
||||
from: computed(() => nuxt.nuxt2Context.from),
|
||||
params: computed(() => route.value.params)
|
||||
}
|
||||
}
|
||||
|
||||
function createEmptyMeta () {
|
||||
return {
|
||||
titleTemplate: null,
|
||||
|
||||
__dangerouslyDisableSanitizers: [],
|
||||
__dangerouslyDisableSanitizersByTagID: {},
|
||||
|
||||
title: undefined,
|
||||
htmlAttrs: {},
|
||||
headAttrs: {},
|
||||
bodyAttrs: {},
|
||||
|
||||
base: undefined,
|
||||
|
||||
meta: [],
|
||||
link: [],
|
||||
style: [],
|
||||
script: [],
|
||||
noscript: [],
|
||||
|
||||
changed: undefined,
|
||||
afterNavigation: undefined
|
||||
}
|
||||
}
|
||||
|
||||
const getHeadOptions = (options) => {
|
||||
const head = function () {
|
||||
const optionHead =
|
||||
options.head instanceof Function ? options.head.call(this) : options.head
|
||||
|
||||
if (!this._computedHead) { return optionHead }
|
||||
|
||||
const computedHead = this._computedHead.map((h) => {
|
||||
if (isReactive(h)) { return toRaw(h) }
|
||||
if (isRef(h)) { return h.value }
|
||||
return h
|
||||
})
|
||||
return defu({}, ...computedHead.reverse(), optionHead)
|
||||
}
|
||||
|
||||
return { head }
|
||||
}
|
||||
|
||||
export const defineComponent = (options) => {
|
||||
if (!('head' in options)) { return options }
|
||||
|
||||
return {
|
||||
...options,
|
||||
...getHeadOptions(options)
|
||||
}
|
||||
}
|
||||
|
||||
export const useMeta = (init) => {
|
||||
warnOnce('useMeta', 'You are using `useMeta`, which has a replacement provided by Nuxt Bridge.')
|
||||
const vm = getCurrentInstance()
|
||||
const refreshMeta = () => vm.$meta().refresh()
|
||||
|
||||
if (!vm._computedHead) {
|
||||
const metaRefs = reactive(createEmptyMeta())
|
||||
vm._computedHead = [metaRefs]
|
||||
vm._metaRefs = toRefs(metaRefs)
|
||||
|
||||
if (process.client) {
|
||||
watch(Object.values(vm._metaRefs), refreshMeta, { immediate: true })
|
||||
}
|
||||
}
|
||||
|
||||
if (init) {
|
||||
const initRef = init instanceof Function ? computed(init) : ref(init)
|
||||
vm._computedHead.push(initRef)
|
||||
|
||||
if (process.client) {
|
||||
watch(initRef, refreshMeta, { immediate: true })
|
||||
}
|
||||
}
|
||||
|
||||
return vm._metaRefs
|
||||
}
|
||||
|
||||
// Wrapped properties
|
||||
export const wrapProperty = (property, makeComputed = true) => () => {
|
||||
warnOnce('wrapProperty', 'You are using `wrapProperty`, which is deprecated.')
|
||||
const vm = getCurrentInstance()
|
||||
return makeComputed ? computed(() => vm[property]) : vm[property]
|
||||
}
|
||||
|
||||
export const useRouter = () => {
|
||||
warnOnce('useRouter', 'You are using `useRouter`, which has a Nuxt 3-compatible replacement.')
|
||||
return _useRouter()
|
||||
}
|
||||
|
||||
export const useRoute = () => {
|
||||
warnOnce('useRoute', 'You are using `useRoute`, which has a Nuxt 3-compatible replacement.')
|
||||
const vm = getCurrentInstance()
|
||||
return computed(() => vm.$route)
|
||||
}
|
||||
|
||||
export const useStore = () => {
|
||||
warnOnce('useRoute', 'You are using `useStore`, which has a Nuxt 3-compatible replacement.')
|
||||
return getCurrentInstance().$store
|
||||
}
|
||||
|
||||
// useFetch and helper functions
|
||||
|
||||
const fetches = new WeakMap()
|
||||
const fetchPromises = new Map()
|
||||
|
||||
const mergeDataOnMount = (data) => {
|
||||
const vm = getCurrentInstance()
|
||||
if (!vm) { throw new Error('This must be called within a setup function.') }
|
||||
|
||||
onBeforeMount(() => {
|
||||
// Merge data
|
||||
for (const key in data) {
|
||||
try {
|
||||
// Assign missing properties
|
||||
if (key in vm) {
|
||||
// Skip functions (not stringifiable)
|
||||
if (typeof vm[key] === 'function') { continue }
|
||||
// Preserve reactive objects
|
||||
if (isReactive(vm[key])) {
|
||||
// Unset keys that do not exist in incoming data
|
||||
for (const k in vm[key]) {
|
||||
if (!(k in data[key])) {
|
||||
delete vm[key][k]
|
||||
}
|
||||
}
|
||||
Object.assign(vm[key], data[key])
|
||||
continue
|
||||
}
|
||||
}
|
||||
set(vm, key, data[key])
|
||||
} catch (e) {
|
||||
if (process.env.NODE_ENV === 'development')
|
||||
// eslint-disable-next-line
|
||||
console.warn(`Could not hydrate ${key}.`)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function createGetCounter (counterObject, defaultKey = '') {
|
||||
return function getCounter (id = defaultKey) {
|
||||
if (counterObject[id] === undefined) {
|
||||
counterObject[id] = 0
|
||||
}
|
||||
return counterObject[id]++
|
||||
}
|
||||
}
|
||||
|
||||
const setFetchState = (vm) => {
|
||||
vm.$fetchState =
|
||||
vm.$fetchState ||
|
||||
reactive({
|
||||
error: null,
|
||||
pending: false,
|
||||
timestamp: 0
|
||||
})
|
||||
}
|
||||
|
||||
function getKey (vm) {
|
||||
const nuxt = useNuxtApp()
|
||||
const nuxtState = nuxt.payload
|
||||
if (process.server && 'push' in vm.$ssrContext.nuxt.fetch) {
|
||||
return undefined
|
||||
} else if (process.client && '_payloadFetchIndex' in nuxtState) {
|
||||
nuxtState._payloadFetchIndex = nuxtState._payloadFetchIndex || 0
|
||||
return nuxtState._payloadFetchIndex++
|
||||
}
|
||||
const defaultKey = vm.$options._scopeId || vm.$options.name || ''
|
||||
const getCounter = createGetCounter(
|
||||
process.server
|
||||
? vm.$ssrContext.fetchCounters
|
||||
: nuxt.vue2App._fetchCounters,
|
||||
defaultKey
|
||||
)
|
||||
|
||||
if (typeof vm.$options.fetchKey === 'function') {
|
||||
return vm.$options.fetchKey.call(vm, getCounter)
|
||||
} else {
|
||||
const key = typeof vm.$options.fetchKey === 'string' ? vm.$options.fetchKey : defaultKey
|
||||
return key ? key + ':' + getCounter(key) : String(getCounter(key))
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeError (err) {
|
||||
let message
|
||||
if (!(err.message || typeof err === 'string')) {
|
||||
try {
|
||||
message = JSON.stringify(err, null, 2)
|
||||
} catch (e) {
|
||||
message = `[${err.constructor.name}]`
|
||||
}
|
||||
} else {
|
||||
message = err.message || err
|
||||
}
|
||||
return {
|
||||
...err,
|
||||
message,
|
||||
statusCode:
|
||||
err.statusCode ||
|
||||
err.status ||
|
||||
(err.response && err.response.status) ||
|
||||
500
|
||||
}
|
||||
}
|
||||
|
||||
const loadFullStatic = (vm) => {
|
||||
vm._fetchKey = getKey(vm)
|
||||
// Check if component has been fetched on server
|
||||
const { fetchOnServer } = vm.$options
|
||||
const fetchedOnServer =
|
||||
typeof fetchOnServer === 'function'
|
||||
? fetchOnServer.call(vm) !== false
|
||||
: fetchOnServer !== false
|
||||
|
||||
if (!fetchedOnServer || vm.$nuxt?.isPreview || !vm.$nuxt?._pagePayload) {
|
||||
return
|
||||
}
|
||||
vm._hydrated = true
|
||||
const data = vm.$nuxt._pagePayload.fetch[vm._fetchKey]
|
||||
|
||||
// If fetch error
|
||||
if (data && data._error) {
|
||||
vm.$fetchState.error = data._error
|
||||
return
|
||||
}
|
||||
|
||||
mergeDataOnMount(data)
|
||||
}
|
||||
|
||||
async function serverPrefetch (vm) {
|
||||
if (!vm._fetchOnServer) { return }
|
||||
|
||||
// Call and await on $fetch
|
||||
setFetchState(vm)
|
||||
|
||||
try {
|
||||
await callFetches.call(vm)
|
||||
} catch (err) {
|
||||
if (process.dev) {
|
||||
console.error('Error in fetch():', err)
|
||||
}
|
||||
vm.$fetchState.error = normalizeError(err)
|
||||
}
|
||||
vm.$fetchState.pending = false
|
||||
|
||||
// Define an ssrKey for hydration
|
||||
vm._fetchKey =
|
||||
// Nuxt 2.15+ uses a different format - an object rather than an array
|
||||
'push' in vm.$ssrContext.nuxt.fetch
|
||||
? vm.$ssrContext.nuxt.fetch.length
|
||||
: vm._fetchKey || vm.$ssrContext.fetchCounters['']++
|
||||
|
||||
// Add data-fetch-key on parent element of Component
|
||||
if (!vm.$vnode.data) { vm.$vnode.data = {} }
|
||||
const attrs = (vm.$vnode.data.attrs = vm.$vnode.data.attrs || {})
|
||||
attrs['data-fetch-key'] = vm._fetchKey
|
||||
|
||||
const data = { ...vm._data }
|
||||
Object.entries(vm.__composition_api_state__.rawBindings).forEach(
|
||||
([key, val]) => {
|
||||
if (val instanceof Function || val instanceof Promise) { return }
|
||||
|
||||
data[key] = isRef(val) ? val.value : val
|
||||
}
|
||||
)
|
||||
|
||||
// Add to ssrContext for window.__NUXT__.fetch
|
||||
const content = vm.$fetchState.error
|
||||
? { _error: vm.$fetchState.error }
|
||||
: JSON.parse(JSON.stringify(data))
|
||||
if ('push' in vm.$ssrContext.nuxt.fetch) {
|
||||
vm.$ssrContext.nuxt.fetch.push(content)
|
||||
} else {
|
||||
vm.$ssrContext.nuxt.fetch[vm._fetchKey] = content
|
||||
}
|
||||
}
|
||||
|
||||
async function callFetches () {
|
||||
const fetchesToCall = fetches.get(this)
|
||||
if (!fetchesToCall) { return }
|
||||
this.$nuxt.nbFetching++
|
||||
|
||||
this.$fetchState.pending = true
|
||||
this.$fetchState.error = null
|
||||
this._hydrated = false
|
||||
|
||||
let error = null
|
||||
const startTime = Date.now()
|
||||
|
||||
try {
|
||||
await Promise.all(
|
||||
fetchesToCall.map((fetch) => {
|
||||
if (fetchPromises.has(fetch)) { return fetchPromises.get(fetch) }
|
||||
const promise = Promise.resolve(fetch(this)).finally(() =>
|
||||
fetchPromises.delete(fetch)
|
||||
)
|
||||
fetchPromises.set(fetch, promise)
|
||||
return promise
|
||||
})
|
||||
)
|
||||
} catch (err) {
|
||||
if (process.dev) {
|
||||
console.error('Error in fetch():', err)
|
||||
}
|
||||
error = normalizeError(err)
|
||||
}
|
||||
|
||||
const delayLeft = (this._fetchDelay || 0) - (Date.now() - startTime)
|
||||
if (delayLeft > 0) {
|
||||
await new Promise(resolve => setTimeout(resolve, delayLeft))
|
||||
}
|
||||
|
||||
this.$fetchState.error = error
|
||||
this.$fetchState.pending = false
|
||||
this.$fetchState.timestamp = Date.now()
|
||||
|
||||
this.$nextTick(() => (this.$nuxt).nbFetching--)
|
||||
}
|
||||
|
||||
const isSsrHydration = vm => vm.$vnode?.elm?.dataset?.fetchKey
|
||||
|
||||
export const useFetch = (callback) => {
|
||||
warnOnce('useFetch', 'You are using `useFetch`, which has a Nuxt 3-compatible replacement.')
|
||||
const vm = getCurrentInstance()
|
||||
const nuxt = useNuxtApp()
|
||||
|
||||
const nuxtState = nuxt.payload
|
||||
|
||||
const callbacks = fetches.get(vm) || []
|
||||
fetches.set(vm, [...callbacks, callback])
|
||||
|
||||
if (typeof vm.$options.fetchOnServer === 'function') {
|
||||
vm._fetchOnServer = vm.$options.fetchOnServer.call(vm) !== false
|
||||
} else {
|
||||
vm._fetchOnServer = vm.$options.fetchOnServer !== false
|
||||
}
|
||||
|
||||
if (process.server) {
|
||||
vm._fetchKey = getKey(vm)
|
||||
}
|
||||
|
||||
setFetchState(vm)
|
||||
|
||||
onServerPrefetch(() => serverPrefetch(vm))
|
||||
|
||||
function result () {
|
||||
return {
|
||||
fetch: vm.$fetch,
|
||||
fetchState: vm.$fetchState
|
||||
}
|
||||
}
|
||||
|
||||
vm._fetchDelay =
|
||||
typeof vm.$options.fetchDelay === 'number' ? vm.$options.fetchDelay : 0
|
||||
|
||||
vm.$fetch = callFetches.bind(vm)
|
||||
|
||||
onBeforeMount(() => !vm._hydrated && callFetches.call(vm))
|
||||
|
||||
if (process.server || !isSsrHydration(vm)) {
|
||||
if (process.client && !process.dev && process.static) { loadFullStatic(vm) }
|
||||
return result()
|
||||
}
|
||||
|
||||
// Hydrate component
|
||||
vm._hydrated = true
|
||||
vm._fetchKey = vm.$vnode.elm?.dataset.fetchKey || getKey(vm)
|
||||
const data = nuxtState.fetch[vm._fetchKey]
|
||||
|
||||
// If fetch error
|
||||
if (data && data._error) {
|
||||
vm.$fetchState.error = data._error
|
||||
return result()
|
||||
}
|
||||
|
||||
mergeDataOnMount(data)
|
||||
|
||||
return result()
|
||||
}
|
||||
|
||||
// -- Private shared utils (across composables) --
|
||||
|
||||
function getCurrentInstance () {
|
||||
const vm = getVM()
|
||||
if (!vm) { throw new Error('This must be called within a setup function.') }
|
||||
return vm.proxy
|
||||
}
|
||||
|
||||
const useSSRRefs = () => {
|
||||
const { payload } = useNuxtApp()
|
||||
payload.ssrRefs = payload.ssrRefs || {}
|
||||
return payload.ssrRefs
|
||||
}
|
||||
|
||||
const isHMR = () => process.env.NODE_ENV === 'development' && process.client && window.$nuxt?.context.isHMR
|
@ -1,20 +0,0 @@
|
||||
import Vue from 'vue'
|
||||
import VueCompositionAPI from '@vue/composition-api'
|
||||
import { defineNuxtPlugin } from '#app'
|
||||
|
||||
Vue.use(VueCompositionAPI.default || VueCompositionAPI)
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
const _originalSetup = nuxtApp.nuxt2Context.app.setup
|
||||
|
||||
nuxtApp.nuxt2Context.app.setup = function (...args) {
|
||||
const result = _originalSetup instanceof Function ? _originalSetup(...args) : {}
|
||||
|
||||
const hookResult = nuxtApp.hooks.callHookWith(hooks => hooks.map(hook => hook()), 'vue:setup')
|
||||
if (process.dev && hookResult && hookResult.some(i => i && 'then' in i)) {
|
||||
console.error('[nuxt] Error in `vue:setup`. Callbacks must be synchronous.')
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
})
|
@ -1,244 +0,0 @@
|
||||
import { getCurrentInstance, onBeforeUnmount, isRef, watch, reactive, toRef, isReactive, Ref, set } from '@vue/composition-api'
|
||||
import type { CombinedVueInstance } from 'vue/types/vue'
|
||||
import type { MetaInfo } from 'vue-meta'
|
||||
import type VueRouter from 'vue-router'
|
||||
import type { Location, Route } from 'vue-router'
|
||||
import type { RuntimeConfig } from '@nuxt/schema'
|
||||
import { sendRedirect } from 'h3'
|
||||
import defu from 'defu'
|
||||
import { useNuxtApp } from './app'
|
||||
|
||||
export { useLazyAsyncData, refreshNuxtData } from './asyncData'
|
||||
export { useLazyFetch } from './fetch'
|
||||
export { useCookie } from './cookie'
|
||||
export { useRequestHeaders } from './ssr'
|
||||
|
||||
export * from '@vue/composition-api'
|
||||
|
||||
const mock = () => () => { throw new Error('not implemented') }
|
||||
|
||||
export const useAsyncData = mock()
|
||||
export const useFetch = mock()
|
||||
export const useHydration = mock()
|
||||
|
||||
// Runtime config helper
|
||||
export const useRuntimeConfig = () => {
|
||||
const nuxtApp = useNuxtApp()
|
||||
if (!nuxtApp.$config) {
|
||||
const runtimeConfig = reactive(nuxtApp.nuxt2Context.app.$config)
|
||||
const compatibilityConfig = new Proxy(runtimeConfig, {
|
||||
get (target, prop) {
|
||||
if (prop === 'public') {
|
||||
return target.public
|
||||
}
|
||||
return target[prop] ?? target.public[prop]
|
||||
},
|
||||
set (target, prop, value) {
|
||||
if (prop === 'public' || prop === 'app') {
|
||||
return false // Throws TypeError
|
||||
}
|
||||
target[prop] = value
|
||||
target.public[prop] = value
|
||||
return true
|
||||
}
|
||||
})
|
||||
nuxtApp.provide('config', compatibilityConfig)
|
||||
nuxtApp.$config = compatibilityConfig
|
||||
}
|
||||
return nuxtApp.$config as RuntimeConfig
|
||||
}
|
||||
|
||||
// Auto-import equivalents for `vue-router`
|
||||
export const useRouter = () => {
|
||||
return useNuxtApp()?.nuxt2Context.app.router as VueRouter
|
||||
}
|
||||
|
||||
// This provides an equivalent interface to `vue-router` (unlike legacy implementation)
|
||||
export const useRoute = () => {
|
||||
const nuxtApp = useNuxtApp()
|
||||
|
||||
if (!nuxtApp._route) {
|
||||
Object.defineProperty(nuxtApp, '__route', {
|
||||
get: () => nuxtApp.nuxt2Context.app.context.route
|
||||
})
|
||||
nuxtApp._route = reactive(nuxtApp.__route)
|
||||
const router = useRouter()
|
||||
router.afterEach(route => Object.assign(nuxtApp._route, route))
|
||||
}
|
||||
|
||||
return nuxtApp._route as Route
|
||||
}
|
||||
|
||||
// payload.state is used for vuex by nuxt 2
|
||||
export const useState = <T>(key: string, init?: (() => T)): Ref<T> => {
|
||||
const nuxtApp = useNuxtApp()
|
||||
if (!nuxtApp.payload.useState) {
|
||||
nuxtApp.payload.useState = {}
|
||||
}
|
||||
if (!isReactive(nuxtApp.payload.useState)) {
|
||||
nuxtApp.payload.useState = reactive(nuxtApp.payload.useState)
|
||||
}
|
||||
|
||||
// see @vuejs/composition-api reactivity tracking on a reactive object with set
|
||||
if (!(key in nuxtApp.payload.useState)) {
|
||||
set(nuxtApp.payload.useState, key, undefined)
|
||||
}
|
||||
|
||||
const state = toRef(nuxtApp.payload.useState, key)
|
||||
if (state.value === undefined && init) {
|
||||
state.value = init()
|
||||
}
|
||||
return state
|
||||
}
|
||||
|
||||
type Reffed<T extends Record<string, any>> = {
|
||||
[P in keyof T]: T[P] extends Array<infer A> ? Ref<Array<Reffed<A>>> | Array<Reffed<A>> : T[P] extends Record<string, any> ? Reffed<T[P]> | Ref<Reffed<T[P]>> : T[P] | Ref<T[P]>
|
||||
}
|
||||
|
||||
function unwrap (value: any): Record<string, any> {
|
||||
if (!value || typeof value === 'string' || typeof value === 'boolean' || typeof value === 'number') { return value }
|
||||
if (Array.isArray(value)) { return value.map(i => unwrap(i)) }
|
||||
if (isRef(value)) { return unwrap(value.value) }
|
||||
if (typeof value === 'object') {
|
||||
return Object.fromEntries(Object.entries(value).map(([key, value]) => [key, unwrap(value)]))
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
type AugmentedComponent = CombinedVueInstance<Vue, object, object, object, Record<never, any>> & {
|
||||
_vueMeta?: boolean
|
||||
$metaInfo?: MetaInfo
|
||||
}
|
||||
|
||||
/** internal */
|
||||
function metaInfoFromOptions (metaOptions: Reffed<MetaInfo> | (() => Reffed<MetaInfo>)) {
|
||||
return metaOptions instanceof Function ? metaOptions : () => metaOptions
|
||||
}
|
||||
|
||||
export const useNuxt2Meta = (metaOptions: Reffed<MetaInfo> | (() => Reffed<MetaInfo>)) => {
|
||||
let vm: AugmentedComponent | null = null
|
||||
try {
|
||||
vm = getCurrentInstance()!.proxy as AugmentedComponent
|
||||
const meta = vm.$meta()
|
||||
const $root = vm.$root
|
||||
|
||||
if (!vm._vueMeta) {
|
||||
vm._vueMeta = true
|
||||
|
||||
let parent = vm.$parent as AugmentedComponent
|
||||
while (parent && parent !== $root) {
|
||||
if (parent._vueMeta === undefined) {
|
||||
parent._vueMeta = false
|
||||
}
|
||||
parent = parent.$parent
|
||||
}
|
||||
}
|
||||
// @ts-ignore
|
||||
vm.$options.head = vm.$options.head || {}
|
||||
|
||||
const unwatch = watch(metaInfoFromOptions(metaOptions), (metaInfo: MetaInfo) => {
|
||||
vm.$metaInfo = {
|
||||
...vm.$metaInfo || {},
|
||||
...unwrap(metaInfo)
|
||||
}
|
||||
if (process.client) {
|
||||
meta.refresh()
|
||||
}
|
||||
}, { immediate: true, deep: true })
|
||||
|
||||
onBeforeUnmount(unwatch)
|
||||
} catch {
|
||||
const app = (useNuxtApp().nuxt2Context as any).app
|
||||
if (typeof app.head === 'function') {
|
||||
const originalHead = app.head
|
||||
app.head = function () {
|
||||
const head = originalHead.call(this) || {}
|
||||
return defu(unwrap(metaInfoFromOptions(metaOptions)()), head)
|
||||
}
|
||||
} else {
|
||||
app.head = defu(unwrap(metaInfoFromOptions(metaOptions)()), app.head)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface AddRouteMiddlewareOptions {
|
||||
global?: boolean
|
||||
}
|
||||
|
||||
/** internal */
|
||||
function convertToLegacyMiddleware (middleware) {
|
||||
return async (ctx: any) => {
|
||||
const result = await middleware(ctx.route, ctx.from)
|
||||
if (result instanceof Error) {
|
||||
return ctx.error(result)
|
||||
}
|
||||
if (result) {
|
||||
return ctx.redirect(result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
const isProcessingMiddleware = () => {
|
||||
try {
|
||||
if (useNuxtApp()._processingMiddleware) {
|
||||
return true
|
||||
}
|
||||
} catch {
|
||||
// Within an async middleware
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export interface NavigateToOptions {
|
||||
replace?: boolean
|
||||
}
|
||||
|
||||
export const navigateTo = (to: Route, options: NavigateToOptions = {}): Promise<Route | void> | Route => {
|
||||
if (isProcessingMiddleware()) {
|
||||
return to
|
||||
}
|
||||
const router = useRouter()
|
||||
if (process.server && useNuxtApp().ssrContext) {
|
||||
// Server-side redirection using h3 res from ssrContext
|
||||
const res = useNuxtApp().ssrContext?.res
|
||||
const redirectLocation = router.resolve(to).route.fullPath
|
||||
return sendRedirect(res, redirectLocation)
|
||||
}
|
||||
// Client-side redirection using vue-router
|
||||
return options.replace ? router.replace(to) : router.push(to)
|
||||
}
|
||||
|
||||
/** This will abort navigation within a Nuxt route middleware handler. */
|
||||
export const abortNavigation = (err?: Error | string) => {
|
||||
if (process.dev && !isProcessingMiddleware()) {
|
||||
throw new Error('abortNavigation() is only usable inside a route middleware handler.')
|
||||
}
|
||||
if (err) {
|
||||
throw err instanceof Error ? err : new Error(err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type RouteMiddlewareReturn = void | Error | string | Location | boolean
|
||||
|
||||
export interface RouteMiddleware {
|
||||
(to: Route, from: Route): RouteMiddlewareReturn | Promise<RouteMiddlewareReturn>
|
||||
}
|
||||
|
||||
export const defineNuxtRouteMiddleware = (middleware: RouteMiddleware) => middleware
|
||||
|
||||
interface AddRouteMiddleware {
|
||||
(name: string, middleware: RouteMiddleware, options?: AddRouteMiddlewareOptions): void
|
||||
(middleware: RouteMiddleware): void
|
||||
}
|
||||
|
||||
export const addRouteMiddleware: AddRouteMiddleware = (name: string | RouteMiddleware, middleware?: RouteMiddleware, options: AddRouteMiddlewareOptions = {}) => {
|
||||
const nuxtApp = useNuxtApp()
|
||||
if (options.global || typeof name === 'function') {
|
||||
nuxtApp._middleware.global.push(typeof name === 'function' ? name : middleware)
|
||||
} else {
|
||||
nuxtApp._middleware.named[name] = convertToLegacyMiddleware(middleware)
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
../../../nuxt3/src/app/composables/cookie.ts
|
@ -1,5 +0,0 @@
|
||||
export default (ctx) => {
|
||||
if (ctx.ssrContext.error) {
|
||||
ctx.error(ctx.ssrContext.error)
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
../../../nuxt3/src/app/composables/fetch.ts
|
@ -1 +0,0 @@
|
||||
../../../nuxt3/src/head/runtime
|
@ -1,2 +0,0 @@
|
||||
export * from './app'
|
||||
export * from './composables'
|
@ -1 +0,0 @@
|
||||
../../../nuxt3/src/core/runtime/nitro
|
@ -1,7 +0,0 @@
|
||||
import { $fetch } from 'ohmyfetch'
|
||||
|
||||
if (!globalThis.$fetch) {
|
||||
globalThis.$fetch = $fetch
|
||||
}
|
||||
|
||||
export default () => {}
|
@ -1,51 +0,0 @@
|
||||
import { defineNuxtPlugin } from '#app'
|
||||
|
||||
const vueMetaRenderer = (nuxt) => {
|
||||
const meta = nuxt.ssrContext.meta.inject({
|
||||
isSSR: nuxt.ssrContext.nuxt.serverRendered,
|
||||
ln: process.env.NODE_ENV === 'development'
|
||||
})
|
||||
|
||||
return {
|
||||
htmlAttrs: meta.htmlAttrs.text(),
|
||||
headAttrs: meta.headAttrs.text(),
|
||||
headTags:
|
||||
meta.title.text() + meta.base.text() +
|
||||
meta.meta.text() + meta.link.text() +
|
||||
meta.style.text() + meta.script.text() +
|
||||
meta.noscript.text(),
|
||||
bodyAttrs: meta.bodyAttrs.text(),
|
||||
bodyScriptsPrepend:
|
||||
meta.meta.text({ pbody: true }) + meta.link.text({ pbody: true }) +
|
||||
meta.style.text({ pbody: true }) + meta.script.text({ pbody: true }) +
|
||||
meta.noscript.text({ pbody: true }),
|
||||
bodyScripts:
|
||||
meta.meta.text({ body: true }) + meta.link.text({ body: true }) +
|
||||
meta.style.text({ body: true }) + meta.script.text({ body: true }) +
|
||||
meta.noscript.text({ body: true })
|
||||
}
|
||||
}
|
||||
|
||||
export default defineNuxtPlugin((nuxtApp) => {
|
||||
const metaRenderers = [vueMetaRenderer]
|
||||
|
||||
nuxtApp.callHook('meta:register', metaRenderers)
|
||||
|
||||
nuxtApp.ssrContext.renderMeta = async () => {
|
||||
const metadata = {
|
||||
htmlAttrs: '',
|
||||
headAttrs: '',
|
||||
headTags: '',
|
||||
bodyAttrs: '',
|
||||
bodyScriptsPrepend: '',
|
||||
bodyScripts: ''
|
||||
}
|
||||
for await (const renderer of metaRenderers) {
|
||||
const result = await renderer(nuxtApp)
|
||||
for (const key in result) {
|
||||
metadata[key] += result[key]
|
||||
}
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
})
|
@ -1 +0,0 @@
|
||||
../../../nuxt3/src/app/composables/ssr.ts
|
@ -1 +0,0 @@
|
||||
export const AbortController = null
|
@ -1 +0,0 @@
|
||||
export const ReadableStream = null
|
@ -1,18 +0,0 @@
|
||||
export const URL = globalThis.URL
|
||||
export const URLSearchParams = globalThis.URLSearchParams
|
||||
|
||||
function notSupported () {
|
||||
throw new Error('[nuxt/vite] whatwg-url low level API is not supported yet!')
|
||||
}
|
||||
|
||||
export const parseURL = notSupported
|
||||
export const basicURLParse = notSupported
|
||||
export const serializeURL = notSupported
|
||||
export const serializeHost = notSupported
|
||||
export const serializeInteger = notSupported
|
||||
export const serializeURLOrigin = notSupported
|
||||
export const setTheUsername = notSupported
|
||||
export const setThePassword = notSupported
|
||||
export const cannotHaveAUsernamePasswordPort = notSupported
|
||||
export const percentDecodeBytes = notSupported
|
||||
export const percentDecodeString = notSupported
|
60
packages/bridge/src/runtime/vue2-bridge.d.ts
vendored
60
packages/bridge/src/runtime/vue2-bridge.d.ts
vendored
@ -1,60 +0,0 @@
|
||||
import * as VueCapi from '@vue/composition-api'
|
||||
|
||||
declare module 'vue' {
|
||||
export const EffectScope: typeof VueCapi['EffectScope']
|
||||
export const computed: typeof VueCapi['computed']
|
||||
export const createApp: typeof VueCapi['createApp']
|
||||
export const createRef: typeof VueCapi['createRef']
|
||||
export const customRef: typeof VueCapi['customRef']
|
||||
export const defineAsyncComponent: typeof VueCapi['defineAsyncComponent']
|
||||
export const defineComponent: typeof VueCapi['defineComponent']
|
||||
export const del: typeof VueCapi['del']
|
||||
export const effectScope: typeof VueCapi['effectScope']
|
||||
export const getCurrentInstance: typeof VueCapi['getCurrentInstance']
|
||||
export const getCurrentScope: typeof VueCapi['getCurrentScope']
|
||||
export const h: typeof VueCapi['h']
|
||||
export const inject: typeof VueCapi['inject']
|
||||
export const isRaw: typeof VueCapi['isRaw']
|
||||
export const isReactive: typeof VueCapi['isReactive']
|
||||
export const isReadonly: typeof VueCapi['isReadonly']
|
||||
export const isRef: typeof VueCapi['isRef']
|
||||
export const markRaw: typeof VueCapi['markRaw']
|
||||
export const nextTick: typeof VueCapi['nextTick']
|
||||
export const onActivated: typeof VueCapi['onActivated']
|
||||
export const onBeforeMount: typeof VueCapi['onBeforeMount']
|
||||
export const onBeforeUnmount: typeof VueCapi['onBeforeUnmount']
|
||||
export const onBeforeUpdate: typeof VueCapi['onBeforeUpdate']
|
||||
export const onDeactivated: typeof VueCapi['onDeactivated']
|
||||
export const onErrorCaptured: typeof VueCapi['onErrorCaptured']
|
||||
export const onMounted: typeof VueCapi['onMounted']
|
||||
export const onScopeDispose: typeof VueCapi['onScopeDispose']
|
||||
export const onServerPrefetch: typeof VueCapi['onServerPrefetch']
|
||||
export const onUnmounted: typeof VueCapi['onUnmounted']
|
||||
export const onUpdated: typeof VueCapi['onUpdated']
|
||||
export const provide: typeof VueCapi['provide']
|
||||
export const proxyRefs: typeof VueCapi['proxyRefs']
|
||||
export const reactive: typeof VueCapi['reactive']
|
||||
export const readonly: typeof VueCapi['readonly']
|
||||
export const ref: typeof VueCapi['ref']
|
||||
export const set: typeof VueCapi['set']
|
||||
export const shallowReactive: typeof VueCapi['shallowReactive']
|
||||
export const shallowReadonly: typeof VueCapi['shallowReadonly']
|
||||
export const shallowRef: typeof VueCapi['shallowRef']
|
||||
export const toRaw: typeof VueCapi['toRaw']
|
||||
export const toRef: typeof VueCapi['toRef']
|
||||
export const toRefs: typeof VueCapi['toRefs']
|
||||
export const triggerRef: typeof VueCapi['triggerRef']
|
||||
export const unref: typeof VueCapi['unref']
|
||||
export const useAttrs: typeof VueCapi['useAttrs']
|
||||
export const useCSSModule: typeof VueCapi['useCSSModule']
|
||||
export const useCssModule: typeof VueCapi['useCssModule']
|
||||
export const useSlots: typeof VueCapi['useSlots']
|
||||
export const warn: typeof VueCapi['warn']
|
||||
export const watch: typeof VueCapi['watch']
|
||||
export const watchEffect: typeof VueCapi['watchEffect']
|
||||
export const watchPostEffect: typeof VueCapi['watchPostEffect']
|
||||
export const watchSyncEffect: typeof VueCapi['watchSyncEffect']
|
||||
export const isFunction: (fn: unknown) => boolean
|
||||
}
|
||||
|
||||
export {}
|
@ -1,14 +0,0 @@
|
||||
import Vue from 'vue'
|
||||
|
||||
export { EffectScope, computed, createApp, createRef, customRef, defineAsyncComponent, defineComponent, del, effectScope, getCurrentInstance, getCurrentScope, h, inject, isRaw, isReactive, isReadonly, isRef, markRaw, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onBeforeUpdate, onDeactivated, onErrorCaptured, onMounted, onScopeDispose, onServerPrefetch, onUnmounted, onUpdated, provide, proxyRefs, reactive, readonly, ref, set, shallowReactive, shallowReadonly, shallowRef, toRaw, toRef, toRefs, triggerRef, unref, useAttrs, useCSSModule, useCssModule, useSlots, warn, watch, watchEffect, watchPostEffect, watchSyncEffect } from '@vue/composition-api'
|
||||
|
||||
export const isFunction = fn => fn instanceof Function
|
||||
|
||||
export { Vue as default }
|
||||
|
||||
// mock for vue-demi
|
||||
export const Vue2 = Vue
|
||||
export const isVue2 = true
|
||||
export const isVue3 = false
|
||||
export const install = () => {}
|
||||
export const version = Vue.version
|
@ -1,16 +0,0 @@
|
||||
import { useNuxt, installModule } from '@nuxt/kit'
|
||||
import scriptSetupPlugin from 'unplugin-vue2-script-setup/nuxt'
|
||||
import type { ScriptSetupOptions } from '../types'
|
||||
|
||||
export const setupScriptSetup = async (options: ScriptSetupOptions) => {
|
||||
const nuxt = useNuxt()
|
||||
const config = options === true ? {} : options
|
||||
|
||||
nuxt.hook('prepare:types', ({ references }) => {
|
||||
references.push({
|
||||
types: 'unplugin-vue2-script-setup/types'
|
||||
})
|
||||
})
|
||||
|
||||
await installModule(scriptSetupPlugin, config)
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
import { useNuxt } from '@nuxt/kit'
|
||||
import { addModuleTranspiles } from '../../nuxt3/src/core/modules'
|
||||
|
||||
export const setupTranspile = () => {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
nuxt.hook('modules:done', () => {
|
||||
addModuleTranspiles({
|
||||
additionalModules: ['@nuxt/bridge-edge']
|
||||
})
|
||||
})
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
import { createRequire } from 'module'
|
||||
import { extendWebpackConfig, useNuxt } from '@nuxt/kit'
|
||||
|
||||
const extensions = ['ts', 'tsx', 'cts', 'mts']
|
||||
const typescriptRE = /\.[cm]?tsx?$/
|
||||
|
||||
export function setupTypescript () {
|
||||
const nuxt = useNuxt()
|
||||
|
||||
nuxt.options.extensions.push(...extensions)
|
||||
nuxt.options.build.additionalExtensions.push(...extensions)
|
||||
|
||||
nuxt.options.build.babel.plugins = nuxt.options.build.babel.plugins || []
|
||||
|
||||
// Error if `@nuxt/typescript-build` is added
|
||||
if (nuxt.options.buildModules.includes('@nuxt/typescript-build')) {
|
||||
throw new Error('Please remove `@nuxt/typescript-build` from `buildModules` or set `bridge.typescript: false` to avoid conflict with bridge.')
|
||||
}
|
||||
|
||||
const _require = createRequire(import.meta.url)
|
||||
nuxt.options.build.babel.plugins.unshift(
|
||||
_require.resolve('@babel/plugin-proposal-optional-chaining'),
|
||||
_require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'),
|
||||
_require.resolve('@babel/plugin-transform-typescript')
|
||||
)
|
||||
|
||||
extendWebpackConfig((config) => {
|
||||
config.resolve.extensions!.push(...extensions.map(e => `.${e}`))
|
||||
const babelRule: any = config.module.rules.find((rule: any) => rule.test?.test('test.js'))
|
||||
config.module.rules.unshift({
|
||||
...babelRule,
|
||||
test: typescriptRE
|
||||
})
|
||||
})
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import * as vite from 'vite'
|
||||
import { createVuePlugin } from 'vite-plugin-vue2'
|
||||
import PluginLegacy from '@vitejs/plugin-legacy'
|
||||
import { logger } from '@nuxt/kit'
|
||||
import { joinURL } from 'ufo'
|
||||
import { devStyleSSRPlugin } from '../../../vite/src/plugins/dev-ssr-css'
|
||||
import { RelativeAssetPlugin } from '../../../vite/src/plugins/dynamic-base'
|
||||
import { jsxPlugin } from './plugins/jsx'
|
||||
import { ViteBuildContext, ViteOptions } from './types'
|
||||
|
||||
export async function buildClient (ctx: ViteBuildContext) {
|
||||
const alias = {
|
||||
'#nitro': resolve(ctx.nuxt.options.buildDir, 'nitro.client.mjs')
|
||||
}
|
||||
for (const p of ctx.builder.plugins) {
|
||||
alias[p.name] = p.mode === 'server'
|
||||
? `defaultexport:${resolve(ctx.nuxt.options.buildDir, 'empty.js')}`
|
||||
: `defaultexport:${p.src}`
|
||||
}
|
||||
|
||||
const clientConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
|
||||
define: {
|
||||
'process.client': true,
|
||||
'process.server': false,
|
||||
'process.static': false,
|
||||
'module.hot': false
|
||||
},
|
||||
cacheDir: resolve(ctx.nuxt.options.rootDir, 'node_modules/.cache/vite/client'),
|
||||
resolve: {
|
||||
alias
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
input: resolve(ctx.nuxt.options.buildDir, 'client.js')
|
||||
},
|
||||
manifest: true,
|
||||
outDir: resolve(ctx.nuxt.options.buildDir, 'dist/client')
|
||||
},
|
||||
plugins: [
|
||||
jsxPlugin(),
|
||||
createVuePlugin(ctx.config.vue),
|
||||
PluginLegacy(),
|
||||
RelativeAssetPlugin(),
|
||||
devStyleSSRPlugin({
|
||||
rootDir: ctx.nuxt.options.rootDir,
|
||||
buildAssetsURL: joinURL(ctx.nuxt.options.app.baseURL, ctx.nuxt.options.app.buildAssetsDir)
|
||||
})
|
||||
],
|
||||
server: {
|
||||
middlewareMode: true
|
||||
}
|
||||
} as ViteOptions)
|
||||
|
||||
await ctx.nuxt.callHook('vite:extendConfig', clientConfig, { isClient: true, isServer: false })
|
||||
|
||||
// Production build
|
||||
if (!ctx.nuxt.options.dev) {
|
||||
const start = Date.now()
|
||||
logger.info('Building client...')
|
||||
await vite.build(clientConfig)
|
||||
logger.success(`Client built in ${Date.now() - start}ms`)
|
||||
return
|
||||
}
|
||||
|
||||
// Create development server
|
||||
const viteServer = await vite.createServer(clientConfig)
|
||||
await ctx.nuxt.callHook('vite:serverCreated', viteServer)
|
||||
|
||||
const viteMiddleware = (req, res, next) => {
|
||||
// Workaround: vite devmiddleware modifies req.url
|
||||
const originalURL = req.url
|
||||
viteServer.middlewares.handle(req, res, (err) => {
|
||||
req.url = originalURL
|
||||
next(err)
|
||||
})
|
||||
}
|
||||
await ctx.nuxt.callHook('server:devMiddleware', viteMiddleware)
|
||||
|
||||
ctx.nuxt.hook('close', async () => {
|
||||
await viteServer.close()
|
||||
})
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
import createResolver from 'postcss-import-resolver'
|
||||
import defu from 'defu'
|
||||
import type { Nuxt, ViteOptions } from './types'
|
||||
|
||||
// Ref: https://github.com/nuxt/nuxt.js/blob/dev/packages/webpack/src/utils/postcss.js
|
||||
|
||||
export function resolveCSSOptions (nuxt: Nuxt): ViteOptions['css'] {
|
||||
const css: ViteOptions['css'] = {
|
||||
postcss: {
|
||||
plugins: []
|
||||
}
|
||||
}
|
||||
|
||||
const plugins = defu(nuxt.options.build.postcss.plugins, {
|
||||
// https://github.com/postcss/postcss-import
|
||||
'postcss-import': {
|
||||
resolve: createResolver({
|
||||
alias: { ...nuxt.options.alias },
|
||||
modules: [
|
||||
nuxt.options.srcDir,
|
||||
nuxt.options.rootDir,
|
||||
...nuxt.options.modulesDir
|
||||
]
|
||||
})
|
||||
},
|
||||
|
||||
// https://github.com/postcss/postcss-url
|
||||
'postcss-url': {},
|
||||
|
||||
// https://github.com/csstools/postcss-preset-env
|
||||
'postcss-preset-env': nuxt.options.build.postcss.preset || {}
|
||||
})
|
||||
|
||||
for (const name in plugins) {
|
||||
const opts = plugins[name]
|
||||
if (!opts) {
|
||||
continue
|
||||
}
|
||||
const plugin = nuxt.resolver.requireModule(name)
|
||||
// @ts-ignore
|
||||
css.postcss.plugins.push(plugin(opts))
|
||||
}
|
||||
|
||||
return css
|
||||
}
|
@ -1,172 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import fse from 'fs-extra'
|
||||
import { uniq, isJS, isCSS, hash } from '../../../vite/src/utils'
|
||||
import { ViteBuildContext } from './types'
|
||||
|
||||
const DEFAULT_APP_TEMPLATE = `
|
||||
<!DOCTYPE html>
|
||||
<html {{ HTML_ATTRS }}>
|
||||
<head {{ HEAD_ATTRS }}>
|
||||
{{ HEAD }}
|
||||
</head>
|
||||
<body {{ BODY_ATTRS }}>
|
||||
{{ APP }}
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
export async function prepareManifests (ctx: ViteBuildContext) {
|
||||
const rDist = (...args: string[]): string => resolve(ctx.nuxt.options.buildDir, 'dist', ...args)
|
||||
await fse.mkdirp(rDist('server'))
|
||||
|
||||
const customAppTemplateFile = resolve(ctx.nuxt.options.srcDir, 'app.html')
|
||||
const APP_TEMPLATE = fse.existsSync(customAppTemplateFile)
|
||||
? (await fse.readFile(customAppTemplateFile, 'utf-8'))
|
||||
: DEFAULT_APP_TEMPLATE
|
||||
|
||||
const DEV_TEMPLATE = APP_TEMPLATE
|
||||
.replace(
|
||||
'</body>',
|
||||
'<script type="module" src="/@vite/client"></script><script type="module" src="/.nuxt/client.js"></script></body>'
|
||||
)
|
||||
const SPA_TEMPLATE = ctx.nuxt.options.dev ? DEV_TEMPLATE : APP_TEMPLATE
|
||||
const SSR_TEMPLATE = ctx.nuxt.options.dev ? DEV_TEMPLATE : APP_TEMPLATE
|
||||
|
||||
await fse.writeFile(rDist('server/index.ssr.html'), SSR_TEMPLATE)
|
||||
await fse.writeFile(rDist('server/index.spa.html'), SPA_TEMPLATE)
|
||||
|
||||
if (ctx.nuxt.options.dev) {
|
||||
await stubManifest(ctx)
|
||||
} else {
|
||||
await generateBuildManifest(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// convert vite's manifest to webpack style
|
||||
export async function generateBuildManifest (ctx: ViteBuildContext) {
|
||||
const rDist = (...args: string[]): string => resolve(ctx.nuxt.options.buildDir, 'dist', ...args)
|
||||
|
||||
const viteClientManifest = await fse.readJSON(rDist('client/manifest.json'))
|
||||
const clientEntries = Object.entries(viteClientManifest)
|
||||
|
||||
const asyncEntries = uniq(clientEntries.filter((id: any) => id[1].isDynamicEntry).flatMap(getModuleIds)).filter(Boolean)
|
||||
const initialEntries = uniq(clientEntries.filter((id: any) => !id[1].isDynamicEntry).flatMap(getModuleIds)).filter(Boolean)
|
||||
const initialJs = initialEntries.filter(isJS)
|
||||
const initialAssets = initialEntries.filter(isCSS)
|
||||
|
||||
// Search for polyfill file, we don't include it in the client entry
|
||||
const polyfillName = initialEntries.find(id => id.startsWith('polyfills-legacy.'))
|
||||
const polyfill = await fse.readFile(rDist('client/' + polyfillName), 'utf-8')
|
||||
|
||||
// @vitejs/plugin-legacy uses SystemJS which need to call `System.import` to load modules
|
||||
const clientImports = initialJs.filter(id => id !== polyfillName)
|
||||
const clientEntryCode = [
|
||||
polyfill,
|
||||
'var appConfig = window?.__NUXT__?.config.app || {}',
|
||||
'var publicBase = appConfig.cdnURL || ("." + appConfig.baseURL)',
|
||||
`var imports = ${JSON.stringify(clientImports)};`,
|
||||
'imports.reduce((p, id) => p.then(() => System.import(publicBase + appConfig.buildAssetsDir.slice(1) + id)), Promise.resolve())'
|
||||
].join('\n')
|
||||
const clientEntryName = 'entry-legacy.' + hash(clientEntryCode) + '.js'
|
||||
|
||||
const clientManifest = {
|
||||
// This publicPath will be ignored by Nitro and computed dynamically
|
||||
publicPath: ctx.nuxt.options.app.buildAssetsDir,
|
||||
all: uniq([
|
||||
clientEntryName,
|
||||
...clientEntries.flatMap(getModuleIds)
|
||||
]).filter(Boolean),
|
||||
initial: [
|
||||
clientEntryName,
|
||||
...initialAssets
|
||||
].filter(Boolean),
|
||||
async: [
|
||||
// We move initial entries to the client entry
|
||||
...initialJs,
|
||||
...asyncEntries
|
||||
].filter(Boolean),
|
||||
modules: {},
|
||||
assetsMapping: {}
|
||||
}
|
||||
|
||||
const serverManifest = {
|
||||
entry: 'server.js',
|
||||
files: {
|
||||
'server.js': 'server.js',
|
||||
...Object.fromEntries(clientEntries.map(([id, entry]) => [id, (entry as any).file]))
|
||||
},
|
||||
maps: {}
|
||||
}
|
||||
|
||||
await fse.writeFile(rDist('client', clientEntryName), clientEntryCode, 'utf-8')
|
||||
|
||||
await writeClientManifest(clientManifest, ctx.nuxt.options.buildDir)
|
||||
await writeServerManifest(serverManifest, ctx.nuxt.options.buildDir)
|
||||
|
||||
// Remove SSR manifest from public client dir
|
||||
await fse.remove(rDist('client/manifest.json'))
|
||||
await fse.remove(rDist('client/ssr-manifest.json'))
|
||||
}
|
||||
|
||||
// stub manifest on dev
|
||||
export async function stubManifest (ctx: ViteBuildContext) {
|
||||
const clientManifest = {
|
||||
publicPath: '',
|
||||
all: [
|
||||
'empty.js'
|
||||
],
|
||||
initial: [
|
||||
'empty.js'
|
||||
],
|
||||
async: [],
|
||||
modules: {},
|
||||
assetsMapping: {}
|
||||
}
|
||||
const serverManifest = {
|
||||
entry: 'server.js',
|
||||
files: {
|
||||
'server.js': 'server.js'
|
||||
},
|
||||
maps: {}
|
||||
}
|
||||
|
||||
await writeClientManifest(clientManifest, ctx.nuxt.options.buildDir)
|
||||
await writeServerManifest(serverManifest, ctx.nuxt.options.buildDir)
|
||||
}
|
||||
|
||||
export async function generateDevSSRManifest (ctx: ViteBuildContext, extraEntries: string[] = []) {
|
||||
const entires = [
|
||||
'@vite/client',
|
||||
'entry.mjs',
|
||||
...extraEntries
|
||||
]
|
||||
|
||||
const clientManifest = {
|
||||
publicPath: '',
|
||||
all: entires,
|
||||
initial: entires,
|
||||
async: [],
|
||||
modules: {},
|
||||
assetsMapping: {}
|
||||
}
|
||||
|
||||
await writeClientManifest(clientManifest, ctx.nuxt.options.buildDir)
|
||||
}
|
||||
|
||||
async function writeServerManifest (serverManifest: any, buildDir: string) {
|
||||
const serverManifestJSON = JSON.stringify(serverManifest, null, 2)
|
||||
await fse.writeFile(resolve(buildDir, 'dist/server/server.manifest.json'), serverManifestJSON, 'utf-8')
|
||||
await fse.writeFile(resolve(buildDir, 'dist/server/server.manifest.mjs'), `export default ${serverManifestJSON}`, 'utf-8')
|
||||
}
|
||||
|
||||
async function writeClientManifest (clientManifest: any, buildDir: string) {
|
||||
const clientManifestJSON = JSON.stringify(clientManifest, null, 2)
|
||||
await fse.writeFile(resolve(buildDir, 'dist/server/client.manifest.json'), clientManifestJSON, 'utf-8')
|
||||
await fse.writeFile(resolve(buildDir, 'dist/server/client.manifest.mjs'), `export default ${clientManifestJSON}`, 'utf-8')
|
||||
}
|
||||
|
||||
function getModuleIds ([, value]: [string, any]) {
|
||||
if (!value) { return [] }
|
||||
// Only include legacy and css ids
|
||||
return [value.file, ...value.css || []].filter(id => isCSS(id) || id.match(/-legacy\./))
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
import { logger, addPluginTemplate, defineNuxtModule, addTemplate } from '@nuxt/kit'
|
||||
import { publicPathTemplate, clientConfigTemplate } from '../../../nuxt3/src/core/templates'
|
||||
import { version } from '../../package.json'
|
||||
import { middlewareTemplate, storeTemplate } from './templates'
|
||||
import type { ViteOptions } from './types'
|
||||
|
||||
export default defineNuxtModule<ViteOptions>({
|
||||
meta: {
|
||||
name: 'nuxt-bridge:vite',
|
||||
version,
|
||||
configKey: 'vite'
|
||||
},
|
||||
defaults: {},
|
||||
setup (viteOptions, nuxt) {
|
||||
nuxt.options.cli.badgeMessages.push(`⚡ Vite Mode Enabled (v${version})`)
|
||||
// eslint-disable-next-line no-console
|
||||
if (viteOptions.experimentWarning !== false && !nuxt.options.test) {
|
||||
logger.log(
|
||||
'🧪 Vite mode is experimental and some nuxt modules might be incompatible\n',
|
||||
' If found a bug, please report via https://github.com/nuxt/framework/issues with a minimal reproduction.'
|
||||
)
|
||||
}
|
||||
|
||||
// Disable loading-screen because why have it!
|
||||
// @ts-expect-error
|
||||
nuxt.options.build.loadingScreen = false
|
||||
// @ts-expect-error
|
||||
nuxt.options.build.indicator = false
|
||||
nuxt.options._modules = nuxt.options._modules
|
||||
.filter(m => !(Array.isArray(m) && m[0] === '@nuxt/loading-screen'))
|
||||
|
||||
// Mask nuxt-vite to avoid other modules depending on it's existence
|
||||
// TODO: Move to kit
|
||||
const getModuleName = (m) => {
|
||||
if (Array.isArray(m)) { m = m[0] }
|
||||
return m.meta ? m.meta.name : m
|
||||
}
|
||||
const filterModule = modules => modules.filter(m => getModuleName(m) !== 'nuxt-bridge:vite')
|
||||
nuxt.options.modules = filterModule(nuxt.options.modules)
|
||||
nuxt.options.buildModules = filterModule(nuxt.options.buildModules)
|
||||
|
||||
if (nuxt.options.store) {
|
||||
addPluginTemplate(storeTemplate)
|
||||
}
|
||||
addPluginTemplate(middlewareTemplate)
|
||||
|
||||
addTemplate(clientConfigTemplate)
|
||||
addTemplate({
|
||||
...publicPathTemplate,
|
||||
options: { nuxt }
|
||||
})
|
||||
|
||||
nuxt.hook('builder:prepared', async (builder) => {
|
||||
if (nuxt.options._prepare) { return }
|
||||
builder.bundleBuilder.close()
|
||||
delete builder.bundleBuilder
|
||||
const { ViteBuilder } = await import('./vite')
|
||||
builder.bundleBuilder = new ViteBuilder(builder)
|
||||
})
|
||||
|
||||
// remove templates from nuxt-app
|
||||
nuxt.hook('build:templates', (templates) => {
|
||||
const templatesFiles = templates.templatesFiles.filter((template) => {
|
||||
return !['store.js', 'middleware.js'].includes(template.dst)
|
||||
})
|
||||
templates.templatesFiles.length = 0
|
||||
templates.templatesFiles.push(...templatesFiles)
|
||||
})
|
||||
}
|
||||
})
|
@ -1,37 +0,0 @@
|
||||
import type { Plugin } from 'vite'
|
||||
import fse from 'fs-extra'
|
||||
import { findExports } from 'mlly'
|
||||
|
||||
const PREFIX = 'defaultexport:'
|
||||
const hasPrefix = (id: string = '') => id.startsWith(PREFIX)
|
||||
const removePrefix = (id: string = '') => hasPrefix(id) ? id.substr(PREFIX.length) : id
|
||||
|
||||
const addDefaultExport = (code: string = '') => code + '\n\n' + 'export default () => {}'
|
||||
|
||||
export function defaultExportPlugin () {
|
||||
return <Plugin>{
|
||||
name: 'nuxt:default-export',
|
||||
enforce: 'pre',
|
||||
resolveId (id, importer) {
|
||||
if (hasPrefix(id)) {
|
||||
return id
|
||||
}
|
||||
if (importer && hasPrefix(importer)) {
|
||||
return this.resolve(id, removePrefix(importer))
|
||||
}
|
||||
return null
|
||||
},
|
||||
|
||||
async load (id) {
|
||||
if (hasPrefix(id)) {
|
||||
let code = await fse.readFile(removePrefix(id), 'utf8')
|
||||
const exports = findExports(code)
|
||||
if (!exports.find(i => i.names.includes('default'))) {
|
||||
code = addDefaultExport(code)
|
||||
}
|
||||
return { map: null, code }
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
import type { Plugin } from 'vite'
|
||||
|
||||
const needsJsxProcessing = (id: string = '') =>
|
||||
!id.includes('node_modules') && ['.vue', '.jsx', '.tsx'].some(extension => id.includes(extension))
|
||||
|
||||
export function jsxPlugin () {
|
||||
return <Plugin>{
|
||||
name: 'nuxt:jsx',
|
||||
transform (code, id) {
|
||||
if (!needsJsxProcessing(id)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
code: code.replace(/render\s*\(\s*\)\s*\{/g, 'render(h){'),
|
||||
map: null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import type { Plugin } from 'vite'
|
||||
|
||||
export function replace (replacements: Record<string, string>) {
|
||||
return <Plugin>{
|
||||
name: 'nuxt:replace',
|
||||
transform (code) {
|
||||
Object.entries(replacements).forEach(([key, value]) => {
|
||||
const escapedKey = key.replace(/\./g, '\\.')
|
||||
code = code.replace(new RegExp(escapedKey, 'g'), value)
|
||||
})
|
||||
return {
|
||||
code,
|
||||
map: null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import * as vite from 'vite'
|
||||
import { createVuePlugin } from 'vite-plugin-vue2'
|
||||
import { logger } from '@nuxt/kit'
|
||||
import fse from 'fs-extra'
|
||||
import { debounce } from 'perfect-debounce'
|
||||
import { bundleRequest } from '../../../vite/src/dev-bundler'
|
||||
import { isCSS } from '../../../vite/src/utils'
|
||||
import { wpfs } from './utils/wpfs'
|
||||
import { ViteBuildContext, ViteOptions } from './types'
|
||||
import { jsxPlugin } from './plugins/jsx'
|
||||
import { generateDevSSRManifest } from './manifest'
|
||||
|
||||
export async function buildServer (ctx: ViteBuildContext) {
|
||||
// Workaround to disable HMR
|
||||
const _env = process.env.NODE_ENV
|
||||
process.env.NODE_ENV = 'production'
|
||||
const vuePlugin = createVuePlugin(ctx.config.vue)
|
||||
process.env.NODE_ENV = _env
|
||||
|
||||
const alias = {}
|
||||
for (const p of ctx.builder.plugins) {
|
||||
alias[p.name] = p.mode === 'client'
|
||||
? `defaultexport:${resolve(ctx.nuxt.options.buildDir, 'empty.js')}`
|
||||
: `defaultexport:${p.src}`
|
||||
}
|
||||
|
||||
const serverConfig: vite.InlineConfig = vite.mergeConfig(ctx.config, {
|
||||
define: {
|
||||
'process.server': true,
|
||||
'process.client': false,
|
||||
'process.static': false,
|
||||
'typeof window': '"undefined"',
|
||||
'typeof document': '"undefined"',
|
||||
'typeof navigator': '"undefined"',
|
||||
'typeof location': '"undefined"',
|
||||
'typeof XMLHttpRequest': '"undefined"'
|
||||
},
|
||||
cacheDir: resolve(ctx.nuxt.options.rootDir, 'node_modules/.cache/vite/server'),
|
||||
resolve: {
|
||||
alias
|
||||
},
|
||||
ssr: {
|
||||
external: [
|
||||
'axios'
|
||||
],
|
||||
noExternal: [
|
||||
// TODO: Use externality for production (rollup) build
|
||||
/\/esm\/.*\.js$/,
|
||||
/\.(es|esm|esm-browser|esm-bundler).js$/,
|
||||
'#app',
|
||||
/@nuxt\/nitro\/(dist|src)/,
|
||||
...ctx.nuxt.options.build.transpile.filter(i => typeof i === 'string')
|
||||
]
|
||||
},
|
||||
build: {
|
||||
outDir: resolve(ctx.nuxt.options.buildDir, 'dist/server'),
|
||||
ssr: ctx.nuxt.options.ssr ?? true,
|
||||
ssrManifest: true,
|
||||
rollupOptions: {
|
||||
external: ['#nitro'],
|
||||
input: resolve(ctx.nuxt.options.buildDir, 'server.js'),
|
||||
output: {
|
||||
entryFileNames: 'server.mjs',
|
||||
chunkFileNames: 'chunks/[name].mjs',
|
||||
preferConst: true,
|
||||
format: 'module'
|
||||
},
|
||||
onwarn (warning, rollupWarn) {
|
||||
if (!['UNUSED_EXTERNAL_IMPORT'].includes(warning.code)) {
|
||||
rollupWarn(warning)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
server: {
|
||||
// https://github.com/vitest-dev/vitest/issues/229#issuecomment-1002685027
|
||||
preTransformRequests: false
|
||||
},
|
||||
plugins: [
|
||||
jsxPlugin(),
|
||||
vuePlugin
|
||||
]
|
||||
} as ViteOptions)
|
||||
|
||||
await ctx.nuxt.callHook('vite:extendConfig', serverConfig, { isClient: false, isServer: true })
|
||||
|
||||
const onBuild = () => ctx.nuxt.callHook('build:resources', wpfs)
|
||||
|
||||
// Production build
|
||||
if (!ctx.nuxt.options.dev) {
|
||||
const start = Date.now()
|
||||
logger.info('Building server...')
|
||||
await vite.build(serverConfig)
|
||||
await onBuild()
|
||||
logger.success(`Server built in ${Date.now() - start}ms`)
|
||||
return
|
||||
}
|
||||
|
||||
// Start development server
|
||||
const viteServer = await vite.createServer(serverConfig)
|
||||
ctx.nuxt.hook('close', () => viteServer.close())
|
||||
|
||||
// Invalidate virtual modules when templates are re-generated
|
||||
ctx.nuxt.hook('app:templatesGenerated', () => {
|
||||
for (const [id, mod] of viteServer.moduleGraph.idToModuleMap) {
|
||||
if (id.startsWith('\x00virtual:')) {
|
||||
viteServer.moduleGraph.invalidateModule(mod)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Initialize plugins
|
||||
await viteServer.pluginContainer.buildStart({})
|
||||
|
||||
// Generate manifest files
|
||||
await fse.writeFile(resolve(ctx.nuxt.options.buildDir, 'dist/server/ssr-manifest.json'), JSON.stringify({}, null, 2), 'utf-8')
|
||||
await generateDevSSRManifest(ctx)
|
||||
|
||||
// Build and watch
|
||||
const _doBuild = async () => {
|
||||
const start = Date.now()
|
||||
const { code, ids } = await bundleRequest({ viteServer }, '/.nuxt/server.js')
|
||||
// Have CSS in the manifest to prevent FOUC on dev SSR
|
||||
await generateDevSSRManifest(ctx, ids.filter(isCSS).map(i => '../' + i.slice(1)))
|
||||
await fse.writeFile(resolve(ctx.nuxt.options.buildDir, 'dist/server/server.mjs'), code, 'utf-8')
|
||||
const time = (Date.now() - start)
|
||||
consola.info(`Server built in ${time}ms`)
|
||||
await onBuild()
|
||||
}
|
||||
const doBuild = debounce(_doBuild)
|
||||
|
||||
// Initial build
|
||||
await _doBuild()
|
||||
|
||||
// Watch
|
||||
viteServer.watcher.on('all', (_event, file) => {
|
||||
if (file.indexOf(ctx.nuxt.options.buildDir) === 0) { return }
|
||||
doBuild()
|
||||
})
|
||||
}
|
@ -1,136 +0,0 @@
|
||||
import hash from 'hash-sum'
|
||||
import { resolve } from 'pathe'
|
||||
|
||||
import type { Nuxt, NuxtApp } from '@nuxt/schema'
|
||||
import { genImport, genObjectFromRawEntries } from 'knitwork'
|
||||
|
||||
type TemplateContext = {
|
||||
nuxt: Nuxt;
|
||||
app: NuxtApp & { templateVars: Record<string, any> };
|
||||
}
|
||||
|
||||
// TODO: Use an alias
|
||||
export const middlewareTemplate = {
|
||||
filename: 'middleware.js',
|
||||
getContents (ctx: TemplateContext) {
|
||||
const { dir, router: { middleware }, srcDir } = ctx.nuxt.options
|
||||
const _middleware = ((typeof middleware !== 'undefined' && middleware) || []).map((m) => {
|
||||
// Normalize string middleware
|
||||
if (typeof m === 'string') {
|
||||
m = { src: m }
|
||||
}
|
||||
return {
|
||||
filePath: resolve(srcDir, dir.middleware, m.src),
|
||||
id: m.name || m.src.replace(/[\\/]/g, '/').replace(/\.(js|ts)$/, '')
|
||||
}
|
||||
})
|
||||
return `${_middleware.map(m => genImport(m.filePath, `$${hash(m.id)}`)).join('\n')}
|
||||
const middleware = ${genObjectFromRawEntries(_middleware.map(m => [m.id, `$${hash(m.id)}`]))}
|
||||
export default middleware`
|
||||
}
|
||||
}
|
||||
|
||||
export const storeTemplate = {
|
||||
filename: 'store.js',
|
||||
getContents (ctx: TemplateContext) {
|
||||
const { dir, srcDir } = ctx.nuxt.options
|
||||
const { templateVars: { storeModules = [] } } = ctx.app
|
||||
const _storeModules = storeModules.map(s => ({
|
||||
filePath: resolve(srcDir, dir.store, s.src),
|
||||
id: (s.src
|
||||
.replace(/\.(js|ts)$/, '')
|
||||
.replace(/[\\/]/g, '/')
|
||||
.replace(/index/, '')
|
||||
) || 'root'
|
||||
}))
|
||||
|
||||
return `import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
${_storeModules.map(s => genImport(s.filePath, { name: '*', as: `$${hash(s.id)}` })).join('\n')}
|
||||
Vue.use(Vuex)
|
||||
|
||||
const VUEX_PROPERTIES = ['state', 'getters', 'actions', 'mutations']
|
||||
|
||||
const storeModules = ${genObjectFromRawEntries(_storeModules.map(m => [m.id, `$${hash(m.id)}`]))}
|
||||
|
||||
export function createStore() {
|
||||
let store = normalizeRoot(storeModules.root || {})
|
||||
delete storeModules.root
|
||||
for (const id in storeModules) {
|
||||
resolveStoreModules(store, storeModules[id], id)
|
||||
}
|
||||
if (typeof store === 'function') {
|
||||
return store
|
||||
}
|
||||
return new Vuex.Store(Object.assign({
|
||||
strict: (process.env.NODE_ENV !== 'production')
|
||||
}, store))
|
||||
}
|
||||
|
||||
function normalizeRoot (moduleData, id) {
|
||||
moduleData = moduleData.default || moduleData
|
||||
if (moduleData.commit) {
|
||||
throw new Error(\`[nuxt] \${id} should export a method that returns a Vuex instance.\`)
|
||||
}
|
||||
if (typeof moduleData !== 'function') {
|
||||
// Avoid TypeError: setting a property that has only a getter when overwriting top level keys
|
||||
moduleData = { ...moduleData }
|
||||
}
|
||||
moduleData.modules = moduleData.modules || {}
|
||||
return moduleData
|
||||
}
|
||||
|
||||
function resolveStoreModules (store, moduleData, id) {
|
||||
moduleData = moduleData.default || moduleData
|
||||
|
||||
const namespaces = id.split('/').filter(Boolean)
|
||||
let moduleName = namespaces[namespaces.length - 1]
|
||||
|
||||
// If src is a known Vuex property
|
||||
if (VUEX_PROPERTIES.includes(moduleName)) {
|
||||
const property = moduleName
|
||||
const propertyStoreModule = getStoreModule(store, namespaces, { isProperty: true })
|
||||
// Replace state since it's a function
|
||||
mergeProperty(propertyStoreModule, moduleData, property)
|
||||
return
|
||||
}
|
||||
|
||||
const storeModule = getStoreModule(store, namespaces)
|
||||
|
||||
for (const property of VUEX_PROPERTIES) {
|
||||
mergeProperty(storeModule, moduleData[property], property)
|
||||
}
|
||||
|
||||
if (moduleData.namespaced === false) {
|
||||
delete storeModule.namespaced
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function getStoreModule (storeModule, namespaces, { isProperty = false } = {}) {
|
||||
// If ./mutations.js
|
||||
if (!namespaces.length || (isProperty && namespaces.length === 1)) {
|
||||
return storeModule
|
||||
}
|
||||
|
||||
const namespace = namespaces.shift()
|
||||
|
||||
storeModule.modules[namespace] = storeModule.modules[namespace] || {}
|
||||
storeModule.modules[namespace].namespaced = true
|
||||
storeModule.modules[namespace].modules = storeModule.modules[namespace].modules || {}
|
||||
|
||||
return getStoreModule(storeModule.modules[namespace], namespaces, { isProperty })
|
||||
}
|
||||
|
||||
function mergeProperty (storeModule, moduleData, property) {
|
||||
if (!moduleData) {
|
||||
return
|
||||
}
|
||||
if (property === 'state') {
|
||||
storeModule.state = moduleData || storeModule.state
|
||||
} else {
|
||||
storeModule[property] = { ...storeModule[property], ...moduleData }
|
||||
}
|
||||
}`
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
import type { InlineConfig, SSROptions } from 'vite'
|
||||
import type { VueViteOptions } from 'vite-plugin-vue2'
|
||||
|
||||
export interface Nuxt {
|
||||
options: any;
|
||||
resolver: any;
|
||||
hook: Function;
|
||||
callHook: Function;
|
||||
}
|
||||
|
||||
export interface ViteOptions extends Omit<InlineConfig, 'build'> {
|
||||
/**
|
||||
* Options for vite-plugin-vue2
|
||||
*
|
||||
* @see https://github.com/underfin/vite-plugin-vue2
|
||||
*/
|
||||
vue?: VueViteOptions
|
||||
|
||||
ssr?: boolean | SSROptions
|
||||
|
||||
build?: boolean | InlineConfig['build']
|
||||
|
||||
experimentWarning?: boolean
|
||||
}
|
||||
|
||||
export interface ViteBuildContext {
|
||||
nuxt: Nuxt;
|
||||
builder: {
|
||||
plugins: { name: string; mode?: 'client' | 'server'; src: string; }[];
|
||||
};
|
||||
config: ViteOptions;
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
import { promises as fsp, readdirSync, statSync } from 'fs'
|
||||
import { join } from 'pathe'
|
||||
|
||||
export function readDirRecursively (dir: string) {
|
||||
return readdirSync(dir).reduce((files, file) => {
|
||||
const name = join(dir, file)
|
||||
const isDirectory = statSync(name).isDirectory()
|
||||
return isDirectory ? [...files, ...readDirRecursively(name)] : [...files, name]
|
||||
}, [])
|
||||
}
|
||||
|
||||
export async function isDirectory (path: string) {
|
||||
try {
|
||||
return (await fsp.stat(path)).isDirectory()
|
||||
} catch (_err) {
|
||||
return false
|
||||
}
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
import { join } from 'pathe'
|
||||
import fsExtra from 'fs-extra'
|
||||
|
||||
export const wpfs = {
|
||||
...fsExtra,
|
||||
join
|
||||
} as any
|
@ -1,157 +0,0 @@
|
||||
import { resolve } from 'pathe'
|
||||
import * as vite from 'vite'
|
||||
import { isIgnored, logger } from '@nuxt/kit'
|
||||
import { sanitizeFilePath } from 'mlly'
|
||||
import { getPort } from 'get-port-please'
|
||||
import { joinURL, withoutLeadingSlash } from 'ufo'
|
||||
import { distDir } from '../dirs'
|
||||
import { warmupViteServer } from '../../../vite/src/utils/warmup'
|
||||
import { DynamicBasePlugin } from '../../../vite/src/plugins/dynamic-base'
|
||||
import { buildClient } from './client'
|
||||
import { buildServer } from './server'
|
||||
import { defaultExportPlugin } from './plugins/default-export'
|
||||
import { jsxPlugin } from './plugins/jsx'
|
||||
import { replace } from './plugins/replace'
|
||||
import { resolveCSSOptions } from './css'
|
||||
import type { Nuxt, ViteBuildContext, ViteOptions } from './types'
|
||||
import { prepareManifests } from './manifest'
|
||||
|
||||
async function bundle (nuxt: Nuxt, builder: any) {
|
||||
for (const p of builder.plugins) {
|
||||
p.src = nuxt.resolver.resolvePath(resolve(nuxt.options.buildDir, p.src))
|
||||
}
|
||||
|
||||
const hmrPortDefault = 24678 // Vite's default HMR port
|
||||
const hmrPort = await getPort({
|
||||
port: hmrPortDefault,
|
||||
ports: Array.from({ length: 20 }, (_, i) => hmrPortDefault + 1 + i)
|
||||
})
|
||||
|
||||
const ctx: ViteBuildContext = {
|
||||
nuxt,
|
||||
builder,
|
||||
config: vite.mergeConfig(
|
||||
{
|
||||
// defaults from packages/schema/src/config/vite
|
||||
root: nuxt.options.srcDir,
|
||||
mode: nuxt.options.dev ? 'development' : 'production',
|
||||
logLevel: 'warn',
|
||||
base: nuxt.options.dev
|
||||
? joinURL(nuxt.options.app.baseURL, nuxt.options.app.buildAssetsDir)
|
||||
: '/__NUXT_BASE__/',
|
||||
publicDir: resolve(nuxt.options.rootDir, nuxt.options.srcDir, nuxt.options.dir.static),
|
||||
vue: {
|
||||
isProduction: !nuxt.options.dev,
|
||||
template: {
|
||||
compilerOptions: nuxt.options.vue.compilerOptions
|
||||
}
|
||||
},
|
||||
esbuild: {
|
||||
jsxFactory: 'h',
|
||||
jsxFragment: 'Fragment',
|
||||
tsconfigRaw: '{}'
|
||||
},
|
||||
clearScreen: false,
|
||||
define: {
|
||||
'process.dev': nuxt.options.dev,
|
||||
'process.static': nuxt.options.target === 'static',
|
||||
'process.env.NODE_ENV': JSON.stringify(nuxt.options.dev ? 'development' : 'production'),
|
||||
'process.mode': JSON.stringify(nuxt.options.dev ? 'development' : 'production'),
|
||||
'process.target': JSON.stringify(nuxt.options.target)
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'],
|
||||
alias: {
|
||||
...nuxt.options.alias,
|
||||
'#build': nuxt.options.buildDir,
|
||||
'.nuxt': nuxt.options.buildDir,
|
||||
'/entry.mjs': resolve(nuxt.options.buildDir, 'client.js'),
|
||||
'web-streams-polyfill/ponyfill/es2018': resolve(distDir, 'runtime/vite/mock/web-streams-polyfill.mjs'),
|
||||
'whatwg-url': resolve(distDir, 'runtime/vite/mock/whatwg-url.mjs'),
|
||||
// Cannot destructure property 'AbortController' of ..
|
||||
'abort-controller': resolve(distDir, 'runtime/vite/mock/abort-controller.mjs')
|
||||
}
|
||||
},
|
||||
optimizeDeps: {
|
||||
exclude: [
|
||||
...nuxt.options.build.transpile.filter(i => typeof i === 'string'),
|
||||
'vue-demi',
|
||||
'ufo',
|
||||
'date-fns',
|
||||
'nanoid',
|
||||
'vue'
|
||||
// TODO(Anthony): waiting for Vite's fix https://github.com/vitejs/vite/issues/5688
|
||||
// ...nuxt.options.build.transpile.filter(i => typeof i === 'string'),
|
||||
// 'vue-demi'
|
||||
]
|
||||
},
|
||||
css: resolveCSSOptions(nuxt),
|
||||
build: {
|
||||
assetsDir: nuxt.options.dev ? withoutLeadingSlash(nuxt.options.app.buildAssetsDir) : '.',
|
||||
emptyOutDir: false,
|
||||
rollupOptions: {
|
||||
output: { sanitizeFileName: sanitizeFilePath }
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
replace({
|
||||
__webpack_public_path__: 'globalThis.__webpack_public_path__'
|
||||
}),
|
||||
jsxPlugin(),
|
||||
DynamicBasePlugin.vite(),
|
||||
defaultExportPlugin()
|
||||
],
|
||||
server: {
|
||||
watch: {
|
||||
ignored: isIgnored
|
||||
},
|
||||
hmr: {
|
||||
// https://github.com/nuxt/framework/issues/4191
|
||||
protocol: 'ws',
|
||||
clientPort: hmrPort,
|
||||
port: hmrPort
|
||||
},
|
||||
fs: {
|
||||
strict: false,
|
||||
allow: [
|
||||
nuxt.options.buildDir,
|
||||
nuxt.options.srcDir,
|
||||
nuxt.options.rootDir,
|
||||
...nuxt.options.modulesDir
|
||||
]
|
||||
}
|
||||
}
|
||||
} as ViteOptions,
|
||||
nuxt.options.vite
|
||||
)
|
||||
}
|
||||
|
||||
await ctx.nuxt.callHook('vite:extend', ctx)
|
||||
|
||||
if (nuxt.options.dev) {
|
||||
ctx.nuxt.hook('vite:serverCreated', (server: vite.ViteDevServer) => {
|
||||
const start = Date.now()
|
||||
warmupViteServer(server, ['/.nuxt/entry.mjs']).then(() => {
|
||||
logger.info(`Vite warmed up in ${Date.now() - start}ms`)
|
||||
}).catch(logger.error)
|
||||
})
|
||||
}
|
||||
|
||||
await buildClient(ctx)
|
||||
await prepareManifests(ctx)
|
||||
await buildServer(ctx)
|
||||
}
|
||||
|
||||
export class ViteBuilder {
|
||||
builder: any
|
||||
nuxt: Nuxt
|
||||
|
||||
constructor (builder: any) {
|
||||
this.builder = builder
|
||||
this.nuxt = builder.nuxt
|
||||
}
|
||||
|
||||
build () {
|
||||
return bundle(this.nuxt, this.builder)
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
import { pathToFileURL } from 'url'
|
||||
import MagicString from 'magic-string'
|
||||
import { findStaticImports } from 'mlly'
|
||||
import { parseQuery, parseURL } from 'ufo'
|
||||
import { createUnplugin } from 'unplugin'
|
||||
|
||||
export const VueCompat = createUnplugin((opts: { src?: string }) => {
|
||||
return {
|
||||
name: 'nuxt-legacy-vue-transform',
|
||||
enforce: 'post',
|
||||
transformInclude (id) {
|
||||
if (id.includes('vue2-bridge')) { return false }
|
||||
|
||||
const { pathname, search } = parseURL(decodeURIComponent(pathToFileURL(id).href))
|
||||
const query = parseQuery(search)
|
||||
|
||||
// vue files
|
||||
if (pathname.endsWith('.vue') && (query.type === 'script' || !search)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// js files
|
||||
if (pathname.match(/\.((c|m)?j|t)sx?/g)) {
|
||||
return true
|
||||
}
|
||||
},
|
||||
transform (code, id) {
|
||||
if (id.includes('vue2-bridge')) { return }
|
||||
|
||||
const s = new MagicString(code)
|
||||
const imports = findStaticImports(code).filter(i => i.type === 'static' && vueAliases.includes(i.specifier))
|
||||
|
||||
for (const i of imports) {
|
||||
s.overwrite(i.start, i.end, i.code.replace(`"${i.specifier}"`, `"${opts.src}"`).replace(`'${i.specifier}'`, `'${opts.src}'`))
|
||||
}
|
||||
|
||||
if (s.hasChanged()) {
|
||||
return {
|
||||
code: s.toString(),
|
||||
map: s.generateMap({ source: id, includeContent: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const vueAliases = [
|
||||
// vue
|
||||
'vue',
|
||||
// vue 3 helper packages
|
||||
'@vue/shared',
|
||||
'@vue/reactivity',
|
||||
'@vue/runtime-core',
|
||||
'@vue/runtime-dom',
|
||||
// vue-demi
|
||||
'vue-demi',
|
||||
...[
|
||||
// vue 2 dist files
|
||||
'vue/dist/vue.common.dev',
|
||||
'vue/dist/vue.common',
|
||||
'vue/dist/vue.common.prod',
|
||||
'vue/dist/vue.esm.browser',
|
||||
'vue/dist/vue.esm.browser.min',
|
||||
'vue/dist/vue.esm',
|
||||
'vue/dist/vue',
|
||||
'vue/dist/vue.min',
|
||||
'vue/dist/vue.runtime.common.dev',
|
||||
'vue/dist/vue.runtime.common',
|
||||
'vue/dist/vue.runtime.common.prod',
|
||||
'vue/dist/vue.runtime.esm',
|
||||
'vue/dist/vue.runtime',
|
||||
'vue/dist/vue.runtime.min'
|
||||
].flatMap(m => [m, `${m}.js`])
|
||||
]
|
36
packages/bridge/types.d.ts
vendored
36
packages/bridge/types.d.ts
vendored
@ -1,36 +0,0 @@
|
||||
/// <reference types="nitropack" />
|
||||
import type { NuxtConfig as _NuxtConfig } from '@nuxt/schema'
|
||||
import type { MetaInfo } from 'vue-meta'
|
||||
import type { PluginOptions as ScriptSetupPluginOptions } from 'unplugin-vue2-script-setup/dist'
|
||||
|
||||
export type ScriptSetupOptions = ScriptSetupPluginOptions
|
||||
|
||||
export interface BridgeConfig {
|
||||
nitro: boolean
|
||||
vite: boolean
|
||||
app: boolean | {}
|
||||
capi: boolean | {
|
||||
legacy?: boolean
|
||||
}
|
||||
scriptSetup: boolean | ScriptSetupOptions
|
||||
autoImports: boolean
|
||||
transpile: boolean
|
||||
compatibility: boolean
|
||||
postcss8: boolean
|
||||
resolve: boolean
|
||||
typescript: boolean
|
||||
meta: boolean | null
|
||||
}
|
||||
|
||||
// TODO: Also inherit from @nuxt/types.NuxtConfig for legacy type compat
|
||||
export interface NuxtConfig extends _NuxtConfig {
|
||||
head?: _NuxtConfig['head'] | MetaInfo
|
||||
}
|
||||
|
||||
declare module '@nuxt/schema' {
|
||||
interface NuxtConfig {
|
||||
bridge?: Partial<BridgeConfig> | false
|
||||
}
|
||||
}
|
||||
|
||||
export declare function defineNuxtConfig(config: NuxtConfig): NuxtConfig
|
@ -2,13 +2,6 @@
|
||||
"extends": [
|
||||
"@nuxtjs"
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchPaths": ["**/bridge/**"],
|
||||
"matchPackageNames": ["vue", "vue-router"],
|
||||
"enabled": false
|
||||
}
|
||||
],
|
||||
"ignoreDeps": [
|
||||
"docus",
|
||||
"@docus/app",
|
||||
|
@ -1,113 +0,0 @@
|
||||
import { fileURLToPath } from 'url'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { setup, $fetch, fetch, startServer } from '@nuxt/test-utils'
|
||||
|
||||
// Moving to nuxt/bridge soon
|
||||
describe.skip('fixtures:bridge', async () => {
|
||||
await setup({
|
||||
rootDir: fileURLToPath(new URL('./fixtures/bridge', import.meta.url)),
|
||||
server: true
|
||||
})
|
||||
|
||||
describe('pages', () => {
|
||||
it('render hello world', async () => {
|
||||
expect(await $fetch('/')).to.contain('Hello Vue 2!')
|
||||
})
|
||||
it('uses server Vue build', async () => {
|
||||
expect(await $fetch('/')).to.contain('Rendered on server: true')
|
||||
})
|
||||
})
|
||||
|
||||
describe('navigate', () => {
|
||||
it('should redirect to index with navigateTo', async () => {
|
||||
const html = await $fetch('/navigate-to/')
|
||||
expect(html).toContain('Hello Vue 2!')
|
||||
})
|
||||
})
|
||||
|
||||
describe('errors', () => {
|
||||
it('should render a JSON error page', async () => {
|
||||
const res = await fetch('/error', {
|
||||
headers: {
|
||||
accept: 'application/json'
|
||||
}
|
||||
})
|
||||
expect(res.status).toBe(500)
|
||||
const error = await res.json()
|
||||
delete error.stack
|
||||
expect(error).toMatchObject({
|
||||
description: process.env.NUXT_TEST_DEV ? expect.stringContaining('<pre>') : '',
|
||||
message: 'This is a custom error',
|
||||
statusCode: 500,
|
||||
statusMessage: 'Internal Server Error',
|
||||
url: '/error'
|
||||
})
|
||||
})
|
||||
|
||||
it('should render a HTML error page', async () => {
|
||||
const res = await fetch('/error')
|
||||
expect(await res.text()).toContain('This is a custom error')
|
||||
})
|
||||
})
|
||||
|
||||
describe('dynamic paths', () => {
|
||||
if (process.env.NUXT_TEST_DEV) {
|
||||
// TODO:
|
||||
it.todo('dynamic paths in dev')
|
||||
return
|
||||
}
|
||||
if (process.env.TEST_WITH_WEBPACK) {
|
||||
// TODO:
|
||||
it.todo('work with webpack')
|
||||
return
|
||||
}
|
||||
it('should work with no overrides', async () => {
|
||||
const html = await $fetch('/assets')
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"/g)) {
|
||||
const url = match[2]
|
||||
expect(url.startsWith('/_nuxt/') || url === '/public.svg').toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
it('adds relative paths to CSS', async () => {
|
||||
const html = await $fetch('/assets')
|
||||
const urls = Array.from(html.matchAll(/(href|src)="(.*?)"/g)).map(m => m[2])
|
||||
const cssURL = urls.find(u => /_nuxt\/assets.*\.css$/.test(u))
|
||||
const css = await $fetch(cssURL)
|
||||
const imageUrls = Array.from(css.matchAll(/url\(([^)]*)\)/g)).map(m => m[1].replace(/[-.][\w]{8}\./g, '.'))
|
||||
expect(imageUrls).toMatchInlineSnapshot(`
|
||||
[
|
||||
"./logo.svg",
|
||||
"../public.svg",
|
||||
]
|
||||
`)
|
||||
})
|
||||
|
||||
it('should allow setting base URL and build assets directory', async () => {
|
||||
process.env.NUXT_APP_BUILD_ASSETS_DIR = '/_other/'
|
||||
process.env.NUXT_APP_BASE_URL = '/foo/'
|
||||
await startServer()
|
||||
|
||||
const html = await $fetch('/foo/assets')
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"/g)) {
|
||||
const url = match[2]
|
||||
// TODO: should be /foo/public.svg
|
||||
expect(url.startsWith('/foo/_other/') || url === '/public.svg').toBeTruthy()
|
||||
}
|
||||
})
|
||||
|
||||
it('should allow setting CDN URL', async () => {
|
||||
process.env.NUXT_APP_BASE_URL = '/foo/'
|
||||
process.env.NUXT_APP_CDN_URL = 'https://example.com/'
|
||||
process.env.NUXT_APP_BUILD_ASSETS_DIR = '/_cdn/'
|
||||
await startServer()
|
||||
|
||||
const html = await $fetch('/foo/assets')
|
||||
for (const match of html.matchAll(/(href|src)="(.*?)"/g)) {
|
||||
const url = match[2]
|
||||
// TODO: should be https://example.com/public.svg
|
||||
expect(url.startsWith('https://example.com/_cdn/') || url === '/public.svg').toBeTruthy()
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
18
test/fixtures/bridge/assets/logo.svg
vendored
18
test/fixtures/bridge/assets/logo.svg
vendored
@ -1,18 +0,0 @@
|
||||
<svg viewBox="0 0 221 65" fill="none" xmlns="http://www.w3.org/2000/svg" class="h-8">
|
||||
<g clip-path="url(#a)">
|
||||
<path fill="currentColor"
|
||||
d="M82.5623 18.5705h7.3017l15.474 24.7415V18.5705h6.741v35.0576h-7.252L89.3025 28.938v24.6901h-6.7402V18.5705ZM142.207 53.628h-6.282v-3.916c-1.429 2.7559-4.339 4.3076-8.015 4.3076-5.822 0-9.603-4.1069-9.603-10.0175V28.3847h6.282v14.3251c0 3.4558 2.146 5.8592 5.362 5.8592 3.524 0 5.974-2.7044 5.974-6.4099V28.3847h6.282V53.628ZM164.064 53.2289l-6.026-8.4144-6.027 8.4144h-6.69l9.296-13.1723-8.58-12.0709h6.843l5.158 7.2641 5.106-7.2641h6.895l-8.632 12.0709 9.295 13.1723h-6.638ZM183.469 20.7726v7.6116h7.149v5.1593h-7.149v12.5311c0 .4208.17.8245.473 1.1223.303.2978.715.4654 1.144.4661h5.532v5.9547h-4.137c-5.617 0-9.293-3.2062-9.293-8.8109V33.5484h-5.056v-5.1642h3.172c1.479 0 2.34-.8639 2.34-2.2932v-5.3184h5.825Z">
|
||||
</path>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M30.1185 11.5456c-1.8853-3.24168-6.5987-3.24169-8.484 0L1.08737 46.8747c-1.885324 3.2417.47133 7.2938 4.24199 7.2938H21.3695c-1.6112-1.4081-2.2079-3.8441-.9886-5.9341l15.5615-26.675-5.8239-10.0138Z"
|
||||
fill="#80EEC0"></path>
|
||||
<path
|
||||
d="M43.1374 19.2952c1.5603-2.6523 5.461-2.6523 7.0212 0l17.0045 28.9057c1.5603 2.6522-.39 5.9676-3.5106 5.9676h-34.009c-3.1206 0-5.0709-3.3154-3.5106-5.9676l17.0045-28.9057ZM209.174 53.8005H198.483c0-1.8514.067-3.4526 0-6.0213h10.641c1.868 0 3.353.1001 4.354-.934 1-1.0341 1.501-2.3351 1.501-3.9029 0-1.8347-.667-3.2191-2.002-4.1532-1.301-.9674-2.985-1.4511-5.054-1.4511h-2.601v-5.2539h2.652c1.701 0 3.119-.4003 4.253-1.2009 1.134-.8006 1.701-1.9849 1.701-3.5527 0-1.301-.434-2.3351-1.301-3.1023-.834-.8007-2.001-1.201-3.503-1.201-1.634 0-2.918.4837-3.853 1.4511-.9.9674-1.401 2.1517-1.501 3.5527h-6.254c.133-3.2358 1.251-5.7877 3.352-7.6558 2.135-1.868 4.887-2.8021 8.256-2.8021 2.402 0 4.42.4337 6.055 1.301 1.668.834 2.919 1.9515 3.753 3.3525.867 1.4011 1.301 2.9523 1.301 4.6536 0 1.9681-.551 3.636-1.651 5.0037-1.068 1.3344-2.402 2.235-4.004 2.7021 1.969.4003 3.57 1.3677 4.804 2.9022 1.234 1.5011 1.852 3.4025 1.852 5.7043 0 1.9347-.468 3.7028-1.402 5.304-.934 1.6012-2.301 2.8855-4.103 3.8529-1.768.9674-3.953 1.4511-6.555 1.4511Z"
|
||||
fill="#00DC82"></path>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<path fill="#fff" d="M0 0h221v65H0z"></path>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.3 KiB |
20
test/fixtures/bridge/components/FetchTest.vue
vendored
20
test/fixtures/bridge/components/FetchTest.vue
vendored
@ -1,20 +0,0 @@
|
||||
<template>
|
||||
<tr><td><b>useFetch</b></td><td> {{ fetched }}</td></tr>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, useFetch } from '@nuxtjs/composition-api'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const fetched = ref('🚧')
|
||||
useFetch(() => {
|
||||
fetched.value = '✅'
|
||||
})
|
||||
|
||||
return {
|
||||
fetched
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
16
test/fixtures/bridge/layouts/default.vue
vendored
16
test/fixtures/bridge/layouts/default.vue
vendored
@ -1,16 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<Nuxt />
|
||||
<hr>
|
||||
Route: {{ route.path }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
setup () {
|
||||
const route = useRoute()
|
||||
return { route }
|
||||
}
|
||||
}
|
||||
</script>
|
11
test/fixtures/bridge/layouts/error.vue
vendored
11
test/fixtures/bridge/layouts/error.vue
vendored
@ -1,11 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
{{ error.message || 'An error occurred' }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: ['error']
|
||||
}
|
||||
</script>
|
25
test/fixtures/bridge/nuxt.config.ts
vendored
25
test/fixtures/bridge/nuxt.config.ts
vendored
@ -1,25 +0,0 @@
|
||||
import { defineNuxtConfig } from '@nuxt/bridge'
|
||||
|
||||
// @ts-ignore
|
||||
global.__NUXT_PREPATHS__ = (global.__NUXT_PREPATHS__ || []).concat(__dirname)
|
||||
|
||||
export default defineNuxtConfig({
|
||||
components: true,
|
||||
serverMiddleware: [
|
||||
{
|
||||
handle (req, _res, next) {
|
||||
req.spa = req.url.includes('?spa')
|
||||
next()
|
||||
}
|
||||
}
|
||||
],
|
||||
buildDir: process.env.NITRO_BUILD_DIR,
|
||||
plugins: ['~/plugins/setup.js'],
|
||||
nitro: {
|
||||
output: { dir: process.env.NITRO_OUTPUT_DIR }
|
||||
},
|
||||
bridge: {
|
||||
meta: true,
|
||||
vite: !process.env.TEST_WITH_WEBPACK
|
||||
}
|
||||
})
|
16
test/fixtures/bridge/package.json
vendored
16
test/fixtures/bridge/package.json
vendored
@ -1,16 +0,0 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "fixture-basic",
|
||||
"scripts": {
|
||||
"dev": "nuxi dev",
|
||||
"build": "nuxi build"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/bridge": "*",
|
||||
"nuxt-edge": "latest",
|
||||
"vue": "^2"
|
||||
},
|
||||
"installConfig": {
|
||||
"hoistingLimits": "workspaces"
|
||||
}
|
||||
}
|
20
test/fixtures/bridge/pages/assets.vue
vendored
20
test/fixtures/bridge/pages/assets.vue
vendored
@ -1,20 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<img src="~/assets/logo.svg" class="h-20 mb-4">
|
||||
<img src="/public.svg" class="h-20 mb-4">
|
||||
<img :src="logo" class="h-20 mb-4">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import logo from '~/assets/logo.svg'
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#__nuxt {
|
||||
background-image: url('~/assets/logo.svg');
|
||||
}
|
||||
body {
|
||||
background-image: url('/public.svg');
|
||||
}
|
||||
</style>
|
7
test/fixtures/bridge/pages/error.vue
vendored
7
test/fixtures/bridge/pages/error.vue
vendored
@ -1,7 +0,0 @@
|
||||
<template>
|
||||
<div />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
throw new Error('This is a custom error')
|
||||
</script>
|
20
test/fixtures/bridge/pages/index.vue
vendored
20
test/fixtures/bridge/pages/index.vue
vendored
@ -1,20 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>Hello Vue {{ version }}!</div>
|
||||
<div>
|
||||
State: {{ state }} <button @click="updateState">
|
||||
Update
|
||||
</button>
|
||||
</div>
|
||||
Rendered on server: {{ serverBuild }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
useMeta({ meta: [{ name: 'description', content: 'This is a page to demo Nuxt Bridge.' }] })
|
||||
const version = ref('2')
|
||||
const state = useState('test-state')
|
||||
state.value = '123'
|
||||
const updateState = () => { state.value = '456' }
|
||||
const serverBuild = useState('server-build', () => getCurrentInstance().proxy.$isServer)
|
||||
</script>
|
88
test/fixtures/bridge/pages/legacy-capi.vue
vendored
88
test/fixtures/bridge/pages/legacy-capi.vue
vendored
@ -1,88 +0,0 @@
|
||||
<template>
|
||||
<table>
|
||||
<tbody>
|
||||
<!-- Basic setup function -->
|
||||
<tr><td><b>setup</b></td><td> {{ setup }}</td></tr>
|
||||
<!-- Ref -->
|
||||
<tr>
|
||||
<td>
|
||||
<b>ref</b>
|
||||
</td>
|
||||
<td>
|
||||
{{ ref }}
|
||||
</td>
|
||||
<td>
|
||||
<button @click="ref = '❇️'">
|
||||
update
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Lifecycle methods -->
|
||||
<tr><td><b>onMounted</b></td><td> {{ mounted }}</td></tr>
|
||||
<!-- Wrappers -->
|
||||
<tr><td><b>useStore</b></td><td> {{ store.state.test }}</td></tr>
|
||||
<tr><td><b>useRoute</b></td><td> {{ route.path === '/legacy-capi' ? '✅' : '❌' }}</td></tr>
|
||||
<tr><td><b>useContext</b></td><td> {{ Object.keys(context).length ? '✅' : '❌' }}</td></tr>
|
||||
<!-- Helpers -->
|
||||
<tr><td><b>useAsync</b></td><td> {{ async }}</td></tr>
|
||||
<tr><td><b>ssrRef</b></td><td> {{ ssrRef }}</td></tr>
|
||||
<tr><td><b>shallowSsrRef</b></td><td> {{ shallow }}</td></tr>
|
||||
<tr><td><b>ssrPromise</b></td><td> {{ promise }}</td></tr>
|
||||
<tr>
|
||||
<td><b>useMeta</b></td><td> {{ title }}</td>
|
||||
<td>
|
||||
<button @click="title = '❇️'">
|
||||
update
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td><b>onGlobalSetup</b></td><td> {{ globalsetup }}</td></tr>
|
||||
<FetchTest />
|
||||
<tr><td><b>reqSsrRef</b></td><td> {{ '⛔️' }}</td></tr>
|
||||
<tr><td><b>reqRef</b></td><td> {{ '⛔️' }}</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { useRoute, useContext, useStore, useAsync, ssrRef, shallowSsrRef, ssrPromise, useMeta } from '@nuxtjs/composition-api'
|
||||
|
||||
export default defineComponent({
|
||||
setup () {
|
||||
const mounted = ref()
|
||||
const shallow = shallowSsrRef('❌')
|
||||
const { isHMR, $globalsetup } = useContext()
|
||||
const { title } = useMeta()
|
||||
if (process.server || isHMR) {
|
||||
shallow.value = '✅'
|
||||
title.value = '❌'
|
||||
}
|
||||
const promise = ref(null)
|
||||
ssrPromise(() => new Promise(resolve => setTimeout(() => resolve(process.server || isHMR ? '✅' : '❌'), 100))).then((r) => {
|
||||
promise.value = r
|
||||
})
|
||||
onMounted(() => {
|
||||
mounted.value = '✅'
|
||||
title.value = '✅'
|
||||
})
|
||||
const store = useStore()
|
||||
const route = useRoute()
|
||||
|
||||
return {
|
||||
setup: '✅',
|
||||
ref: ref('✅'),
|
||||
mounted,
|
||||
store,
|
||||
route,
|
||||
context: useContext(),
|
||||
async: useAsync(() => new Promise(resolve => setTimeout(() => resolve(process.server || isHMR ? '✅' : '❌'), 100))),
|
||||
ssrRef: ssrRef(() => process.server || isHMR ? '✅' : '❌'),
|
||||
shallow,
|
||||
promise,
|
||||
title,
|
||||
globalsetup: $globalsetup
|
||||
}
|
||||
},
|
||||
head: {}
|
||||
})
|
||||
</script>
|
7
test/fixtures/bridge/pages/navigate-to.vue
vendored
7
test/fixtures/bridge/pages/navigate-to.vue
vendored
@ -1,7 +0,0 @@
|
||||
<template>
|
||||
<div>You should not see me</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
navigateTo('/', { replace: true })
|
||||
</script>
|
7
test/fixtures/bridge/pages/test/_slug.vue
vendored
7
test/fixtures/bridge/pages/test/_slug.vue
vendored
@ -1,7 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<nuxt-link :to="`/test/${Math.random() * 100}`">
|
||||
Random path
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</template>
|
15
test/fixtures/bridge/plugins/setup.js
vendored
15
test/fixtures/bridge/plugins/setup.js
vendored
@ -1,15 +0,0 @@
|
||||
import { onGlobalSetup, ref } from '@nuxtjs/composition-api'
|
||||
|
||||
import { defineNuxtPlugin } from '#app'
|
||||
|
||||
export default defineNuxtPlugin(() => {
|
||||
const globalsetup = ref('🚧')
|
||||
onGlobalSetup(() => {
|
||||
globalsetup.value = '✅'
|
||||
})
|
||||
return {
|
||||
provide: {
|
||||
globalsetup
|
||||
}
|
||||
}
|
||||
})
|
1
test/fixtures/bridge/server/api/hello.ts
vendored
1
test/fixtures/bridge/server/api/hello.ts
vendored
@ -1 +0,0 @@
|
||||
export default () => 'Hello API'
|
4
test/fixtures/bridge/server/api/hey/index.ts
vendored
4
test/fixtures/bridge/server/api/hey/index.ts
vendored
@ -1,4 +0,0 @@
|
||||
export default () => ({
|
||||
foo: 'bar',
|
||||
baz: 'qux'
|
||||
})
|
18
test/fixtures/bridge/static/public.svg
vendored
18
test/fixtures/bridge/static/public.svg
vendored
@ -1,18 +0,0 @@
|
||||
<svg viewBox="0 0 221 65" fill="none" xmlns="http://www.w3.org/2000/svg" class="h-8">
|
||||
<g clip-path="url(#a)">
|
||||
<path fill="currentColor"
|
||||
d="M82.5623 18.5705h7.3017l15.474 24.7415V18.5705h6.741v35.0576h-7.252L89.3025 28.938v24.6901h-6.7402V18.5705ZM142.207 53.628h-6.282v-3.916c-1.429 2.7559-4.339 4.3076-8.015 4.3076-5.822 0-9.603-4.1069-9.603-10.0175V28.3847h6.282v14.3251c0 3.4558 2.146 5.8592 5.362 5.8592 3.524 0 5.974-2.7044 5.974-6.4099V28.3847h6.282V53.628ZM164.064 53.2289l-6.026-8.4144-6.027 8.4144h-6.69l9.296-13.1723-8.58-12.0709h6.843l5.158 7.2641 5.106-7.2641h6.895l-8.632 12.0709 9.295 13.1723h-6.638ZM183.469 20.7726v7.6116h7.149v5.1593h-7.149v12.5311c0 .4208.17.8245.473 1.1223.303.2978.715.4654 1.144.4661h5.532v5.9547h-4.137c-5.617 0-9.293-3.2062-9.293-8.8109V33.5484h-5.056v-5.1642h3.172c1.479 0 2.34-.8639 2.34-2.2932v-5.3184h5.825Z">
|
||||
</path>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd"
|
||||
d="M30.1185 11.5456c-1.8853-3.24168-6.5987-3.24169-8.484 0L1.08737 46.8747c-1.885324 3.2417.47133 7.2938 4.24199 7.2938H21.3695c-1.6112-1.4081-2.2079-3.8441-.9886-5.9341l15.5615-26.675-5.8239-10.0138Z"
|
||||
fill="#80EEC0"></path>
|
||||
<path
|
||||
d="M43.1374 19.2952c1.5603-2.6523 5.461-2.6523 7.0212 0l17.0045 28.9057c1.5603 2.6522-.39 5.9676-3.5106 5.9676h-34.009c-3.1206 0-5.0709-3.3154-3.5106-5.9676l17.0045-28.9057ZM209.174 53.8005H198.483c0-1.8514.067-3.4526 0-6.0213h10.641c1.868 0 3.353.1001 4.354-.934 1-1.0341 1.501-2.3351 1.501-3.9029 0-1.8347-.667-3.2191-2.002-4.1532-1.301-.9674-2.985-1.4511-5.054-1.4511h-2.601v-5.2539h2.652c1.701 0 3.119-.4003 4.253-1.2009 1.134-.8006 1.701-1.9849 1.701-3.5527 0-1.301-.434-2.3351-1.301-3.1023-.834-.8007-2.001-1.201-3.503-1.201-1.634 0-2.918.4837-3.853 1.4511-.9.9674-1.401 2.1517-1.501 3.5527h-6.254c.133-3.2358 1.251-5.7877 3.352-7.6558 2.135-1.868 4.887-2.8021 8.256-2.8021 2.402 0 4.42.4337 6.055 1.301 1.668.834 2.919 1.9515 3.753 3.3525.867 1.4011 1.301 2.9523 1.301 4.6536 0 1.9681-.551 3.636-1.651 5.0037-1.068 1.3344-2.402 2.235-4.004 2.7021 1.969.4003 3.57 1.3677 4.804 2.9022 1.234 1.5011 1.852 3.4025 1.852 5.7043 0 1.9347-.468 3.7028-1.402 5.304-.934 1.6012-2.301 2.8855-4.103 3.8529-1.768.9674-3.953 1.4511-6.555 1.4511Z"
|
||||
fill="#00DC82"></path>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<path fill="#fff" d="M0 0h221v65H0z"></path>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.3 KiB |
9
test/fixtures/bridge/store/index.js
vendored
9
test/fixtures/bridge/store/index.js
vendored
@ -1,9 +0,0 @@
|
||||
export const state = () => ({
|
||||
test: '❌'
|
||||
})
|
||||
|
||||
export const actions = {
|
||||
nuxtServerInit ({ state }) {
|
||||
state.test = '✅'
|
||||
}
|
||||
}
|
3
test/fixtures/bridge/tsconfig.json
vendored
3
test/fixtures/bridge/tsconfig.json
vendored
@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "./.nuxt/tsconfig.json"
|
||||
}
|
Loading…
Reference in New Issue
Block a user