ember-source
Version:
A JavaScript framework for creating ambitious web applications
313 lines (296 loc) • 10.2 kB
JavaScript
import { j as setInternalHelperManager, C as CustomHelperManager, k as setInternalModifierManager, s as setInternalComponentManager } from './api-CkUl6KyJ.js';
import { registerDestructor } from '../@glimmer/destroyable/index.js';
import { debugAssert } from '../@glimmer/global-context/index.js';
import './debug-to-string-BsFOvUtQ.js';
import { isDevelopingApp } from '@embroider/macros';
import { untrack, createUpdatableTag } from '../@glimmer/validator/index.js';
import { d as createConstRef } from './reference-B6HMX4y0.js';
import { a as argsProxyFor } from './args-proxy-B91L3LRK.js';
import { b as buildCapabilities, F as FROM_CAPABILITIES } from './capabilities-DHiXCCuB.js';
import { c as castToBrowser } from './simple-cast-BXTrayoV.js';
const CAPABILITIES = {
dynamicLayout: false,
dynamicTag: false,
prepareArgs: false,
createArgs: true,
attributeHook: false,
elementHook: false,
createCaller: false,
dynamicScope: true,
updateHook: true,
createInstance: true,
wrapped: false,
willDestroy: false,
hasSubOwner: false
};
function componentCapabilities(managerAPI, options = {}) {
if (isDevelopingApp() && managerAPI !== '3.13') {
throw new Error('Invalid component manager compatibility specified');
}
let updateHook = Boolean(options.updateHook);
return buildCapabilities({
asyncLifeCycleCallbacks: Boolean(options.asyncLifecycleCallbacks),
destructor: Boolean(options.destructor),
updateHook
});
}
function hasAsyncLifeCycleCallbacks(delegate) {
return delegate.capabilities.asyncLifeCycleCallbacks;
}
function hasUpdateHook(delegate) {
return delegate.capabilities.updateHook;
}
function hasAsyncUpdateHook(delegate) {
return hasAsyncLifeCycleCallbacks(delegate) && hasUpdateHook(delegate);
}
function hasDestructors(delegate) {
return delegate.capabilities.destructor;
}
/**
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
} = this;
let delegate = componentManagerDelegates.get(owner);
if (delegate === undefined) {
let {
factory
} = this;
delegate = factory(owner);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- @fixme
if (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
// eslint-disable-next-line @typescript-eslint/no-base-to-string
)}\` for: \`${delegate}\``);
}
componentManagerDelegates.set(owner, delegate);
}
return delegate;
}
create(owner, definition, vmArgs) {
let delegate = this.getDelegateFor(owner);
let args = argsProxyFor(vmArgs.capture(), 'component');
let component = delegate.createComponent(definition, args);
return new CustomComponentState(component, delegate, args);
}
getDebugName(definition) {
// eslint-disable-next-line @typescript-eslint/no-base-to-string
return typeof definition === 'function' ? definition.name : definition.toString();
}
update(bucket) {
let {
delegate
} = bucket;
if (hasUpdateHook(delegate)) {
let {
component,
args
} = bucket;
delegate.updateComponent(component, args);
}
}
didCreate({
component,
delegate
}) {
if (hasAsyncLifeCycleCallbacks(delegate)) {
delegate.didCreateComponent(component);
}
}
didUpdate({
component,
delegate
}) {
if (hasAsyncUpdateHook(delegate)) {
delegate.didUpdateComponent(component);
}
}
didRenderLayout() {}
didUpdateLayout() {}
getSelf({
component,
delegate
}) {
return createConstRef(delegate.getContext(component), 'this');
}
getDestroyable(bucket) {
const {
delegate
} = bucket;
if (hasDestructors(delegate)) {
const {
component
} = bucket;
registerDestructor(bucket, () => delegate.destroyComponent(component));
return 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 = {}) {
debugAssert(managerAPI === '3.22', () => `Invalid modifier manager compatibility specified; you specified ${managerAPI}, but only '3.22' is supported.`);
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
} = this;
let delegate = componentManagerDelegates.get(owner);
if (delegate === undefined) {
let {
factory
} = this;
delegate = factory(owner);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- @fixme
if (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
// eslint-disable-next-line @typescript-eslint/no-base-to-string
)}\` for: \`${delegate}\``);
}
componentManagerDelegates.set(owner, delegate);
}
return delegate;
}
create(owner, element, definition, capturedArgs) {
let delegate = this.getDelegateFor(owner);
let args = argsProxyFor(capturedArgs, 'modifier');
let instance = delegate.createModifier(definition, args);
let tag = createUpdatableTag();
let state;
state = {
tag,
element,
delegate,
args,
modifier: instance
};
registerDestructor(state, () => delegate.destroyModifier(instance, args));
return state;
}
getDebugName(definition) {
if (typeof definition === 'function') {
return definition.name || definition.toString();
} else {
return '<unknown>';
}
}
getDebugInstance({
modifier
}) {
return modifier;
}
getTag({
tag
}) {
return tag;
}
install({
element,
args,
modifier,
delegate
}) {
let {
capabilities
} = delegate;
if (capabilities.disableAutoTracking) {
untrack(() => delegate.installModifier(modifier, castToBrowser(element, 'ELEMENT'), args));
} else {
delegate.installModifier(modifier, castToBrowser(element), args);
}
}
update({
args,
modifier,
delegate
}) {
let {
capabilities
} = delegate;
if (capabilities.disableAutoTracking) {
untrack(() => delegate.updateModifier(modifier, args));
} else {
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);
}
export { CustomComponentManager as C, CustomModifierManager as a, setHelperManager as b, componentCapabilities as c, setModifierManager as d, modifierCapabilities as m, setComponentManager as s };