chore: move bridge to nuxt/bridge (#4305)

This commit is contained in:
pooya parsa 2022-04-13 13:43:42 +02:00 committed by GitHub
parent 70610d8858
commit f91f987401
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
76 changed files with 195 additions and 11991 deletions

View File

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

View File

@ -1,6 +1,6 @@
name: "\U0001F41E Bug report (Nuxt 3)" name: "\U0001F41E Bug report"
description: Create a report to help us improve Nuxt 3 description: Create a report to help us improve Nuxt
labels: ["pending triage", "nuxt3"] labels: ["pending triage"]
body: body:
- type: markdown - type: markdown
attributes: attributes:

View File

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

View File

@ -1,2 +0,0 @@
#!/usr/bin/env node
import 'nuxi/cli'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@
../../../nuxt3/src/app/composables/asyncData.ts

View File

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

View File

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

View File

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

View File

@ -1 +0,0 @@
../../../nuxt3/src/app/composables/cookie.ts

View File

@ -1,5 +0,0 @@
export default (ctx) => {
if (ctx.ssrContext.error) {
ctx.error(ctx.ssrContext.error)
}
}

View File

@ -1 +0,0 @@
../../../nuxt3/src/app/composables/fetch.ts

View File

@ -1 +0,0 @@
../../../nuxt3/src/head/runtime

View File

@ -1,2 +0,0 @@
export * from './app'
export * from './composables'

View File

@ -1 +0,0 @@
../../../nuxt3/src/core/runtime/nitro

View File

@ -1,7 +0,0 @@
import { $fetch } from 'ohmyfetch'
if (!globalThis.$fetch) {
globalThis.$fetch = $fetch
}
export default () => {}

View File

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

View File

@ -1 +0,0 @@
../../../nuxt3/src/app/composables/ssr.ts

View File

@ -1 +0,0 @@
export const AbortController = null

View File

@ -1 +0,0 @@
export const ReadableStream = null

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +0,0 @@
import { join } from 'pathe'
import fsExtra from 'fs-extra'
export const wpfs = {
...fsExtra,
join
} as any

View File

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

View File

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

View File

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

View File

@ -2,13 +2,6 @@
"extends": [ "extends": [
"@nuxtjs" "@nuxtjs"
], ],
"packageRules": [
{
"matchPaths": ["**/bridge/**"],
"matchPackageNames": ["vue", "vue-router"],
"enabled": false
}
],
"ignoreDeps": [ "ignoreDeps": [
"docus", "docus",
"@docus/app", "@docus/app",

View File

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

View File

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

View File

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

View File

@ -1,16 +0,0 @@
<template>
<div>
<Nuxt />
<hr>
Route: {{ route.path }}
</div>
</template>
<script>
export default {
setup () {
const route = useRoute()
return { route }
}
}
</script>

View File

@ -1,11 +0,0 @@
<template>
<div>
{{ error.message || 'An error occurred' }}
</div>
</template>
<script>
export default {
props: ['error']
}
</script>

View File

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

View File

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

View File

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

View File

@ -1,7 +0,0 @@
<template>
<div />
</template>
<script setup>
throw new Error('This is a custom error')
</script>

View File

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

View File

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

View File

@ -1,7 +0,0 @@
<template>
<div>You should not see me</div>
</template>
<script setup>
navigateTo('/', { replace: true })
</script>

View File

@ -1,7 +0,0 @@
<template>
<div>
<nuxt-link :to="`/test/${Math.random() * 100}`">
Random path
</nuxt-link>
</div>
</template>

View File

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

View File

@ -1 +0,0 @@
export default () => 'Hello API'

View File

@ -1,4 +0,0 @@
export default () => ({
foo: 'bar',
baz: 'qux'
})

View File

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

View File

@ -1,9 +0,0 @@
export const state = () => ({
test: '❌'
})
export const actions = {
nuxtServerInit ({ state }) {
state.test = '✅'
}
}

View File

@ -1,3 +0,0 @@
{
"extends": "./.nuxt/tsconfig.json"
}

8189
yarn.lock

File diff suppressed because it is too large Load Diff