UNPKG

ember-source

Version:

A JavaScript framework for creating ambitious web applications

566 lines (546 loc) 23.7 kB
import { debugToString as debugToString$1, castToBrowser } from '../util/index.js'; import { associateDestroyableChild, registerDestructor } from '../destroyable/index.js'; import { createComputeRef, createConstRef, UNDEFINED_REFERENCE, valueForRef } from '../reference/index.js'; import { untrack, track, createUpdatableTag } from '../validator/index.js'; import { Op, InternalComponentCapabilities } from '../vm/index.js'; import { isDevelopingApp } from '@embroider/macros'; const CUSTOM_TAG_FOR = new WeakMap(); function getCustomTagFor(obj) { return CUSTOM_TAG_FOR.get(obj); } function setCustomTagFor(obj, customTagFn) { CUSTOM_TAG_FOR.set(obj, customTagFn); } function convertToInt(prop) { if ("symbol" == typeof prop) return null; const num = Number(prop); return isNaN(num) ? null : num % 1 == 0 ? num : null; } class NamedArgsProxy { constructor(named) { this.named = named; } get(_target, prop) { const ref = this.named[prop]; if (void 0 !== ref) return valueForRef(ref); } has(_target, prop) { return prop in this.named; } ownKeys() { return Object.keys(this.named); } isExtensible() { return !1; } getOwnPropertyDescriptor(_target, prop) { if (isDevelopingApp() && !(prop in this.named)) throw new Error(`args proxies do not have real property descriptors, so you should never need to call getOwnPropertyDescriptor yourself. This code exists for enumerability, such as in for-in loops and Object.keys(). Attempted to get the descriptor for \`${String(prop)}\``); return { enumerable: !0, configurable: !0 }; } } class PositionalArgsProxy { constructor(positional) { this.positional = positional; } get(target, prop) { let { positional: positional } = this; if ("length" === prop) return positional.length; const parsed = convertToInt(prop); return null !== parsed && parsed < positional.length ? valueForRef(positional[parsed]) : target[prop]; } isExtensible() { return !1; } has(_target, prop) { const parsed = convertToInt(prop); return null !== parsed && parsed < this.positional.length; } } const argsProxyFor = (capturedArgs, type) => { const { named: named, positional: positional } = capturedArgs, namedHandler = new NamedArgsProxy(named), positionalHandler = new PositionalArgsProxy(positional), namedTarget = Object.create(null); if (isDevelopingApp()) { const setHandler = function (_target, prop) { throw new Error(`You attempted to set ${String(prop)} on the arguments of a component, helper, or modifier. Arguments are immutable and cannot be updated directly; they always represent the values that are passed down. If you want to set default values, you should use a getter and local tracked state instead.`); }, forInDebugHandler = () => { throw new Error(`Object.keys() was called on the positional arguments array for a ${type}, which is not supported. This function is a low-level function that should not need to be called for positional argument arrays. You may be attempting to iterate over the array using for...in instead of for...of.`); }; namedHandler.set = setHandler, positionalHandler.set = setHandler, positionalHandler.ownKeys = forInDebugHandler; } const namedProxy = new Proxy(namedTarget, namedHandler), positionalProxy = new Proxy([], positionalHandler); return setCustomTagFor(namedProxy, (_obj, key) => function (namedArgs, key) { return track(() => { key in namedArgs && valueForRef(namedArgs[key]); }); }(named, key)), setCustomTagFor(positionalProxy, (_obj, key) => function (positionalArgs, key) { return track(() => { "[]" === key && // consume all of the tags in the positional array positionalArgs.forEach(valueForRef); const parsed = convertToInt(key); null !== parsed && parsed < positionalArgs.length && // consume the tag of the referenced index valueForRef(positionalArgs[parsed]); }); }(positional, key)), { named: namedProxy, positional: positionalProxy }; }; /* This file is generated by build/debug.js */ new Array(Op.Size).fill(null), new Array(Op.Size).fill(null); const FROM_CAPABILITIES = isDevelopingApp() ? new WeakSet() : void 0; function buildCapabilities(capabilities) { return isDevelopingApp() && (FROM_CAPABILITIES.add(capabilities), Object.freeze(capabilities)), capabilities; } const EMPTY = InternalComponentCapabilities.Empty; /** * Converts a ComponentCapabilities object into a 32-bit integer representation. */ function capabilityFlagsFrom(capabilities) { return EMPTY | capability(capabilities, "dynamicLayout") | capability(capabilities, "dynamicTag") | capability(capabilities, "prepareArgs") | capability(capabilities, "createArgs") | capability(capabilities, "attributeHook") | capability(capabilities, "elementHook") | capability(capabilities, "dynamicScope") | capability(capabilities, "createCaller") | capability(capabilities, "updateHook") | capability(capabilities, "createInstance") | capability(capabilities, "wrapped") | capability(capabilities, "willDestroy") | capability(capabilities, "hasSubOwner"); } function capability(capabilities, capability) { return capabilities[capability] ? InternalComponentCapabilities[capability] : EMPTY; } function managerHasCapability(_manager, capabilities, capability) { return !!(capabilities & capability); } function hasCapability(capabilities, capability) { return !!(capabilities & capability); } function helperCapabilities(managerAPI, options = {}) { if (isDevelopingApp() && "3.23" !== managerAPI) throw new Error("Invalid helper manager compatibility specified"); if (isDevelopingApp() && (!options.hasValue && !options.hasScheduledEffect || options.hasValue && options.hasScheduledEffect)) throw new Error("You must pass either the `hasValue` OR the `hasScheduledEffect` capability when defining a helper manager. Passing neither, or both, is not permitted."); if (isDevelopingApp() && options.hasScheduledEffect) throw new Error("The `hasScheduledEffect` capability has not yet been implemented for helper managers. Please pass `hasValue` instead"); return buildCapabilities({ hasValue: Boolean(options.hasValue), hasDestroyable: Boolean(options.hasDestroyable), hasScheduledEffect: Boolean(options.hasScheduledEffect) }); } //////////// function hasValue(manager) { return manager.capabilities.hasValue; } function hasDestroyable(manager) { return manager.capabilities.hasDestroyable; } //////////// class CustomHelperManager { constructor(factory) { this.factory = factory; } helperManagerDelegates = new WeakMap(); undefinedDelegate = null; getDelegateForOwner(owner) { let delegate = this.helperManagerDelegates.get(owner); if (void 0 === delegate) { let { factory: factory } = this; if (delegate = factory(owner), isDevelopingApp() && !FROM_CAPABILITIES.has(delegate.capabilities)) // TODO: This error message should make sense in both Ember and Glimmer https://github.com/glimmerjs/glimmer-vm/issues/1200 throw new Error(`Custom helper managers must have a \`capabilities\` property that is the result of calling the \`capabilities('3.23')\` (imported via \`import { capabilities } from '@ember/helper';\`). Received: \`${JSON.stringify(delegate.capabilities)}\` for: \`${delegate}\``); this.helperManagerDelegates.set(owner, delegate); } return delegate; } getDelegateFor(owner) { if (void 0 === owner) { let { undefinedDelegate: undefinedDelegate } = this; if (null === undefinedDelegate) { let { factory: factory } = this; this.undefinedDelegate = undefinedDelegate = factory(void 0); } return undefinedDelegate; } return this.getDelegateForOwner(owner); } getHelper(definition) { return (capturedArgs, owner) => { let manager = this.getDelegateFor(owner); const args = argsProxyFor(capturedArgs, "helper"), bucket = manager.createHelper(definition, args); if (hasValue(manager)) { let cache = createComputeRef(() => manager.getValue(bucket), null, isDevelopingApp() && manager.getDebugName && manager.getDebugName(definition)); return hasDestroyable(manager) && associateDestroyableChild(cache, manager.getDestroyable(bucket)), cache; } if (hasDestroyable(manager)) { let ref = createConstRef(void 0, isDevelopingApp() && (manager.getDebugName?.(definition) ?? "unknown helper")); return associateDestroyableChild(ref, manager.getDestroyable(bucket)), ref; } return UNDEFINED_REFERENCE; }; } } class FunctionHelperManager { capabilities = buildCapabilities({ hasValue: !0, hasDestroyable: !1, hasScheduledEffect: !1 }); createHelper(fn, args) { return { fn: fn, args: args }; } getValue({ fn: fn, args: args }) { return Object.keys(args.named).length > 0 ? fn(...args.positional, args.named) : fn(...args.positional); } getDebugName(fn) { return fn.name ? `(helper function ${fn.name})` : "(anonymous helper function)"; } } const COMPONENT_MANAGERS = new WeakMap(), MODIFIER_MANAGERS = new WeakMap(), HELPER_MANAGERS = new WeakMap(), getPrototypeOf$1 = Object.getPrototypeOf; function setManager(map, manager, obj) { if (isDevelopingApp() && ("object" != typeof obj || null === obj) && "function" != typeof obj) throw new Error(`Attempted to set a manager on a non-object value. Managers can only be associated with objects or functions. Value was ${debugToString$1(obj)}`); if (isDevelopingApp() && map.has(obj)) throw new Error(`Attempted to set the same type of manager multiple times on a value. You can only associate one manager of each type with a given value. Value was ${debugToString$1(obj)}`); return map.set(obj, manager), obj; } function getManager(map, obj) { let pointer = obj; for (; null != pointer;) { const manager = map.get(pointer); if (void 0 !== manager) return manager; pointer = getPrototypeOf$1(pointer); } } /////////// function setInternalModifierManager(manager, definition) { return setManager(MODIFIER_MANAGERS, manager, definition); } function getInternalModifierManager(definition, isOptional) { if (isDevelopingApp() && "function" != typeof definition && ("object" != typeof definition || null === definition)) throw new Error(`Attempted to use a value as a modifier, but it was not an object or function. Modifier definitions must be objects or functions with an associated modifier manager. The value was: ${definition}`); const manager = getManager(MODIFIER_MANAGERS, definition); if (void 0 === manager) { if (!0 === isOptional) return null; if (isDevelopingApp()) throw new Error(`Attempted to load a modifier, but there wasn't a modifier manager associated with the definition. The definition was: ${debugToString$1(definition)}`); } return manager; } function setInternalHelperManager(manager, definition) { return setManager(HELPER_MANAGERS, manager, definition); } const DEFAULT_MANAGER = new CustomHelperManager(() => new FunctionHelperManager()); function getInternalHelperManager(definition, isOptional) { if (isDevelopingApp() && "function" != typeof definition && ("object" != typeof definition || null === definition)) throw new Error(`Attempted to use a value as a helper, but it was not an object or function. Helper definitions must be objects or functions with an associated helper manager. The value was: ${definition}`); let manager = getManager(HELPER_MANAGERS, definition); // Functions are special-cased because functions are defined // as the "default" helper, per: https://github.com/emberjs/rfcs/pull/756 if (void 0 === manager && "function" == typeof definition && (manager = DEFAULT_MANAGER), manager) return manager; if (!0 === isOptional) return null; if (isDevelopingApp()) throw new Error(`Attempted to load a helper, but there wasn't a helper manager associated with the definition. The definition was: ${debugToString$1(definition)}`); return null; } function setInternalComponentManager(factory, obj) { return setManager(COMPONENT_MANAGERS, factory, obj); } function getInternalComponentManager(definition, isOptional) { if (isDevelopingApp() && "function" != typeof definition && ("object" != typeof definition || null === definition)) throw new Error(`Attempted to use a value as a component, but it was not an object or function. Component definitions must be objects or functions with an associated component manager. The value was: ${definition}`); const manager = getManager(COMPONENT_MANAGERS, definition); if (void 0 === manager) { if (!0 === isOptional) return null; if (isDevelopingApp()) throw new Error(`Attempted to load a component, but there wasn't a component manager associated with the definition. The definition was: ${debugToString$1(definition)}`); } return manager; } /////////// function hasInternalComponentManager(definition) { return void 0 !== getManager(COMPONENT_MANAGERS, definition); } function hasInternalHelperManager(definition) { return function (definition) { return "function" == typeof definition; }(definition) || void 0 !== getManager(HELPER_MANAGERS, definition); } function hasInternalModifierManager(definition) { return void 0 !== getManager(MODIFIER_MANAGERS, definition); } const CAPABILITIES = { dynamicLayout: !1, dynamicTag: !1, prepareArgs: !1, createArgs: !0, attributeHook: !1, elementHook: !1, createCaller: !1, dynamicScope: !0, updateHook: !0, createInstance: !0, wrapped: !1, willDestroy: !1, hasSubOwner: !1 }; function componentCapabilities(managerAPI, options = {}) { if (isDevelopingApp() && "3.13" !== managerAPI) throw new Error("Invalid component manager compatibility specified"); let updateHook = Boolean(options.updateHook); return buildCapabilities({ asyncLifeCycleCallbacks: Boolean(options.asyncLifecycleCallbacks), destructor: Boolean(options.destructor), updateHook: updateHook }); } function hasAsyncLifeCycleCallbacks(delegate) { return delegate.capabilities.asyncLifeCycleCallbacks; } function hasUpdateHook(delegate) { return delegate.capabilities.updateHook; } /** The CustomComponentManager allows addons to provide custom component implementations that integrate seamlessly into Ember. This is accomplished through a delegate, registered with the custom component manager, which implements a set of hooks that determine component behavior. To create a custom component manager, instantiate a new CustomComponentManager class and pass the delegate as the first argument: ```js let manager = new CustomComponentManager({ // ...delegate implementation... }); ``` ## Delegate Hooks Throughout the lifecycle of a component, the component manager will invoke delegate hooks that are responsible for surfacing those lifecycle changes to the end developer. * `create()` - invoked when a new instance of a component should be created * `update()` - invoked when the arguments passed to a component change * `getContext()` - returns the object that should be */ class CustomComponentManager { componentManagerDelegates = new WeakMap(); constructor(factory) { this.factory = factory; } getDelegateFor(owner) { let { componentManagerDelegates: componentManagerDelegates } = this, delegate = componentManagerDelegates.get(owner); if (void 0 === delegate) { let { factory: factory } = this; if (delegate = factory(owner), isDevelopingApp() && !FROM_CAPABILITIES.has(delegate.capabilities)) // TODO: This error message should make sense in both Ember and Glimmer https://github.com/glimmerjs/glimmer-vm/issues/1200 throw new Error(`Custom component managers must have a \`capabilities\` property that is the result of calling the \`capabilities('3.13')\` (imported via \`import { capabilities } from '@ember/component';\`). Received: \`${JSON.stringify(delegate.capabilities)}\` for: \`${delegate}\``); componentManagerDelegates.set(owner, delegate); } return delegate; } create(owner, definition, vmArgs) { let delegate = this.getDelegateFor(owner), args = argsProxyFor(vmArgs.capture(), "component"), component = delegate.createComponent(definition, args); return new CustomComponentState(component, delegate, args); } getDebugName(definition) { return "function" == typeof definition ? definition.name : definition.toString(); } update(bucket) { let { delegate: delegate } = bucket; if (hasUpdateHook(delegate)) { let { component: component, args: args } = bucket; delegate.updateComponent(component, args); } } didCreate({ component: component, delegate: delegate }) { hasAsyncLifeCycleCallbacks(delegate) && delegate.didCreateComponent(component); } didUpdate({ component: component, delegate: delegate }) { (function (delegate) { return hasAsyncLifeCycleCallbacks(delegate) && hasUpdateHook(delegate); })(delegate) && delegate.didUpdateComponent(component); } didRenderLayout() {} didUpdateLayout() {} getSelf({ component: component, delegate: delegate }) { return createConstRef(delegate.getContext(component), "this"); } getDestroyable(bucket) { const { delegate: delegate } = bucket; if (function (delegate) { return delegate.capabilities.destructor; }(delegate)) { const { component: component } = bucket; return registerDestructor(bucket, () => delegate.destroyComponent(component)), bucket; } return null; } getCapabilities() { return CAPABILITIES; } } /** * Stores internal state about a component instance after it's been created. */ class CustomComponentState { constructor(component, delegate, args) { this.component = component, this.delegate = delegate, this.args = args; } } function modifierCapabilities(managerAPI, optionalFeatures = {}) { if (isDevelopingApp() && "3.22" !== managerAPI) throw new Error("Invalid modifier manager compatibility specified"); return buildCapabilities({ disableAutoTracking: Boolean(optionalFeatures.disableAutoTracking) }); } /** The CustomModifierManager allows addons to provide custom modifier implementations that integrate seamlessly into Ember. This is accomplished through a delegate, registered with the custom modifier manager, which implements a set of hooks that determine modifier behavior. To create a custom modifier manager, instantiate a new CustomModifierManager class and pass the delegate as the first argument: ```js let manager = new CustomModifierManager({ // ...delegate implementation... }); ``` ## Delegate Hooks Throughout the lifecycle of a modifier, the modifier manager will invoke delegate hooks that are responsible for surfacing those lifecycle changes to the end developer. * `createModifier()` - invoked when a new instance of a modifier should be created * `installModifier()` - invoked when the modifier is installed on the element * `updateModifier()` - invoked when the arguments passed to a modifier change * `destroyModifier()` - invoked when the modifier is about to be destroyed */ class CustomModifierManager { componentManagerDelegates = new WeakMap(); constructor(factory) { this.factory = factory; } getDelegateFor(owner) { let { componentManagerDelegates: componentManagerDelegates } = this, delegate = componentManagerDelegates.get(owner); if (void 0 === delegate) { let { factory: factory } = this; if (delegate = factory(owner), isDevelopingApp() && !FROM_CAPABILITIES.has(delegate.capabilities)) // TODO: This error message should make sense in both Ember and Glimmer https://github.com/glimmerjs/glimmer-vm/issues/1200 throw new Error(`Custom modifier managers must have a \`capabilities\` property that is the result of calling the \`capabilities('3.22')\` (imported via \`import { capabilities } from '@ember/modifier';\`). Received: \`${JSON.stringify(delegate.capabilities)}\` for: \`${delegate}\``); componentManagerDelegates.set(owner, delegate); } return delegate; } create(owner, element, definition, capturedArgs) { let state, delegate = this.getDelegateFor(owner), args = argsProxyFor(capturedArgs, "modifier"), instance = delegate.createModifier(definition, args); return state = { tag: createUpdatableTag(), element: element, delegate: delegate, args: args, modifier: instance }, registerDestructor(state, () => delegate.destroyModifier(instance, args)), state; } getDebugName(definition) { return "function" == typeof definition ? definition.name || definition.toString() : "<unknown>"; } getDebugInstance({ modifier: modifier }) { return modifier; } getTag({ tag: tag }) { return tag; } install({ element: element, args: args, modifier: modifier, delegate: delegate }) { let { capabilities: capabilities } = delegate; !0 === capabilities.disableAutoTracking ? untrack(() => delegate.installModifier(modifier, castToBrowser(element, "ELEMENT"), args)) : delegate.installModifier(modifier, castToBrowser(element, "ELEMENT"), args); } update({ args: args, modifier: modifier, delegate: delegate }) { let { capabilities: capabilities } = delegate; !0 === capabilities.disableAutoTracking ? untrack(() => delegate.updateModifier(modifier, args)) : delegate.updateModifier(modifier, args); } getDestroyable(state) { return state; } } function setComponentManager(factory, obj) { return setInternalComponentManager(new CustomComponentManager(factory), obj); } function setModifierManager(factory, obj) { return setInternalModifierManager(new CustomModifierManager(factory), obj); } function setHelperManager(factory, obj) { return setInternalHelperManager(new CustomHelperManager(factory), obj); } const TEMPLATES = new WeakMap(), getPrototypeOf = Object.getPrototypeOf; function setComponentTemplate(factory, obj) { if (isDevelopingApp() && (null === obj || "object" != typeof obj && "function" != typeof obj)) throw new Error(`Cannot call \`setComponentTemplate\` on \`${debugToString$1(obj)}\``); if (isDevelopingApp() && TEMPLATES.has(obj)) throw new Error(`Cannot call \`setComponentTemplate\` multiple times on the same class (\`${debugToString$1(obj)}\`)`); return TEMPLATES.set(obj, factory), obj; } function getComponentTemplate(obj) { let pointer = obj; for (; null !== pointer;) { let template = TEMPLATES.get(pointer); if (void 0 !== template) return template; pointer = getPrototypeOf(pointer); } } export { CustomComponentManager, CustomHelperManager, CustomModifierManager, capabilityFlagsFrom, componentCapabilities, getComponentTemplate, getCustomTagFor, getInternalComponentManager, getInternalHelperManager, getInternalModifierManager, hasCapability, hasDestroyable, hasInternalComponentManager, hasInternalHelperManager, hasInternalModifierManager, hasValue, helperCapabilities, managerHasCapability, modifierCapabilities, setComponentManager, setComponentTemplate, setCustomTagFor, setHelperManager, setInternalComponentManager, setInternalHelperManager, setInternalModifierManager, setModifierManager };