Skip to content

Commit

Permalink
init (graduate from prototype)
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Sep 19, 2018
0 parents commit 3401f6b
Show file tree
Hide file tree
Showing 63 changed files with 8,372 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
dist
.DS_Store
node_modules
explorations
TODOs.md
3 changes: 3 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
semi: false
singleQuote: true
printWidth: 80
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"typescript.tsdk": "node_modules/typescript/lib"
}
6 changes: 6 additions & 0 deletions lerna.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"packages": [
"packages/*"
],
"version": "3.0.0-alpha.1"
}
26 changes: 26 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"private": true,
"workspaces": [
"packages/*"
],
"scripts": {
"dev": "node scripts/dev.js",
"build": "node scripts/build.js",
"lint": "prettier --write --parser typescript 'packages/*/src/**/*.ts'"
},
"devDependencies": {
"chalk": "^2.4.1",
"dts-bundle": "^0.7.3",
"execa": "^1.0.0",
"fs-extra": "^7.0.0",
"lerna": "^3.4.0",
"minimist": "^1.2.0",
"prettier": "^1.14.2",
"rollup": "^0.65.0",
"rollup-plugin-alias": "^1.4.0",
"rollup-plugin-replace": "^2.0.0",
"rollup-plugin-terser": "^2.0.2",
"rollup-plugin-typescript2": "^0.17.0",
"typescript": "^3.0.3"
}
}
3 changes: 3 additions & 0 deletions packages/core/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__tests__/
__mocks__/
dist/packages
3 changes: 3 additions & 0 deletions packages/core/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# @vue/core

> This package is published only for typing and building custom renderers. It is NOT meant to be used in applications.
7 changes: 7 additions & 0 deletions packages/core/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict'

if (process.env.NODE_ENV === 'production') {
module.exports = require('./dist/core.cjs.prod.js')
} else {
module.exports = require('./dist/core.cjs.js')
}
24 changes: 24 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"name": "@vue/core",
"version": "3.0.0-alpha.1",
"description": "@vue/core",
"main": "index.js",
"module": "dist/core.esm.js",
"typings": "dist/index.d.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/vuejs/vue.git"
},
"keywords": [
"vue"
],
"author": "Evan You",
"license": "MIT",
"bugs": {
"url": "https://github.com/vuejs/vue/issues"
},
"homepage": "https://github.com/vuejs/vue/tree/dev/packages/core#readme",
"dependencies": {
"@vue/observer": "3.0.0-alpha.1"
}
}
171 changes: 171 additions & 0 deletions packages/core/src/component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { EMPTY_OBJ } from './utils'
import { VNode, Slots, RenderNode, RenderFragment } from './vdom'
import {
Data,
RenderFunction,
ComponentOptions,
ComponentPropsOptions
} from './componentOptions'
import { setupWatcher } from './componentWatch'
import { Autorun, DebuggerEvent, ComputedGetter } from '@vue/observer'

type Flatten<T> = { [K in keyof T]: T[K] }

export interface ComponentClass extends Flatten<typeof Component> {
new <D = Data, P = Data>(): MountedComponent<D, P> & D & P
}

export interface FunctionalComponent<P = Data> extends RenderFunction<P> {
pure?: boolean
props?: ComponentPropsOptions<P>
}

// this interface is merged with the class type
// to represent a mounted component
export interface MountedComponent<D = Data, P = Data> extends Component {
$vnode: VNode
$data: D
$props: P
$computed: Data
$slots: Slots
$root: MountedComponent
$children: MountedComponent[]
$options: ComponentOptions<D, P>

render: RenderFunction<P>
data?(): Partial<D>
beforeCreate?(): void
created?(): void
beforeMount?(): void
mounted?(): void
beforeUpdate?(e: DebuggerEvent): void
updated?(): void
beforeDestroy?(): void
destroyed?(): void

_updateHandle: Autorun
$forceUpdate: () => void

_self: MountedComponent<D, P> // on proxies only
}

