mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-11 08:33:53 +00:00
refactor(bridge): provide vue2 compat with a transform plugin (#3886)
This commit is contained in:
parent
e34ed887f2
commit
9e67d58005
@ -1,9 +1,10 @@
|
||||
import { useNuxt, resolveModule, addTemplate, resolveAlias, extendWebpackConfig } from '@nuxt/kit'
|
||||
import { useNuxt, addTemplate, resolveAlias, addWebpackPlugin, addVitePlugin } 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()
|
||||
@ -21,24 +22,13 @@ export function setupAppBridge (_options: any) {
|
||||
})
|
||||
}
|
||||
|
||||
// Resolve vue2 builds
|
||||
const vue2ESM = resolveModule('vue/dist/vue.runtime.esm.js', { paths: nuxt.options.modulesDir })
|
||||
const vue2CJS = resolveModule('vue/dist/vue.runtime.common.js', { paths: nuxt.options.modulesDir })
|
||||
nuxt.options.alias.vue2 = vue2ESM
|
||||
nuxt.options.build.transpile.push('vue')
|
||||
// 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')
|
||||
|
||||
extendWebpackConfig((config) => {
|
||||
(config.resolve.alias as any).vue2 = vue2CJS
|
||||
}, { client: false })
|
||||
nuxt.hook('vite:extendConfig', (config, { isServer }) => {
|
||||
if (isServer && !nuxt.options.dev) {
|
||||
(config.resolve.alias as any).vue2 = vue2CJS
|
||||
}
|
||||
})
|
||||
|
||||
// Disable legacy fetch polyfills
|
||||
nuxt.options.fetch.server = false
|
||||
nuxt.options.fetch.client = false
|
||||
@ -70,43 +60,18 @@ export function setupAppBridge (_options: any) {
|
||||
})
|
||||
|
||||
// Alias vue to have identical vue3 exports
|
||||
nuxt.options.alias['vue2-bridge'] = resolve(distDir, 'runtime/vue2-bridge.mjs')
|
||||
for (const alias of [
|
||||
// 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`])
|
||||
]) {
|
||||
nuxt.options.alias[alias] = nuxt.options.alias['vue2-bridge']
|
||||
}
|
||||
addWebpackPlugin(VueCompat.webpack({
|
||||
src: resolve(distDir, 'runtime/vue2-bridge.mjs')
|
||||
}))
|
||||
addVitePlugin(VueCompat.vite({
|
||||
src: resolve(distDir, 'runtime/vue2-bridge.mjs')
|
||||
}))
|
||||
|
||||
// Ensure TS still recognises vue imports
|
||||
nuxt.hook('prepare:types', ({ tsConfig }) => {
|
||||
tsConfig.compilerOptions.paths.vue2 = ['vue']
|
||||
delete tsConfig.compilerOptions.paths.vue
|
||||
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
|
||||
|
@ -1,4 +1,4 @@
|
||||
import Vue from 'vue' // eslint-disable-line import/default
|
||||
import Vue from 'vue'
|
||||
import VueCompositionAPI from '@vue/composition-api'
|
||||
import { defineNuxtPlugin } from '#app'
|
||||
|
||||
|
63
packages/bridge/src/runtime/vue2-bridge.d.ts
vendored
63
packages/bridge/src/runtime/vue2-bridge.d.ts
vendored
@ -1,7 +1,60 @@
|
||||
import Vue from 'vue'
|
||||
import * as VueCapi from '@vue/composition-api'
|
||||
|
||||
export * 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 declare const isFunction: (fn: unknown) => boolean
|
||||
|
||||
export { Vue as default }
|
||||
export {}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import Vue from 'vue2'
|
||||
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'
|
||||
|
||||
|
@ -58,9 +58,7 @@ async function bundle (nuxt: Nuxt, builder: any) {
|
||||
'ufo',
|
||||
'date-fns',
|
||||
'nanoid',
|
||||
'vue',
|
||||
'vue2',
|
||||
'vue2-bridge'
|
||||
'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'
|
||||
|
74
packages/bridge/src/vue-compat.ts
Normal file
74
packages/bridge/src/vue-compat.ts
Normal file
@ -0,0 +1,74 @@
|
||||
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`])
|
||||
]
|
@ -168,7 +168,8 @@ export const getRollupConfig = (nitroContext: NitroContext) => {
|
||||
'process.env.RUNTIME_CONFIG': devalue(nitroContext._nuxt.runtimeConfig),
|
||||
'process.env.DEBUG': JSON.stringify(nitroContext._nuxt.dev),
|
||||
// Needed for vue 2 server build
|
||||
'commonjsGlobal.process.env.VUE_ENV': '"server"'
|
||||
'commonjsGlobal.process.env.VUE_ENV': '"server"',
|
||||
'global["process"].env.VUE_ENV': '"server"'
|
||||
}
|
||||
}))
|
||||
|
||||
|
@ -12,6 +12,9 @@ describe('fixtures:bridge', async () => {
|
||||
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', () => {
|
||||
|
2
test/fixtures/bridge/pages/index.vue
vendored
2
test/fixtures/bridge/pages/index.vue
vendored
@ -6,6 +6,7 @@
|
||||
Update
|
||||
</button>
|
||||
</div>
|
||||
Rendered on server: {{ serverBuild }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -15,4 +16,5 @@ 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>
|
||||
|
Loading…
Reference in New Issue
Block a user