Skip to content

Commit

Permalink
feat(2.7): createApp polyfill (#170)
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu authored Jul 5, 2022
1 parent f3ddb50 commit 5637e7b
Show file tree
Hide file tree
Showing 4 changed files with 190 additions and 58 deletions.
109 changes: 70 additions & 39 deletions .github/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,62 +51,93 @@ const mod = fs.readFileSync(resolve(DIR, `node_modules/vue-demi/lib/${indexFile}

let failed = false

if (isCjs && !mod.includes(`exports.isVue2 = ${isVue2}`)) {
console.log('CJS:', mod)
failed = true
}
;(function codeCheck() {
if (isCjs && !mod.includes(`exports.isVue2 = ${isVue2}`)) {
console.log('CJS:', mod)
failed = true
}

if (!isCjs && !/export\s\{\n\s\sVue,\n\s\sVue2,\n\s\sisVue2/gm.test(mod)) {
console.log('ESM:', mod)
failed = true
}
if (!isCjs && !/export\s*\{\s*Vue,\s*Vue2,\s*isVue2/gm.test(mod)) {
console.log('ESM:', mod)
failed = true
}
})()

const outputVersion = execSync(`node -e "console.log(require('vue-demi').version)"`, { cwd: DIR }).toString().trim()
console.log('version: ' + outputVersion)
;(function versionCheck() {
const outputVersion = execSync(`node -e "console.log(require('vue-demi').version)"`, { cwd: DIR }).toString().trim()
console.log('version: ' + outputVersion)

// isVue2
const is2 = execSync(`node -e "console.log(require('vue-demi').isVue2)"`, { cwd: DIR }).toString().trim()
// isVue2
const is2 = execSync(`node -e "console.log(require('vue-demi').isVue2)"`, { cwd: DIR }).toString().trim()

if (is2 !== `${isVue2}`) {
console.log(`isVue2: ${is2} !== ${isVue2}`)
failed = true
}
if (is2 !== `${isVue2}`) {
console.log(`isVue2: ${is2} !== ${isVue2}`)
failed = true
}

const hasVue2 = execSync(`node -e "console.log(require('vue-demi').Vue2 !== undefined)"`, { cwd: DIR }).toString().trim()
const hasVue2 = execSync(`node -e "console.log(require('vue-demi').Vue2 !== undefined)"`, { cwd: DIR }).toString().trim()

if (hasVue2 !== `${isVue2}`) {
console.log(`hasVue2: ${hasVue2} !== ${isVue2}`)
failed = true
}
if (hasVue2 !== `${isVue2}`) {
console.log(`hasVue2: ${hasVue2} !== ${isVue2}`)
failed = true
}
})()

const importCJS = `const { ref, computed } = require('vue-demi');`
const importESM = `const { ref, computed } = await import('vue-demi');`
;(function reactivity() {
const importCJS = `const { ref, computed } = require('vue-demi');`
const importESM = `const { ref, computed } = await import('vue-demi');`

const snippet = `
const snippet = `
let a = ref(12)
let b = computed(() => a.value * 2)
console.log(b.value)
a.value += 1
console.log(b.value)
`
.replace(/\n/g, ';')
.trim()

// ref
const refCJS = execSync(`node -e "${importCJS}${snippet}"`, { cwd: DIR }).toString().trim()
if (refCJS !== `24\n26`) {
console.log(`ref(cjs): ${refCJS} !== 24\n26`)
failed = true
}
.replace(/\n/g, ';')
.trim()

// TODO: 2.7's ESM can't runs in Node currently
if (!isVue27) {
const refESM = execSync(`node -e "(async ()=>{${importESM}${snippet}})()"`, { cwd: DIR }).toString().trim()
if (refESM !== `24\n26`) {
console.log(`ref(esm): ${refESM} !== 24\n26`)
// ref
const refCJS = execSync(`node -e "${importCJS}${snippet}"`, { cwd: DIR }).toString().trim()
if (refCJS !== `24\n26`) {
console.log(`ref(cjs): ${refCJS} !== 24\n26`)
failed = true
}
}

// TODO: 2.7's ESM can't runs in Node currently
if (!isVue27) {
const refESM = execSync(`node -e "(async ()=>{${importESM}${snippet}})()"`, { cwd: DIR }).toString().trim()
if (refESM !== `24\n26`) {
console.log(`ref(esm): ${refESM} !== 24\n26`)
failed = true
}
}
})()

;(function createApp() {
const snippet = `
const { createApp, h } = require('vue-demi');
let app = createApp({
component: {
setup(props) {
return () => h('div', props.msg);
}
}
}, {
msg: 'Hello'
});
console.log(!!app)
`
.replace(/\n/g, '')
.trim()

const result = execSync(`node -e "${snippet}"`, { cwd: DIR }).toString().trim()
if (result !== `true`) {
console.log(`createApp(cjs): ${result} !== true`)
failed = true
}
})()

if (failed) {
setTimeout(() => {
Expand Down
50 changes: 45 additions & 5 deletions lib/v2.7/index.cjs
Original file line number Diff line number Diff line change
@@ -1,11 +1,51 @@
var Vue = require('vue')

Object.keys(Vue).forEach(function(key) {
exports[key] = Vue[key]
})

exports.Vue = Vue
exports.Vue2 = Vue
exports.isVue2 = true
exports.isVue3 = false
exports.install = function(){}
exports.install = function () {}

// createApp polyfill
exports.createApp = function (rootComponent, rootProps) {
var vm
var provide = {}
var app = {
config: Vue.config,
use: Vue.use.bind(Vue),
mixin: Vue.mixin.bind(Vue),
component: Vue.component.bind(Vue),
provide: function (key, value) {
provide[key] = value
return this
},
directive: function (name, dir) {
if (dir) {
Vue.directive(name, dir)
return app
} else {
return Vue.directive(name)
}
},
mount: function (el, hydrating) {
if (!vm) {
vm = new Vue(Object.assign({ propsData: rootProps }, rootComponent, { provide: Object.assign(provide, rootComponent.provide) }))
vm.$mount(el, hydrating)
return vm
} else {
return vm
}
},
unmount: function () {
if (vm) {
vm.$destroy()
vm = undefined
}
},
}
return app
}

Object.keys(Vue).forEach(function (key) {
exports[key] = Vue[key]
})
41 changes: 34 additions & 7 deletions lib/v2.7/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,38 @@ declare const V: Vue
export declare type Plugin = PluginObject<any> | PluginFunction<any>
export type { VNode } from 'vue'
export * from 'vue'
export {
V as Vue,
Vue2,
isVue2,
isVue3,
version,
install,
export { V as Vue, Vue2, isVue2, isVue3, version, install }

import Vue, { VueConstructor, VNode, VNodeDirective } from 'vue'

// #region createApp polyfill
export type DirectiveModifiers = Record<string, boolean>
export interface DirectiveBinding<V> extends Readonly<VNodeDirective> {
readonly modifiers: DirectiveModifiers
readonly value: V
readonly oldValue: V | null
}
export type DirectiveHook<T = any, Prev = VNode | null, V = any> = (el: T, binding: DirectiveBinding<V>, vnode: VNode, prevVNode: Prev) => void
export interface ObjectDirective<T = any, V = any> {
bind?: DirectiveHook<T, any, V>
inserted?: DirectiveHook<T, any, V>
update?: DirectiveHook<T, any, V>
componentUpdated?: DirectiveHook<T, any, V>
unbind?: DirectiveHook<T, any, V>
}
export type FunctionDirective<T = any, V = any> = DirectiveHook<T, any, V>
export type Directive<T = any, V = any> = ObjectDirective<T, V> | FunctionDirective<T, V>
export interface InjectionKey<T> extends Symbol {}
export interface App<T = any> {
config: VueConstructor['config']
use: VueConstructor['use']
mixin: VueConstructor['mixin']
component: VueConstructor['component']
directive(name: string): Directive | undefined
directive(name: string, directive: Directive): this
provide<T>(key: InjectionKey<T> | symbol | string, value: T): this
mount: Vue['$mount']
unmount: Vue['$destroy']
}
export declare function createApp(rootComponent: any, rootProps?: any): App
// #endregion
48 changes: 41 additions & 7 deletions lib/v2.7/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,45 @@ var Vue2 = Vue

function install() {}

export * from 'vue'
export {
Vue,
Vue2,
isVue2,
isVue3,
install,
// createApp polyfill
export function createApp(rootComponent, rootProps) {
var vm
var provide = {}
var app = {
config: Vue.config,
use: Vue.use.bind(Vue),
mixin: Vue.mixin.bind(Vue),
component: Vue.component.bind(Vue),
provide: function (key, value) {
provide[key] = value
return this
},
directive: function (name, dir) {
if (dir) {
Vue.directive(name, dir)
return app
} else {
return Vue.directive(name)
}
},
mount: function (el, hydrating) {
if (!vm) {
vm = new Vue(Object.assign({ propsData: rootProps }, rootComponent, { provide: Object.assign(provide, rootComponent.provide) }))
vm.$mount(el, hydrating)
return vm
} else {
return vm
}
},
unmount: function () {
if (vm) {
vm.$destroy()
vm = undefined
}
},
}
return app
}

export { Vue, Vue2, isVue2, isVue3, install }
export * from 'vue'

0 comments on commit 5637e7b

Please sign in to comment.