export class Component {
public static options?: ComponentOptions

public get $el(): RenderNode | RenderFragment | null {
return this.$vnode && this.$vnode.el
}

public $vnode: VNode | null = null
public $parentVNode: VNode | null = null
public $data: Data | null = null
public $props: Data | null = null
public $computed: Data | null = null
public $slots: Slots | null = null
public $root: MountedComponent | null = null
public $parent: MountedComponent | null = null
public $children: MountedComponent[] = []
public $options: any
public $proxy: any = null
public $forceUpdate: (() => void) | null = null

public _rawData: Data | null = null
public _computedGetters: Record<string, ComputedGetter> | null = null
public _watchHandles: Set<Autorun> | null = null
public _mounted: boolean = false
public _destroyed: boolean = false
public _events: { [event: string]: Function[] | null } | null = null
public _updateHandle: Autorun | null = null
public _revokeProxy: () => void
public _isVue: boolean = true

constructor(options?: ComponentOptions) {
this.$options = options || (this.constructor as any).options || EMPTY_OBJ
// root instance
if (options !== void 0) {
// mount this
}
}

$watch(
this: MountedComponent,
keyOrFn: string | (() => any),
cb: () => void
) {
return setupWatcher(this, keyOrFn, cb)
}

// eventEmitter interface
$on(event: string, fn: Function): Component {
if (Array.isArray(event)) {
for (let i = 0; i < event.length; i++) {
this.$on(event[i], fn)
}
} else {
const events = this._events || (this._events = Object.create(null))
;(events[event] || (events[event] = [])).push(fn)
}
return this
}

$once(event: string, fn: Function): Component {
const onceFn = (...args: any[]) => {
this.$off(event, onceFn)
fn.apply(this, args)
}
;(onceFn as any).fn = fn
return this.$on(event, onceFn)
}

$off(event?: string, fn?: Function) {
if (this._events) {
if (!event && !fn) {
this._events = null
} else if (Array.isArray(event)) {
for (let i = 0; i < event.length; i++) {
this.$off(event[i], fn)
}
} else if (!fn) {
this._events[event as string] = null
} else {
const fns = this._events[event as string]
if (fns) {
for (let i = 0; i < fns.length; i++) {
const f = fns[i]
if (fn === f || fn === (f as any).fn) {
fns.splice(i, 1)
break
}
}
}
}
}
return this
}

$emit(this: MountedComponent, name: string, ...payload: any[]) {
const parentListener =
this.$props['on' + name] || this.$props['on' + name.toLowerCase()]
if (parentListener) {
invokeListeners(parentListener, payload)
}
if (this._events) {
const handlers = this._events[name]
if (handlers) {
invokeListeners(handlers, payload)
}
}
return this
}
}

function invokeListeners(value: Function | Function[], payload: any[]) {
// TODO handle error
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
value[i](...payload)
}
} else {
value(...payload)
}
}
65 changes: 65 additions & 0 deletions packages/core/src/componentComputed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { EMPTY_OBJ } from './utils'
import { computed, ComputedGetter } from '@vue/observer'
import { Component, ComponentClass } from './component'
import { ComponentComputedOptions } from './componentOptions'

const extractionCache: WeakMap<
ComponentClass,
ComponentComputedOptions
> = new WeakMap()

export function getComputedOptions(
comp: ComponentClass
): ComponentComputedOptions {
let computedOptions = extractionCache.get(comp)
if (computedOptions) {
return computedOptions
}
computedOptions = {}
const descriptors = Object.getOwnPropertyDescriptors(comp.prototype as any)
for (const key in descriptors) {
const d = descriptors[key]
if (d.get) {
computedOptions[key] = d.get
// there's no need to do anything for the setter
// as it's already defined on the prototype
}
}
return computedOptions
}

export function initializeComputed(
instance: Component,
computedOptions: ComponentComputedOptions | undefined
) {
if (!computedOptions) {
instance.$computed = EMPTY_OBJ
return
}
const handles: Record<
string,
ComputedGetter
> = (instance._computedGetters = {})
const proxy = instance.$proxy
for (const key in computedOptions) {
handles[key] = computed(computedOptions[key], proxy)
}
instance.$computed = new Proxy(
{},
{
get(_, key: any) {
return handles[key]()
}
// TODO should be readonly
}
)
}

export function teardownComputed(instance: Component) {
const handles = instance._computedGetters
if (handles !== null) {
for (const key in handles) {
handles[key].stop()
}
}
}
51 changes: 51 additions & 0 deletions packages/core/src/componentOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Slots } from './vdom'
import { MountedComponent } from './component'

export type Data = Record<string, any>

export interface RenderFunction<P = Data> {
(props: P, slots: Slots): any
}

export interface ComponentOptions<D = Data, P = Data> {
data?: () => Partial<D>
props?: ComponentPropsOptions<P>
computed?: ComponentComputedOptions<D, P>
watch?: ComponentWatchOptions<D, P>
render?: RenderFunction<P>
// TODO other options
readonly [key: string]: any
}

export type ComponentPropsOptions<P = Data> = {
[K in keyof P]: PropValidator<P[K]>
}

export type NormalizedPropsOptions<P = Data> = {
[K in keyof P]: PropOptions<P[K]>
}

export type Prop<T> = { (): T } | { new (...args: any[]): T & object }

export type PropType<T> = Prop<T> | Prop<T>[]

export type PropValidator<T> = PropOptions<T> | PropType<T>

export interface PropOptions<T = any> {
type?: PropType<T>
required?: boolean
default?: T | null | undefined | (() => T | null | undefined)
validator?(value: T): boolean
}

export interface ComponentComputedOptions<D = Data, P = Data> {
[key: string]: (this: MountedComponent<D, P> & D & P, c: any) => any
}

export interface ComponentWatchOptions<D = Data, P = Data> {
[key: string]: (
this: MountedComponent<D, P> & D & P,
oldValue: any,
newValue: any
) => void
}
Loading

0 comments on commit 3401f6b

Please sign in to comment.