diff --git a/packages/@glimmer/component/package.json b/packages/@glimmer/component/package.json index d39417f55..b938f010c 100644 --- a/packages/@glimmer/component/package.json +++ b/packages/@glimmer/component/package.json @@ -27,7 +27,7 @@ }, "dependencies": { "@glimmer/env": "^0.1.7", - "@glimmer/util": "0.65.0", + "@glimmer/util": "0.73.0", "@glimmer/core": "2.0.0-beta.11", "broccoli-file-creator": "^2.1.1", "broccoli-merge-trees": "^3.0.2", @@ -43,11 +43,11 @@ "devDependencies": { "@ember/optional-features": "^0.6.1", "@glimmer/application-test-helpers": "^1.0.0", - "@glimmer/compiler": "0.65.0", - "@glimmer/interfaces": "0.65.0", + "@glimmer/compiler": "0.73.0", + "@glimmer/interfaces": "0.73.0", "@glimmer/resolver": "^0.3.0", "@glimmer/tracking": "2.0.0-beta.11", - "@glimmer/wire-format": "0.65.0", + "@glimmer/wire-format": "0.73.0", "@types/ember": "~3.0.29", "@types/ember-qunit": "~3.4.3", "@types/ember-test-helpers": "~1.0.6", diff --git a/packages/@glimmer/component/src/component.ts b/packages/@glimmer/component/src/component.ts index b729afa27..d0894e0fb 100644 --- a/packages/@glimmer/component/src/component.ts +++ b/packages/@glimmer/component/src/component.ts @@ -4,7 +4,7 @@ import _GlimmerComponent from '../addon/-private/component'; import { DEBUG } from '@glimmer/env'; export default class GlimmerComponent extends _GlimmerComponent { - constructor(owner: unknown, args: Args) { + constructor(owner: object, args: Args) { super(owner, args); if (DEBUG && !(owner !== null && typeof owner === 'object')) { diff --git a/packages/@glimmer/component/test/interactive/args-test.ts b/packages/@glimmer/component/test/interactive/args-test.ts index 8e7c8e3ab..1142c9918 100644 --- a/packages/@glimmer/component/test/interactive/args-test.ts +++ b/packages/@glimmer/component/test/interactive/args-test.ts @@ -2,9 +2,9 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { setComponentTemplate, createTemplate } from '@glimmer/core'; -import { module, test, render, settled } from '@glimmer/core/test/utils'; +import { test, render, settled } from '@glimmer/core/test/utils'; -module('[@glimmer/component] Component Arguments', () => { +QUnit.module('[@glimmer/component] Component Arguments', () => { test('Getters that depend on `args` re-render correctly', async function (assert) { assert.expect(2); diff --git a/packages/@glimmer/component/test/interactive/lifecycle-hook-test.ts b/packages/@glimmer/component/test/interactive/lifecycle-hook-test.ts index 3d69e45ef..533bb79f2 100644 --- a/packages/@glimmer/component/test/interactive/lifecycle-hook-test.ts +++ b/packages/@glimmer/component/test/interactive/lifecycle-hook-test.ts @@ -1,10 +1,10 @@ import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; -import { module, test, render, settled } from '@glimmer/core/test/utils'; +import { test, render, settled } from '@glimmer/core/test/utils'; import { setComponentTemplate, createTemplate } from '@glimmer/core'; -module('[@glimmer/component] Lifecycle Hooks', () => { +QUnit.module('[@glimmer/component] Lifecycle Hooks', () => { test('Lifecycle hook ordering', async function (assert) { assert.expect(2); diff --git a/packages/@glimmer/core/index.ts b/packages/@glimmer/core/index.ts index d8eb54e06..52eec018c 100644 --- a/packages/@glimmer/core/index.ts +++ b/packages/@glimmer/core/index.ts @@ -3,6 +3,7 @@ export { RenderComponentOptions, didRender, getTemplateIterator, + ComponentDefinition, } from './src/render-component'; export { BaseEnvDelegate } from './src/environment/delegates'; @@ -12,29 +13,22 @@ export type { ModifierCapabilities, ComponentManager, ComponentCapabilities, + HelperManager, } from '@glimmer/interfaces'; -export { setComponentManager, setModifierManager } from '@glimmer/runtime'; -export { setHelperManager } from './src/managers'; +export { templateOnlyComponent } from '@glimmer/runtime'; export { TemplateArgs } from './src/interfaces'; -export { ModifierDefinition, capabilities as modifierCapabilities } from './src/managers/modifier'; - -export { - HelperManager, - HelperDefinition, - capabilities as helperCapabilities, - Capabilities as HelperCapabilities, -} from './src/managers/helper'; - export { - ComponentDefinition, - capabilities as componentCapabilities, -} from './src/managers/component/custom'; + setComponentManager, + setModifierManager, + modifierCapabilities, + setHelperManager, + componentCapabilities, + helperCapabilities, +} from '@glimmer/manager'; -export { templateOnlyComponent } from './src/managers/component/template-only'; +export { getOwner, setOwner } from '@glimmer/owner'; export { createTemplate, setComponentTemplate } from './src/template'; - -export { getOwner, setOwner } from './src/owner'; diff --git a/packages/@glimmer/core/package.json b/packages/@glimmer/core/package.json index d798ebe26..8e28e8e99 100644 --- a/packages/@glimmer/core/package.json +++ b/packages/@glimmer/core/package.json @@ -13,16 +13,18 @@ }, "dependencies": { "@glimmer/env": "^0.1.7", - "@glimmer/global-context": "0.65.0", - "@glimmer/interfaces": "0.65.0", - "@glimmer/opcode-compiler": "0.65.0", - "@glimmer/program": "0.65.0", - "@glimmer/runtime": "0.65.0", - "@glimmer/validator": "0.65.0", + "@glimmer/global-context": "0.73.0", + "@glimmer/interfaces": "0.73.0", + "@glimmer/manager": "0.73.0", + "@glimmer/opcode-compiler": "0.73.0", + "@glimmer/owner": "0.73.0", + "@glimmer/program": "0.73.0", + "@glimmer/runtime": "0.73.0", + "@glimmer/validator": "0.73.0", "@simple-dom/interface": "^1.4.0" }, "devDependencies": { - "@glimmer/compiler": "0.65.0", + "@glimmer/compiler": "0.73.0", "@glimmer/component": "2.0.0-beta.11", "@glimmer/tracking": "2.0.0-beta.11" }, diff --git a/packages/@glimmer/core/src/environment/delegates.ts b/packages/@glimmer/core/src/environment/delegates.ts index 3296fd051..326ba686c 100644 --- a/packages/@glimmer/core/src/environment/delegates.ts +++ b/packages/@glimmer/core/src/environment/delegates.ts @@ -31,6 +31,16 @@ setGlobalContext({ return obj[key]; }, + setPath(obj: Record, key: string, newValue: unknown) { + if (DEBUG && key.includes('.')) { + throw new Error( + 'You attempted to set a path with a `.` in it, but Glimmer.js does not support paths with dots.' + ); + } + + obj[key] = newValue; + }, + scheduleRevalidate, toBool, diff --git a/packages/@glimmer/core/src/managers/component/custom.ts b/packages/@glimmer/core/src/managers/component/custom.ts deleted file mode 100644 index 52d29e648..000000000 --- a/packages/@glimmer/core/src/managers/component/custom.ts +++ /dev/null @@ -1,251 +0,0 @@ -import { assert, unwrapTemplate } from '@glimmer/util'; -import { - InternalComponentManager, - InternalComponentCapabilities, - VMArguments, - CapturedArguments, - WithStaticLayout, - Template, - TemplateOk, - Environment, - DynamicScope, - ComponentManager, - ComponentManagerWithUpdateHook, - ComponentManagerWithAsyncLifeCycleCallbacks, - ComponentCapabilitiesVersions, - ComponentCapabilities, - ComponentManagerWithAsyncUpdateHook, - ComponentManagerWithDestructors, -} from '@glimmer/interfaces'; -import { valueForRef, createConstRef, Reference } from '@glimmer/reference'; -import { OWNER_KEY, DEFAULT_OWNER } from '../../owner'; - -import { TemplateArgs } from '../../interfaces'; -import { argsProxyFor } from '../util'; -import { registerDestructor, getComponentManager, buildCapabilities } from '@glimmer/runtime'; - -export const VM_CAPABILITIES: InternalComponentCapabilities = { - createInstance: true, - dynamicLayout: false, - dynamicTag: false, - wrapped: false, - prepareArgs: false, - createArgs: true, - attributeHook: false, - elementHook: false, - updateHook: false, - createCaller: false, - dynamicScope: true, - willDestroy: false, -}; - -export function capabilities( - managerAPI: Version, - options: ComponentCapabilitiesVersions[Version] = {} -): ComponentCapabilities { - assert( - managerAPI === '3.4' || managerAPI === '3.13', - 'Invalid component manager compatibility specified' - ); - - let updateHook = true; - - if (managerAPI === '3.13') { - updateHook = Boolean((options as ComponentCapabilitiesVersions['3.13']).updateHook); - } - - return buildCapabilities({ - asyncLifeCycleCallbacks: Boolean(options.asyncLifecycleCallbacks), - destructor: Boolean(options.destructor), - updateHook, - }); -} - -/////////// - -export function hasAsyncLifecycleCallbacks( - delegate: ComponentManager -): delegate is ComponentManagerWithAsyncLifeCycleCallbacks { - return delegate.capabilities.asyncLifeCycleCallbacks; -} - -export function hasUpdateHook( - delegate: ComponentManager -): delegate is ComponentManagerWithUpdateHook { - return delegate.capabilities.updateHook; -} - -export function hasAsyncUpdateHook( - delegate: ComponentManager -): delegate is ComponentManagerWithAsyncUpdateHook { - return hasAsyncLifecycleCallbacks(delegate) && hasUpdateHook(delegate); -} - -export function hasDestructors( - delegate: ComponentManager -): delegate is ComponentManagerWithDestructors { - 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 -*/ -export default class CustomComponentManager - implements - InternalComponentManager< - VMCustomComponentState, - VMCustomComponentDefinitionState - >, - WithStaticLayout< - VMCustomComponentState, - VMCustomComponentDefinitionState - > { - create( - env: Environment, - definition: VMCustomComponentDefinitionState, - args: VMArguments, - dynamicScope: DynamicScope - ): VMCustomComponentState { - const { ComponentDefinition } = definition; - const capturedArgs = args.capture(); - const owner = valueForRef(dynamicScope.get(OWNER_KEY)) as object; - const delegate = getComponentManager(owner, ComponentDefinition) as ComponentManager< - ComponentInstance - >; - - const argsProxy = argsProxyFor(capturedArgs, 'component'); - const component = delegate.createComponent(ComponentDefinition, argsProxy); - - return new VMCustomComponentState(env, delegate, component, capturedArgs, argsProxy); - } - - update({ delegate, component, argsProxy }: VMCustomComponentState): void { - if (hasUpdateHook(delegate)) { - delegate.updateComponent(component, argsProxy); - } - } - - didCreate({ delegate, component }: VMCustomComponentState): void { - if (hasAsyncLifecycleCallbacks(delegate)) { - delegate.didCreateComponent(component); - } - } - - didUpdate({ delegate, component }: VMCustomComponentState): void { - if (hasAsyncUpdateHook(delegate)) { - delegate.didUpdateComponent(component); - } - } - - getContext({ delegate, component }: VMCustomComponentState): void { - delegate.getContext(component); - } - - getDebugName(state: VMCustomComponentDefinitionState): string { - // TODO: This should likely call `delegate.getDebugName` somehow - return String(state.ComponentDefinition); - } - - getSelf({ delegate, component }: VMCustomComponentState): Reference { - return createConstRef(delegate.getContext(component) as object, 'this'); - } - - getDestroyable(state: VMCustomComponentState): object { - return state; - } - - getCapabilities({ - capabilities, - }: VMCustomComponentDefinitionState): InternalComponentCapabilities { - return Object.assign({}, VM_CAPABILITIES, { - updateHook: capabilities.updateHook, - }); - } - - didRenderLayout(): void {} // eslint-disable-line @typescript-eslint/no-empty-function - didUpdateLayout(): void {} // eslint-disable-line @typescript-eslint/no-empty-function - - getStaticLayout({ definition }: VMCustomComponentDefinitionState): Template { - return definition.template; - } -} - -/////////// - -/** - * Stores internal state about a component instance after it's been created. - */ -export class VMCustomComponentState { - constructor( - public env: Environment, - public delegate: ComponentManager, - public component: ComponentInstance, - public args: CapturedArguments, - public argsProxy: TemplateArgs - ) { - if (hasDestructors(delegate)) { - registerDestructor(this, () => delegate.destroyComponent(component)); - } - } -} - -export interface VMCustomComponentDefinitionState { - ComponentDefinition: ComponentDefinition; - capabilities: ComponentCapabilities; - definition: VMCustomComponentDefinition; -} - -export const CUSTOM_COMPONENT_MANAGER = new CustomComponentManager(); - -export class VMCustomComponentDefinition { - public state: VMCustomComponentDefinitionState; - public manager = CUSTOM_COMPONENT_MANAGER as CustomComponentManager; - public template: TemplateOk; - public handle: number; - - constructor( - handle: number, - ComponentDefinition: ComponentDefinition, - template: Template - ) { - this.handle = handle; - this.template = unwrapTemplate(template); - - const manager = getComponentManager(DEFAULT_OWNER, ComponentDefinition) as ComponentManager< - ComponentInstance - >; - const capabilities = manager.capabilities; - - this.state = { - ComponentDefinition, - capabilities, - definition: this, - }; - } -} - -export type ComponentDefinition<_Instance = unknown> = {}; diff --git a/packages/@glimmer/core/src/managers/component/template-only.ts b/packages/@glimmer/core/src/managers/component/template-only.ts deleted file mode 100644 index 9cf501583..000000000 --- a/packages/@glimmer/core/src/managers/component/template-only.ts +++ /dev/null @@ -1,141 +0,0 @@ -import { - InternalComponentManager, - InternalComponentCapabilities, - CapturedArguments, - Environment, - Template, - TemplateOk, - WithStaticLayout, -} from '@glimmer/interfaces'; -import { createComputeRef, createConstRef, Reference } from '@glimmer/reference'; -import { CONSTANT_TAG, Tag } from '@glimmer/validator'; -import { unwrapTemplate } from '@glimmer/util'; -import { DEBUG } from '@glimmer/env'; - -export const CAPABILITIES: InternalComponentCapabilities = { - attributeHook: false, - createArgs: false, - createCaller: false, - createInstance: true, - dynamicLayout: false, - dynamicScope: false, - dynamicTag: false, - elementHook: false, - prepareArgs: false, - updateHook: false, - wrapped: false, - willDestroy: false, -}; - -export class ComponentStateBucket { - public name: string; - - constructor(public args: CapturedArguments) {} -} - -const EMPTY_SELF = createConstRef(null, 'this'); - -/** - * For performance reasons, we want to avoid instantiating component buckets for - * components that don't have an associated component class that we would need - * instantiate and invoke lifecycle hooks on. - * - * In development mode, however, we need to track some state about the component - * in order to produce more useful error messages. This - * TemplateOnlyComponentDebugBucket is only created in development mode to hold - * that state. - */ -export class TemplateOnlyComponentDebugBucket { - constructor(public definition: TemplateOnlyComponentDefinition) {} -} - -export default class TemplateOnlyComponentManager - implements - InternalComponentManager< - TemplateOnlyComponentDebugBucket | null, - TemplateOnlyComponentDefinitionState - >, - WithStaticLayout< - TemplateOnlyComponentDebugBucket | null, - TemplateOnlyComponentDefinitionState - > { - static create(): TemplateOnlyComponentManager { - return new TemplateOnlyComponentManager(); - } - - getDebugName(state: TemplateOnlyComponentDefinitionState): string { - return state.name; - } - - getCapabilities(): InternalComponentCapabilities { - return CAPABILITIES; - } - - getStaticLayout({ definition }: TemplateOnlyComponentDefinitionState): Template { - return definition.template; - } - - create( - _env: Environment, - state: TemplateOnlyComponentDefinitionState - ): TemplateOnlyComponentDebugBucket | void { - // In development mode, save off state needed for error messages. This will - // get stripped in production mode and no bucket will be instantiated. - return DEBUG ? new TemplateOnlyComponentDebugBucket(state.definition) : undefined; - } - - getSelf(bucket: TemplateOnlyComponentDebugBucket): Reference { - // TODO: Make this error message better, https://github.com/glimmerjs/glimmer-vm/issues/1153 - return DEBUG - ? createComputeRef(() => { - throw new Error( - `You attempted to access \`this\` on a template only component, ${bucket.definition.state.name}. Template only components do not have a \`this\` context, and can only access arguments` - ); - }) - : EMPTY_SELF; - } - - getTag(): Tag { - return CONSTANT_TAG; - } - - didRenderLayout(): void {} // eslint-disable-line @typescript-eslint/no-empty-function - didCreate(): void {} // eslint-disable-line @typescript-eslint/no-empty-function - didUpdateLayout(): void {} // eslint-disable-line @typescript-eslint/no-empty-function - didUpdate(): void {} // eslint-disable-line @typescript-eslint/no-empty-function - getDestroyable(): null { - return null; - } -} - -export interface TemplateOnlyComponentDefinitionState { - name: string; - definition: TemplateOnlyComponentDefinition; -} - -export const TEMPLATE_ONLY_MANAGER = new TemplateOnlyComponentManager(); - -export class TemplateOnlyComponentDefinition { - public state: TemplateOnlyComponentDefinitionState; - public manager = TEMPLATE_ONLY_MANAGER; - public template: TemplateOk; - public handle: number; - - constructor(handle: number, name: string, template: Template) { - this.handle = handle; - this.template = unwrapTemplate(template); - - this.state = { - name, - definition: this, - }; - } -} - -export class TemplateOnlyComponent {} - -// TODO: We end up creating an extra object here mainly to be the weakmap key -// for setComponentTemplate. It might be possible to optimize. -export function templateOnlyComponent(): TemplateOnlyComponent { - return new TemplateOnlyComponent(); -} diff --git a/packages/@glimmer/core/src/managers/helper.ts b/packages/@glimmer/core/src/managers/helper.ts deleted file mode 100644 index 694e0a63f..000000000 --- a/packages/@glimmer/core/src/managers/helper.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { assert } from '@glimmer/util'; -import { reifyPositional } from '@glimmer/runtime'; -import { Helper as VMHelperFactory, CapturedArguments, VM } from '@glimmer/interfaces'; -import { createComputeRef, valueForRef, Reference } from '@glimmer/reference'; -import { DEBUG } from '@glimmer/env'; -import { TemplateArgs } from '../interfaces'; -import { argsProxyFor } from './util'; -import { OWNER_KEY } from '../owner'; -import { getHelperManager } from '.'; -import { unbindableFunction } from '../utils/unbindable-function'; -import { trackedMemoize } from '../utils/autotracking'; - -/////////// - -export interface Capabilities { - destroyable: boolean; - updateHook: boolean; -} - -export type OptionalCapabilities = Partial; - -export type ManagerAPIVersion = 'glimmerjs-2.0.0'; - -export function capabilities( - managerAPI: ManagerAPIVersion, - options: OptionalCapabilities = {} -): Capabilities { - assert(managerAPI === 'glimmerjs-2.0.0', 'Invalid helper manager compatibility specified'); - - return { - destroyable: Boolean(options.destroyable), - updateHook: Boolean(options.updateHook), - }; -} - -/////////// - -export type HelperDefinition<_HelperStateBucket = unknown> = {}; - -export interface HelperManager { - capabilities: Capabilities; - - getValue(bucket: HelperStateBucket): unknown; - createHelper( - definition: HelperDefinition, - args: TemplateArgs - ): HelperStateBucket; -} - -export function hasUpdateHook( - delegate: HelperManager -): delegate is HelperManagerWithUpdateHook { - return delegate.capabilities.updateHook; -} - -export interface HelperManagerWithUpdateHook - extends HelperManager { - updateHelper(bucket: HelperStateBucket, args: TemplateArgs): void; -} - -export function hasDestroyable( - delegate: HelperManager -): delegate is HelperManagerWithDestroyable { - return delegate.capabilities.destroyable; -} - -export interface HelperManagerWithDestroyable - extends HelperManager { - getDestroyable(bucket: HelperStateBucket): object; -} - -/////////// - -function customHelperFn( - manager: HelperManager, - definition: HelperDefinition, - capturedArgs: CapturedArguments, - vm: VM -): () => unknown { - let bucket: undefined | T; - - const argsProxy = argsProxyFor(capturedArgs, 'helper'); - const hasUpdate = hasUpdateHook(manager); - - const getValue = trackedMemoize(() => manager.getValue(bucket!)); - - const createOrUpdate = trackedMemoize(() => { - if (bucket === undefined) { - bucket = manager.createHelper(definition, argsProxy); - - if (hasDestroyable(manager)) { - vm.associateDestroyable(manager.getDestroyable(bucket)); - } - } else if (hasUpdate) { - (manager as HelperManagerWithUpdateHook).updateHelper(bucket!, argsProxy); - } - }); - - return (): unknown => { - createOrUpdate(); - return getValue(); - }; -} - -export type SimpleHelper = (...args: T[]) => U; - -/** - * Returns a factory that produces a HelperRootReference, which is how the VM - * expects to receive helpers currently. - * - * @param definition the helper definition - */ -export function vmHelperFactoryFor( - definition: HelperDefinition -): VMHelperFactory { - return (args, vm): Reference => { - const owner = valueForRef(vm.dynamicScope().get(OWNER_KEY)) as object; - const manager = getHelperManager(owner, definition)!; - const capturedArgs = args.capture(); - - let helperFn: (capturedArgs: CapturedArguments) => unknown; - - if (manager !== undefined) { - helperFn = customHelperFn(manager, definition, capturedArgs, vm); - } else { - if (DEBUG) { - assert( - typeof definition === 'function', - `Attempted to use ${definition} as a helper, but it was not a function and did not have an associated helper manager. Helpers must either be plain JavaScript functions, or managed with a helper manager.` - ); - } - - const func = DEBUG - ? unbindableFunction!(definition as SimpleHelper) - : (definition as SimpleHelper); - - helperFn = (capturedArgs: CapturedArguments): unknown => { - if (DEBUG && Object.keys(capturedArgs.named).length > 0) { - throw new Error( - `You used named arguments with the ${func.name.replace( - /^bound /, - '' - )} helper, but it is a standard function. Normal functions cannot receive named arguments when used as helpers.` - ); - } - - return func(...reifyPositional(capturedArgs.positional)); - }; - } - - return createComputeRef(() => helperFn(capturedArgs), null, helperFn.name); - }; -} diff --git a/packages/@glimmer/core/src/managers/index.ts b/packages/@glimmer/core/src/managers/index.ts deleted file mode 100644 index 21e9e7a3e..000000000 --- a/packages/@glimmer/core/src/managers/index.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { HelperManager, HelperDefinition } from './helper'; - -type ManagedItemDefinition = HelperDefinition; - -////////// - -export type ManagerFactory = (owner: Owner) => D; - -type ManagerDelegate = HelperManager; - -interface HelperManagerWrapper { - factory: ManagerFactory>; - type: 'helper'; -} - -type ManagerWrapper = HelperManagerWrapper; - -/////////// - -const MANAGERS: WeakMap> = new WeakMap(); -const MANAGER_INSTANCES: WeakMap< - object, - WeakMap, ManagerDelegate> -> = new WeakMap(); - -const getPrototypeOf = Object.getPrototypeOf; - -export function setManager( - wrapper: ManagerWrapper, - obj: Def -): Def { - MANAGERS.set(obj, wrapper); - return obj; -} - -function getManager( - obj: ManagedItemDefinition -): ManagerWrapper | undefined { - let pointer = obj; - while (pointer !== undefined && pointer !== null) { - const manager = MANAGERS.get(pointer); - - if (manager !== undefined) { - return manager as ManagerWrapper; - } - - pointer = getPrototypeOf(pointer); - } - - return undefined; -} - -function getManagerInstanceForOwner( - owner: object, - factory: ManagerFactory -): D { - let managers = MANAGER_INSTANCES.get(owner); - - if (managers === undefined) { - managers = new WeakMap(); - MANAGER_INSTANCES.set(owner, managers); - } - - let instance = managers.get(factory); - - if (instance === undefined) { - instance = factory(owner); - managers.set(factory, instance!); - } - - // We know for sure that it's the correct type at this point, but TS can't know - return instance as D; -} - -/////////// - -export function setHelperManager< - StateBucket, - Def extends HelperDefinition, - Owner extends object = object ->(factory: ManagerFactory>, definition: Def): Def { - return setManager({ factory, type: 'helper' }, definition); -} - -export function getHelperManager( - owner: object, - definition: HelperDefinition -): HelperManager | undefined { - const wrapper = getManager(definition); - - if (wrapper !== undefined && wrapper.type === 'helper') { - return getManagerInstanceForOwner(owner, wrapper.factory); - } -} diff --git a/packages/@glimmer/core/src/managers/modifier.ts b/packages/@glimmer/core/src/managers/modifier.ts deleted file mode 100644 index 2884fc993..000000000 --- a/packages/@glimmer/core/src/managers/modifier.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { DEBUG } from '@glimmer/env'; -import { - InternalModifierManager, - VMArguments, - CapturedArguments, - Destroyable, - DynamicScope, - ModifierManager, - ModifierCapabilitiesVersions, - ModifierCapabilities, -} from '@glimmer/interfaces'; -import { UpdatableTag, createUpdatableTag, untrack } from '@glimmer/validator'; -import { assert } from '@glimmer/util'; -import { SimpleElement } from '@simple-dom/interface'; -import { TemplateArgs } from '../interfaces'; -import { argsProxyFor } from './util'; -import { OWNER_KEY } from '../owner'; -import { VMModifierDefinitionWithHandle } from '../render-component/vm-definitions'; -import { registerDestructor, getModifierManager, buildCapabilities } from '@glimmer/runtime'; -import { valueForRef } from '@glimmer/reference'; - -/////////// - -export function capabilities( - managerAPI: Version, - optionalFeatures: ModifierCapabilitiesVersions[Version] = {} -): ModifierCapabilities { - assert( - managerAPI === '3.13' || managerAPI === '3.22', - 'Invalid modifier manager compatibility specified' - ); - - return buildCapabilities({ - disableAutoTracking: Boolean(optionalFeatures.disableAutoTracking), - useArgsProxy: managerAPI === '3.13' ? false : true, - passFactoryToCreate: managerAPI === '3.13', - }); -} - -/////////// - -export type ModifierDefinition<_Instance = unknown> = {}; - -export type SimpleModifier = (element: Element, ...args: unknown[]) => undefined | (() => void); - -interface SimpleModifierStateBucket { - definition: SimpleModifier; - destructor?(): void; - element?: SimpleElement; -} - -class SimpleModifierManager implements ModifierManager { - capabilities = capabilities('3.13'); - - createModifier(definition: SimpleModifier, args: TemplateArgs): SimpleModifierStateBucket { - if (DEBUG) { - assert( - Object.keys(args.named).length === 0, - `You used named arguments with the ${definition.name} modifier, but it is a standard function. Normal functions cannot receive named arguments when used as modifiers.` - ); - } - - return { definition }; - } - - installModifier( - bucket: SimpleModifierStateBucket, - element: SimpleElement, - args: TemplateArgs - ): void { - bucket.destructor = bucket.definition((element as unknown) as Element, ...args.positional); - bucket.element = element; - } - - updateModifier(bucket: SimpleModifierStateBucket, args: TemplateArgs): void { - this.destroyModifier(bucket); - this.installModifier(bucket, bucket.element!, args); - } - - destroyModifier(bucket: SimpleModifierStateBucket): void { - const { destructor } = bucket; - - if (destructor !== undefined) { - destructor(); - } - } -} - -const SIMPLE_MODIFIER_MANAGER = new SimpleModifierManager(); - -/////////// - -export class CustomModifierState { - public tag = createUpdatableTag(); - - constructor( - public element: SimpleElement, - public delegate: ModifierManager, - public modifier: ModifierStateBucket, - public argsProxy: TemplateArgs, - public capturedArgs: CapturedArguments - ) { - registerDestructor(this, () => delegate.destroyModifier(modifier, argsProxy)); - } -} - -export class CustomModifierManager - implements - InternalModifierManager< - CustomModifierState, - ModifierDefinition - > { - create( - element: SimpleElement, - definition: ModifierDefinition, - args: VMArguments, - dynamicScope: DynamicScope - ): CustomModifierState { - const owner = valueForRef(dynamicScope.get(OWNER_KEY)) as object; - let delegate = getModifierManager(owner, definition) as ModifierManager; - - if (delegate === undefined) { - if (DEBUG) { - assert( - typeof definition === 'function', - `No modifier manager found for ${definition}, and it was not a plain function, so it could not be used as a modifier` - ); - } - - delegate = (SIMPLE_MODIFIER_MANAGER as unknown) as ModifierManager; - } - - const capturedArgs = args.capture(); - const argsProxy = argsProxyFor(capturedArgs, 'modifier'); - - const instance = delegate.createModifier(definition, argsProxy); - - return new CustomModifierState(element, delegate, instance, argsProxy, capturedArgs); - } - - getDebugName(state: CustomModifierState): string { - // TODO: This should be updated to call `delegate.getDebugName` or something along those lines - return String(state.modifier); - } - - getTag({ tag }: CustomModifierState): UpdatableTag { - return tag; - } - - install(state: CustomModifierState): void { - const { element, argsProxy, delegate, modifier } = state; - - if (delegate.capabilities.disableAutoTracking === true) { - untrack(() => delegate.installModifier(modifier, element, argsProxy)); - } else { - delegate.installModifier(modifier, element, argsProxy); - } - } - - update(state: CustomModifierState): void { - const { argsProxy, delegate, modifier } = state; - - if (delegate.capabilities.disableAutoTracking === true) { - untrack(() => delegate.updateModifier(modifier, argsProxy)); - } else { - delegate.updateModifier(modifier, argsProxy); - } - } - - getDestroyable(state: CustomModifierState): Destroyable { - return state; - } -} - -export const CUSTOM_MODIFIER_MANAGER = new CustomModifierManager(); - -export class VMCustomModifierDefinition - implements VMModifierDefinitionWithHandle { - public manager = CUSTOM_MODIFIER_MANAGER; - - constructor(public handle: number, public state: ModifierDefinition) {} -} diff --git a/packages/@glimmer/core/src/managers/util.ts b/packages/@glimmer/core/src/managers/util.ts deleted file mode 100644 index e4f5dfc7e..000000000 --- a/packages/@glimmer/core/src/managers/util.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { CapturedArguments } from '@glimmer/interfaces'; -import { assert } from '@glimmer/util'; -import { DEBUG } from '@glimmer/env'; -import { valueForRef } from '@glimmer/reference'; -import { TemplateArgs } from '../interfaces'; - -function convertToInt(prop: number | string | symbol): number | null { - if (typeof prop === 'symbol') return null; - - const num = Number(prop); - - if (isNaN(num)) return null; - - return num % 1 === 0 ? num : null; -} - -export function argsProxyFor( - capturedArgs: CapturedArguments, - type: 'component' | 'helper' | 'modifier' -): TemplateArgs { - const { named, positional } = capturedArgs; - - const namedHandler: ProxyHandler<{}> = { - get(_target, prop) { - const ref = named[prop as string]; - - if (ref !== undefined) { - return valueForRef(ref); - } - }, - - has(_target, prop) { - return prop in named; - }, - - ownKeys(_target) { - return Object.keys(named); - }, - - isExtensible() { - return false; - }, - - getOwnPropertyDescriptor(_target, prop) { - if (DEBUG) { - assert( - prop in named, - '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()' - ); - } - return { - enumerable: true, - configurable: true, - }; - }, - }; - - const positionalHandler: ProxyHandler<[]> = { - get(target, prop) { - if (prop === 'length') { - return positional.length; - } - - const parsed = convertToInt(prop); - - if (parsed !== null && parsed < positional.length) { - return valueForRef(positional[parsed]); - } - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (target as any)[prop]; - }, - - isExtensible() { - return false; - }, - - has(_target, prop) { - const parsed = convertToInt(prop); - - return parsed !== null && parsed < positional.length; - }, - }; - - const namedTarget = Object.create(null); - const positionalTarget: unknown[] = []; - - if (DEBUG) { - const setHandler = function (_target: unknown, prop: symbol | string | number): never { - 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 is passed down. If you want to set default values, you should use a getter and local tracked state instead.` - ); - }; - - const forInDebugHandler = (): never => { - 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; - } - - return { - named: new Proxy(namedTarget, namedHandler), - positional: new Proxy(positionalTarget, positionalHandler), - }; -} diff --git a/packages/@glimmer/core/src/owner.ts b/packages/@glimmer/core/src/owner.ts deleted file mode 100644 index f0789cec2..000000000 --- a/packages/@glimmer/core/src/owner.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { DEBUG } from '@glimmer/env'; - -const OWNER_MAP = new WeakMap<{}, unknown>(); - -export const OWNER_KEY = `__OWNER_${Math.floor(Math.random() * Date.now())}__`; - -export let DEFAULT_OWNER = {}; - -if (DEBUG) { - const OWNER_ERROR = - 'You attempted to use the Owner for a component, modifier, or helper, but did not provide an owner to `renderComponent`.'; - DEFAULT_OWNER = new Proxy(DEFAULT_OWNER, { - get(): never { - throw new Error(OWNER_ERROR); - }, - set(): never { - throw new Error(OWNER_ERROR); - }, - }); -} - -export function getOwner(obj: object): T { - return OWNER_MAP.get(obj) as T; -} - -export function setOwner(obj: object, owner: unknown): void { - OWNER_MAP.set(obj, owner); -} diff --git a/packages/@glimmer/core/src/render-component/built-ins.ts b/packages/@glimmer/core/src/render-component/built-ins.ts deleted file mode 100644 index 0ab4a219e..000000000 --- a/packages/@glimmer/core/src/render-component/built-ins.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { VMArguments } from '@glimmer/interfaces'; -import { createComputeRef, valueForRef, Reference } from '@glimmer/reference'; -import { DEBUG } from '@glimmer/env'; -import { toBool } from '@glimmer/global-context'; - -export function ifHelper(args: VMArguments): Reference { - const positional = args.positional.capture(); - - return createComputeRef( - () => { - if ((DEBUG && positional.length < 2) || positional.length > 3) { - throw new Error( - 'The inline form of the `if` helper expects two or three arguments, e.g. `{{if trialExpired "Expired" expiryDate}}`.' - ); - } - - const [condition, truthyValue, falsyValue] = positional; - - if (toBool(valueForRef(condition)) === true) { - return valueForRef(truthyValue); - } else { - return falsyValue !== undefined ? valueForRef(falsyValue) : undefined; - } - }, - null, - 'if' - ); -} diff --git a/packages/@glimmer/core/src/render-component/index.ts b/packages/@glimmer/core/src/render-component/index.ts index 4003625c8..6bf40e959 100644 --- a/packages/@glimmer/core/src/render-component/index.ts +++ b/packages/@glimmer/core/src/render-component/index.ts @@ -3,7 +3,6 @@ import { renderComponent as glimmerRenderComponent, runtimeContext, EnvironmentDelegate, - DynamicScopeImpl, renderSync, rehydrationBuilder, } from '@glimmer/runtime'; @@ -13,22 +12,16 @@ import { Dict, TemplateIterator, EnvironmentOptions, - WithStaticLayout, Environment, ElementBuilder, } from '@glimmer/interfaces'; import { artifacts } from '@glimmer/program'; -import { syntaxCompilationContext } from '@glimmer/opcode-compiler'; +import { programCompilationContext } from '@glimmer/opcode-compiler'; import { ClientEnvDelegate } from '../environment/delegates'; import { CompileTimeResolver, RuntimeResolver } from './resolvers'; -import { createConstRef, childRefFor, Reference } from '@glimmer/reference'; -import { ComponentDefinition } from '../managers/component/custom'; - -import { OWNER_KEY, DEFAULT_OWNER } from '../owner'; import { SimpleElement, SimpleDocument } from '@simple-dom/interface'; -import { unwrapTemplate } from '@glimmer/util'; export interface RenderComponentOptions { element: Element; @@ -51,6 +44,8 @@ export function didRender(): Promise { return Promise.resolve(); } +export type ComponentDefinition = object; + async function renderComponent( ComponentClass: ComponentDefinition, options: RenderComponentOptions @@ -67,7 +62,7 @@ async function renderComponent( optionsOrElement instanceof HTMLElement ? { element: optionsOrElement } : optionsOrElement; const { element, args, owner } = options; - const document = self.document as SimpleDocument; + const document = (self.document as unknown) as SimpleDocument; const { env, iterator } = getTemplateIterator( ComponentClass, @@ -117,16 +112,7 @@ function revalidate(): void { const resolver = new RuntimeResolver(); const sharedArtifacts = artifacts(); -const context = syntaxCompilationContext(sharedArtifacts, new CompileTimeResolver(resolver)); - -function dictToReference(dict: Dict): Dict { - const root = createConstRef(dict, 'args'); - - return Object.keys(dict).reduce((acc, key) => { - acc[key] = childRefFor(root, key); - return acc; - }, {} as Dict); -} +const context = programCompilationContext(sharedArtifacts, new CompileTimeResolver()); export function getTemplateIterator( ComponentClass: ComponentDefinition, @@ -134,7 +120,7 @@ export function getTemplateIterator( envOptions: EnvironmentOptions, envDelegate: EnvironmentDelegate, componentArgs: Dict = {}, - owner = DEFAULT_OWNER, + owner: object = {}, builderFactory: (env: Environment, cursor: GlimmerCursor) => ElementBuilder = clientBuilder ): { iterator: TemplateIterator; env: Environment } { const runtime = runtimeContext(envOptions, envDelegate, sharedArtifacts, resolver); @@ -143,27 +129,14 @@ export function getTemplateIterator( nextSibling: null, } as GlimmerCursor); - const handle = resolver.registerRoot(ComponentClass); - const definition = resolver.lookupComponent(handle)!; - const compilable = (definition.manager as WithStaticLayout).getStaticLayout(definition.state); - - let dynamicScope; - - if (owner) { - dynamicScope = new DynamicScopeImpl({ - [OWNER_KEY]: createConstRef(owner, 'owner'), - }); - } - return { iterator: glimmerRenderComponent( runtime, builder, context, - definition, - unwrapTemplate(compilable).asLayout(), - dictToReference(componentArgs), - dynamicScope + owner, + ComponentClass, + componentArgs ), env: runtime.env, }; diff --git a/packages/@glimmer/core/src/render-component/resolvers.ts b/packages/@glimmer/core/src/render-component/resolvers.ts index b7197c8aa..b7584abfd 100644 --- a/packages/@glimmer/core/src/render-component/resolvers.ts +++ b/packages/@glimmer/core/src/render-component/resolvers.ts @@ -1,132 +1,63 @@ import { - ResolvedValue, - ComponentDefinition as VMComponentDefinition, RuntimeResolver as VMRuntimeResolver, CompileTimeResolver as VMCompileTimeResolver, Option, - CompileTimeComponent, + InternalComponentManager, + PartialDefinition, + ResolvedComponentDefinition, } from '@glimmer/interfaces'; -import { unwrapTemplate } from '@glimmer/util'; - -import { - vmDefinitionForComponent, - vmDefinitionForHelper, - vmDefinitionForModifier, - vmDefinitionForBuiltInHelper, - VMHelperDefinition, -} from './vm-definitions'; - -import { ComponentDefinition } from '../managers/component/custom'; -import { TemplateScope } from '../template'; -import { HelperDefinition } from '../managers/helper'; -import { ifHelper } from './built-ins'; -import { DEBUG } from '@glimmer/env'; -import { ModifierDefinition } from '../managers/modifier'; - -const builtInHelpers: { [key: string]: VMHelperDefinition } = { - if: vmDefinitionForBuiltInHelper(ifHelper), -}; /////////// /** - * The RuntimeResolver is what is used to resolve everything. It is responsible - * for registering root components (passed to `renderComponent`), and resolving - * all other types of resolvables. - * - * The CompileTimeResolver is responsible for registering everything but root - * components, which is why `registry` is public, for ease of access. + * Resolution for non built ins is now handled by the vm as we are using strict mode */ export class RuntimeResolver implements VMRuntimeResolver { - registry: unknown[] = []; - - // TODO: This is only necessary because `renderJitComponent` only receives a - // string, can't receive a handle. We should make that optional somehow. - registerRoot(definition: ComponentDefinition): string { - const vmDefinition = vmDefinitionForComponent(definition); - const { handle } = vmDefinition; - - this.registry[handle] = vmDefinition; - - // We're lying to the type system here so we can pass handle around as a - // string. Should definitely fix this in the future. - return (handle as unknown) as string; - } - - lookupComponent(handle: string, _referrer?: unknown): Option { - return this.registry[(handle as unknown) as number] as Option; - } - - resolve(handle: number): U { - return this.registry[handle] as U; + lookupComponent( + _name: string, + _owner: object + ): Option< + ResolvedComponentDefinition> + > { + return null; } - - lookupPartial(_name: string, _referrer?: unknown): Option { - throw new Error('Method not implemented.'); + lookupPartial(_name: string, _owner: object): Option { + return null; } } /////////// /** - * The CompileTimeResolver is what is used to lookup most things, with the - * exception of root components rendered with `renderComponent`. It registers - * the values on the RuntimeResolver, which Glimmer then uses to actually - * resolve later on via the handle that is returned. + * Resolution for non built ins is now handled by the vm as we are using strict mode */ export class CompileTimeResolver implements VMCompileTimeResolver { - constructor(private inner: RuntimeResolver) {} - - lookupHelper(name: string, _scope: TemplateScope): Option { - const scope = _scope(); - const { helper, handle } = - builtInHelpers[name] || vmDefinitionForHelper(scope[name] as HelperDefinition); - - this.inner.registry[handle] = helper; - return handle; + lookupHelper(_name: string, _owner: object): Option { + return null; } - lookupModifier(name: string, _scope: TemplateScope): Option { - const scope = _scope(); - const modifier = scope[name] as ModifierDefinition; - - const definition = vmDefinitionForModifier(modifier); - const { handle } = definition; - - this.inner.registry[handle] = definition; - - return handle; + lookupModifier(_name: string, _owner: object): Option { + return null; } - lookupComponent(name: string, _scope: TemplateScope): Option { - const scope = _scope(); - const ComponentDefinition = scope[name] as ComponentDefinition; - - if (DEBUG && ComponentDefinition === undefined) { - throw new Error( - `Cannot find component \`${name}\` in scope. It was used in a template, but not imported into the template scope or defined as a local variable. If you meant to access a property, you must add \`this\` to it: \`\`` - ); - } - - const definition = vmDefinitionForComponent(ComponentDefinition); - const { state, manager, template, handle } = definition; - - this.inner.registry[handle] = definition; + lookupComponent( + _name: string, + _owner: object + ): Option< + ResolvedComponentDefinition> + > { + return null; + } - return { - handle, - capabilities: manager.getCapabilities(state), - compilable: unwrapTemplate(template).asLayout(), - }; + lookupPartial(_name: string, _owner: object): Option { + return null; } - // TODO: Remove this in the future - resolve(): null { + lookupBuiltInHelper(_name: string): Option { return null; } - // TODO: Make this optional - lookupPartial(_name: string, _referrer: unknown): Option { - throw new Error('Method not implemented.'); + lookupBuiltInModifier(_name: string): Option { + return null; } } diff --git a/packages/@glimmer/core/src/render-component/vm-definitions.ts b/packages/@glimmer/core/src/render-component/vm-definitions.ts deleted file mode 100644 index ebff455e3..000000000 --- a/packages/@glimmer/core/src/render-component/vm-definitions.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { - ComponentDefinition as VMComponentDefinition, - ModifierDefinition as VMModifierDefinition, - Helper as VMHelperFactory, - TemplateOk, - InternalModifierManager, -} from '@glimmer/interfaces'; -import { templateFactory } from '@glimmer/opcode-compiler'; - -import { getComponentTemplate, SCOPE_MAP } from '../template'; -import { VMCustomComponentDefinition, ComponentDefinition } from '../managers/component/custom'; -import { HelperDefinition, vmHelperFactoryFor } from '../managers/helper'; -import { - TemplateOnlyComponentDefinition, - TemplateOnlyComponent, -} from '../managers/component/template-only'; -import { DEBUG } from '@glimmer/env'; -import { ModifierDefinition, VMCustomModifierDefinition } from '../managers/modifier'; - -export interface VMComponentDefinitionWithHandle extends VMComponentDefinition { - handle: number; - template: TemplateOk; -} - -export interface VMModifierDefinitionWithHandle extends VMModifierDefinition { - handle: number; -} - -export interface VMHelperDefinition { - helper: VMHelperFactory; - handle: number; -} - -export interface Modifier { - state: unknown; - manager: InternalModifierManager; -} - -/////////// - -let HANDLE = 0; - -const VM_COMPONENT_DEFINITIONS = new WeakMap< - ComponentDefinition, - VMComponentDefinitionWithHandle ->(); -const VM_HELPER_DEFINITIONS = new WeakMap(); -const VM_MODIFIER_DEFINITIONS = new WeakMap(); - -export function vmDefinitionForComponent( - ComponentDefinition: ComponentDefinition -): VMComponentDefinitionWithHandle { - return ( - VM_COMPONENT_DEFINITIONS.get(ComponentDefinition) || - createVMComponentDefinition(ComponentDefinition) - ); -} - -export function vmDefinitionForHelper(Helper: HelperDefinition): VMHelperDefinition { - return VM_HELPER_DEFINITIONS.get(Helper) || createVMHelperDefinition(Helper); -} - -export function vmDefinitionForModifier( - Modifier: ModifierDefinition -): VMModifierDefinitionWithHandle { - return VM_MODIFIER_DEFINITIONS.get(Modifier) || createVMModifierDefinition(Modifier); -} - -/////////// - -let BUILT_INS: WeakSet | undefined; - -if (DEBUG) { - BUILT_INS = new WeakSet(); -} - -function handleForBuiltIn(builtIn: object): number { - if (DEBUG && BUILT_INS!.has(builtIn)) { - throw new Error('attempted to register the same built-in twice'); - } - - return HANDLE++; -} - -export function vmDefinitionForBuiltInHelper(helper: VMHelperFactory): VMHelperDefinition { - return { - helper, - handle: handleForBuiltIn(helper), - }; -} - -/////////// - -function createVMComponentDefinition( - ComponentDefinition: ComponentDefinition | TemplateOnlyComponent -): VMComponentDefinitionWithHandle { - const serializedTemplate = getComponentTemplate(ComponentDefinition)!; - const scope = SCOPE_MAP.get(serializedTemplate); - const template = templateFactory(serializedTemplate)(scope); - - let definition; - - if (ComponentDefinition instanceof TemplateOnlyComponent) { - // TODO: We probably need a better way to get a name for the template, - // currently it'll just be `template-only-component` which is not great - // for debugging - definition = new TemplateOnlyComponentDefinition(HANDLE++, 'template-only-component', template); - } else { - definition = new VMCustomComponentDefinition(HANDLE++, ComponentDefinition, template); - } - - VM_COMPONENT_DEFINITIONS.set(ComponentDefinition, definition); - - return definition; -} - -function createVMHelperDefinition(userDefinition: HelperDefinition): VMHelperDefinition { - const definition = { - helper: vmHelperFactoryFor(userDefinition), - handle: HANDLE++, - }; - - VM_HELPER_DEFINITIONS.set(userDefinition, definition); - return definition; -} - -function createVMModifierDefinition(Modifier: ModifierDefinition): VMModifierDefinitionWithHandle { - const definition = new VMCustomModifierDefinition(HANDLE++, Modifier); - - VM_MODIFIER_DEFINITIONS.set(Modifier, definition); - - return definition; -} diff --git a/packages/@glimmer/core/src/template.ts b/packages/@glimmer/core/src/template.ts index 7a9fe9c1a..c11de33a1 100644 --- a/packages/@glimmer/core/src/template.ts +++ b/packages/@glimmer/core/src/template.ts @@ -1,92 +1,25 @@ import { DEBUG } from '@glimmer/env'; -import { - SerializedTemplateWithLazyBlock, - Dict, - SerializedTemplateBlockJSON, -} from '@glimmer/interfaces'; - -export interface TemplateMeta { - scope: () => Dict; -} -export type TemplateScope = () => Dict; - -const TEMPLATE_MAP = new WeakMap(); -const getPrototypeOf = Object.getPrototypeOf; - -export const SCOPE_MAP = new WeakMap(); - -export interface CustomSerializedTemplate { - id?: string | null; - block: SerializedTemplateBlockJSON; - meta: TemplateMeta; -} +import { setComponentTemplate as vmSetComponentTemplate } from '@glimmer/manager'; +import { SerializedTemplateWithLazyBlock, Dict } from '@glimmer/interfaces'; +import { templateFactory } from '@glimmer/opcode-compiler'; // This is provided by the `babel-plugin-strict-template-precompile` plugin export let createTemplate: ( scopeOrTemplate: Dict | string, template?: string -) => CustomSerializedTemplate; +) => SerializedTemplateWithLazyBlock; if (DEBUG) { - createTemplate = (): CustomSerializedTemplate => { + createTemplate = (): SerializedTemplateWithLazyBlock => { throw new Error( 'createTemplate() is meant to be preprocessed with a babel plugin, @glimmer/babel-plugin-strict-template-precompile. If you are seeing this error message, it means that you do not have this babel plugin installed, or it is not enabled correctly' ); }; } -const builtInHelpers = { - if: true, - each: true, -}; - -export function setComponentTemplate( - template: CustomSerializedTemplate, - ComponentClass: T -): T { - const { - id, - block, - meta: { scope }, - } = template; - - if (DEBUG) { - const evaluatedScope = scope(); - const parsed = JSON.parse(block); - - if (parsed.upvars) { - for (const upvar of parsed.upvars) { - if (!(upvar in builtInHelpers) && evaluatedScope[upvar] === undefined) { - throw new Error( - `Cannot find identifier \`${upvar}\` in scope. It was used in a template, but not imported into the template scope or defined as a local variable. If you meant to access a property, you must add \`this\` to it: \`{{this.${upvar}}}\`` - ); - } - } - } - } - - const newTemplate = { id, block, moduleName: '(unknown module)' }; - - SCOPE_MAP.set(newTemplate, scope); - - TEMPLATE_MAP.set(ComponentClass, newTemplate); - return ComponentClass; -} - -export function getComponentTemplate( - ComponentClass: T -): SerializedTemplateWithLazyBlock | undefined { - let pointer = ComponentClass; - - while (pointer !== undefined && pointer !== null) { - const manager = TEMPLATE_MAP.get(pointer); - - if (manager !== undefined) { - return manager; - } - - pointer = getPrototypeOf(pointer); - } - - return undefined; +export function setComponentTemplate( + template: SerializedTemplateWithLazyBlock, + ComponentClass: object +): object { + return vmSetComponentTemplate(templateFactory(template), ComponentClass); } diff --git a/packages/@glimmer/core/test/interactive/fn-test.ts b/packages/@glimmer/core/test/interactive/fn-test.ts index 949b427c7..862812690 100644 --- a/packages/@glimmer/core/test/interactive/fn-test.ts +++ b/packages/@glimmer/core/test/interactive/fn-test.ts @@ -1,11 +1,11 @@ -import { module, test, render, settled, tracked } from '../utils'; +import { test, render, settled, tracked } from '../utils'; import Component from '@glimmer/component'; import { fn } from '@glimmer/helper'; import { on, action } from '@glimmer/modifier'; import { setComponentTemplate, createTemplate, templateOnlyComponent } from '@glimmer/core'; -module('[@glimmer/core] interactive - {{fn}}', () => { +QUnit.module('[@glimmer/core] interactive - {{fn}}', () => { test('can curry arguments via fn', async function (assert) { assert.expect(9); @@ -14,7 +14,7 @@ module('[@glimmer/core] interactive - {{fn}}', () => { const args = tracked({ name: 'world' }); class HelloWorld extends Component { - constructor(owner: unknown, args: {}) { + constructor(owner: object, args: {}) { super(owner, args); helloWorldComponent = this; } @@ -69,7 +69,7 @@ module('[@glimmer/core] interactive - {{fn}}', () => { class ParentComponent extends Component { name = 'world'; - constructor(owner: unknown, args: {}) { + constructor(owner: object, args: {}) { super(owner, args); parentComponent = this; } @@ -120,25 +120,6 @@ module('[@glimmer/core] interactive - {{fn}}', () => { Parent ); - assert.rejects(render(Parent), /fn must receive a function as its first parameter/); - }); - - test('fn helper invoked without a parameter raises an error', function (assert) { - class Parent extends Component { - @action - exists(): void { - assert.ok(false, 'this shouldnt run'); - } - } - - setComponentTemplate( - createTemplate({ on, fn }, ''), - Parent - ); - - assert.rejects( - render(Parent), - /fn must receive at least one argument to pass to the function, otherwise there is no need to use fn./ - ); + assert.rejects(render(Parent), /You must pass a function as the `fn` helpers first argument/); }); }); diff --git a/packages/@glimmer/core/test/interactive/helper-test.ts b/packages/@glimmer/core/test/interactive/helper-test.ts deleted file mode 100644 index 4fc92f3c8..000000000 --- a/packages/@glimmer/core/test/interactive/helper-test.ts +++ /dev/null @@ -1,427 +0,0 @@ -import { module, test, render, settled, tracked as trackedObj } from '../utils'; - -import Component from '@glimmer/component'; -import { tracked } from '@glimmer/tracking'; -import { - setComponentTemplate, - createTemplate, - getOwner, - templateOnlyComponent, -} from '@glimmer/core'; -import { helper } from '../utils/custom-helper'; - -module('[@glimmer/core] interactive - helper', () => { - test('simple helpers update when args change', async (assert) => { - let count = 0; - - function myHelper(name: string, greeting: string): string { - count++; - return `helper ${greeting} ${name}`; - } - - const args = trackedObj({ name: 'Rob' }); - - const MyComponent = setComponentTemplate( - createTemplate({ myHelper }, '

{{myHelper @name "Hello"}}

'), - templateOnlyComponent() - ); - - let html = await render(MyComponent, { args }); - - assert.strictEqual(html, '

helper Hello Rob

', 'the template was rendered'); - assert.equal(count, 1, 'helper rendered once'); - - args.name = 'Tom'; - - html = await settled(); - - assert.strictEqual(html, '

helper Hello Tom

', 'the template was rendered'); - assert.equal(count, 2, 'simple helper reran after positional args changed'); - }); - - test('custom helpers update when positional args change', async (assert) => { - let count = 0; - - const myHelper = helper( - class { - args: { - positional: [string]; - named: { - greeting: string; - }; - }; - - get value(): string { - count++; - return `helper ${this.args.named.greeting} ${this.args.positional[0]}`; - } - } - ); - - let component: MyComponent; - - class MyComponent extends Component { - constructor(owner: unknown, args: {}) { - super(owner, args); - component = this; - } - - @tracked name = 'Rob'; - @tracked greeting = 'Hello'; - } - - setComponentTemplate( - createTemplate({ myHelper }, '

{{myHelper this.name greeting=this.greeting}}

'), - MyComponent - ); - - let html = await render(MyComponent); - assert.equal(count, 1, 'helper rendered once'); - - assert.strictEqual(html, '

helper Hello Rob

', 'the template was rendered'); - - component!.name = 'Tom'; - - html = await settled(); - - assert.strictEqual(html, '

helper Hello Tom

', 'the template was rendered'); - assert.equal(count, 2, 'helper reran after positional args changed'); - }); - - test('custom helpers update when named args change', async (assert) => { - let count = 0; - - const myHelper = helper( - class { - args: { - positional: [string]; - named: { - greeting: string; - }; - }; - - get value(): string { - count++; - return `helper ${this.args.named.greeting} ${this.args.positional[0]}`; - } - } - ); - - let component: MyComponent; - - class MyComponent extends Component { - constructor(owner: unknown, args: {}) { - super(owner, args); - component = this; - } - - @tracked name = 'Rob'; - @tracked greeting = 'Hello'; - } - - setComponentTemplate( - createTemplate({ myHelper }, '

{{myHelper this.name greeting=this.greeting}}

'), - MyComponent - ); - - let html = await render(MyComponent); - assert.equal(count, 1, 'helper rendered once'); - - assert.strictEqual(html, '

helper Hello Rob

', 'the template was rendered'); - - component!.greeting = 'Hola'; - - html = await settled(); - - assert.strictEqual(html, '

helper Hola Rob

', 'the template was rendered'); - assert.equal(count, 2, 'helper reran after named args changed'); - }); - - test('helpers are not volatile', async (assert) => { - let count = 0; - - function myHelper(name: string, greeting: string): string { - count++; - - return `helper ${greeting} ${name}`; - } - - let component: MyComponent; - - class MyComponent extends Component { - constructor(owner: unknown, args: {}) { - super(owner, args); - component = this; - } - - @tracked name = 'Rob'; - @tracked foo = 123; - } - - setComponentTemplate( - createTemplate({ myHelper }, '

{{myHelper this.name "Hello"}}

'), - MyComponent - ); - - let html = await render(MyComponent); - - assert.strictEqual(html, '

helper Hello Rob

', 'the template was rendered'); - assert.equal(count, 1, 'helper rendered once'); - - component!.foo = 456; - - html = await settled(); - - assert.strictEqual(html, '

helper Hello Rob

', 'the template was rendered'); - assert.equal(count, 1, 'helper did not rerender after unrelated change'); - }); - - test('custom helpers lifecycle (basic)', async (assert) => { - let calls: string[] = []; - - const myHelper = helper( - class { - args: { - positional: [string]; - named: { - greeting: string; - }; - }; - - setup(): void { - // entangle a value that changes - this.args.positional[0]; - calls.push('setup'); - } - - update(): void { - calls.push('update'); - } - - teardown(): void { - calls.push('teardown'); - } - - get value(): string { - return `helper ${this.args.named.greeting} ${this.args.positional[0]}`; - } - } - ); - - let component: MyComponent; - - class MyComponent extends Component { - constructor(owner: unknown, args: {}) { - super(owner, args); - component = this; - } - - @tracked name = 'Rob'; - @tracked cond = true; - } - - setComponentTemplate( - createTemplate( - { myHelper }, - '{{#if this.cond}}{{myHelper this.name greeting="Hello"}}{{/if}}' - ), - MyComponent - ); - - let html = await render(MyComponent); - - assert.strictEqual(html, 'helper Hello Rob', 'the template was rendered'); - assert.deepEqual(calls, ['setup'], 'setup hook called correctly'); - - calls = []; - component!.name = 'Tom'; - html = await settled(); - - assert.strictEqual(html, 'helper Hello Tom', 'the template was updated'); - assert.deepEqual(calls, ['update'], 'update hook called correctly'); - - calls = []; - component!.cond = false; - html = await settled(); - - assert.strictEqual(html, '', 'the template was updated'); - assert.deepEqual(calls, ['teardown'], 'teardown hook called correctly'); - }); - - test('update and value hook lifecycle and memoization', async (assert) => { - // This is necessary because we can't create and dirty tags directly because - // that doesn't trigger `propertyDidChange` currently. - class Tag { - @tracked private value = undefined; - - consume(): void { - this.value; - } - - dirty(): void { - // eslint-disable-next-line no-self-assign - this.value = this.value; - } - } - - const updateTag = new Tag(); - const valueTag = new Tag(); - - let calls: string[] = []; - - const myHelper = helper( - class { - args: { - positional: [string]; - named: { - greeting: string; - }; - }; - - setup(): void { - updateTag.consume(); - calls.push('setup'); - } - - update(): void { - updateTag.consume(); - calls.push('update'); - } - - get value(): string { - valueTag.consume(); - calls.push('value'); - - return 'Hello, world!'; - } - } - ); - - const MyComponent = setComponentTemplate( - createTemplate({ myHelper }, '{{myHelper}}'), - templateOnlyComponent() - ); - - let html = await render(MyComponent); - - assert.strictEqual(html, 'Hello, world!', 'the template was rendered'); - assert.deepEqual( - calls, - ['setup', 'value'], - 'setup and value hooks called on initial render in correct order' - ); - - calls = []; - valueTag.dirty(); - html = await settled(); - - assert.deepEqual(calls, ['value'], 'value hook called when tag used in value dirtied'); - - calls = []; - updateTag.dirty(); - html = await settled(); - - assert.deepEqual( - calls, - ['update'], - 'update hook called when tag used in previous setup dirtied' - ); - - calls = []; - valueTag.dirty(); - html = await settled(); - - assert.deepEqual(calls, ['value'], 'value hook still called with tag used in value dirtied'); - - calls = []; - updateTag.dirty(); - html = await settled(); - - assert.deepEqual( - calls, - ['update'], - 'update hook called when tag used in previous update dirtied' - ); - - calls = []; - updateTag.dirty(); - valueTag.dirty(); - html = await settled(); - - assert.deepEqual( - calls, - ['update', 'value'], - 'update and value hooks called in correct order when both tags dirtied' - ); - }); - - test('custom helpers update when local tracked props change', async (assert) => { - let helperInstance: MyHelper; - - class MyHelper { - constructor() { - helperInstance = this; - } - - @tracked locale = 'en_US'; - - get value(): string { - return this.locale; - } - } - - const myHelper = helper(MyHelper); - - const MyComponent = setComponentTemplate( - createTemplate({ myHelper }, '

{{myHelper}}

'), - templateOnlyComponent() - ); - - let html = await render(MyComponent); - - assert.strictEqual(html, '

en_US

', 'the template was rendered'); - - helperInstance!.locale = 'en_UK'; - - html = await settled(); - - assert.strictEqual(html, '

en_UK

', 'the template was updated'); - }); - - test('custom helpers update when values on owner change', async (assert) => { - class Owner { - services = { - locale: new LocaleService(), - }; - } - - class LocaleService { - @tracked currentLocale = 'en_US'; - } - - const myHelper = helper( - class { - get value(): string { - return getOwner(this).services.locale.currentLocale; - } - } - ); - - const MyComponent = setComponentTemplate( - createTemplate({ myHelper }, '

{{myHelper}}

'), - templateOnlyComponent() - ); - - const owner = new Owner(); - - let html = await render(MyComponent, { owner }); - - assert.strictEqual(html, '

en_US

', 'the template was rendered'); - - owner.services.locale.currentLocale = 'en_UK'; - - html = await settled(); - - assert.strictEqual(html, '

en_UK

', 'the template was updated'); - }); -}); diff --git a/packages/@glimmer/core/test/interactive/index.ts b/packages/@glimmer/core/test/interactive/index.ts index 316734e7b..6367af9c0 100644 --- a/packages/@glimmer/core/test/interactive/index.ts +++ b/packages/@glimmer/core/test/interactive/index.ts @@ -1,4 +1,3 @@ import './fn-test'; import './render-test'; -import './helper-test'; import './modifier-test'; diff --git a/packages/@glimmer/core/test/interactive/modifier-test.ts b/packages/@glimmer/core/test/interactive/modifier-test.ts index 36f9d7baa..1a12237e6 100644 --- a/packages/@glimmer/core/test/interactive/modifier-test.ts +++ b/packages/@glimmer/core/test/interactive/modifier-test.ts @@ -1,57 +1,10 @@ -import { module, test, render, settled, tracked } from '../utils'; -import { click, find } from '../utils/dom'; - +import { test, render, settled, tracked } from '../utils'; +import { click } from '../utils/dom'; import { on, action } from '@glimmer/modifier'; import Component from '@glimmer/component'; -import { - setComponentTemplate, - createTemplate, - modifierCapabilities, - TemplateArgs, - setModifierManager, - ModifierManager, - templateOnlyComponent, -} from '@glimmer/core'; -import { Dict } from '@glimmer/interfaces'; - -class CustomModifier { - element?: Element; - // eslint-disable-next-line @typescript-eslint/no-empty-function - didInsertElement(_positional: unknown[], _named: Dict): void {} - // eslint-disable-next-line @typescript-eslint/no-empty-function - didUpdate(_positional: unknown[], _named: Dict): void {} - // eslint-disable-next-line @typescript-eslint/no-empty-function - willDestroyElement(): void {} -} - -class CustomModifierManager implements ModifierManager { - capabilities = modifierCapabilities('3.13'); - - constructor(private owner: unknown) {} - - createModifier(factory: { new (owner: unknown): CustomModifier }): CustomModifier { - return new factory(this.owner); - } - - installModifier(instance: CustomModifier, element: Element, args: TemplateArgs): void { - instance.element = element; - const { positional, named } = args; - instance.didInsertElement(positional, named); - } - - updateModifier(instance: CustomModifier, args: TemplateArgs): void { - const { positional, named } = args; - instance.didUpdate(positional, named); - } +import { setComponentTemplate, createTemplate } from '@glimmer/core'; - destroyModifier(instance: CustomModifier): void { - instance.willDestroyElement(); - } -} - -setModifierManager((owner) => new CustomModifierManager(owner), CustomModifier); - -module('Modifier Tests', () => { +QUnit.module('Modifier Tests', () => { test('Supports the on modifier', async (assert) => { const args = tracked({ count: 0 }); @@ -84,262 +37,4 @@ module('Modifier Tests', () => { 'the component was rerendered' ); }); - - test('simple functions can be used as modifiers', async (assert) => { - function modifier(element: Element, arg1: string, arg2: number): void { - assert.equal(element, find('h1'), 'modifier received'); - assert.equal(arg1, 'string', 'modifier received'); - assert.equal(arg2, 123, 'modifier received'); - } - - const Component = setComponentTemplate( - createTemplate({ modifier }, '

hello world

'), - templateOnlyComponent() - ); - - await render(Component); - }); - - test('simple function modifiers throw an error when using named arguments', async (assert) => { - function modifier(): void { - assert.ok(false, 'should not be called'); - } - - const Component = setComponentTemplate( - createTemplate({ modifier }, '

hello world

'), - templateOnlyComponent() - ); - - try { - await render(Component); - } catch (e) { - assert.equal( - e.message, - 'You used named arguments with the modifier modifier, but it is a standard function. Normal functions cannot receive named arguments when used as modifiers.', - 'error thrown correctly' - ); - } - }); - - test('simple function modifier lifecycle', async (assert) => { - const hooks: string[] = []; - - function modifier(): () => void { - hooks.push('installed'); - - return (): void => { - hooks.push('removed'); - }; - } - - const Component = setComponentTemplate( - createTemplate( - { modifier }, - '{{#if @truthy}}

hello world

{{/if}}' - ), - templateOnlyComponent() - ); - - await render(Component); - - const args = tracked({ - truthy: true, - value: 123, - }); - - await render(Component, { args }); - - assert.deepEqual(hooks, ['installed'], 'installs correctly'); - - // trigger update - args.value = 456; - await settled(); - - assert.deepEqual( - hooks, - ['installed', 'removed', 'installed'], - 'removes and reinstalls on updates' - ); - - // trigger destruction - args.truthy = false; - await settled(); - - assert.deepEqual( - hooks, - ['installed', 'removed', 'installed', 'removed'], - 'removes on final destruction' - ); - }); - - test('custom modifiers correctly receive element', async (assert) => { - assert.expect(3); - - class Modifier extends CustomModifier { - didInsertElement(positional: unknown[]): void { - positional[0]; - assert.equal(this.element, find('h1'), 'element is correctly assigned in didInsertElement'); - } - - didUpdate(): void { - assert.equal(this.element, find('h1'), 'element still exists in didUpdate'); - } - - willDestroyElement(): void { - assert.ok( - this.element instanceof HTMLElement, - 'element still exists in willDestroyElement' - ); - } - } - - const Component = setComponentTemplate( - createTemplate( - { modifier: Modifier }, - '{{#if @truthy}}

hello world

{{/if}}' - ), - templateOnlyComponent() - ); - - const args = tracked({ - truthy: true, - value: 123, - }); - - await render(Component, { args }); - - // trigger update - args.value = 456; - await settled(); - - // trigger destruction - args.truthy = false; - await settled(); - }); - - test('custom lifecycle hooks', async (assert) => { - const hooks: string[] = []; - const positionalArgs: unknown[][] = []; - const namedArgs: Dict[] = []; - - class Modifier extends CustomModifier { - didInsertElement(positional: unknown[], named: Dict): void { - hooks.push('didInsertElement'); - positionalArgs.push(positional.slice()); - namedArgs.push(Object.assign({}, named)); - } - - didUpdate(positional: unknown[], named: Dict): void { - hooks.push('didUpdate'); - positionalArgs.push(positional.slice()); - namedArgs.push(Object.assign({}, named)); - } - - willDestroyElement(): void { - hooks.push('willDestroyElement'); - } - } - - const Component = setComponentTemplate( - createTemplate( - { modifier: Modifier }, - '{{#if @truthy}}

hello world

{{/if}}' - ), - templateOnlyComponent() - ); - - const args = tracked({ - truthy: true, - value: 123, - }); - - await render(Component, { args }); - - assert.deepEqual(hooks, ['didInsertElement'], 'modifier initialized correctly'); - assert.deepEqual(positionalArgs, [[123]], 'modifier initialized correctly'); - assert.deepEqual(namedArgs, [{ foo: 123 }], 'modifier initialized correctly'); - - args.value = 456; - await settled(); - - assert.deepEqual(hooks, ['didInsertElement', 'didUpdate'], 'modifier initialized correctly'); - assert.deepEqual(positionalArgs, [[123], [456]], 'modifier initialized correctly'); - assert.deepEqual(namedArgs, [{ foo: 123 }, { foo: 456 }], 'modifier initialized correctly'); - - args.truthy = false; - await settled(); - - assert.deepEqual( - hooks, - ['didInsertElement', 'didUpdate', 'willDestroyElement'], - 'modifier initialized correctly' - ); - assert.deepEqual(positionalArgs, [[123], [456]], 'modifier initialized correctly'); - assert.deepEqual(namedArgs, [{ foo: 123 }, { foo: 456 }], 'modifier initialized correctly'); - }); - - test('lifecycle hooks are autotracked by default', async (assert) => { - const obj = tracked({ - foo: 123, - bar: 456, - }); - - const hooks: string[] = []; - - class Modifier extends CustomModifier { - didInsertElement(): void { - // read and entangle - obj.foo; - hooks.push('insert'); - } - - didUpdate(): void { - // read and entangle - obj.bar; - hooks.push('update'); - } - } - - const Component = setComponentTemplate( - createTemplate({ modifier: Modifier }, '

hello world

'), - templateOnlyComponent() - ); - - const html = await render(Component); - assert.equal(html, `

hello world

`, 'rendered correctly'); - - assert.deepEqual(hooks, ['insert'], 'correct hooks called on initial render'); - - obj.bar++; - await settled(); - - assert.deepEqual(hooks, ['insert'], 'update not called when unconsumed prop is updated'); - - obj.foo++; - await settled(); - - assert.deepEqual( - hooks, - ['insert', 'update'], - 'update called when prop consumed in prop is updated' - ); - - obj.foo++; - await settled(); - - assert.deepEqual( - hooks, - ['insert', 'update'], - 'update not called when unconsumed prop is updated' - ); - - obj.bar++; - await settled(); - - assert.deepEqual( - hooks, - ['insert', 'update', 'update'], - 'update called when prop consumed in prop is updated' - ); - }); }); diff --git a/packages/@glimmer/core/test/interactive/render-test.ts b/packages/@glimmer/core/test/interactive/render-test.ts index 650ef6db5..45fbd765d 100644 --- a/packages/@glimmer/core/test/interactive/render-test.ts +++ b/packages/@glimmer/core/test/interactive/render-test.ts @@ -1,8 +1,8 @@ import { createTemplate } from '@glimmer/core'; -import { module, test, render } from '../utils'; +import { test, render } from '../utils'; -module(`[@glimmer/core] interactive rendering tests`, () => { +QUnit.module(`[@glimmer/core] interactive rendering tests`, () => { test('renders multiple components in different places', async (assert) => { assert.expect(2); diff --git a/packages/@glimmer/core/test/non-interactive/component-template-test.ts b/packages/@glimmer/core/test/non-interactive/component-template-test.ts deleted file mode 100644 index 3183fe9b1..000000000 --- a/packages/@glimmer/core/test/non-interactive/component-template-test.ts +++ /dev/null @@ -1,85 +0,0 @@ -const { module, test } = QUnit; - -import { - getComponentTemplate, - setComponentTemplate, - CustomSerializedTemplate, -} from '../../src/template'; -import { SerializedTemplateWithLazyBlock } from '@glimmer/interfaces'; - -class FakeTemplate implements CustomSerializedTemplate { - constructor(public block: string) {} - - meta = { - scope: (): {} => ({}), - }; -} - -function makeInternalTemplate(block: string): SerializedTemplateWithLazyBlock { - return { - block, - id: undefined, - moduleName: '(unknown module)', - }; -} - -module('component templates', () => { - test('setting and getting', (assert) => { - const templateA = new FakeTemplate('{ "foo": "A" }'); - const templateAAB = new FakeTemplate('{ "foo": "AAB" }'); - - const internalTemplateA = makeInternalTemplate('{ "foo": "A" }'); - const internalTemplateAAB = makeInternalTemplate('{ "foo": "AAB" }'); - - class A {} - setComponentTemplate(templateA, A); - class AA extends A {} - class AB extends A {} - class AAA extends AA {} - class AAB extends AA {} - setComponentTemplate(templateAAB, AAB); - - class B {} - class BA {} - - assert.deepEqual( - getComponentTemplate(A), - internalTemplateA, - 'class A returns explicitly associated template' - ); - - assert.deepEqual( - getComponentTemplate(AA), - internalTemplateA, - 'class AA returns inherited template from parent' - ); - - assert.deepEqual( - getComponentTemplate(AB), - internalTemplateA, - 'class AA returns inherited template from parent' - ); - - assert.deepEqual( - getComponentTemplate(AAA), - internalTemplateA, - 'class AAA returns inherited template from grandparent' - ); - - assert.deepEqual( - getComponentTemplate(AAB), - internalTemplateAAB, - 'class AAA returns explicitly associated template' - ); - - assert.deepEqual( - getComponentTemplate(AAB), - internalTemplateAAB, - 'class AAA returns explicitly associated template' - ); - - assert.strictEqual(getComponentTemplate(B), undefined, 'class B returns undefined template'); - - assert.strictEqual(getComponentTemplate(BA), undefined, 'class BA returns undefined template'); - }); -}); diff --git a/packages/@glimmer/core/test/non-interactive/each-test.ts b/packages/@glimmer/core/test/non-interactive/each-test.ts index 9b1a10a9d..d013b4c1f 100644 --- a/packages/@glimmer/core/test/non-interactive/each-test.ts +++ b/packages/@glimmer/core/test/non-interactive/each-test.ts @@ -1,6 +1,6 @@ import Component from '@glimmer/component'; -import { module, test, render } from '../utils'; +import { test, render } from '../utils'; import { setComponentTemplate, createTemplate } from '@glimmer/core'; function freeze(array: T[]): ReadonlyArray> { @@ -18,7 +18,7 @@ class HelloWorld extends Component { frozenObjects = freeze(this.objects); } -module('[@glimmer/core] each helper', () => { +QUnit.module('[@glimmer/core] each helper', () => { test('throw error if unknown special key used as key for #each', function (assert) { const Component = class extends HelloWorld {}; diff --git a/packages/@glimmer/core/test/non-interactive/helper-test.ts b/packages/@glimmer/core/test/non-interactive/helper-test.ts deleted file mode 100644 index 311ad5f3a..000000000 --- a/packages/@glimmer/core/test/non-interactive/helper-test.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { module, test, render } from '../utils'; -import { helper } from '../utils/custom-helper'; - -import Component from '@glimmer/component'; -import { tracked } from '@glimmer/tracking'; -import { - setComponentTemplate, - createTemplate, - getOwner, - templateOnlyComponent, -} from '@glimmer/core'; - -module('[@glimmer/core] non-interactive - helper', () => { - test('simple helpers work', async (assert) => { - function myHelper(name: string, greeting: string): string { - return `helper ${greeting} ${name}`; - } - - class MyComponent extends Component { - @tracked name = 'Rob'; - @tracked foo = 123; - } - - setComponentTemplate( - createTemplate({ myHelper }, '

{{myHelper this.name "Hello"}}

'), - MyComponent - ); - - const html = await render(MyComponent); - - assert.strictEqual(html, '

helper Hello Rob

', 'the template was rendered'); - }); - - test('simple helpers throw when using named args', (assert) => { - function myHelper(): void { - assert.ok(false, 'helper should not be called'); - } - - class MyComponent extends Component {} - - setComponentTemplate( - createTemplate({ myHelper }, '

{{myHelper this.name greeting="Hello"}}

'), - MyComponent - ); - - assert.rejects( - render(MyComponent), - /Error: You used named arguments with the myHelper helper, but it is a standard function. Normal functions cannot receive named arguments when used as helpers./ - ); - }); - - test('custom helpers work', async (assert) => { - const myHelper = helper( - class { - args: { - positional: [string]; - named: { - greeting: string; - }; - }; - - get value(): string { - return `helper ${this.args.named.greeting} ${this.args.positional[0]}`; - } - } - ); - - class MyComponent extends Component { - @tracked name = 'Rob'; - @tracked foo = 123; - } - - setComponentTemplate( - createTemplate({ myHelper }, '

{{myHelper this.name greeting="Hello"}}

'), - MyComponent - ); - - const html = await render(MyComponent); - - assert.strictEqual(html, '

helper Hello Rob

', 'the template was rendered'); - }); - - test('custom helpers have access to host meta', async (assert) => { - class Owner { - services = { - locale: new LocaleService(), - }; - } - - class LocaleService { - get currentLocale(): string { - return 'en_US'; - } - } - - const myHelper = helper( - class { - get value(): string { - return getOwner(this).services.locale.currentLocale; - } - } - ); - - const MyComponent = setComponentTemplate( - createTemplate({ myHelper }, '

{{myHelper}}

'), - templateOnlyComponent() - ); - - const html = await render(MyComponent, { - owner: new Owner(), - }); - - assert.strictEqual(html, '

en_US

', 'the template was rendered'); - }); -}); diff --git a/packages/@glimmer/core/test/non-interactive/index.ts b/packages/@glimmer/core/test/non-interactive/index.ts index 369791e35..5d046edf7 100644 --- a/packages/@glimmer/core/test/non-interactive/index.ts +++ b/packages/@glimmer/core/test/non-interactive/index.ts @@ -1,6 +1,3 @@ -import './component-template-test'; import './render-test'; import './each-test'; -import './helper-test'; import './to-bool-test'; -import './strict-mode-test'; diff --git a/packages/@glimmer/core/test/non-interactive/render-test.ts b/packages/@glimmer/core/test/non-interactive/render-test.ts index 717f5e765..ce6771fb7 100644 --- a/packages/@glimmer/core/test/non-interactive/render-test.ts +++ b/packages/@glimmer/core/test/non-interactive/render-test.ts @@ -10,10 +10,9 @@ import { templateOnlyComponent, } from '@glimmer/core'; -import { module, test, render } from '../utils'; -import { DEBUG } from '@glimmer/env'; +import { test, render } from '../utils'; -module(`[@glimmer/core] non-interactive rendering tests`, () => { +QUnit.module(`[@glimmer/core] non-interactive rendering tests`, () => { test('it renders a component', async (assert) => { class MyComponent extends Component {} @@ -152,24 +151,6 @@ module(`[@glimmer/core] non-interactive rendering tests`, () => { }); }); - test('inline if cannot be overwritten', async function (assert) { - class Main extends Component { - pred = true; - salutation = 'Glimmer'; - alternative = 'Glimmer.js'; - } - - setComponentTemplate( - createTemplate( - { if: () => assert.ok(false, 'custom if was called') }, - 'Hello {{if this.pred this.salutation this.alternative}}!' - ), - Main - ); - - assert.equal(await render(Main), 'Hello Glimmer!', 'output is correct'); - }); - // test('can render a component with the component helper', async function(assert) { // const HelloWorld = templateOnlyComponent(); @@ -266,40 +247,4 @@ module(`[@glimmer/core] non-interactive rendering tests`, () => { const html = await render(MyComponent, { args: { src: './logo.svg' } }); assert.strictEqual(html, '', 'the template was rendered'); }); - - if (DEBUG) { - test('accessing properties in template-only components produces a helpful error in development mode', async function (assert) { - assert.expect(1); - - const component = setComponentTemplate( - createTemplate('

Hello, {{this.name}}!

'), - templateOnlyComponent() - ); - - try { - await render(component); - } catch (err) { - assert.ok( - err.message.match( - 'You attempted to access `this` on a template only component, template-only-component. Template only components do not have a `this` context, and can only access arguments' - ) - ); - } - }); - } else { - test('accessing properties in template-only components produces an exception in production mode', async function (assert) { - assert.expect(1); - - const component = setComponentTemplate( - createTemplate('

Hello, {{this.name}}!

'), - templateOnlyComponent() - ); - - try { - await render(component); - } catch (err) { - assert.ok(err instanceof TypeError); - } - }); - } }); diff --git a/packages/@glimmer/core/test/non-interactive/strict-mode-test.ts b/packages/@glimmer/core/test/non-interactive/strict-mode-test.ts deleted file mode 100644 index b7418d046..000000000 --- a/packages/@glimmer/core/test/non-interactive/strict-mode-test.ts +++ /dev/null @@ -1,73 +0,0 @@ -import Component from '@glimmer/component'; - -import { setComponentTemplate, createTemplate } from '@glimmer/core'; - -import { module, test, render } from '../utils'; -import { DEBUG } from '@glimmer/env'; - -if (DEBUG) { - module(`[@glimmer/core] non-interactive strict-mode tests`, () => { - test('throws a helpful error if upvar is used directly in a template invocation and not found in scope', async (assert) => { - assert.throws(() => { - class MyComponent extends Component { - say = 'Hello Dolly!'; - } - - setComponentTemplate(createTemplate('

{{say}}

'), MyComponent); - }, /Error: Cannot find identifier `say` in scope. It was used in a template, but not imported into the template scope or defined as a local variable. If you meant to access a property, you must add `this` to it: `{{this.say}}`/); - }); - - test('throws a helpful error if upvar is used as an argument and not found in scope', async (assert) => { - assert.throws(() => { - class MyComponent extends Component { - say = 'Hello Dolly!'; - } - - function myHelper(param1: unknown): unknown { - return param1; - } - - setComponentTemplate( - createTemplate({ myHelper }, '

{{myHelper say}}

'), - MyComponent - ); - }, /Error: Cannot find identifier `say` in scope. It was used in a template, but not imported into the template scope or defined as a local variable. If you meant to access a property, you must add `this` to it: `{{this.say}}`/); - }); - - test('throws a helpful error if upvar is used as a helper', async (assert) => { - assert.throws(() => { - class MyComponent extends Component { - say = 'Hello Dolly!'; - } - - setComponentTemplate(createTemplate('

{{say this.bar}}

'), MyComponent); - }, /Error: Cannot find identifier `say` in scope. It was used in a template, but not imported into the template scope or defined as a local variable. If you meant to access a property, you must add `this` to it: `{{this.say}}`/); - }); - - test('throws a helpful error if upvar is used as a modifier', async (assert) => { - assert.throws(() => { - class MyComponent extends Component { - say = 'Hello Dolly!'; - } - - setComponentTemplate(createTemplate('

Hello Dolly!

'), MyComponent); - }, /Error: Cannot find identifier `say` in scope. It was used in a template, but not imported into the template scope or defined as a local variable. If you meant to access a property, you must add `this` to it: `{{this.say}}`/); - }); - - test('throws a helpful error if upvar is used as a component', async (assert) => { - assert.expect(1); - - try { - await render(createTemplate(``)); - } catch (err) { - assert.ok( - err - .toString() - .match( - /Cannot find component `NonExistent` in scope. It was used in a template, but not imported into the template scope or defined as a local variable. If you meant to access a property, you must add `this` to it: ``/ - ) - ); - } - }); - }); -} diff --git a/packages/@glimmer/core/test/non-interactive/to-bool-test.ts b/packages/@glimmer/core/test/non-interactive/to-bool-test.ts index 9c3271cb5..f30f57b61 100644 --- a/packages/@glimmer/core/test/non-interactive/to-bool-test.ts +++ b/packages/@glimmer/core/test/non-interactive/to-bool-test.ts @@ -2,9 +2,9 @@ import Component from '@glimmer/component'; import { setComponentTemplate, createTemplate } from '@glimmer/core'; -import { module, test, render } from '../utils'; +import { test, render } from '../utils'; -module(`[@glimmer/core] non-interactive rendering tests`, () => { +QUnit.module(`[@glimmer/core] non-interactive rendering tests`, () => { test(`normal if treats empty arrays as falsy`, async function (assert) { class Main extends Component { pred = []; diff --git a/packages/@glimmer/core/test/utils.ts b/packages/@glimmer/core/test/utils.ts index cf7d8c971..78c3b2a50 100644 --- a/packages/@glimmer/core/test/utils.ts +++ b/packages/@glimmer/core/test/utils.ts @@ -12,7 +12,6 @@ import { tracked as glimmerTracked } from '@glimmer/tracking'; import TrackedObject from './utils/tracked-object'; -export const module = QUnit.module; export const test = QUnit.test; const IS_INTERACTIVE = typeof document !== 'undefined'; @@ -21,7 +20,7 @@ export async function render( component: ComponentDefinition | SerializedTemplateWithLazyBlock, options?: HTMLElement | Partial ): Promise { - if ('id' in component && 'block' in component && 'meta' in component) { + if ('id' in component && 'block' in component) { const template = component; component = setComponentTemplate(template, templateOnlyComponent()); diff --git a/packages/@glimmer/core/test/utils/custom-helper.ts b/packages/@glimmer/core/test/utils/custom-helper.ts deleted file mode 100644 index 0f6a57a16..000000000 --- a/packages/@glimmer/core/test/utils/custom-helper.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { - TemplateArgs, - helperCapabilities, - HelperManager, - setHelperManager, - setOwner, -} from '@glimmer/core'; -import { Dict } from '@glimmer/interfaces'; -import { registerDestructor } from '@glimmer/runtime'; - -interface Helper< - Positional extends unknown[] = unknown[], - Named extends Dict = Dict, - Result = unknown -> { - args?: Partial>; - - value?: Result; - - setup?(): void; - update?(): void; - teardown?(): void; -} - -interface HelperConstructor { - new (): Helper; -} - -class CustomHelperManager implements HelperManager { - capabilities = helperCapabilities('glimmerjs-2.0.0', { - destroyable: true, - updateHook: true, - }); - - constructor(private owner: unknown) {} - - createHelper(definition: HelperConstructor, args: TemplateArgs): Helper { - const instance = new definition(); - instance.args = args; - setOwner(instance, this.owner); - instance.setup?.(); - - if (instance.teardown) { - registerDestructor(instance, () => instance.teardown!()); - } - - return instance; - } - - getValue(instance: Helper): unknown { - return instance.value; - } - - updateHelper(instance: Helper, args: TemplateArgs): void { - instance.args = args; - instance.update?.(); - } - - getDestroyable(instance: Helper): object { - return instance; - } -} - -const CustomHelperManagerFactory = (owner: unknown): CustomHelperManager => - new CustomHelperManager(owner); - -export function helper(Class: HelperConstructor): HelperConstructor { - setHelperManager(CustomHelperManagerFactory, Class); - return Class; -} diff --git a/packages/@glimmer/helper/index.ts b/packages/@glimmer/helper/index.ts index ea2d996dc..6c986da0d 100644 --- a/packages/@glimmer/helper/index.ts +++ b/packages/@glimmer/helper/index.ts @@ -1 +1 @@ -export { default as fn } from './src/fn'; +export { fn } from '@glimmer/runtime'; diff --git a/packages/@glimmer/helper/package.json b/packages/@glimmer/helper/package.json index d9d59fd54..9b4d50519 100644 --- a/packages/@glimmer/helper/package.json +++ b/packages/@glimmer/helper/package.json @@ -14,8 +14,7 @@ "dependencies": { "@glimmer/component": "2.0.0-beta.11", "@glimmer/core": "2.0.0-beta.11", - "@glimmer/interfaces": "0.65.0", - "@glimmer/reference": "0.65.0" + "@glimmer/runtime": "0.73.0" }, "volta": { "node": "12.16.1", diff --git a/packages/@glimmer/helper/src/fn.ts b/packages/@glimmer/helper/src/fn.ts deleted file mode 100644 index 7ddb6df89..000000000 --- a/packages/@glimmer/helper/src/fn.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { DEBUG } from '@glimmer/env'; -import { assert } from '@glimmer/util'; - -// eslint-disable-next-line @typescript-eslint/explicit-function-return-type -export default function fn(fn: (...args: unknown[]) => unknown, ...args: unknown[]) { - if (DEBUG) { - assert(typeof fn === 'function', 'fn must receive a function as its first parameter'); - assert( - args.length > 0, - 'fn must receive at least one argument to pass to the function, otherwise there is no need to use fn.' - ); - } - return fn.bind(null, ...args); -} diff --git a/packages/@glimmer/modifier/index.ts b/packages/@glimmer/modifier/index.ts index e1a6fe32e..7bc4b103b 100644 --- a/packages/@glimmer/modifier/index.ts +++ b/packages/@glimmer/modifier/index.ts @@ -1,2 +1,2 @@ -export { on } from './src/on'; +export { on } from '@glimmer/runtime'; export { action } from './src/action'; diff --git a/packages/@glimmer/modifier/package.json b/packages/@glimmer/modifier/package.json index 301ac36a2..3b0d728dc 100644 --- a/packages/@glimmer/modifier/package.json +++ b/packages/@glimmer/modifier/package.json @@ -12,8 +12,7 @@ "build": "webpack" }, "dependencies": { - "@glimmer/interfaces": "0.65.0", - "@simple-dom/interface": "^1.4.0" + "@glimmer/runtime": "0.73.0" }, "volta": { "node": "12.16.1", diff --git a/packages/@glimmer/modifier/src/on.ts b/packages/@glimmer/modifier/src/on.ts deleted file mode 100644 index 4fb8f6c52..000000000 --- a/packages/@glimmer/modifier/src/on.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable @typescript-eslint/ban-ts-ignore */ -import { ModifierManager, modifierCapabilities, setModifierManager } from '@glimmer/core'; - -// This function is just used to have an importable value to assign the modifier manager -// to, so it doesn't actually get run. Having the typings is good for documentation -// and discoverabilitity purposes though. - -export function on( - // @ts-ignore - element: Element, - // @ts-ignore - eventName: string, - // @ts-ignore - callBack: EventListenerOrEventListenerObject, - // @ts-ignore - options?: AddEventListenerOptions -): void {} // eslint-disable-line @typescript-eslint/no-empty-function - -interface OnArgs { - positional: [string, EventListenerOrEventListenerObject]; - named: AddEventListenerOptions; -} - -interface OnStateBucket { - element?: Element; - args: OnArgs; - previousArgs: OnArgs; -} - -class OnModifierManager implements ModifierManager { - capabilities = modifierCapabilities('3.13'); - - createModifier(_definition: {}, args: unknown): OnStateBucket { - return { args: args as OnArgs, previousArgs: args as OnArgs }; - } - - installModifier(bucket: OnStateBucket, element: Element): void { - const { args } = bucket; - const [eventName, listener] = args.positional; - const named = Object.assign({}, args.named); - - element.addEventListener(eventName, listener, named); - - bucket.element = element; - bucket.previousArgs = { - positional: [eventName, listener], - named, - }; - } - - updateModifier(bucket: OnStateBucket): void { - this.destroyModifier(bucket); - this.installModifier(bucket, bucket.element!); - } - - destroyModifier({ element, previousArgs }: OnStateBucket): void { - const [eventName, listener] = previousArgs.positional; - element!.removeEventListener(eventName, listener, previousArgs.named); - } -} - -setModifierManager(() => new OnModifierManager(), on); diff --git a/packages/@glimmer/ssr/package.json b/packages/@glimmer/ssr/package.json index a9ebb72c9..c1ea3357c 100644 --- a/packages/@glimmer/ssr/package.json +++ b/packages/@glimmer/ssr/package.json @@ -11,10 +11,10 @@ "module": "dist/modules/index.js", "dependencies": { "@glimmer/core": "2.0.0-beta.11", - "@glimmer/node": "0.65.0", - "@glimmer/reference": "0.65.0", - "@glimmer/runtime": "0.65.0", - "@glimmer/util": "0.65.0", + "@glimmer/node": "0.73.0", + "@glimmer/reference": "0.73.0", + "@glimmer/runtime": "0.73.0", + "@glimmer/util": "0.73.0", "@simple-dom/document": "^1.4.0", "@simple-dom/serializer": "^1.4.0", "@simple-dom/void-map": "^1.4.0" diff --git a/packages/@glimmer/tracking/package.json b/packages/@glimmer/tracking/package.json index 80ca9c496..8fb2b403c 100644 --- a/packages/@glimmer/tracking/package.json +++ b/packages/@glimmer/tracking/package.json @@ -23,14 +23,14 @@ ], "dependencies": { "@glimmer/env": "^0.1.7", - "@glimmer/validator": "0.65.0" + "@glimmer/validator": "0.73.0" }, "devDependencies": { "@glimmer/application-test-helpers": "^1.0.0", - "@glimmer/compiler": "0.65.0", - "@glimmer/interfaces": "0.65.0", + "@glimmer/compiler": "0.73.0", + "@glimmer/interfaces": "0.73.0", "@glimmer/resolver": "^0.3.0", - "@glimmer/wire-format": "0.65.0" + "@glimmer/wire-format": "0.73.0" }, "ember-addon": { "main": "ember-addon-main.js" diff --git a/packages/@glimmer/tracking/test/tracked-decorator-test.ts b/packages/@glimmer/tracking/test/tracked-decorator-test.ts index 0b76d4578..1fa533b67 100644 --- a/packages/@glimmer/tracking/test/tracked-decorator-test.ts +++ b/packages/@glimmer/tracking/test/tracked-decorator-test.ts @@ -1,5 +1,5 @@ /* tslint:disable:no-unused-expression */ -const { module, test } = QUnit; +const { test } = QUnit; import { DEBUG } from '@glimmer/env'; import { track, valueForTag, validateTag } from '@glimmer/validator'; @@ -14,7 +14,7 @@ import { assertValidAfterUnrelatedBump } from './helpers/tags'; ['Babel', BabelFixtures], ['TypeScript', TSFixtures], ].forEach(([compiler, F]) => { - module(`[@glimmer/tracking] Tracked Property Decorators with ${compiler}`); + QUnit.module(`[@glimmer/tracking] Tracked Property Decorators with ${compiler}`); test('tracked properties can be read and written to', (assert) => { const obj = new F.Tom(); diff --git a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/index.js b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/index.js index c11b387d1..7a55f4d27 100644 --- a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/index.js +++ b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/index.js @@ -56,9 +56,9 @@ module.exports = function strictTemplatePrecompile(babel, options) { ); } - scope = t.arrowFunctionExpression([], scopePath.node); + scope = scopePath.node; } else { - scope = t.arrowFunctionExpression([], t.objectExpression([])); + scope = t.objectExpression([]); } const { type } = templatePath.node; @@ -90,32 +90,46 @@ module.exports = function strictTemplatePrecompile(babel, options) { }; function _precompileTemplate(parse, templateString, scopeNode, precompileOptions) { - const compiled = precompile(templateString, precompileOptions); + const localsMap = scopeNode.properties.reduce((map, property) => { + // Object keys can either be identifiers or literals + const keyName = t.isIdentifier(property.key) + ? property.key.name + : property.key.value.toString(); + if (!t.isIdentifier(property.value)) { + throw new Error( + `Currently the babel-plugin-strict-template-precompile only supports identifiers as values for the imported values object literal. Received: ${property.value.type}` + ); + } + map[keyName] = property.value.name; + return map; + }, {}); + + const compiled = precompile(templateString, { + ...precompileOptions, + strictMode: true, + locals: Object.keys(localsMap), + }); const ast = parse(`(${compiled})`); - let metaSeen = false; - t.traverseFast(ast, (node) => { - if (t.isObjectProperty(node)) { - if (node.key.value === 'meta') { - metaSeen = true; - node.value.properties.push(t.objectProperty(t.identifier('scope'), scopeNode)); - } - if (t.isStringLiteral(node.key)) { - node.key = t.identifier(node.key.value); - } + if ( + t.isObjectProperty(node) && + node.key.value === 'scope' && + t.isArrowFunctionExpression(node.value) + ) { + const scopeFnNode = node.value; + const scopeArray = scopeFnNode.body.elements; + scopeArray.forEach((scopeElementNode) => { + if ( + t.isIdentifier(scopeElementNode) && + scopeElementNode.name !== localsMap[scopeElementNode.name] + ) { + scopeElementNode.name = localsMap[scopeElementNode.name]; + } + }); } }); - if (metaSeen === false) { - ast.program.body[0].expression.properties.push( - t.objectProperty( - t.identifier('meta'), - t.objectExpression([t.objectProperty(t.identifier('scope'), scopeNode)]) - ) - ); - } - return ast; } @@ -130,16 +144,13 @@ function _precompileTemplate(parse, templateString, scopeNode, precompileOptions * @param precompileOptions object - the options to be passed to the compiler */ function precompileTemplate(templateString, importIdentifiers = [], precompileOptions = {}) { - const scope = t.arrowFunctionExpression( - [], - t.objectExpression( - importIdentifiers.map((id) => t.objectProperty(t.identifier(id), t.identifier(id))) - ) + const scope = t.objectExpression( + importIdentifiers.map((id) => t.objectProperty(t.identifier(id), t.identifier(id))) ); let ast = _precompileTemplate(parse, templateString, scope, precompileOptions); return generate(ast).code; } - +// TODO: Is this still required with the new strict mode precompile function from the vm? module.exports.precompileTemplate = precompileTemplate; diff --git a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/package.json b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/package.json index 6a4464572..e90a23d01 100644 --- a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/package.json +++ b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/package.json @@ -15,7 +15,7 @@ "@babel/helper-module-imports": "^7.0.0", "@babel/parser": "^7.9.4", "@babel/types": "^7.9.0", - "@glimmer/compiler": "0.65.0" + "@glimmer/compiler": "0.73.0" }, "devDependencies": { "@babel/preset-env": "^7.8.3", diff --git a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures-options/errors/non-existent-component/code.js b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures-options/errors/non-existent-component/code.js new file mode 100644 index 000000000..93aa35a8c --- /dev/null +++ b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures-options/errors/non-existent-component/code.js @@ -0,0 +1,3 @@ +import { createTemplate } from '@glimmer/core'; + +createTemplate(``); diff --git a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures-options/precompile/ast-transform/output.js b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures-options/precompile/ast-transform/output.js index a76b5bf73..3e688e83a 100644 --- a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures-options/precompile/ast-transform/output.js +++ b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures-options/precompile/ast-transform/output.js @@ -1,8 +1,7 @@ ({ - id: "9bMgfwbA", - block: "{\"symbols\":[],\"statements\":[[10,\"h1\"],[12],[2,\"Hello world\"],[13]],\"hasEval\":false,\"upvars\":[]}", - moduleName: "(unknown template module)", - meta: { - scope: () => ({}) - } + "id": "iQBI6eOx", + "block": "[[[10,\"h1\"],[12],[1,\"Hello world\"],[13]],[],false,[]]", + "moduleName": "(unknown template module)", + "scope": null, + "isStrictMode": true }); diff --git a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/custom-imports/output.js b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/custom-imports/output.js index f6d0df296..f5345556f 100644 --- a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/custom-imports/output.js +++ b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/custom-imports/output.js @@ -1,11 +1,8 @@ import Component from './component'; ({ - id: "mtRxClUL", - block: "{\"symbols\":[],\"statements\":[[8,\"Component\",[],[[],[]],null]],\"hasEval\":false,\"upvars\":[]}", - moduleName: "(unknown template module)", - meta: { - scope: () => ({ - Component - }) - } + "id": "zK8QcLjf", + "block": "[[[8,[32,0],null,null,null]],[],false,[]]", + "moduleName": "(unknown template module)", + "scope": () => [Component], + "isStrictMode": true }); diff --git a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/first-parameter/code.js b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/first-parameter/code.js index bd4ead222..61a297625 100644 --- a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/first-parameter/code.js +++ b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/first-parameter/code.js @@ -1,5 +1,5 @@ import { createTemplate } from '@glimmer/core'; -createTemplate(``); +createTemplate(`

Hello World

`); -createTemplate(''); +createTemplate('

Hello World

'); diff --git a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/first-parameter/output.js b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/first-parameter/output.js index 0ac8aaa41..6eb8d9ad7 100644 --- a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/first-parameter/output.js +++ b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/first-parameter/output.js @@ -1,16 +1,14 @@ ({ - id: "mtRxClUL", - block: "{\"symbols\":[],\"statements\":[[8,\"Component\",[],[[],[]],null]],\"hasEval\":false,\"upvars\":[]}", - moduleName: "(unknown template module)", - meta: { - scope: () => ({}) - } + "id": "gPHf663l", + "block": "[[[10,\"h1\"],[12],[1,\"Hello World\"],[13]],[],false,[]]", + "moduleName": "(unknown template module)", + "scope": null, + "isStrictMode": true }); ({ - id: "mtRxClUL", - block: "{\"symbols\":[],\"statements\":[[8,\"Component\",[],[[],[]],null]],\"hasEval\":false,\"upvars\":[]}", - moduleName: "(unknown template module)", - meta: { - scope: () => ({}) - } + "id": "gPHf663l", + "block": "[[[10,\"h1\"],[12],[1,\"Hello World\"],[13]],[],false,[]]", + "moduleName": "(unknown template module)", + "scope": null, + "isStrictMode": true }); diff --git a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/rename-scope-variable/code.js b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/rename-scope-variable/code.js new file mode 100644 index 000000000..9fecc88b8 --- /dev/null +++ b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/rename-scope-variable/code.js @@ -0,0 +1,4 @@ +import { createTemplate } from '@glimmer/core'; +import Component from './component'; + +createTemplate({ MyComponent: Component }, ``); diff --git a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/rename-scope-variable/output.js b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/rename-scope-variable/output.js new file mode 100644 index 000000000..f5345556f --- /dev/null +++ b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/rename-scope-variable/output.js @@ -0,0 +1,8 @@ +import Component from './component'; +({ + "id": "zK8QcLjf", + "block": "[[[8,[32,0],null,null,null]],[],false,[]]", + "moduleName": "(unknown template module)", + "scope": () => [Component], + "isStrictMode": true +}); diff --git a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/second-parameter/output.js b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/second-parameter/output.js index bdeb98735..7c3d0db01 100644 --- a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/second-parameter/output.js +++ b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/fixtures/second-parameter/output.js @@ -1,21 +1,15 @@ import Component from './component'; ({ - id: "mtRxClUL", - block: "{\"symbols\":[],\"statements\":[[8,\"Component\",[],[[],[]],null]],\"hasEval\":false,\"upvars\":[]}", - moduleName: "(unknown template module)", - meta: { - scope: () => ({ - Component - }) - } + "id": "zK8QcLjf", + "block": "[[[8,[32,0],null,null,null]],[],false,[]]", + "moduleName": "(unknown template module)", + "scope": () => [Component], + "isStrictMode": true }); ({ - id: "mtRxClUL", - block: "{\"symbols\":[],\"statements\":[[8,\"Component\",[],[[],[]],null]],\"hasEval\":false,\"upvars\":[]}", - moduleName: "(unknown template module)", - meta: { - scope: () => ({ - Component - }) - } + "id": "zK8QcLjf", + "block": "[[[8,[32,0],null,null,null]],[],false,[]]", + "moduleName": "(unknown template module)", + "scope": () => [Component], + "isStrictMode": true }); diff --git a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/index.js b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/index.js index dffd79f46..53516bb20 100644 --- a/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/index.js +++ b/packages/babel-plugins/@glimmer/babel-plugin-strict-template-precompile/test/index.js @@ -22,20 +22,23 @@ pluginTester({ outputFixture: path.join(__dirname, 'fixtures-options/precompile/ast-transform/output.js'), pluginOptions: astTransformTestPluginOptions, }, + { + fixture: path.join(__dirname, 'fixtures-options/errors/non-existent-component/code.js'), + error: /Attempted to invoke a component that was not in scope in a strict mode template, ``. If you wanted to create an element with that name, convert it to lowercase - ``/, + }, ], }); describe('precompileTemplate', () => { it('works for basic templates', () => { - let precompiled = precompileTemplate(``); + let precompiled = precompileTemplate(`

Hello World

`); expect(precompiled).to.equal(`({ - id: "mtRxClUL", - block: "{\\"symbols\\":[],\\"statements\\":[[8,\\"Component\\",[],[[],[]],null]],\\"hasEval\\":false,\\"upvars\\":[]}", - moduleName: "(unknown template module)", - meta: { - scope: () => ({}) - } + "id": "gPHf663l", + "block": "[[[10,\\"h1\\"],[12],[1,\\"Hello World\\"],[13]],[],false,[]]", + "moduleName": "(unknown template module)", + "scope": null, + "isStrictMode": true });`); }); @@ -43,14 +46,11 @@ describe('precompileTemplate', () => { let precompiled = precompileTemplate(``, ['Component']); expect(precompiled).to.equal(`({ - id: "mtRxClUL", - block: "{\\"symbols\\":[],\\"statements\\":[[8,\\"Component\\",[],[[],[]],null]],\\"hasEval\\":false,\\"upvars\\":[]}", - moduleName: "(unknown template module)", - meta: { - scope: () => ({ - Component: Component - }) - } + "id": "zK8QcLjf", + "block": "[[[8,[32,0],null,null,null]],[],false,[]]", + "moduleName": "(unknown template module)", + "scope": () => [Component], + "isStrictMode": true });`); }); @@ -62,12 +62,11 @@ describe('precompileTemplate', () => { ); expect(precompiled).to.equal(`({ - id: "9bMgfwbA", - block: "{\\"symbols\\":[],\\"statements\\":[[10,\\"h1\\"],[12],[2,\\"Hello world\\"],[13]],\\"hasEval\\":false,\\"upvars\\":[]}", - moduleName: "(unknown template module)", - meta: { - scope: () => ({}) - } + "id": "iQBI6eOx", + "block": "[[[10,\\"h1\\"],[12],[1,\\"Hello world\\"],[13]],[],false,[]]", + "moduleName": "(unknown template module)", + "scope": null, + "isStrictMode": true });`); }); }); diff --git a/yarn.lock b/yarn.lock index aec45e3d9..0bec07b0c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1015,15 +1015,15 @@ "@glimmer/util" "^0.44.0" "@glimmer/wire-format" "^0.44.0" -"@glimmer/compiler@0.65.0": - version "0.65.0" - resolved "https://registry.yarnpkg.com/@glimmer/compiler/-/compiler-0.65.0.tgz#d501526659c6dd885e0235556d87ca393439fae7" - integrity sha512-JDB6dKP94rqcRQQUGFM+iwh9/Sk20Gu4f9K9g7K1q1pYrxGaJ+Vjq/W03fLawd7iZEVUTRFR4OOG5M1lntoFlQ== - dependencies: - "@glimmer/interfaces" "0.65.0" - "@glimmer/syntax" "0.65.0" - "@glimmer/util" "0.65.0" - "@glimmer/wire-format" "0.65.0" +"@glimmer/compiler@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/compiler/-/compiler-0.73.0.tgz#206d3b3bf5de07bfa95591bc6aa9c43493e52bce" + integrity sha512-vBq4CNPdA1wdKysOshbkjUgy2YHc8IfPIdish4qIx7S3g2ibfH9+EfcFHwb+CrXnijG7dUrVMu/AUmuurbvTNQ== + dependencies: + "@glimmer/interfaces" "0.73.0" + "@glimmer/syntax" "0.73.0" + "@glimmer/util" "0.73.0" + "@glimmer/wire-format" "0.73.0" "@simple-dom/interface" "^1.4.0" "@glimmer/compiler@^0.44.0": @@ -1036,6 +1036,16 @@ "@glimmer/util" "^0.44.0" "@glimmer/wire-format" "^0.44.0" +"@glimmer/destroyable@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/destroyable/-/destroyable-0.73.0.tgz#04a0ce1d31c6325c13f85831e63d392ad103646e" + integrity sha512-yx2Ci2s7Q0zY6A934a/eJBVofBg/YsuLv9et6FXVYgQFjb45V3Pm+lklitKCZ401kXKJqozTC08jHgka40U3rA== + dependencies: + "@glimmer/env" "0.1.7" + "@glimmer/global-context" "0.73.0" + "@glimmer/interfaces" "0.73.0" + "@glimmer/util" "0.73.0" + "@glimmer/di@^0.1.9": version "0.1.11" resolved "https://registry.yarnpkg.com/@glimmer/di/-/di-0.1.11.tgz#a6878c07a13a2c2c76fcde598a5c97637bfc4280" @@ -1046,13 +1056,14 @@ resolved "https://registry.yarnpkg.com/@glimmer/di/-/di-0.2.1.tgz#5286b6b32040232b751138f6d006130c728d4b3d" integrity sha512-0D53YVuEgGdHfTl9LGWDZqVzGhn4cT0CXqyAuOYkKFLvqboJXz6SnkRhQNPhhA2hLVrPnvUz3+choQmPhHLGGQ== -"@glimmer/encoder@0.65.0": - version "0.65.0" - resolved "https://registry.yarnpkg.com/@glimmer/encoder/-/encoder-0.65.0.tgz#a11102c2ece4004678bb2bc0ab39276d7f0e3d26" - integrity sha512-UycgQr+b4l9X6e78fypO6/SWRDypFuIJkSuS4q43Of/a+hss11xVQAy+oANZjo6shrQtzH159v5F3LlSjYKbzg== +"@glimmer/encoder@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/encoder/-/encoder-0.73.0.tgz#5ad9ebcdca8fc7952367ab4b0d77c8452cda96e7" + integrity sha512-v2jcMem2TFLP/VJF4HI+GPX9PEwkmcussyLjNG65ZMCcsqOKHcmGcXULtMxeQR4Y7PpjaZRwWCMpuGgL9QLo5Q== dependencies: - "@glimmer/interfaces" "0.65.0" - "@glimmer/vm" "0.65.0" + "@glimmer/env" "0.1.7" + "@glimmer/interfaces" "0.73.0" + "@glimmer/vm" "0.73.0" "@glimmer/encoder@^0.44.0": version "0.44.0" @@ -1067,17 +1078,17 @@ resolved "https://registry.yarnpkg.com/@glimmer/env/-/env-0.1.7.tgz#fd2d2b55a9029c6b37a6c935e8c8871ae70dfa07" integrity sha1-/S0rVakCnGs3psk16MiHGucN+gc= -"@glimmer/global-context@0.65.0": - version "0.65.0" - resolved "https://registry.yarnpkg.com/@glimmer/global-context/-/global-context-0.65.0.tgz#96d8b99d1a805be72d4dcf5d2eb90cd2ecfff550" - integrity sha512-czdgBmQRHFdrpKicZ1qJ0cd2yzNf/T4OV9VT+7dR356At3C7OWU0GJiIfA1IcW7nqS7x27rC8GDsUZm0ntEFRQ== +"@glimmer/global-context@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/global-context/-/global-context-0.73.0.tgz#273f757779a17595cb4cb76685c17087dc619bb8" + integrity sha512-DnatUdDcF6STNhN4aOFjSW5cn1lmgMpjaauwIKZSy9O45Eo8uqNCMpYRCKf4fkVmYVDRwg6N6ua0Sv0q6OlWSw== dependencies: "@glimmer/env" "^0.1.7" -"@glimmer/interfaces@0.65.0": - version "0.65.0" - resolved "https://registry.yarnpkg.com/@glimmer/interfaces/-/interfaces-0.65.0.tgz#e3852efb63fc9f18268991caea4ab362b75ae231" - integrity sha512-Ka9/w1u7SjWt8ZKa3Gs9IbFtLLDlaCdCTO7B+/Lat0bxGIctyEAyjxUdVqUwXWO4pMfkEFWKBP3L/t9CRT8sVQ== +"@glimmer/interfaces@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/interfaces/-/interfaces-0.73.0.tgz#ea926b2542cf666a9a479e8aa67224cc11f7710d" + integrity sha512-DagAc0Ul+lrid54QKrUVctlKfloIZMRhaz/oaEJ3nVf4Nqz/6YtT3tOrJ+fOxLYjsDy3Dq0uJMDOHzdd6IqS2g== dependencies: "@simple-dom/interface" "^1.4.0" @@ -1086,48 +1097,70 @@ resolved "https://registry.yarnpkg.com/@glimmer/interfaces/-/interfaces-0.44.0.tgz#0896204815f05fd8907b5703cbaee9d1b9edf5d3" integrity sha512-O1VBrB1uhWh/XpBRaMbT5zncZiJJNTAqe0rnhRr4JicrlmNoIC4/5ADRgDORj5oBxuENjuZ+6UTul3WCOHETiw== -"@glimmer/low-level@0.65.0": - version "0.65.0" - resolved "https://registry.yarnpkg.com/@glimmer/low-level/-/low-level-0.65.0.tgz#948e7513f36776608ee6cca605bb387ef1cc6c4b" - integrity sha512-BjLmBofuSst0t2iYShUBCsFgitVbmsWd9Lkw8rs1mvB3+jfPsan0yN53sjkIhganccntcW9T2Y2mi/TLGWt+PQ== +"@glimmer/low-level@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/low-level/-/low-level-0.73.0.tgz#70e42e35954ed07c544413bd5064cdd4aa2f6ce9" + integrity sha512-n52STqtO54R/1GUv+oe7TXjwIdLBaZ1/+DqWPsh5XlzIH/MY6RipNeCpaIKJDYdSshJYZEp6i/7x0NBfzmocdQ== "@glimmer/low-level@^0.44.0": version "0.44.0" resolved "https://registry.yarnpkg.com/@glimmer/low-level/-/low-level-0.44.0.tgz#c164efc7e946d7cbbe54c258e64b9148e971c871" integrity sha512-qOayju1J3vpQUT21QxafbP/7EIxY0ve5B9Biyk3i9MCb1BRnTL2OPoNK7Ia5uuA8qbwIk/PuNqx+uHYoOipXMw== -"@glimmer/node@0.65.0": - version "0.65.0" - resolved "https://registry.yarnpkg.com/@glimmer/node/-/node-0.65.0.tgz#e07432564a076652bddee4f1fb405e05d374bcac" - integrity sha512-t7bqcPmmPRvRC+FkNlHxNzD3ooXmJnepdADKeYnJy+nLyZkgcxZ6ZjQWzHTl8K7BRYcVvIhOnk13/AMQ94orKQ== +"@glimmer/manager@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/manager/-/manager-0.73.0.tgz#7af1c290e5ad5ce0d38517367996452346b776d6" + integrity sha512-Sjn2eTBbDxS7fdr3tY2nfHQ34DTGmobNDUIwNGBv7RgNWJ6yCcgM3HYxfynK6AoCrzQyNcHkthefNtFRZOQJSQ== dependencies: - "@glimmer/interfaces" "0.65.0" - "@glimmer/runtime" "0.65.0" - "@glimmer/util" "0.65.0" + "@glimmer/destroyable" "0.73.0" + "@glimmer/env" "0.1.7" + "@glimmer/interfaces" "0.73.0" + "@glimmer/reference" "0.73.0" + "@glimmer/util" "0.73.0" + "@glimmer/validator" "0.73.0" + +"@glimmer/node@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/node/-/node-0.73.0.tgz#ad1fb21423044b78781c2a0a522ca74b7655312d" + integrity sha512-IVQuV+jezP5L81KwswnyW8vT+M+ef+KhgMtW3gI8+6D5Msdx06QUrLzPjbcFyLwHUZ6AHO3IOydEAsk8JclgCg== + dependencies: + "@glimmer/interfaces" "0.73.0" + "@glimmer/runtime" "0.73.0" + "@glimmer/util" "0.73.0" "@simple-dom/document" "^1.4.0" "@simple-dom/interface" "^1.4.0" -"@glimmer/opcode-compiler@0.65.0": - version "0.65.0" - resolved "https://registry.yarnpkg.com/@glimmer/opcode-compiler/-/opcode-compiler-0.65.0.tgz#cfc2461e2cb69fe8f6f35ac519634b7a6a223ba2" - integrity sha512-12A0TZgGr6FwGRwA1DwGiQcLiwsBH2i/HIxW3prtNEglmFnpJiDo3VmJJ3uwGNz5gL1qGt0Ftus2VmYHHJIPtA== +"@glimmer/opcode-compiler@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/opcode-compiler/-/opcode-compiler-0.73.0.tgz#302b3f2d3ca0c8fa6b4dded1230aa63e53007208" + integrity sha512-5RTVLx1CpdTeTNeNw9Nrbc8FKeTACrptacfL5pBD/3hWkj2XqaXIpsuMv6rXm1oGrr79KhFqpSimPM1qcdPGAg== dependencies: - "@glimmer/encoder" "0.65.0" - "@glimmer/interfaces" "0.65.0" - "@glimmer/program" "0.65.0" - "@glimmer/reference" "0.65.0" - "@glimmer/util" "0.65.0" - "@glimmer/vm" "0.65.0" - "@glimmer/wire-format" "0.65.0" + "@glimmer/encoder" "0.73.0" + "@glimmer/env" "0.1.7" + "@glimmer/interfaces" "0.73.0" + "@glimmer/reference" "0.73.0" + "@glimmer/util" "0.73.0" + "@glimmer/vm" "0.73.0" + "@glimmer/wire-format" "0.73.0" -"@glimmer/program@0.65.0": - version "0.65.0" - resolved "https://registry.yarnpkg.com/@glimmer/program/-/program-0.65.0.tgz#c26d6a843c54586d0e6268a2360ad45fd28d9d93" - integrity sha512-cYWLF3bd/wcgZOttDBhcLXmMj8cqWiXm8S3/8IUvnkWRLPmfK+xqFYd7j9kFtJKHjiQ9UWue7wfV6lURZiuC+g== +"@glimmer/owner@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/owner/-/owner-0.73.0.tgz#a76378812f5def9cc47bcecb09caf0838111fcc7" + integrity sha512-fJ/ZzGNbGZiHwTlohqvPOXLdStmlm57iNuKg3PmqXYOpOfe4Ga/l7qbwDeat/+vBskVT7ZWitawM6lsyRtuVlg== dependencies: - "@glimmer/encoder" "0.65.0" - "@glimmer/interfaces" "0.65.0" - "@glimmer/util" "0.65.0" + "@glimmer/util" "0.73.0" + +"@glimmer/program@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/program/-/program-0.73.0.tgz#a6a368e0c9d7e5a47375577a93f5773fbace8d8c" + integrity sha512-P631EYGtkOJ+05oNy4taxh1G+D9Hd12uc5STUB9jRutxN/6U4WJy2+V935Vyac2disuKTj7RVQuWI0EE6BAXgQ== + dependencies: + "@glimmer/encoder" "0.73.0" + "@glimmer/env" "0.1.7" + "@glimmer/interfaces" "0.73.0" + "@glimmer/manager" "0.73.0" + "@glimmer/opcode-compiler" "0.73.0" + "@glimmer/util" "0.73.0" "@glimmer/program@^0.44.0": version "0.44.0" @@ -1138,16 +1171,16 @@ "@glimmer/interfaces" "^0.44.0" "@glimmer/util" "^0.44.0" -"@glimmer/reference@0.65.0": - version "0.65.0" - resolved "https://registry.yarnpkg.com/@glimmer/reference/-/reference-0.65.0.tgz#8863308d0865d45ae1fc5d07d5ccef9da4dff2d7" - integrity sha512-Cxb5Q91x+oV494dMtjx/fo5QudFCI1yJ4gg2Uj9hWWp2r8EqRmW2rlboLdnkjBDuY5MMuqwY9I8mc+SSAqQ46w== +"@glimmer/reference@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/reference/-/reference-0.73.0.tgz#77d421254c0d58dd0bdcb341bdcb76744d0b28ba" + integrity sha512-m2vWCAD0bYLI03EvvJo4ow8a2cmfpLi7mHT+s9tMER7c7/TNenLmh6/5A7NKzbwCb6onqXQGOT4Sb/vAZqFgQw== dependencies: "@glimmer/env" "^0.1.7" - "@glimmer/global-context" "0.65.0" - "@glimmer/interfaces" "0.65.0" - "@glimmer/util" "0.65.0" - "@glimmer/validator" "0.65.0" + "@glimmer/global-context" "0.73.0" + "@glimmer/interfaces" "0.73.0" + "@glimmer/util" "0.73.0" + "@glimmer/validator" "0.73.0" "@glimmer/reference@^0.44.0": version "0.44.0" @@ -1171,21 +1204,23 @@ dependencies: "@glimmer/di" "^0.2.0" -"@glimmer/runtime@0.65.0": - version "0.65.0" - resolved "https://registry.yarnpkg.com/@glimmer/runtime/-/runtime-0.65.0.tgz#a65889bf3bb23e35dcca6a4d6de0989887119b47" - integrity sha512-CHODyKgeAUrLaGhq5/s4mLpayIviqqDvpDnPNcKNBS3RQeJRzcTdb+JevZyAeR05PuPUxQ4wRLtwpaLJ5XNSRA== +"@glimmer/runtime@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/runtime/-/runtime-0.73.0.tgz#621a354ed7040afd29fedd8acd78ba3eb7cd8b52" + integrity sha512-O7YJSDbw5zRSdAcGBSi1YIWtoFmDqYR1HK7eSyLpobHOyhNLOYajdK0QLFreB9RZSELHACMWUbHpEQa3NPo5RA== dependencies: + "@glimmer/destroyable" "0.73.0" "@glimmer/env" "0.1.7" - "@glimmer/global-context" "0.65.0" - "@glimmer/interfaces" "0.65.0" - "@glimmer/low-level" "0.65.0" - "@glimmer/program" "0.65.0" - "@glimmer/reference" "0.65.0" - "@glimmer/util" "0.65.0" - "@glimmer/validator" "0.65.0" - "@glimmer/vm" "0.65.0" - "@glimmer/wire-format" "0.65.0" + "@glimmer/global-context" "0.73.0" + "@glimmer/interfaces" "0.73.0" + "@glimmer/low-level" "0.73.0" + "@glimmer/owner" "0.73.0" + "@glimmer/program" "0.73.0" + "@glimmer/reference" "0.73.0" + "@glimmer/util" "0.73.0" + "@glimmer/validator" "0.73.0" + "@glimmer/vm" "0.73.0" + "@glimmer/wire-format" "0.73.0" "@simple-dom/interface" "^1.4.0" "@glimmer/runtime@^0.44.0": @@ -1202,14 +1237,14 @@ "@glimmer/vm" "^0.44.0" "@glimmer/wire-format" "^0.44.0" -"@glimmer/syntax@0.65.0": - version "0.65.0" - resolved "https://registry.yarnpkg.com/@glimmer/syntax/-/syntax-0.65.0.tgz#a4fd943ca558e15c77619d49a4d4500dd252868c" - integrity sha512-juVFrA62oFdr2FSxLMb7c3j7KU/onQlC7c4E9rnkivXqE7EmNGI7/+I48L/DsMmDOZDOf4bIhjeCWbXNB4dS9g== +"@glimmer/syntax@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/syntax/-/syntax-0.73.0.tgz#f45f820d19fba8e7d87054f4bb87b7ce9edd1aac" + integrity sha512-dgxEUocjkbtlfkDPLFDkJO+RhZigffnOeDkdDAR4lbsEFgm0Pd3zNvnFhYyXSg/UUzioLdknyuixlwihG9OGow== dependencies: - "@glimmer/interfaces" "0.65.0" - "@glimmer/util" "0.65.0" - "@handlebars/parser" "^1.1.0" + "@glimmer/interfaces" "0.73.0" + "@glimmer/util" "0.73.0" + "@handlebars/parser" "^2.0.0" simple-html-tokenizer "^0.5.10" "@glimmer/syntax@^0.44.0": @@ -1222,13 +1257,13 @@ handlebars "^4.5.1" simple-html-tokenizer "^0.5.8" -"@glimmer/util@0.65.0": - version "0.65.0" - resolved "https://registry.yarnpkg.com/@glimmer/util/-/util-0.65.0.tgz#8a90382500334e415415bf51b269bc7484b17543" - integrity sha512-tfsKVJpCJ5ADWXgpouiitIKlNacWQseYAlppgn8Y6LnzxcywgnumwFObkfzYlFE6Nd1IiymNxgEeSNJKylWl1A== +"@glimmer/util@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/util/-/util-0.73.0.tgz#21c93fd7f822ba641485953c3ad4d7e929ec17c1" + integrity sha512-S8DfVvNAIOUxSawKjSSxa8U6yu2VVcZjaMVy4yoJR0QKpo++V6x1HDBjDOQU0xKgr98lCJqoAnPKZT6t1/eahg== dependencies: "@glimmer/env" "0.1.7" - "@glimmer/interfaces" "0.65.0" + "@glimmer/interfaces" "0.73.0" "@simple-dom/interface" "^1.4.0" "@glimmer/util@^0.44.0": @@ -1236,26 +1271,26 @@ resolved "https://registry.yarnpkg.com/@glimmer/util/-/util-0.44.0.tgz#45df98d73812440206ae7bda87cfe04aaae21ed9" integrity sha512-duAsm30uVK9jSysElCbLyU6QQYO2X9iLDLBIBUcCqck9qN1o3tK2qWiHbGK5d6g8E2AJ4H88UrfElkyaJlGrwg== -"@glimmer/validator@0.65.0": - version "0.65.0" - resolved "https://registry.yarnpkg.com/@glimmer/validator/-/validator-0.65.0.tgz#e4411e0a979f002efe236e3bcc91de5f5aaeaed2" - integrity sha512-nwFReFT9zOOV+9iSqhNookTfHbOsqqPvtsALXl5Rn4QmtAHFLdehiWyN6ddb/sJwllWq+fNW8DxQdiY1xRwn9A== +"@glimmer/validator@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/validator/-/validator-0.73.0.tgz#d523904f4faff0a3af570d215d679bc7c2148e29" + integrity sha512-fAs3XREHTw/ePYkAM8lKkNeo0//d/TJYWn2iM5SuPPglRJGATg/8p1Nk2iw31BofPvsT3WAnFbr0aWGGZF9paQ== dependencies: "@glimmer/env" "^0.1.7" - "@glimmer/global-context" "0.65.0" + "@glimmer/global-context" "0.73.0" "@glimmer/validator@^0.44.0": version "0.44.0" resolved "https://registry.yarnpkg.com/@glimmer/validator/-/validator-0.44.0.tgz#03d127097dc9cb23052cdb7fcae59d0a9dca53e1" integrity sha512-i01plR0EgFVz69GDrEuFgq1NheIjZcyTy3c7q+w7d096ddPVeVcRzU3LKaqCfovvLJ+6lJx40j45ecycASUUyw== -"@glimmer/vm@0.65.0": - version "0.65.0" - resolved "https://registry.yarnpkg.com/@glimmer/vm/-/vm-0.65.0.tgz#8a6ff00eced670d7bf0acc45659b7d1510146dc6" - integrity sha512-xJ2NJlg8D0TS3prn/FzCq/nzVNEYmv+v4LShrH1Thp+rFwesFsCYwLpidD6btkH8odYF+2+aZZLmq6uBxs3HGQ== +"@glimmer/vm@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/vm/-/vm-0.73.0.tgz#286b7fd3049bd922e6e61293ff040e3a4a392210" + integrity sha512-DokPu8ls8akbQcGwPpm1Z4I9m81de4PqkC8hhkMFQ9qj8BAA/4LRoJ5nxaJFJTq4Ub+kKIZ5owzGosSnucGZEg== dependencies: - "@glimmer/interfaces" "0.65.0" - "@glimmer/util" "0.65.0" + "@glimmer/interfaces" "0.73.0" + "@glimmer/util" "0.73.0" "@glimmer/vm@^0.44.0": version "0.44.0" @@ -1265,13 +1300,13 @@ "@glimmer/interfaces" "^0.44.0" "@glimmer/util" "^0.44.0" -"@glimmer/wire-format@0.65.0": - version "0.65.0" - resolved "https://registry.yarnpkg.com/@glimmer/wire-format/-/wire-format-0.65.0.tgz#d51b3d8f2a94e113190302b2bdbe7a3ee70bb1b5" - integrity sha512-8KgxyLzYbelVLLIWheFwI3yXqkW17k/rfQnWQ8SV337LLvx9CFa6cqF9YTgjtaj9/cHMQq+CF8aMFRFhEwx+qw== +"@glimmer/wire-format@0.73.0": + version "0.73.0" + resolved "https://registry.yarnpkg.com/@glimmer/wire-format/-/wire-format-0.73.0.tgz#2069aa68763dc409a3ea28b3819298376b4de5be" + integrity sha512-I3iLdUXClTDUuusq+/FiYA3g6Crr8KW4VmX6qgOxE4aG8kWqnlmDZNX73RvWJVDS0t7t/Cjrv+YwOf51DA75pg== dependencies: - "@glimmer/interfaces" "0.65.0" - "@glimmer/util" "0.65.0" + "@glimmer/interfaces" "0.73.0" + "@glimmer/util" "0.73.0" "@glimmer/wire-format@^0.44.0": version "0.44.0" @@ -1281,10 +1316,10 @@ "@glimmer/interfaces" "^0.44.0" "@glimmer/util" "^0.44.0" -"@handlebars/parser@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@handlebars/parser/-/parser-1.1.0.tgz#d6dbc7574774b238114582410e8fee0dc3532bdf" - integrity sha512-rR7tJoSwJ2eooOpYGxGGW95sLq6GXUaS1UtWvN7pei6n2/okYvCGld9vsUTvkl2migxbkszsycwtMf/GEc1k1A== +"@handlebars/parser@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@handlebars/parser/-/parser-2.0.0.tgz#5e8b7298f31ff8f7b260e6b7363c7e9ceed7d9c5" + integrity sha512-EP9uEDZv/L5Qh9IWuMUGJRfwhXJ4h1dqKTT4/3+tY0eu7sPis7xh23j61SYUnNF4vqCQvvUXpDo9Bh/+q1zASA== "@iarna/toml@2.2.5": version "2.2.5"