UNPKG

vue-next

Version:

## Status: Pre-Alpha.

706 lines (694 loc) 21.7 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); // Make a map and return a function for checking if a key // is in that map. // // IMPORTANT: all calls of this function must be prefixed with /*#__PURE__*/ // So that rollup can tree-shake them if necessary. function makeMap(str, expectsLowerCase) { const map = Object.create(null); const list = str.split(','); for (let i = 0; i < list.length; i++) { map[list[i]] = true; } return expectsLowerCase ? val => !!map[val.toLowerCase()] : val => !!map[val]; } const EMPTY_OBJ = Object.freeze({}) ; const extend = (a, b) => { for (const key in b) { a[key] = b[key]; } return a; }; const hasOwnProperty = Object.prototype.hasOwnProperty; const hasOwn = (val, key) => hasOwnProperty.call(val, key); const isFunction = (val) => typeof val === 'function'; const isSymbol = (val) => typeof val === 'symbol'; const isObject = (val) => val !== null && typeof val === 'object'; const objectToString = Object.prototype.toString; const toTypeString = (value) => objectToString.call(value); function toRawType(value) { return toTypeString(value).slice(8, -1); } const capitalize = (str) => { return str.charAt(0).toUpperCase() + str.slice(1); }; // compare whether a value has changed, accounting for NaN. const hasChanged = (value, oldValue) => value !== oldValue && (value === value || oldValue === oldValue); // global immutability lock let LOCKED = true; function lock() { LOCKED = true; } function unlock() { LOCKED = false; } const builtInSymbols = new Set(Object.getOwnPropertyNames(Symbol) .map(key => Symbol[key]) .filter(isSymbol)); function createGetter(isReadonly) { return function get(target, key, receiver) { const res = Reflect.get(target, key, receiver); if (isSymbol(key) && builtInSymbols.has(key)) { return res; } if (isRef(res)) { return res.value; } track(target, "get" /* GET */, key); return isObject(res) ? isReadonly ? // need to lazy access readonly and reactive here to avoid // circular dependency readonly(res) : reactive(res) : res; }; } function set(target, key, value, receiver) { value = toRaw(value); const oldValue = target[key]; if (isRef(oldValue) && !isRef(value)) { oldValue.value = value; return true; } const hadKey = hasOwn(target, key); const result = Reflect.set(target, key, value, receiver); // don't trigger if target is something up in the prototype chain of original if (target === toRaw(receiver)) { /* istanbul ignore else */ { const extraInfo = { oldValue, newValue: value }; if (!hadKey) { trigger(target, "add" /* ADD */, key, extraInfo); } else if (hasChanged(value, oldValue)) { trigger(target, "set" /* SET */, key, extraInfo); } } } return result; } function deleteProperty(target, key) { const hadKey = hasOwn(target, key); const oldValue = target[key]; const result = Reflect.deleteProperty(target, key); if (result && hadKey) { /* istanbul ignore else */ { trigger(target, "delete" /* DELETE */, key, { oldValue }); } } return result; } function has(target, key) { const result = Reflect.has(target, key); track(target, "has" /* HAS */, key); return result; } function ownKeys(target) { track(target, "iterate" /* ITERATE */); return Reflect.ownKeys(target); } const mutableHandlers = { get: createGetter(false), set, deleteProperty, has, ownKeys }; const readonlyHandlers = { get: createGetter(true), set(target, key, value, receiver) { if (LOCKED) { { console.warn(`Set operation on key "${String(key)}" failed: target is readonly.`, target); } return true; } else { return set(target, key, value, receiver); } }, deleteProperty(target, key) { if (LOCKED) { { console.warn(`Delete operation on key "${String(key)}" failed: target is readonly.`, target); } return true; } else { return deleteProperty(target, key); } }, has, ownKeys }; const toReactive = (value) => isObject(value) ? reactive(value) : value; const toReadonly = (value) => isObject(value) ? readonly(value) : value; const getProto = (v) => Reflect.getPrototypeOf(v); function get(target, key, wrap) { target = toRaw(target); key = toRaw(key); track(target, "get" /* GET */, key); return wrap(getProto(target).get.call(target, key)); } function has$1(key) { const target = toRaw(this); key = toRaw(key); track(target, "has" /* HAS */, key); return getProto(target).has.call(target, key); } function size(target) { target = toRaw(target); track(target, "iterate" /* ITERATE */); return Reflect.get(getProto(target), 'size', target); } function add(value) { value = toRaw(value); const target = toRaw(this); const proto = getProto(target); const hadKey = proto.has.call(target, value); const result = proto.add.call(target, value); if (!hadKey) { /* istanbul ignore else */ { trigger(target, "add" /* ADD */, value, { newValue: value }); } } return result; } function set$1(key, value) { value = toRaw(value); const target = toRaw(this); const proto = getProto(target); const hadKey = proto.has.call(target, key); const oldValue = proto.get.call(target, key); const result = proto.set.call(target, key, value); /* istanbul ignore else */ { const extraInfo = { oldValue, newValue: value }; if (!hadKey) { trigger(target, "add" /* ADD */, key, extraInfo); } else if (hasChanged(value, oldValue)) { trigger(target, "set" /* SET */, key, extraInfo); } } return result; } function deleteEntry(key) { const target = toRaw(this); const proto = getProto(target); const hadKey = proto.has.call(target, key); const oldValue = proto.get ? proto.get.call(target, key) : undefined; // forward the operation before queueing reactions const result = proto.delete.call(target, key); if (hadKey) { /* istanbul ignore else */ { trigger(target, "delete" /* DELETE */, key, { oldValue }); } } return result; } function clear() { const target = toRaw(this); const hadItems = target.size !== 0; const oldTarget = target instanceof Map ? new Map(target) : new Set(target) ; // forward the operation before queueing reactions const result = getProto(target).clear.call(target); if (hadItems) { /* istanbul ignore else */ { trigger(target, "clear" /* CLEAR */, void 0, { oldTarget }); } } return result; } function createForEach(isReadonly) { return function forEach(callback, thisArg) { const observed = this; const target = toRaw(observed); const wrap = isReadonly ? toReadonly : toReactive; track(target, "iterate" /* ITERATE */); // important: create sure the callback is // 1. invoked with the reactive map as `this` and 3rd arg // 2. the value received should be a corresponding reactive/readonly. function wrappedCallback(value, key) { return callback.call(observed, wrap(value), wrap(key), observed); } return getProto(target).forEach.call(target, wrappedCallback, thisArg); }; } function createIterableMethod(method, isReadonly) { return function (...args) { const target = toRaw(this); const isPair = method === 'entries' || (method === Symbol.iterator && target instanceof Map); const innerIterator = getProto(target)[method].apply(target, args); const wrap = isReadonly ? toReadonly : toReactive; track(target, "iterate" /* ITERATE */); // return a wrapped iterator which returns observed versions of the // values emitted from the real iterator return { // iterator protocol next() { const { value, done } = innerIterator.next(); return done ? { value, done } : { value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value), done }; }, // iterable protocol [Symbol.iterator]() { return this; } }; }; } function createReadonlyMethod(method, type) { return function (...args) { if (LOCKED) { { const key = args[0] ? `on key "${args[0]}" ` : ``; console.warn(`${capitalize(type)} operation ${key}failed: target is readonly.`, toRaw(this)); } return type === "delete" /* DELETE */ ? false : this; } else { return method.apply(this, args); } }; } const mutableInstrumentations = { get(key) { return get(this, key, toReactive); }, get size() { return size(this); }, has: has$1, add, set: set$1, delete: deleteEntry, clear, forEach: createForEach(false) }; const readonlyInstrumentations = { get(key) { return get(this, key, toReadonly); }, get size() { return size(this); }, has: has$1, add: createReadonlyMethod(add, "add" /* ADD */), set: createReadonlyMethod(set$1, "set" /* SET */), delete: createReadonlyMethod(deleteEntry, "delete" /* DELETE */), clear: createReadonlyMethod(clear, "clear" /* CLEAR */), forEach: createForEach(true) }; const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator]; iteratorMethods.forEach(method => { mutableInstrumentations[method] = createIterableMethod(method, false); readonlyInstrumentations[method] = createIterableMethod(method, true); }); function createInstrumentationGetter(instrumentations) { return (target, key, receiver) => Reflect.get(hasOwn(instrumentations, key) && key in target ? instrumentations : target, key, receiver); } const mutableCollectionHandlers = { get: createInstrumentationGetter(mutableInstrumentations) }; const readonlyCollectionHandlers = { get: createInstrumentationGetter(readonlyInstrumentations) }; const targetMap = new WeakMap(); // WeakMaps that store {raw <-> observed} pairs. const rawToReactive = new WeakMap(); const reactiveToRaw = new WeakMap(); const rawToReadonly = new WeakMap(); const readonlyToRaw = new WeakMap(); // WeakSets for values that are marked readonly or non-reactive during // observable creation. const readonlyValues = new WeakSet(); const nonReactiveValues = new WeakSet(); const collectionTypes = new Set([Set, Map, WeakMap, WeakSet]); const isObservableType = /*#__PURE__*/ makeMap('Object,Array,Map,Set,WeakMap,WeakSet'); const canObserve = (value) => { return (!value._isVue && !value._isVNode && isObservableType(toRawType(value)) && !nonReactiveValues.has(value)); }; function reactive(target) { // if trying to observe a readonly proxy, return the readonly version. if (readonlyToRaw.has(target)) { return target; } // target is explicitly marked as readonly by user if (readonlyValues.has(target)) { return readonly(target); } return createReactiveObject(target, rawToReactive, reactiveToRaw, mutableHandlers, mutableCollectionHandlers); } function readonly(target) { // value is a mutable observable, retrieve its original and return // a readonly version. if (reactiveToRaw.has(target)) { target = reactiveToRaw.get(target); } return createReactiveObject(target, rawToReadonly, readonlyToRaw, readonlyHandlers, readonlyCollectionHandlers); } function createReactiveObject(target, toProxy, toRaw, baseHandlers, collectionHandlers) { if (!isObject(target)) { { console.warn(`value cannot be made reactive: ${String(target)}`); } return target; } // target already has corresponding Proxy let observed = toProxy.get(target); if (observed !== void 0) { return observed; } // target is already a Proxy if (toRaw.has(target)) { return target; } // only a whitelist of value types can be observed. if (!canObserve(target)) { return target; } const handlers = collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers; observed = new Proxy(target, handlers); toProxy.set(target, observed); toRaw.set(observed, target); if (!targetMap.has(target)) { targetMap.set(target, new Map()); } return observed; } function isReactive(value) { return reactiveToRaw.has(value) || readonlyToRaw.has(value); } function isReadonly(value) { return readonlyToRaw.has(value); } function toRaw(observed) { return reactiveToRaw.get(observed) || readonlyToRaw.get(observed) || observed; } function markReadonly(value) { readonlyValues.add(value); return value; } function markNonReactive(value) { nonReactiveValues.add(value); return value; } const effectStack = []; const ITERATE_KEY = Symbol('iterate'); function isEffect(fn) { return fn != null && fn._isEffect === true; } function effect(fn, options = EMPTY_OBJ) { if (isEffect(fn)) { fn = fn.raw; } const effect = createReactiveEffect(fn, options); if (!options.lazy) { effect(); } return effect; } function stop(effect) { if (effect.active) { cleanup(effect); if (effect.onStop) { effect.onStop(); } effect.active = false; } } function createReactiveEffect(fn, options) { const effect = function reactiveEffect(...args) { return run(effect, fn, args); }; effect._isEffect = true; effect.active = true; effect.raw = fn; effect.scheduler = options.scheduler; effect.onTrack = options.onTrack; effect.onTrigger = options.onTrigger; effect.onStop = options.onStop; effect.computed = options.computed; effect.deps = []; return effect; } function run(effect, fn, args) { if (!effect.active) { return fn(...args); } if (!effectStack.includes(effect)) { cleanup(effect); try { effectStack.push(effect); return fn(...args); } finally { effectStack.pop(); } } } function cleanup(effect) { const { deps } = effect; if (deps.length) { for (let i = 0; i < deps.length; i++) { deps[i].delete(effect); } deps.length = 0; } } let shouldTrack = true; function pauseTracking() { shouldTrack = false; } function resumeTracking() { shouldTrack = true; } function track(target, type, key) { if (!shouldTrack || effectStack.length === 0) { return; } const effect = effectStack[effectStack.length - 1]; if (type === "iterate" /* ITERATE */) { key = ITERATE_KEY; } let depsMap = targetMap.get(target); if (depsMap === void 0) { targetMap.set(target, (depsMap = new Map())); } let dep = depsMap.get(key); if (dep === void 0) { depsMap.set(key, (dep = new Set())); } if (!dep.has(effect)) { dep.add(effect); effect.deps.push(dep); if ( effect.onTrack) { effect.onTrack({ effect, target, type, key }); } } } function trigger(target, type, key, extraInfo) { const depsMap = targetMap.get(target); if (depsMap === void 0) { // never been tracked return; } const effects = new Set(); const computedRunners = new Set(); if (type === "clear" /* CLEAR */) { // collection being cleared, trigger all effects for target depsMap.forEach(dep => { addRunners(effects, computedRunners, dep); }); } else { // schedule runs for SET | ADD | DELETE if (key !== void 0) { addRunners(effects, computedRunners, depsMap.get(key)); } // also run for iteration key on ADD | DELETE if (type === "add" /* ADD */ || type === "delete" /* DELETE */) { const iterationKey = Array.isArray(target) ? 'length' : ITERATE_KEY; addRunners(effects, computedRunners, depsMap.get(iterationKey)); } } const run = (effect) => { scheduleRun(effect, target, type, key, extraInfo); }; // Important: computed effects must be run first so that computed getters // can be invalidated before any normal effects that depend on them are run. computedRunners.forEach(run); effects.forEach(run); } function addRunners(effects, computedRunners, effectsToAdd) { if (effectsToAdd !== void 0) { effectsToAdd.forEach(effect => { if (effect.computed) { computedRunners.add(effect); } else { effects.add(effect); } }); } } function scheduleRun(effect, target, type, key, extraInfo) { if ( effect.onTrigger) { const event = { effect, target, key, type }; effect.onTrigger(extraInfo ? extend(event, extraInfo) : event); } if (effect.scheduler !== void 0) { effect.scheduler(effect); } else { effect(); } } const convert = (val) => isObject(val) ? reactive(val) : val; function ref(raw) { if (isRef(raw)) { return raw; } raw = convert(raw); const r = { _isRef: true, get value() { track(r, "get" /* GET */, ''); return raw; }, set value(newVal) { raw = convert(newVal); trigger(r, "set" /* SET */, ''); } }; return r; } function isRef(r) { return r ? r._isRef === true : false; } function toRefs(object) { const ret = {}; for (const key in object) { ret[key] = toProxyRef(object, key); } return ret; } function toProxyRef(object, key) { return { _isRef: true, get value() { return object[key]; }, set value(newVal) { object[key] = newVal; } }; } function computed(getterOrOptions) { let getter; let setter; if (isFunction(getterOrOptions)) { getter = getterOrOptions; setter = () => { console.warn('Write operation failed: computed value is readonly'); } ; } else { getter = getterOrOptions.get; setter = getterOrOptions.set; } let dirty = true; let value; const runner = effect(getter, { lazy: true, // mark effect as computed so that it gets priority during trigger computed: true, scheduler: () => { dirty = true; } }); return { _isRef: true, // expose effect so computed can be stopped effect: runner, get value() { if (dirty) { value = runner(); dirty = false; } // When computed effects are accessed in a parent effect, the parent // should track all the dependencies the computed property has tracked. // This should also apply for chained computed properties. trackChildRun(runner); return value; }, set value(newValue) { setter(newValue); } }; } function trackChildRun(childRunner) { if (effectStack.length === 0) { return; } const parentRunner = effectStack[effectStack.length - 1]; for (let i = 0; i < childRunner.deps.length; i++) { const dep = childRunner.deps[i]; if (!dep.has(parentRunner)) { dep.add(parentRunner); parentRunner.deps.push(dep); } } } exports.ITERATE_KEY = ITERATE_KEY; exports.computed = computed; exports.effect = effect; exports.isReactive = isReactive; exports.isReadonly = isReadonly; exports.isRef = isRef; exports.lock = lock; exports.markNonReactive = markNonReactive; exports.markReadonly = markReadonly; exports.pauseTracking = pauseTracking; exports.reactive = reactive; exports.readonly = readonly; exports.ref = ref; exports.resumeTracking = resumeTracking; exports.stop = stop; exports.toRaw = toRaw; exports.toRefs = toRefs; exports.unlock = unlock;