diff --git a/broccoli/packages.js b/broccoli/packages.js index b037810bd0e..fe20c08c7d8 100644 --- a/broccoli/packages.js +++ b/broccoli/packages.js @@ -254,7 +254,7 @@ function glimmerTrees(entries) { } seen.add(name); - if (!name.startsWith('@glimmer/')) { + if (!name.startsWith('@glimmer/') && !name.startsWith('@simple-dom/')) { continue; } @@ -284,7 +284,12 @@ module.exports.glimmerCompilerES = () => { }; module.exports.glimmerES = function glimmerES(environment) { - let glimmerEntries = ['@glimmer/node', '@glimmer/opcode-compiler', '@glimmer/runtime']; + let glimmerEntries = [ + '@glimmer/node', + '@simple-dom/document', + '@glimmer/opcode-compiler', + '@glimmer/runtime', + ]; if (environment === 'development') { let hasGlimmerDebug = true; diff --git a/ember-cli-build.js b/ember-cli-build.js index 52343226be9..64f0b7d1aa5 100644 --- a/ember-cli-build.js +++ b/ember-cli-build.js @@ -10,6 +10,8 @@ const rollupPackage = require('./broccoli/rollup-package'); const minify = require('./broccoli/minify'); const debugTree = require('broccoli-debug').buildDebugCallback('ember-source:ember-cli-build'); +Error.stackTraceLimit = Infinity; + const { routerES, jquery, diff --git a/package.json b/package.json index 6f87fd190fb..cb36959bdc3 100644 --- a/package.json +++ b/package.json @@ -74,14 +74,16 @@ }, "devDependencies": { "@babel/preset-env": "^7.7.7", - "@glimmer/compiler": "0.38.5-alpha.3", + "@glimmer/compiler": "0.45.3", "@glimmer/env": "^0.1.7", - "@glimmer/interfaces": "0.38.5-alpha.3", - "@glimmer/node": "0.38.5-alpha.3", - "@glimmer/opcode-compiler": "0.38.5-alpha.3", - "@glimmer/program": "0.38.5-alpha.3", - "@glimmer/reference": "0.38.5-alpha.3", - "@glimmer/runtime": "0.38.5-alpha.3", + "@glimmer/interfaces": "0.45.3", + "@glimmer/node": "0.45.3", + "@glimmer/opcode-compiler": "0.45.3", + "@glimmer/program": "0.45.3", + "@glimmer/reference": "0.45.3", + "@glimmer/runtime": "0.45.3", + "@glimmer/validator": "0.45.3", + "@simple-dom/document": "^1.4.0", "@types/qunit": "^2.5.4", "@types/rsvp": "^4.0.3", "@typescript-eslint/parser": "^2.15.0", @@ -98,7 +100,7 @@ "broccoli-rollup": "^2.1.1", "broccoli-source": "^3.0.0", "broccoli-string-replace": "^0.1.2", - "broccoli-typescript-compiler": "^4.1.0", + "broccoli-typescript-compiler": "^4.2.0", "broccoli-uglify-sourcemap": "^3.1.1", "common-tags": "^1.8.0", "core-js": "^2.6.5", diff --git a/packages/@ember/-internals/glimmer/index.ts b/packages/@ember/-internals/glimmer/index.ts index d1a705e18ad..22932826b3c 100644 --- a/packages/@ember/-internals/glimmer/index.ts +++ b/packages/@ember/-internals/glimmer/index.ts @@ -352,7 +352,6 @@ export { default as TextArea } from './lib/components/textarea'; export { default as LinkComponent } from './lib/components/link-to'; export { default as Component } from './lib/component'; export { default as Helper, helper } from './lib/helper'; -export { default as Environment } from './lib/environment'; export { SafeString, escapeExpression, htmlSafe, isHTMLSafe } from './lib/utils/string'; export { Renderer, @@ -380,9 +379,9 @@ export { default as AbstractComponentManager } from './lib/component-managers/ab // TODO just test these through public API // a lot of these are testing how a problem was solved // rather than the problem was solved -export { UpdatableReference, INVOKE } from './lib/utils/references'; -export { default as iterableFor } from './lib/utils/iterable'; +export { INVOKE } from './lib/helpers/mut'; export { default as OutletView } from './lib/views/outlet'; +export { OutletState } from './lib/utils/outlet'; export { capabilities } from './lib/component-managers/custom'; export { setComponentManager, getComponentManager } from './lib/utils/custom-component-manager'; export { setModifierManager, getModifierManager } from './lib/utils/custom-modifier-manager'; diff --git a/packages/@ember/-internals/glimmer/lib/compile-time-lookup.ts b/packages/@ember/-internals/glimmer/lib/compile-time-lookup.ts index e824f71332b..cefb743314b 100644 --- a/packages/@ember/-internals/glimmer/lib/compile-time-lookup.ts +++ b/packages/@ember/-internals/glimmer/lib/compile-time-lookup.ts @@ -1,46 +1,28 @@ import { OwnedTemplateMeta } from '@ember/-internals/views'; import { - CompilableTemplate, - CompileTimeLookup as ICompileTimeLookup, + CompileTimeComponent, + CompileTimeResolverDelegate, ComponentCapabilities, + ComponentDefinition, + ComponentManager, Option, - ProgramSymbolTable, + WithJitStaticLayout, } from '@glimmer/interfaces'; -import { ComponentDefinition, ComponentManager, WithStaticLayout } from '@glimmer/runtime'; import RuntimeResolver from './resolver'; -interface StaticComponentManager - extends WithStaticLayout, - ComponentManager {} +interface StaticComponentManager + extends WithJitStaticLayout, + ComponentManager {} -export default class CompileTimeLookup implements ICompileTimeLookup { - constructor(private resolver: RuntimeResolver) {} - - getCapabilities(handle: number): ComponentCapabilities { - let definition = this.resolver.resolve>(handle); - let { manager, state } = definition!; - return manager.getCapabilities(state); - } - - getLayout(handle: number): Option> { - const { manager, state } = this.resolver.resolve< - ComponentDefinition> - >(handle); - const capabilities = manager.getCapabilities(state); - - if (capabilities.dynamicLayout) { - return null; - } +function isStaticComponentManager( + _manager: ComponentManager, + capabilities: ComponentCapabilities +): _manager is StaticComponentManager { + return !capabilities.dynamicLayout; +} - const invocation = manager.getLayout(state, this.resolver); - return { - // TODO: this seems weird, it already is compiled - compile() { - return invocation.handle; - }, - symbolTable: invocation.symbolTable, - }; - } +export default class CompileTimeResolver implements CompileTimeResolverDelegate { + constructor(private resolver: RuntimeResolver) {} lookupHelper(name: string, referrer: OwnedTemplateMeta): Option { return this.resolver.lookupHelper(name, referrer); @@ -50,11 +32,38 @@ export default class CompileTimeLookup implements ICompileTimeLookup { - return this.resolver.lookupComponentHandle(name, referrer); + lookupComponent(name: string, referrer: OwnedTemplateMeta): Option { + let definitionHandle = this.resolver.lookupComponentHandle(name, referrer); + + if (definitionHandle === null) { + return null; + } + + const { manager, state } = this.resolver.resolve>( + definitionHandle + ); + const capabilities = manager.getCapabilities(state); + + if (!isStaticComponentManager(manager, capabilities)) { + return { + handle: definitionHandle, + capabilities, + compilable: null, + }; + } + + return { + handle: definitionHandle, + capabilities, + compilable: manager.getJitStaticLayout(state, this.resolver), + }; } lookupPartial(name: string, referrer: OwnedTemplateMeta): Option { return this.resolver.lookupPartial(name, referrer); } + + resolve(handle: number): OwnedTemplateMeta { + return this.resolver.resolve(handle); + } } diff --git a/packages/@ember/-internals/glimmer/lib/component-managers/abstract.ts b/packages/@ember/-internals/glimmer/lib/component-managers/abstract.ts index a2500a49894..4bf8f7c69fd 100644 --- a/packages/@ember/-internals/glimmer/lib/component-managers/abstract.ts +++ b/packages/@ember/-internals/glimmer/lib/component-managers/abstract.ts @@ -1,38 +1,41 @@ -import { ComponentCapabilities, Simple } from '@glimmer/interfaces'; -import { Tag, VersionedPathReference } from '@glimmer/reference'; import { - Arguments, Bounds, + ComponentCapabilities, ComponentManager, + Destroyable, DynamicScope, ElementOperations, - Environment, + Option, PreparedArguments, -} from '@glimmer/runtime'; -import { Destroyable, Opaque, Option } from '@glimmer/util'; + VMArguments, +} from '@glimmer/interfaces'; +import { VersionedPathReference } from '@glimmer/reference'; +import { Tag } from '@glimmer/validator'; +import { SimpleElement } from '@simple-dom/interface'; +import { EmberVMEnvironment } from '../environment'; // implements the ComponentManager interface as defined in glimmer: // tslint:disable-next-line:max-line-length // https://github.com/glimmerjs/glimmer-vm/blob/v0.24.0-beta.4/packages/%40glimmer/runtime/lib/component/interfaces.ts#L21 export default abstract class AbstractManager implements ComponentManager { - prepareArgs(_state: U, _args: Arguments): Option { + prepareArgs(_state: U, _args: VMArguments): Option { return null; } abstract create( - env: Environment, + env: EmberVMEnvironment, definition: U, - args: Arguments, + args: VMArguments, dynamicScope: DynamicScope, caller: VersionedPathReference, hasDefaultBlock: boolean ): T; - abstract getSelf(component: T): VersionedPathReference; + abstract getSelf(component: T): VersionedPathReference; abstract getCapabilities(state: U): ComponentCapabilities; - didCreateElement(_component: T, _element: Simple.Element, _operations: ElementOperations): void { + didCreateElement(_component: T, _element: SimpleElement, _operations: ElementOperations): void { // noop } diff --git a/packages/@ember/-internals/glimmer/lib/component-managers/curly.ts b/packages/@ember/-internals/glimmer/lib/component-managers/curly.ts index 4b4b7e23bd9..6fea6b84fd9 100644 --- a/packages/@ember/-internals/glimmer/lib/component-managers/curly.ts +++ b/packages/@ember/-internals/glimmer/lib/component-managers/curly.ts @@ -2,57 +2,49 @@ import { privatize as P } from '@ember/-internals/container'; import { ENV } from '@ember/-internals/environment'; import { getOwner } from '@ember/-internals/owner'; import { guidFor } from '@ember/-internals/utils'; -import { - addChildView, - OwnedTemplateMeta, - setElementView, - setViewElement, -} from '@ember/-internals/views'; +import { addChildView, setElementView, setViewElement } from '@ember/-internals/views'; import { assert, debugFreeze } from '@ember/debug'; import { EMBER_COMPONENT_IS_VISIBLE } from '@ember/deprecated-features'; import { _instrumentStart } from '@ember/instrumentation'; import { assign } from '@ember/polyfills'; import { DEBUG } from '@glimmer/env'; import { - ComponentCapabilities, - Option, - ProgramSymbolTable, - Simple, - VMHandle, -} from '@glimmer/interfaces'; -import { combine, Tag, validate, value, VersionedPathReference } from '@glimmer/reference'; -import { - Arguments, Bounds, + ComponentCapabilities, ComponentDefinition, + Destroyable, ElementOperations, - Invocation, + Option, PreparedArguments, - PrimitiveReference, - WithDynamicLayout, + VMArguments, WithDynamicTagName, - WithStaticLayout, -} from '@glimmer/runtime'; -import { Destroyable, EMPTY_ARRAY } from '@glimmer/util'; + WithJitDynamicLayout, + WithJitStaticLayout, +} from '@glimmer/interfaces'; +import { unwrapTemplate } from '@glimmer/opcode-compiler'; +import { RootReference, VersionedPathReference } from '@glimmer/reference'; +import { PrimitiveReference } from '@glimmer/runtime'; +import { EMPTY_ARRAY } from '@glimmer/util'; +import { combine, Tag, validate, value } from '@glimmer/validator'; +import { SimpleElement } from '@simple-dom/interface'; import { BOUNDS, DIRTY_TAG, HAS_BLOCK, IS_DISPATCHING_ATTRS } from '../component'; -import Environment from '../environment'; +import { EmberVMEnvironment } from '../environment'; import { DynamicScope } from '../renderer'; import RuntimeResolver from '../resolver'; import { Factory as TemplateFactory, isTemplateFactory, OwnedTemplate } from '../template'; import { AttributeBinding, ClassNameBinding, - IsVisibleBinding, + installIsVisibleBinding, referenceForKey, SimpleClassNameBindingReference, } from '../utils/bindings'; import ComponentStateBucket, { Component } from '../utils/curly-component-state-bucket'; import { processComponentArgs } from '../utils/process-args'; -import { RootReference } from '../utils/references'; import AbstractManager from './abstract'; import DefinitionState from './definition-state'; -function aliasIdToElementId(args: Arguments, props: any) { +function aliasIdToElementId(args: VMArguments, props: any) { if (args.named.has('id')) { // tslint:disable-next-line:max-line-length assert( @@ -67,11 +59,11 @@ function aliasIdToElementId(args: Arguments, props: any) { // what has already been applied. This is essentially refining the concatenated // properties applying right to left. function applyAttributeBindings( - element: Simple.Element, attributeBindings: Array, component: Component, rootRef: RootReference, - operations: ElementOperations + operations: ElementOperations, + environment: EmberVMEnvironment ) { let seen: string[] = []; let i = attributeBindings.length - 1; @@ -83,7 +75,7 @@ function applyAttributeBindings( if (seen.indexOf(attribute) === -1) { seen.push(attribute); - AttributeBinding.install(element, component, rootRef, parsed, operations); + AttributeBinding.install(component, rootRef, parsed, operations, environment); } i--; @@ -96,10 +88,10 @@ function applyAttributeBindings( if ( EMBER_COMPONENT_IS_VISIBLE && - IsVisibleBinding !== undefined && + installIsVisibleBinding !== undefined && seen.indexOf('style') === -1 ) { - IsVisibleBinding.install(element, component, rootRef, operations); + installIsVisibleBinding(rootRef, operations, environment); } } @@ -111,17 +103,9 @@ debugFreeze(EMPTY_POSITIONAL_ARGS); export default class CurlyComponentManager extends AbstractManager implements - WithStaticLayout, - WithDynamicTagName, - WithDynamicLayout { - getLayout(state: DefinitionState, _resolver: RuntimeResolver): Invocation { - return { - // TODO fix - handle: (state.handle as any) as number, - symbolTable: state.symbolTable!, - }; - } - + WithJitStaticLayout, + WithJitDynamicLayout, + WithDynamicTagName { protected templateFor(component: Component): OwnedTemplate { let { layout, layoutName } = component; let owner = getOwner(component); @@ -146,19 +130,19 @@ export default class CurlyComponentManager return factory(owner); } - getDynamicLayout(bucket: ComponentStateBucket): Invocation { + getJitStaticLayout(state: DefinitionState, _resolver: RuntimeResolver) { + return unwrapTemplate(state.template!).asLayout(); + } + + getJitDynamicLayout(bucket: ComponentStateBucket) { let component = bucket.component; let template = this.templateFor(component); - let layout = template.asWrappedLayout(); if (ENV._DEBUG_RENDER_TREE) { - bucket.environment.debugRenderTree.setTemplate(bucket, template); + bucket.environment.extra.debugRenderTree.setTemplate(bucket, template); } - return { - handle: layout.compile(), - symbolTable: layout.symbolTable, - }; + return template; } getTagName(state: ComponentStateBucket): Option { @@ -175,7 +159,7 @@ export default class CurlyComponentManager return state.capabilities; } - prepareArgs(state: DefinitionState, args: Arguments): Option { + prepareArgs(state: DefinitionState, args: VMArguments): Option { if (args.named.has('__ARGS__')) { let { __ARGS__, ...rest } = args.named.capture().map; @@ -183,7 +167,7 @@ export default class CurlyComponentManager positional: EMPTY_POSITIONAL_ARGS, named: { ...rest, - ...__ARGS__.value(), + ...(__ARGS__.value() as { [key: string]: VersionedPathReference }), }, }; @@ -239,9 +223,9 @@ export default class CurlyComponentManager * etc. */ create( - environment: Environment, + environment: EmberVMEnvironment, state: DefinitionState, - args: Arguments, + args: VMArguments, dynamicScope: DynamicScope, callerSelfRef: VersionedPathReference, hasBlock: boolean @@ -341,7 +325,7 @@ export default class CurlyComponentManager } if (ENV._DEBUG_RENDER_TREE) { - environment.debugRenderTree.create(bucket, { + environment.extra.debugRenderTree.create(bucket, { type: 'component', name: state.name, args: args.capture(), @@ -359,7 +343,7 @@ export default class CurlyComponentManager didCreateElement( { component, classRef, environment, rootRef }: ComponentStateBucket, - element: Simple.Element, + element: SimpleElement, operations: ElementOperations ): void { setViewElement(component, element); @@ -368,12 +352,12 @@ export default class CurlyComponentManager let { attributeBindings, classNames, classNameBindings } = component; if (attributeBindings && attributeBindings.length) { - applyAttributeBindings(element, attributeBindings, component, rootRef, operations); + applyAttributeBindings(attributeBindings, component, rootRef, operations, environment); } else { let id = component.elementId ? component.elementId : guidFor(component); operations.setAttribute('id', PrimitiveReference.create(id), false, null); - if (EMBER_COMPONENT_IS_VISIBLE && IsVisibleBinding !== undefined) { - IsVisibleBinding.install(element, component, rootRef, operations); + if (EMBER_COMPONENT_IS_VISIBLE) { + installIsVisibleBinding!(rootRef, operations, environment); } } @@ -411,7 +395,7 @@ export default class CurlyComponentManager bucket.finalize(); if (ENV._DEBUG_RENDER_TREE) { - bucket.environment.debugRenderTree.didRender(bucket, bounds); + bucket.environment.extra.debugRenderTree.didRender(bucket, bounds); } } @@ -431,7 +415,7 @@ export default class CurlyComponentManager let { component, args, argsRevision, environment } = bucket; if (ENV._DEBUG_RENDER_TREE) { - environment.debugRenderTree.update(bucket); + environment.extra.debugRenderTree.update(bucket); } bucket.finalizer = _instrumentStart('render.component', rerenderInstrumentDetails, component); @@ -459,7 +443,7 @@ export default class CurlyComponentManager bucket.finalize(); if (ENV._DEBUG_RENDER_TREE) { - bucket.environment.debugRenderTree.didRender(bucket, bounds); + bucket.environment.extra.debugRenderTree.didRender(bucket, bounds); } } @@ -473,8 +457,11 @@ export default class CurlyComponentManager getDestructor(bucket: ComponentStateBucket): Option { if (ENV._DEBUG_RENDER_TREE) { return { + willDestroy() { + bucket.willDestroy(); + }, destroy() { - bucket.environment.debugRenderTree.willDestroy(bucket); + bucket.environment.extra.debugRenderTree.willDestroy(bucket); bucket.destroy(); }, }; @@ -598,34 +585,26 @@ export const CURLY_CAPABILITIES: ComponentCapabilities = { dynamicScope: true, updateHook: true, createInstance: true, + wrapped: true, + willDestroy: true, }; const CURLY_COMPONENT_MANAGER = new CurlyComponentManager(); export class CurlyComponentDefinition implements ComponentDefinition { - public args: CurriedArgs | undefined; public state: DefinitionState; - public symbolTable: ProgramSymbolTable | undefined; public manager: CurlyComponentManager = CURLY_COMPONENT_MANAGER; - // tslint:disable-next-line:no-shadowed-variable + constructor( public name: string, public ComponentClass: any, - public handle: Option, - public template: Option, - args?: CurriedArgs + public template?: OwnedTemplate, + public args?: CurriedArgs ) { - const layout = template && template.asLayout(); - const symbolTable = layout ? layout.symbolTable : undefined; - this.symbolTable = symbolTable; - this.template = template; - this.args = args; this.state = { name, ComponentClass, - handle, template, capabilities: CURLY_CAPABILITIES, - symbolTable, }; } } diff --git a/packages/@ember/-internals/glimmer/lib/component-managers/custom.ts b/packages/@ember/-internals/glimmer/lib/component-managers/custom.ts index c1179cfab03..580a2b2ef6c 100644 --- a/packages/@ember/-internals/glimmer/lib/component-managers/custom.ts +++ b/packages/@ember/-internals/glimmer/lib/component-managers/custom.ts @@ -1,33 +1,28 @@ -import { ARGS_PROXY_TAGS, consume } from '@ember/-internals/metal'; +import { ARGS_PROXY_TAGS } from '@ember/-internals/metal'; import { Factory } from '@ember/-internals/owner'; import { HAS_NATIVE_PROXY } from '@ember/-internals/utils'; -import { OwnedTemplateMeta } from '@ember/-internals/views'; import { EMBER_CUSTOM_COMPONENT_ARG_PROXY } from '@ember/canary-features'; import { assert } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; import { + Bounds, + CapturedArguments, ComponentCapabilities, + ComponentDefinition, + Destroyable, Dict, - Opaque, Option, - ProgramSymbolTable, + VMArguments, + WithJitStaticLayout, } from '@glimmer/interfaces'; -import { createTag, isConst, PathReference, Tag } from '@glimmer/reference'; -import { - Arguments, - Bounds, - CapturedArguments, - ComponentDefinition, - Invocation, - WithStaticLayout, -} from '@glimmer/runtime'; -import { Destroyable } from '@glimmer/util'; +import { unwrapTemplate } from '@glimmer/opcode-compiler'; +import { ComponentRootReference, PathReference } from '@glimmer/reference'; +import { consume, createTag, isConst, Tag } from '@glimmer/validator'; import { ENV } from '@ember/-internals/environment'; -import Environment from '../environment'; +import { EmberVMEnvironment } from '../environment'; import RuntimeResolver from '../resolver'; import { OwnedTemplate } from '../template'; -import { RootReference } from '../utils/references'; import AbstractComponentManager from './abstract'; const CAPABILITIES = { @@ -41,6 +36,8 @@ const CAPABILITIES = { dynamicScope: true, updateHook: true, createInstance: true, + wrapped: false, + willDestroy: false, }; export interface OptionalCapabilities { @@ -76,8 +73,7 @@ export function capabilities( export interface DefinitionState { name: string; ComponentClass: Factory; - symbolTable: ProgramSymbolTable; - template?: any; + template: OwnedTemplate; } export interface Capabilities { @@ -88,14 +84,14 @@ export interface Capabilities { // TODO: export ICapturedArgumentsValue from glimmer and replace this export interface Args { - named: Dict; - positional: Opaque[]; + named: Dict; + positional: unknown[]; } export interface ManagerDelegate { capabilities: Capabilities; - createComponent(factory: Opaque, args: Args): ComponentInstance; - getContext(instance: ComponentInstance): Opaque; + createComponent(factory: unknown, args: Args): ComponentInstance; + getContext(instance: ComponentInstance): unknown; } export function hasAsyncLifeCycleCallbacks( @@ -144,8 +140,8 @@ export interface ManagerDelegateWithDestructors } export interface ComponentArguments { - positional: Opaque[]; - named: Dict; + positional: unknown[]; + named: Dict; } /** @@ -179,16 +175,15 @@ export default class CustomComponentManager CustomComponentDefinitionState > implements - WithStaticLayout< + WithJitStaticLayout< CustomComponentState, CustomComponentDefinitionState, - OwnedTemplateMeta, RuntimeResolver > { create( - env: Environment, + env: EmberVMEnvironment, definition: CustomComponentDefinitionState, - args: Arguments + args: VMArguments ): CustomComponentState { const { delegate } = definition; const capturedArgs = args.capture(); @@ -272,7 +267,7 @@ export default class CustomComponentManager let bucket = new CustomComponentState(delegate, component, capturedArgs, env, namedArgsProxy); if (ENV._DEBUG_RENDER_TREE) { - env.debugRenderTree.create(bucket, { + env.extra.debugRenderTree.create(bucket, { type: 'component', name: definition.name, args: args.capture(), @@ -286,7 +281,7 @@ export default class CustomComponentManager update(bucket: CustomComponentState) { if (ENV._DEBUG_RENDER_TREE) { - bucket.env.debugRenderTree.update(bucket); + bucket.env.extra.debugRenderTree.update(bucket); } let { delegate, component, args, namedArgsProxy } = bucket; @@ -327,8 +322,8 @@ export default class CustomComponentManager env, delegate, component, - }: CustomComponentState): PathReference { - return RootReference.create(delegate.getContext(component), env); + }: CustomComponentState): PathReference { + return new ComponentRootReference(delegate.getContext(component), env); } getDestructor(state: CustomComponentState): Option { @@ -343,7 +338,7 @@ export default class CustomComponentManager destructor = { destroy() { - state.env.debugRenderTree.willDestroy(state); + state.env.extra.debugRenderTree.willDestroy(state); if (inner) { inner.destroy(); @@ -374,21 +369,18 @@ export default class CustomComponentManager didRenderLayout(bucket: CustomComponentState, bounds: Bounds) { if (ENV._DEBUG_RENDER_TREE) { - bucket.env.debugRenderTree.didRender(bucket, bounds); + bucket.env.extra.debugRenderTree.didRender(bucket, bounds); } } didUpdateLayout(bucket: CustomComponentState, bounds: Bounds) { if (ENV._DEBUG_RENDER_TREE) { - bucket.env.debugRenderTree.didRender(bucket, bounds); + bucket.env.extra.debugRenderTree.didRender(bucket, bounds); } } - getLayout(state: DefinitionState): Invocation { - return { - handle: state.template.asLayout().compile(), - symbolTable: state.symbolTable!, - }; + getJitStaticLayout(state: DefinitionState) { + return unwrapTemplate(state.template).asLayout(); } } const CUSTOM_COMPONENT_MANAGER = new CustomComponentManager(); @@ -401,7 +393,7 @@ export class CustomComponentState { public delegate: ManagerDelegate, public component: ComponentInstance, public args: CapturedArguments, - public env: Environment, + public env: EmberVMEnvironment, public namedArgsProxy?: {} ) {} @@ -421,7 +413,6 @@ export interface CustomComponentDefinitionState export class CustomManagerDefinition implements ComponentDefinition { public state: CustomComponentDefinitionState; - public symbolTable: ProgramSymbolTable; public manager: CustomComponentManager< ComponentInstance > = CUSTOM_COMPONENT_MANAGER as CustomComponentManager; @@ -432,15 +423,10 @@ export class CustomManagerDefinition implements ComponentDefi public delegate: ManagerDelegate, public template: OwnedTemplate ) { - const layout = template.asLayout(); - const symbolTable = layout.symbolTable; - this.symbolTable = symbolTable; - this.state = { name, ComponentClass, template, - symbolTable, delegate, }; } diff --git a/packages/@ember/-internals/glimmer/lib/component-managers/definition-state.ts b/packages/@ember/-internals/glimmer/lib/component-managers/definition-state.ts index 2f909e46789..2b198ecae0f 100644 --- a/packages/@ember/-internals/glimmer/lib/component-managers/definition-state.ts +++ b/packages/@ember/-internals/glimmer/lib/component-managers/definition-state.ts @@ -1,6 +1,6 @@ import { Factory } from '@ember/-internals/owner'; -import { ComponentCapabilities, ProgramSymbolTable, VMHandle } from '@glimmer/interfaces'; -import { Option } from '@glimmer/util'; +import { ComponentCapabilities } from '@glimmer/interfaces'; +import { OwnedTemplate } from '../template'; import { Component } from '../utils/curly-component-state-bucket'; export default interface DefinitionState { @@ -10,7 +10,5 @@ export default interface DefinitionState { Component, { create(props?: any): Component; positionalParams: string | string[] | undefined | null } >; - handle: Option; - symbolTable?: ProgramSymbolTable; - template?: any; + template?: OwnedTemplate; } diff --git a/packages/@ember/-internals/glimmer/lib/component-managers/input.ts b/packages/@ember/-internals/glimmer/lib/component-managers/input.ts index ff129510624..c6704e6fb9a 100644 --- a/packages/@ember/-internals/glimmer/lib/component-managers/input.ts +++ b/packages/@ember/-internals/glimmer/lib/component-managers/input.ts @@ -2,12 +2,18 @@ import { ENV } from '@ember/-internals/environment'; import { set } from '@ember/-internals/metal'; import { Owner } from '@ember/-internals/owner'; import { assert, debugFreeze } from '@ember/debug'; -import { ComponentCapabilities, Dict } from '@glimmer/interfaces'; -import { CONSTANT_TAG, createTag, isConst, VersionedPathReference } from '@glimmer/reference'; -import { Arguments, Bounds, DynamicScope, PreparedArguments } from '@glimmer/runtime'; -import { Destroyable } from '@glimmer/util'; -import Environment from '../environment'; -import { RootReference } from '../utils/references'; +import { + Bounds, + ComponentCapabilities, + Destroyable, + Dict, + DynamicScope, + PreparedArguments, + VMArguments, +} from '@glimmer/interfaces'; +import { ComponentRootReference, ConstReference, VersionedPathReference } from '@glimmer/reference'; +import { CONSTANT_TAG, createTag, isConst } from '@glimmer/validator'; +import { EmberVMEnvironment } from '../environment'; import InternalComponentManager, { InternalDefinitionState } from './internal'; const CAPABILITIES: ComponentCapabilities = { @@ -21,10 +27,12 @@ const CAPABILITIES: ComponentCapabilities = { dynamicScope: false, updateHook: true, createInstance: true, + wrapped: false, + willDestroy: false, }; export interface InputComponentState { - env: Environment; + env: EmberVMEnvironment; type: VersionedPathReference; instance: Destroyable; } @@ -38,7 +46,7 @@ export default class InputComponentManager extends InternalComponentManager` component does not take any positional arguments', args.positional.length === 0 @@ -49,16 +57,16 @@ export default class InputComponentManager extends InternalComponentManager - implements ComponentDefinition> { + implements ComponentDefinition> { public state: InternalDefinitionState; constructor( @@ -25,17 +25,12 @@ export class InternalComponentDefinition export default abstract class InternalManager extends AbstractComponentManager - implements WithStaticLayout { + implements WithJitStaticLayout { constructor(protected owner: Owner) { super(); } - getLayout({ layout: _layout }: InternalDefinitionState): Invocation { - let layout = _layout.asLayout(); - - return { - handle: layout.compile(), - symbolTable: layout.symbolTable, - }; + getJitStaticLayout({ layout: template }: InternalDefinitionState) { + return unwrapTemplate(template).asLayout(); } } diff --git a/packages/@ember/-internals/glimmer/lib/component-managers/mount.ts b/packages/@ember/-internals/glimmer/lib/component-managers/mount.ts index b15ee6d3efa..984ac6bd6f5 100644 --- a/packages/@ember/-internals/glimmer/lib/component-managers/mount.ts +++ b/packages/@ember/-internals/glimmer/lib/component-managers/mount.ts @@ -1,39 +1,32 @@ import { DEBUG } from '@glimmer/env'; -import { ComponentCapabilities } from '@glimmer/interfaces'; import { - CONSTANT_TAG, - createTag, - isConstTag, - Tag, - VersionedPathReference, -} from '@glimmer/reference'; -import { - Arguments, Bounds, + ComponentCapabilities, ComponentDefinition, - Invocation, - WithDynamicLayout, -} from '@glimmer/runtime'; -import { Destroyable, Opaque, Option } from '@glimmer/util'; + Destroyable, + Option, + VMArguments, + WithJitDynamicLayout, +} from '@glimmer/interfaces'; +import { ComponentRootReference, VersionedPathReference } from '@glimmer/reference'; +import { CONSTANT_TAG, createTag, isConstTag, Tag } from '@glimmer/validator'; import { generateControllerFactory } from '@ember/-internals/routing'; -import { OwnedTemplateMeta } from '@ember/-internals/views'; import { EMBER_ROUTING_MODEL_ARG } from '@ember/canary-features'; import { ENV } from '@ember/-internals/environment'; import EngineInstance from '@ember/engine/instance'; import { TemplateFactory } from '../..'; -import Environment from '../environment'; +import { EmberVMEnvironment } from '../environment'; import RuntimeResolver from '../resolver'; -import { RootReference } from '../utils/references'; import AbstractManager from './abstract'; interface EngineState { engine: EngineInstance; controller: any; - self: RootReference; - environment: Environment; - modelRef?: VersionedPathReference; + self: ComponentRootReference; + environment: EmberVMEnvironment; + modelRef?: VersionedPathReference; } interface EngineDefinitionState { @@ -51,6 +44,8 @@ const CAPABILITIES = { dynamicScope: true, updateHook: true, createInstance: true, + wrapped: false, + willDestroy: false, }; // TODO @@ -59,40 +54,36 @@ const CAPABILITIES = { export const MODEL_ARG_NAME = EMBER_ROUTING_MODEL_ARG || !DEBUG ? 'model' : ' untypable model arg '; class MountManager extends AbstractManager - implements WithDynamicLayout { - getDynamicLayout(state: EngineState, _: RuntimeResolver): Invocation { + implements WithJitDynamicLayout { + getJitDynamicLayout(state: EngineState, _: RuntimeResolver) { let templateFactory = state.engine.lookup('template:application') as TemplateFactory; let template = templateFactory(state.engine); - let layout = template.asLayout(); if (ENV._DEBUG_RENDER_TREE) { - state.environment.debugRenderTree.setTemplate(state.controller, template); + state.environment.extra.debugRenderTree.setTemplate(state.controller, template); } - return { - handle: layout.compile(), - symbolTable: layout.symbolTable, - }; + return template; } getCapabilities(): ComponentCapabilities { return CAPABILITIES; } - create(environment: Environment, { name }: EngineDefinitionState, args: Arguments) { + create(environment: EmberVMEnvironment, { name }: EngineDefinitionState, args: VMArguments) { // TODO // mount is a runtime helper, this shouldn't use dynamic layout // we should resolve the engine app template in the helper // it also should use the owner that looked up the mount helper. - let engine = environment.owner.buildChildEngineInstance(name); + let engine = environment.extra.owner.buildChildEngineInstance(name); engine.boot(); let applicationFactory = engine.factoryFor(`controller:application`); let controllerFactory = applicationFactory || generateControllerFactory(engine, 'application'); let controller: any; - let self: RootReference; + let self: ComponentRootReference; let bucket: EngineState; let modelRef; @@ -102,17 +93,17 @@ class MountManager extends AbstractManager if (modelRef === undefined) { controller = controllerFactory.create(); - self = new RootReference(controller, environment); + self = new ComponentRootReference(controller, environment); bucket = { engine, controller, self, environment }; } else { let model = modelRef.value(); controller = controllerFactory.create({ model }); - self = new RootReference(controller, environment); + self = new ComponentRootReference(controller, environment); bucket = { engine, controller, self, modelRef, environment }; } if (ENV._DEBUG_RENDER_TREE) { - environment.debugRenderTree.create(bucket, { + environment.extra.debugRenderTree.create(bucket, { type: 'engine', name, args: args.capture(), @@ -120,7 +111,7 @@ class MountManager extends AbstractManager template: undefined, }); - environment.debugRenderTree.create(controller, { + environment.extra.debugRenderTree.create(controller, { type: 'route-template', name: 'application', args: args.capture(), @@ -133,7 +124,7 @@ class MountManager extends AbstractManager return bucket; } - getSelf({ self }: EngineState): VersionedPathReference { + getSelf({ self }: EngineState): VersionedPathReference { return self; } @@ -157,8 +148,8 @@ class MountManager extends AbstractManager if (ENV._DEBUG_RENDER_TREE) { return { destroy() { - environment.debugRenderTree.willDestroy(controller); - environment.debugRenderTree.willDestroy(bucket); + environment.extra.debugRenderTree.willDestroy(controller); + environment.extra.debugRenderTree.willDestroy(bucket); engine.destroy(); }, }; @@ -169,8 +160,8 @@ class MountManager extends AbstractManager didRenderLayout(bucket: EngineState, bounds: Bounds): void { if (ENV._DEBUG_RENDER_TREE) { - bucket.environment.debugRenderTree.didRender(bucket.controller, bounds); - bucket.environment.debugRenderTree.didRender(bucket, bounds); + bucket.environment.extra.debugRenderTree.didRender(bucket.controller, bounds); + bucket.environment.extra.debugRenderTree.didRender(bucket, bounds); } } @@ -182,15 +173,15 @@ class MountManager extends AbstractManager } if (ENV._DEBUG_RENDER_TREE) { - environment.debugRenderTree.update(bucket); - environment.debugRenderTree.update(bucket.controller); + environment.extra.debugRenderTree.update(bucket); + environment.extra.debugRenderTree.update(bucket.controller); } } didUpdateLayout(bucket: EngineState, bounds: Bounds): void { if (ENV._DEBUG_RENDER_TREE) { - bucket.environment.debugRenderTree.didRender(bucket.controller, bounds); - bucket.environment.debugRenderTree.didRender(bucket, bounds); + bucket.environment.extra.debugRenderTree.didRender(bucket.controller, bounds); + bucket.environment.extra.debugRenderTree.didRender(bucket, bounds); } } } diff --git a/packages/@ember/-internals/glimmer/lib/component-managers/outlet.ts b/packages/@ember/-internals/glimmer/lib/component-managers/outlet.ts index 9ee9376a6e6..bc483d03220 100644 --- a/packages/@ember/-internals/glimmer/lib/component-managers/outlet.ts +++ b/packages/@ember/-internals/glimmer/lib/component-managers/outlet.ts @@ -1,29 +1,31 @@ import { ENV } from '@ember/-internals/environment'; import { guidFor } from '@ember/-internals/utils'; -import { OwnedTemplateMeta } from '@ember/-internals/views'; import { assert } from '@ember/debug'; import EngineInstance from '@ember/engine/instance'; import { _instrumentStart } from '@ember/instrumentation'; import { assign } from '@ember/polyfills'; -import { ComponentCapabilities, Option, Simple } from '@glimmer/interfaces'; -import { CONSTANT_TAG, createTag, Tag, VersionedPathReference } from '@glimmer/reference'; import { - Arguments, Bounds, + ComponentCapabilities, ComponentDefinition, + Destroyable, ElementOperations, - EMPTY_ARGS, - Invocation, + Option, + VMArguments, WithDynamicTagName, - WithStaticLayout, -} from '@glimmer/runtime'; -import { Destroyable } from '@glimmer/util'; -import Environment from '../environment'; + WithJitStaticLayout, +} from '@glimmer/interfaces'; +import { unwrapTemplate } from '@glimmer/opcode-compiler'; +import { ComponentRootReference, VersionedPathReference } from '@glimmer/reference'; +import { EMPTY_ARGS } from '@glimmer/runtime'; +import { CONSTANT_TAG, createTag, Tag } from '@glimmer/validator'; + +import { SimpleElement } from '@simple-dom/interface'; +import { EmberVMEnvironment } from '../environment'; import { DynamicScope } from '../renderer'; import RuntimeResolver from '../resolver'; import { OwnedTemplate } from '../template'; import { OutletState } from '../utils/outlet'; -import { RootReference } from '../utils/references'; import OutletView from '../views/outlet'; import AbstractManager from './abstract'; @@ -33,7 +35,7 @@ function instrumentationPayload(def: OutletDefinitionState) { interface OutletInstanceState { self: VersionedPathReference; - environment: Environment; + environment: EmberVMEnvironment; outlet?: { name: string }; engine?: { mountPoint: string }; finalize: () => void; @@ -59,20 +61,16 @@ const CAPABILITIES: ComponentCapabilities = { dynamicScope: true, updateHook: ENV._DEBUG_RENDER_TREE, createInstance: true, + wrapped: false, + willDestroy: false, }; class OutletComponentManager extends AbstractManager - implements - WithStaticLayout< - OutletInstanceState, - OutletDefinitionState, - OwnedTemplateMeta, - RuntimeResolver - > { + implements WithJitStaticLayout { create( - environment: Environment, + environment: EmberVMEnvironment, definition: OutletDefinitionState, - args: Arguments, + args: VMArguments, dynamicScope: DynamicScope ): OutletInstanceState { let parentStateRef = dynamicScope.outletState; @@ -81,7 +79,7 @@ class OutletComponentManager extends AbstractManager { + implements + ComponentDefinition { constructor( public state: OutletDefinitionState, public manager: OutletComponentManager = OUTLET_MANAGER @@ -231,6 +226,7 @@ export function createRootOutlet(outletView: OutletView): OutletComponentDefinit const WRAPPED_CAPABILITIES = assign({}, CAPABILITIES, { dynamicTag: true, elementHook: true, + wrapped: true, }); const WrappedOutletComponentManager = class extends OutletComponentManager @@ -239,14 +235,9 @@ export function createRootOutlet(outletView: OutletView): OutletComponentDefinit return 'div'; } - getLayout(state: OutletDefinitionState): Invocation { + getJitStaticLayout({ template }: OutletDefinitionState) { // The router has already resolved the template - const template = state.template; - const layout = template.asWrappedLayout(); - return { - handle: layout.compile(), - symbolTable: layout.symbolTable, - }; + return unwrapTemplate(template).asWrappedLayout(); } getCapabilities(): ComponentCapabilities { @@ -255,7 +246,7 @@ export function createRootOutlet(outletView: OutletView): OutletComponentDefinit didCreateElement( component: OutletInstanceState, - element: Simple.Element, + element: SimpleElement, _operations: ElementOperations ): void { // to add GUID id and class diff --git a/packages/@ember/-internals/glimmer/lib/component-managers/root.ts b/packages/@ember/-internals/glimmer/lib/component-managers/root.ts index 1d957523d30..32101b3c895 100644 --- a/packages/@ember/-internals/glimmer/lib/component-managers/root.ts +++ b/packages/@ember/-internals/glimmer/lib/component-managers/root.ts @@ -3,10 +3,16 @@ import { ENV } from '@ember/-internals/environment'; import { Factory } from '@ember/-internals/owner'; import { _instrumentStart } from '@ember/instrumentation'; import { DEBUG } from '@glimmer/env'; -import { ComponentCapabilities, Option } from '@glimmer/interfaces'; -import { Arguments, ComponentDefinition, EMPTY_ARGS } from '@glimmer/runtime'; +import { + ComponentCapabilities, + ComponentDefinition, + Option, + VMArguments, +} from '@glimmer/interfaces'; +import { unwrapTemplate } from '@glimmer/opcode-compiler'; +import { EMPTY_ARGS } from '@glimmer/runtime'; import { DIRTY_TAG } from '../component'; -import Environment from '../environment'; +import { EmberVMEnvironment } from '../environment'; import { DynamicScope } from '../renderer'; import ComponentStateBucket, { Component } from '../utils/curly-component-state-bucket'; import CurlyComponentManager, { @@ -23,19 +29,15 @@ class RootComponentManager extends CurlyComponentManager { this.component = component; } - getLayout(_state: DefinitionState) { + getJitStaticLayout(_state: DefinitionState) { const template = this.templateFor(this.component); - const layout = template.asWrappedLayout(); - return { - handle: layout.compile(), - symbolTable: layout.symbolTable, - }; + return unwrapTemplate(template).asWrappedLayout(); } create( - environment: Environment, + environment: EmberVMEnvironment, state: DefinitionState, - _args: Option, + _args: Option, dynamicScope: DynamicScope ) { let component = this.component; @@ -72,12 +74,12 @@ class RootComponentManager extends CurlyComponentManager { ); if (ENV._DEBUG_RENDER_TREE) { - environment.debugRenderTree.create(bucket, { + environment.extra.debugRenderTree.create(bucket, { type: 'component', name: state.name, args: EMPTY_ARGS, instance: component, - template: state.template, + template: state.template!, }); } @@ -98,6 +100,8 @@ export const ROOT_CAPABILITIES: ComponentCapabilities = { dynamicScope: true, updateHook: true, createInstance: true, + wrapped: true, + willDestroy: false, }; export class RootComponentDefinition implements ComponentDefinition { @@ -112,7 +116,6 @@ export class RootComponentDefinition implements ComponentDefinition { name: factory!.fullName.slice(10), capabilities: ROOT_CAPABILITIES, ComponentClass: factory as Factory, - handle: null, }; } diff --git a/packages/@ember/-internals/glimmer/lib/component-managers/template-only.ts b/packages/@ember/-internals/glimmer/lib/component-managers/template-only.ts index 00a7b572f35..83282c7fef5 100644 --- a/packages/@ember/-internals/glimmer/lib/component-managers/template-only.ts +++ b/packages/@ember/-internals/glimmer/lib/component-managers/template-only.ts @@ -1,16 +1,16 @@ import { ENV } from '@ember/-internals/environment'; -import { OwnedTemplateMeta } from '@ember/-internals/views'; -import { ComponentCapabilities, Option } from '@glimmer/interfaces'; -import { CONSTANT_TAG, createTag } from '@glimmer/reference'; import { - Arguments, Bounds, + ComponentCapabilities, ComponentDefinition, - Invocation, - NULL_REFERENCE, - WithStaticLayout, -} from '@glimmer/runtime'; -import Environment from '../environment'; + Option, + VMArguments, + WithJitStaticLayout, +} from '@glimmer/interfaces'; +import { unwrapTemplate } from '@glimmer/opcode-compiler'; +import { NULL_REFERENCE } from '@glimmer/runtime'; +import { CONSTANT_TAG, createTag } from '@glimmer/validator'; +import { EmberVMEnvironment } from '../environment'; import RuntimeResolver from '../resolver'; import { OwnedTemplate } from '../template'; import AbstractManager from './abstract'; @@ -26,27 +26,24 @@ const CAPABILITIES: ComponentCapabilities = { dynamicScope: false, updateHook: ENV._DEBUG_RENDER_TREE, createInstance: true, + wrapped: false, + willDestroy: false, }; export interface DebugStateBucket { - environment: Environment; + environment: EmberVMEnvironment; } export default class TemplateOnlyComponentManager extends AbstractManager, TemplateOnlyComponentDefinitionState> implements - WithStaticLayout< + WithJitStaticLayout< Option, TemplateOnlyComponentDefinitionState, - OwnedTemplateMeta, RuntimeResolver > { - getLayout({ template }: TemplateOnlyComponentDefinitionState): Invocation { - const layout = template.asLayout(); - return { - handle: layout.compile(), - symbolTable: layout.symbolTable, - }; + getJitStaticLayout({ template }: TemplateOnlyComponentDefinitionState) { + return unwrapTemplate(template).asLayout(); } getCapabilities(): ComponentCapabilities { @@ -54,13 +51,13 @@ export default class TemplateOnlyComponentManager } create( - environment: Environment, + environment: EmberVMEnvironment, { name, template }: TemplateOnlyComponentDefinitionState, - args: Arguments + args: VMArguments ): Option { if (ENV._DEBUG_RENDER_TREE) { let bucket = { environment }; - environment.debugRenderTree.create(bucket, { + environment.extra.debugRenderTree.create(bucket, { type: 'component', name: name, args: args.capture(), @@ -91,7 +88,7 @@ export default class TemplateOnlyComponentManager if (ENV._DEBUG_RENDER_TREE) { return { destroy() { - bucket!.environment.debugRenderTree.willDestroy(bucket!); + bucket!.environment.extra.debugRenderTree.willDestroy(bucket!); }, }; } else { @@ -101,19 +98,19 @@ export default class TemplateOnlyComponentManager didRenderLayout(bucket: Option, bounds: Bounds): void { if (ENV._DEBUG_RENDER_TREE) { - bucket!.environment.debugRenderTree.didRender(bucket!, bounds); + bucket!.environment.extra.debugRenderTree.didRender(bucket!, bounds); } } update(bucket: Option): void { if (ENV._DEBUG_RENDER_TREE) { - bucket!.environment.debugRenderTree.update(bucket!); + bucket!.environment.extra.debugRenderTree.update(bucket!); } } didUpdateLayout(bucket: Option, bounds: Bounds): void { if (ENV._DEBUG_RENDER_TREE) { - bucket!.environment.debugRenderTree.didRender(bucket!, bounds); + bucket!.environment.extra.debugRenderTree.didRender(bucket!, bounds); } } } @@ -128,7 +125,11 @@ export interface TemplateOnlyComponentDefinitionState { export class TemplateOnlyComponentDefinition implements TemplateOnlyComponentDefinitionState, - ComponentDefinition { + ComponentDefinition< + TemplateOnlyComponentDefinitionState, + Option, + TemplateOnlyComponentManager + > { manager = MANAGER; constructor(public name: string, public template: OwnedTemplate) {} diff --git a/packages/@ember/-internals/glimmer/lib/component.ts b/packages/@ember/-internals/glimmer/lib/component.ts index cb94bc9478f..ca16516243d 100644 --- a/packages/@ember/-internals/glimmer/lib/component.ts +++ b/packages/@ember/-internals/glimmer/lib/component.ts @@ -13,10 +13,10 @@ import { } from '@ember/-internals/views'; import { assert, deprecate } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; -import { createTag, dirty } from '@glimmer/reference'; -import { normalizeProperty, SVG_NAMESPACE } from '@glimmer/runtime'; - -import { UPDATE } from './utils/references'; +import { UPDATE_REFERENCED_VALUE } from '@glimmer/reference'; +import { normalizeProperty } from '@glimmer/runtime'; +import { createTag, dirty } from '@glimmer/validator'; +import { Namespace } from '@simple-dom/interface'; export const DIRTY_TAG = symbol('DIRTY_TAG'); export const ARGS = symbol('ARGS'); @@ -786,8 +786,8 @@ const Component = CoreView.extend( let args = this[ARGS]; let reference = args !== undefined ? args[key] : undefined; - if (reference !== undefined && reference[UPDATE] !== undefined) { - reference[UPDATE](get(this, key)); + if (reference !== undefined && reference[UPDATE_REFERENCED_VALUE] !== undefined) { + reference[UPDATE_REFERENCED_VALUE](get(this, key)); } }, @@ -837,7 +837,7 @@ const Component = CoreView.extend( ); let element = _element!; - let isSVG = element.namespaceURI === SVG_NAMESPACE; + let isSVG = element.namespaceURI === Namespace.SVG; let { type, normalized } = normalizeProperty(element, name); if (isSVG || type === 'attr') { diff --git a/packages/@ember/-internals/glimmer/lib/dom.ts b/packages/@ember/-internals/glimmer/lib/dom.ts index 2a9b05a0a76..23731e97af5 100644 --- a/packages/@ember/-internals/glimmer/lib/dom.ts +++ b/packages/@ember/-internals/glimmer/lib/dom.ts @@ -1,4 +1,3 @@ -/// export { DOMChanges, DOMTreeConstruction, diff --git a/packages/@ember/-internals/glimmer/lib/environment.ts b/packages/@ember/-internals/glimmer/lib/environment.ts index 1cd399f27ba..d9d78f54f3a 100644 --- a/packages/@ember/-internals/glimmer/lib/environment.ts +++ b/packages/@ember/-internals/glimmer/lib/environment.ts @@ -1,55 +1,38 @@ -import { OWNER, Owner } from '@ember/-internals/owner'; +import { ENV } from '@ember/-internals/environment'; +import { get, set } from '@ember/-internals/metal'; +import { Owner } from '@ember/-internals/owner'; import { constructStyleDeprecationMessage } from '@ember/-internals/views'; import { warn } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; -import { Option, Simple } from '@glimmer/interfaces'; -import { OpaqueIterable, VersionedReference } from '@glimmer/reference'; +import { ElementBuilder, Environment, Option } from '@glimmer/interfaces'; +import { + IterationItemReference, + PropertyReference, + VersionedPathReference, +} from '@glimmer/reference'; import { - ElementBuilder, - Environment as GlimmerEnvironment, + DynamicAttribute, + dynamicAttribute, + EnvironmentDelegate, SimpleDynamicAttribute, } from '@glimmer/runtime'; -import { Destroyable, Opaque } from '@glimmer/util'; -import createIterable from './utils/iterable'; -import { ConditionalReference, UpdatableReference } from './utils/references'; -import { isHTMLSafe } from './utils/string'; - +import { AttrNamespace as SimpleAttrNamespace, SimpleElement } from '@simple-dom/interface'; import installPlatformSpecificProtocolForURL from './protocol-for-url'; - -import { ENV } from '@ember/-internals/environment'; import { OwnedTemplate } from './template'; -import DebugRenderTree from './utils/debug-render-tree'; +import DebugRenderTree, { PathNodeType } from './utils/debug-render-tree'; +import toIterator from './utils/iterator'; +import { isHTMLSafe } from './utils/string'; +import toBool from './utils/to-bool'; export interface CompilerFactory { id: string; new (template: OwnedTemplate): any; } -export default class Environment extends GlimmerEnvironment { - static create(options: any) { - return new this(options); - } - - public owner: Owner; - public isInteractive: boolean; - public destroyedComponents: Destroyable[]; - - private _debugRenderTree: DebugRenderTree | undefined; - public inTransaction = false; - - constructor(injections: any) { - super(injections); - - let owner: Owner = injections[OWNER]; - - this.owner = owner; - this.isInteractive = owner.lookup('-environment:main').isInteractive; - - // can be removed once https://github.com/tildeio/glimmer/pull/305 lands - this.destroyedComponents = []; - - installPlatformSpecificProtocolForURL(this); +export class EmberEnvironmentExtra { + private _debugRenderTree?: DebugRenderTree; + constructor(public owner: Owner) { if (ENV._DEBUG_RENDER_TREE) { this._debugRenderTree = new DebugRenderTree(); } @@ -65,71 +48,86 @@ export default class Environment extends GlimmerEnvironment { } } - // this gets clobbered by installPlatformSpecificProtocolForURL - // it really should just delegate to a platform specific injection - protocolForURL(s: string): string { - return s; + begin(): void { + if (ENV._DEBUG_RENDER_TREE) { + this.debugRenderTree.begin(); + } } - toConditionalReference(reference: UpdatableReference): VersionedReference { - return ConditionalReference.create(reference); + commit(): void { + if (ENV._DEBUG_RENDER_TREE) { + this.debugRenderTree.commit(); + } } +} - iterableFor(ref: VersionedReference, key: string): OpaqueIterable { - return createIterable(ref, key); - } +export class EmberEnvironmentDelegate implements EnvironmentDelegate { + public isInteractive: boolean; - scheduleInstallModifier(modifier: any, manager: any): void { - if (this.isInteractive) { - super.scheduleInstallModifier(modifier, manager); - } - } + public toBool = toBool; + public toIterator = toIterator; - scheduleUpdateModifier(modifier: any, manager: any): void { - if (this.isInteractive) { - super.scheduleUpdateModifier(modifier, manager); - } - } + public getPath = get; + public setPath = set; + + public attributeFor?: ( + element: SimpleElement, + attr: string, + isTrusting: boolean, + namespace: Option + ) => DynamicAttribute; + + public extra: EmberEnvironmentExtra; - didDestroy(destroyable: Destroyable): void { - destroyable.destroy(); + constructor(owner: Owner, isInteractive: boolean) { + this.extra = new EmberEnvironmentExtra(owner); + this.isInteractive = isInteractive; + + installPlatformSpecificProtocolForURL(this); } - begin(): void { - if (ENV._DEBUG_RENDER_TREE) { - this.debugRenderTree.begin(); - } + // this gets clobbered by installPlatformSpecificProtocolForURL + // it really should just delegate to a platform specific injection + protocolForURL(s: string): string { + return s; + } - this.inTransaction = true; + getTemplatePathDebugContext(pathRef: VersionedPathReference) { + let stack = this.extra.debugRenderTree.logRenderStackForPath(pathRef); - super.begin(); + return `While rendering:\n\n${stack}`; } - commit(): void { - let destroyedComponents = this.destroyedComponents; - this.destroyedComponents = []; - // components queued for destruction must be destroyed before firing - // `didCreate` to prevent errors when removing and adding a component - // with the same name (would throw an error when added to view registry) - for (let i = 0; i < destroyedComponents.length; i++) { - destroyedComponents[i].destroy(); - } + setTemplatePathDebugContext( + pathRef: VersionedPathReference, + desc: string, + parentRef: Option + ) { + let type: PathNodeType = 'root'; - try { - super.commit(); - } finally { - this.inTransaction = false; + if (pathRef instanceof IterationItemReference) { + type = 'iterator'; + } else if (pathRef instanceof PropertyReference) { + type = 'property'; } - if (ENV._DEBUG_RENDER_TREE) { - this.debugRenderTree.commit(); - } + this.extra.debugRenderTree.createPath(pathRef, desc, type, parentRef); + } + + onTransactionBegin() { + this.extra.begin(); + } + + onTransactionCommit() { + this.extra.commit(); } } +export type EmberVMEnvironment = Environment; + if (DEBUG) { class StyleAttributeManager extends SimpleDynamicAttribute { - set(dom: ElementBuilder, value: Opaque, env: GlimmerEnvironment): void { + set(dom: ElementBuilder, value: unknown, env: Environment): void { warn( constructStyleDeprecationMessage(value), (() => { @@ -142,7 +140,7 @@ if (DEBUG) { ); super.set(dom, value, env); } - update(value: Opaque, env: GlimmerEnvironment): void { + update(value: unknown, env: Environment): void { warn( constructStyleDeprecationMessage(value), (() => { @@ -157,22 +155,16 @@ if (DEBUG) { } } - Environment.prototype.attributeFor = function( + EmberEnvironmentDelegate.prototype.attributeFor = function( element, attribute: string, isTrusting: boolean, - namespace: Option + namespace: Option ) { if (attribute === 'style' && !isTrusting) { return new StyleAttributeManager({ element, name: attribute, namespace }); } - return GlimmerEnvironment.prototype.attributeFor.call( - this, - element, - attribute, - isTrusting, - namespace - ); + return dynamicAttribute(element, attribute, namespace); }; } diff --git a/packages/@ember/-internals/glimmer/lib/helper.ts b/packages/@ember/-internals/glimmer/lib/helper.ts index 89c12b798eb..8b85ba4eb11 100644 --- a/packages/@ember/-internals/glimmer/lib/helper.ts +++ b/packages/@ember/-internals/glimmer/lib/helper.ts @@ -6,12 +6,12 @@ import { Factory } from '@ember/-internals/owner'; import { FrameworkObject, setFrameworkClass } from '@ember/-internals/runtime'; import { symbol } from '@ember/-internals/utils'; import { join } from '@ember/runloop'; -import { Dict, Opaque } from '@glimmer/interfaces'; -import { createTag, dirty } from '@glimmer/reference'; +import { Dict } from '@glimmer/interfaces'; +import { createTag, dirty } from '@glimmer/validator'; export const RECOMPUTE_TAG = symbol('RECOMPUTE_TAG'); -export type HelperFunction = (positional: Opaque[], named: Dict) => Opaque; +export type HelperFunction = (positional: unknown[], named: Dict) => T; export type SimpleHelperFactory = Factory>; export type ClassHelperFactory = Factory>; @@ -21,13 +21,13 @@ export interface HelperFactory { create(): T; } -export interface HelperInstance { - compute(positional: Opaque[], named: Dict): Opaque; +export interface HelperInstance { + compute(positional: unknown[], named: Dict): T; destroy(): void; } -export interface SimpleHelper { - compute: HelperFunction; +export interface SimpleHelper { + compute: HelperFunction; } export function isHelperFactory( diff --git a/packages/@ember/-internals/glimmer/lib/helpers/-assert-implicit-component-helper-argument.ts b/packages/@ember/-internals/glimmer/lib/helpers/-assert-implicit-component-helper-argument.ts index 3d6a0b6bd5c..a827e52bc27 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/-assert-implicit-component-helper-argument.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/-assert-implicit-component-helper-argument.ts @@ -1,20 +1,20 @@ import { assert } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; -import { Opaque } from '@glimmer/interfaces'; -import { Tag, VersionedPathReference } from '@glimmer/reference'; -import { Arguments, Helper, VM } from '@glimmer/runtime'; +import { Helper, VMArguments } from '@glimmer/interfaces'; +import { VersionedPathReference } from '@glimmer/reference'; +import { Tag } from '@glimmer/validator'; let helper: Helper; if (DEBUG) { - class ComponentAssertionReference implements VersionedPathReference { + class ComponentAssertionReference implements VersionedPathReference { public tag: Tag; - constructor(private component: VersionedPathReference, private message: string) { + constructor(private component: VersionedPathReference, private message: string) { this.tag = component.tag; } - value(): Opaque { + value(): unknown { let value = this.component.value(); assert(this.message, typeof value !== 'string'); @@ -22,15 +22,15 @@ if (DEBUG) { return value; } - get(property: string): VersionedPathReference { + get(property: string): VersionedPathReference { return this.component.get(property); } } - helper = (_vm: VM, args: Arguments) => + helper = (args: VMArguments) => new ComponentAssertionReference(args.positional.at(0), args.positional.at(1).value() as string); } else { - helper = (_vm: VM, args: Arguments) => args.positional.at(0); + helper = (args: VMArguments) => args.positional.at(0); } export default helper; diff --git a/packages/@ember/-internals/glimmer/lib/helpers/-input-type.ts b/packages/@ember/-internals/glimmer/lib/helpers/-input-type.ts index 70a0efaf751..58286e7f6fe 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/-input-type.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/-input-type.ts @@ -1,7 +1,7 @@ -import { Arguments, VM } from '@glimmer/runtime'; -import { InternalHelperReference } from '../utils/references'; +import { CapturedArguments, VM, VMArguments } from '@glimmer/interfaces'; +import { HelperRootReference } from '@glimmer/reference'; -function inputTypeHelper({ positional }: any) { +function inputTypeHelper({ positional }: CapturedArguments) { let type = positional.at(0).value(); if (type === 'checkbox') { return '-checkbox'; @@ -9,6 +9,6 @@ function inputTypeHelper({ positional }: any) { return '-text-field'; } -export default function(_vm: VM, args: Arguments) { - return new InternalHelperReference(inputTypeHelper, args.capture()); +export default function(args: VMArguments, vm: VM) { + return new HelperRootReference(inputTypeHelper, args.capture(), vm.env); } diff --git a/packages/@ember/-internals/glimmer/lib/helpers/-normalize-class.ts b/packages/@ember/-internals/glimmer/lib/helpers/-normalize-class.ts index 57f0903d4ce..401996e826d 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/-normalize-class.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/-normalize-class.ts @@ -1,12 +1,9 @@ import { dasherize } from '@ember/string'; -import { Arguments, VM } from '@glimmer/runtime'; -import { InternalHelperReference } from '../utils/references'; +import { CapturedArguments, VM, VMArguments } from '@glimmer/interfaces'; +import { HelperRootReference } from '@glimmer/reference'; -function normalizeClass({ positional }: any) { - let classNameParts = positional - .at(0) - .value() - .split('.'); +function normalizeClass({ positional }: CapturedArguments) { + let classNameParts = (positional.at(0).value() as string).split('.'); let className = classNameParts[classNameParts.length - 1]; let value = positional.at(1).value(); @@ -19,6 +16,6 @@ function normalizeClass({ positional }: any) { } } -export default function(_vm: VM, args: Arguments) { - return new InternalHelperReference(normalizeClass, args.capture()); +export default function(args: VMArguments, vm: VM) { + return new HelperRootReference(normalizeClass, args.capture(), vm.env); } diff --git a/packages/@ember/-internals/glimmer/lib/helpers/-track-array.ts b/packages/@ember/-internals/glimmer/lib/helpers/-track-array.ts new file mode 100644 index 00000000000..26054743da6 --- /dev/null +++ b/packages/@ember/-internals/glimmer/lib/helpers/-track-array.ts @@ -0,0 +1,39 @@ +/** +@module ember +*/ +import { tagForProperty } from '@ember/-internals/metal'; +import { VMArguments } from '@glimmer/interfaces'; +import { VersionedPathReference } from '@glimmer/reference'; +import { combine, createUpdatableTag, Tag, update } from '@glimmer/validator'; + +/** + This reference is used to get the `[]` tag of iterables, so we can trigger + updates to `{{each}}` when it changes. It is put into place by a template + transform at build time, similar to the (-each-in) helper +*/ +class TrackArrayReference implements VersionedPathReference { + public tag: Tag; + private valueTag = createUpdatableTag(); + + constructor(private inner: VersionedPathReference) { + this.tag = combine([inner.tag, this.valueTag]); + } + + value(): unknown { + let iterable = this.inner.value(); + + let tag = tagForProperty(iterable, '[]'); + + update(this.valueTag, tag); + + return iterable; + } + + get(key: string): VersionedPathReference { + return this.inner.get(key); + } +} + +export default function trackArray(args: VMArguments) { + return new TrackArrayReference(args.positional.at(0)); +} diff --git a/packages/@ember/-internals/glimmer/lib/helpers/action.ts b/packages/@ember/-internals/glimmer/lib/helpers/action.ts index 9167b39281e..76beddaee3d 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/action.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/action.ts @@ -2,14 +2,18 @@ @module ember */ import { get } from '@ember/-internals/metal'; +import { symbol } from '@ember/-internals/utils'; import { assert } from '@ember/debug'; import { flaggedInstrument } from '@ember/instrumentation'; import { join } from '@ember/runloop'; import { DEBUG } from '@glimmer/env'; -import { isConst, VersionedPathReference } from '@glimmer/reference'; -import { Arguments, VM } from '@glimmer/runtime'; -import { Opaque } from '@glimmer/util'; -import { ACTION, INVOKE, UnboundReference } from '../utils/references'; +import { VM, VMArguments } from '@glimmer/interfaces'; +import { VersionedPathReference } from '@glimmer/reference'; +import { isConst } from '@glimmer/validator'; +import { UnboundRootReference } from '../utils/references'; +import { INVOKE } from './mut'; + +export const ACTION = symbol('ACTION'); /** The `{{action}}` helper provides a way to pass triggers for behavior (usually @@ -274,7 +278,7 @@ import { ACTION, INVOKE, UnboundReference } from '../utils/references'; @for Ember.Templates.helpers @public */ -export default function(_vm: VM, args: Arguments): UnboundReference { +export default function(args: VMArguments, vm: VM): UnboundRootReference { let { named, positional } = args; let capturedArgs = positional.capture(); @@ -303,21 +307,21 @@ export default function(_vm: VM, args: Arguments): UnboundReference { fn[ACTION] = true; - return new UnboundReference(fn); + return new UnboundRootReference(fn, vm.env); } -function NOOP(args: Arguments) { +function NOOP(args: VMArguments) { return args; } function makeArgsProcessor( - valuePathRef: VersionedPathReference | false, - actionArgsRef: Array> + valuePathRef: VersionedPathReference | false, + actionArgsRef: Array> ) { let mergeArgs: any; if (actionArgsRef.length > 0) { - mergeArgs = (args: Arguments) => { + mergeArgs = (args: VMArguments) => { return actionArgsRef.map(ref => ref.value()).concat(args); }; } @@ -337,7 +341,7 @@ function makeArgsProcessor( } if (mergeArgs && readValue) { - return (args: Arguments) => { + return (args: VMArguments) => { return readValue(mergeArgs(args)); }; } else { diff --git a/packages/@ember/-internals/glimmer/lib/helpers/array.ts b/packages/@ember/-internals/glimmer/lib/helpers/array.ts index 48a181720a5..f076c374ecb 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/array.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/array.ts @@ -1,6 +1,5 @@ +import { VMArguments } from '@glimmer/interfaces'; import { PathReference } from '@glimmer/reference'; -import { Arguments, VM } from '@glimmer/runtime'; -import { Opaque } from '@glimmer/util'; /** @module ember @@ -42,6 +41,6 @@ import { Opaque } from '@glimmer/util'; @public */ -export default function(_vm: VM, args: Arguments): PathReference { +export default function(args: VMArguments): PathReference { return args.positional.capture(); } diff --git a/packages/@ember/-internals/glimmer/lib/helpers/concat.ts b/packages/@ember/-internals/glimmer/lib/helpers/concat.ts index 375b4d29ce4..fab1f7ebf3e 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/concat.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/concat.ts @@ -1,5 +1,5 @@ -import { Arguments, CapturedArguments, VM } from '@glimmer/runtime'; -import { InternalHelperReference } from '../utils/references'; +import { CapturedArguments, VM, VMArguments } from '@glimmer/interfaces'; +import { HelperRootReference } from '@glimmer/reference'; const isEmpty = (value: any): boolean => { return value === null || value === undefined || typeof value.toString !== 'function'; @@ -45,6 +45,6 @@ function concat({ positional }: CapturedArguments) { .join(''); } -export default function(_vm: VM, args: Arguments) { - return new InternalHelperReference(concat, args.capture()); +export default function(args: VMArguments, vm: VM) { + return new HelperRootReference(concat, args.capture(), vm.env); } diff --git a/packages/@ember/-internals/glimmer/lib/helpers/each-in.ts b/packages/@ember/-internals/glimmer/lib/helpers/each-in.ts index 4b51dd5989d..ac00fe5af8c 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/each-in.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/each-in.ts @@ -1,10 +1,12 @@ /** @module ember */ -import { symbol } from '@ember/-internals/utils'; -import { Tag, VersionedPathReference } from '@glimmer/reference'; -import { Arguments, VM } from '@glimmer/runtime'; -import { Opaque } from '@glimmer/util'; +import { tagForObject } from '@ember/-internals/metal'; +import { _contentFor } from '@ember/-internals/runtime'; +import { isProxy } from '@ember/-internals/utils'; +import { VMArguments } from '@glimmer/interfaces'; +import { VersionedPathReference } from '@glimmer/reference'; +import { combine, createUpdatableTag, Tag, update } from '@glimmer/validator'; /** The `{{#each}}` helper loops over elements in a collection. It is an extension @@ -147,18 +149,28 @@ import { Opaque } from '@glimmer/util'; @public @since 2.1.0 */ -const EACH_IN_REFERENCE = symbol('EACH_IN'); - class EachInReference implements VersionedPathReference { public tag: Tag; + private valueTag = createUpdatableTag(); constructor(private inner: VersionedPathReference) { - this.tag = inner.tag; - this[EACH_IN_REFERENCE] = true; + this.tag = combine([inner.tag, this.valueTag]); } - value(): Opaque { - return this.inner.value(); + value(): unknown { + let iterable = this.inner.value(); + + let tag = tagForObject(iterable); + + if (isProxy(iterable)) { + // this is because the each-in doesn't actually get(proxy, 'key') but bypasses it + // and the proxy's tag is lazy updated on access + iterable = _contentFor(iterable); + } + + update(this.valueTag, tag); + + return new EachInWrapper(iterable); } get(key: string): VersionedPathReference { @@ -166,10 +178,10 @@ class EachInReference implements VersionedPathReference { } } -export function isEachIn(ref: Opaque): ref is VersionedPathReference { - return ref !== null && typeof ref === 'object' && ref[EACH_IN_REFERENCE]; +export class EachInWrapper { + constructor(public inner: unknown) {} } -export default function(_vm: VM, args: Arguments) { +export default function(args: VMArguments) { return new EachInReference(args.positional.at(0)); } diff --git a/packages/@ember/-internals/glimmer/lib/helpers/fn.ts b/packages/@ember/-internals/glimmer/lib/helpers/fn.ts index 443258afca6..36ed5dec393 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/fn.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/fn.ts @@ -1,10 +1,9 @@ import { assert } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; -import { Arguments, VM } from '@glimmer/runtime'; -import { ICapturedArguments } from '@glimmer/runtime/dist/types/lib/vm/arguments'; -import { Opaque } from '@glimmer/util'; -import { InternalHelperReference, INVOKE } from '../utils/references'; +import { CapturedArguments, VM, VMArguments } from '@glimmer/interfaces'; +import { HelperRootReference } from '@glimmer/reference'; import buildUntouchableThis from '../utils/untouchable-this'; +import { INVOKE } from './mut'; const context = buildUntouchableThis('`fn` helper'); @@ -80,7 +79,7 @@ const context = buildUntouchableThis('`fn` helper'); @since 3.11.0 */ -function fnHelper({ positional }: ICapturedArguments) { +function fn({ positional }: CapturedArguments) { let callbackRef = positional.at(0); if (DEBUG && typeof callbackRef[INVOKE] !== 'function') { @@ -92,7 +91,7 @@ function fnHelper({ positional }: ICapturedArguments) { ); } - return (...invocationArgs: Opaque[]) => { + return (...invocationArgs: unknown[]) => { let [fn, ...args] = positional.value(); if (typeof callbackRef[INVOKE] === 'function') { @@ -100,11 +99,11 @@ function fnHelper({ positional }: ICapturedArguments) { // the symbol to be bound to the reference return callbackRef[INVOKE](...args, ...invocationArgs); } else { - return fn!['call'](context, ...args, ...invocationArgs); + return (fn as Function).call(context, ...args, ...invocationArgs); } }; } -export default function(_vm: VM, args: Arguments) { - return new InternalHelperReference(fnHelper, args.capture()); +export default function(args: VMArguments, vm: VM) { + return new HelperRootReference(fn, args.capture(), vm.env); } diff --git a/packages/@ember/-internals/glimmer/lib/helpers/get.ts b/packages/@ember/-internals/glimmer/lib/helpers/get.ts index ede0d7e0153..45ad217cfe2 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/get.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/get.ts @@ -1,17 +1,14 @@ -import { set } from '@ember/-internals/metal'; -import { Opaque } from '@glimmer/interfaces'; +import { get as emberGet, set as emberSet } from '@ember/-internals/metal'; +import { isObject } from '@ember/-internals/utils'; +import { CapturedArguments, Environment, VM, VMArguments } from '@glimmer/interfaces'; import { - combine, - createUpdatableTag, - isConst, - PathReference, - Tag, - UpdatableTag, - update, + HelperRootReference, + UPDATE_REFERENCED_VALUE, VersionedPathReference, } from '@glimmer/reference'; -import { Arguments, NULL_REFERENCE, VM } from '@glimmer/runtime'; -import { CachedReference, referenceFromParts, UPDATE } from '../utils/references'; +import { NULL_REFERENCE } from '@glimmer/runtime'; +import { isConst } from '@glimmer/validator'; +import { referenceFromParts } from '../utils/references'; /** @module ember @@ -97,77 +94,55 @@ import { CachedReference, referenceFromParts, UPDATE } from '../utils/references @for Ember.Templates.helpers @since 2.1.0 */ - -export default function(_vm: VM, args: Arguments) { - return GetHelperReference.create(args.positional.at(0), args.positional.at(1)); -} - -function referenceFromPath( - source: VersionedPathReference, - path: string -): VersionedPathReference { - let innerReference; - if (path === undefined || path === null || path === '') { - innerReference = NULL_REFERENCE; - } else if (typeof path === 'string' && path.indexOf('.') > -1) { - innerReference = referenceFromParts(source, path.split('.')); +export default function(args: VMArguments, vm: VM) { + let sourceReference = args.positional.at(0); + let pathReference = args.positional.at(1); + + if (isConst(pathReference)) { + // Since the path is constant, we can create a normal chain of property + // references. The source reference will update like normal, and all of the + // child references will update accordingly. + let path = pathReference.value(); + + if (path === undefined || path === null || path === '') { + return NULL_REFERENCE; + } else if (typeof path === 'string' && path.indexOf('.') > -1) { + return referenceFromParts(sourceReference, path.split('.')); + } else { + return sourceReference.get(String(path)); + } } else { - innerReference = source.get(path); + return new GetHelperRootReference(args.capture(), vm.env); } - return innerReference; } -class GetHelperReference extends CachedReference { - public sourceReference: VersionedPathReference; - public pathReference: PathReference; - public lastPath: string | null; - public innerReference: VersionedPathReference; - public innerTag: UpdatableTag; - public tag: Tag; - - static create( - sourceReference: VersionedPathReference, - pathReference: PathReference - ) { - if (isConst(pathReference)) { - let path = pathReference.value(); - return referenceFromPath(sourceReference, path); - } else { - return new GetHelperReference(sourceReference, pathReference); - } - } +function get({ positional }: CapturedArguments) { + let source = positional.at(0).value(); - constructor( - sourceReference: VersionedPathReference, - pathReference: PathReference - ) { - super(); - this.sourceReference = sourceReference; - this.pathReference = pathReference; + if (isObject(source)) { + let path = positional.at(1).value(); - this.lastPath = null; - this.innerReference = NULL_REFERENCE; + return emberGet(source, String(path)); + } +} - let innerTag = (this.innerTag = createUpdatableTag()); +class GetHelperRootReference extends HelperRootReference { + private sourceReference: VersionedPathReference; + private pathReference: VersionedPathReference; - this.tag = combine([sourceReference.tag, pathReference.tag, innerTag]); + constructor(args: CapturedArguments, env: Environment) { + super(get, args, env); + this.sourceReference = args.positional.at(0); + this.pathReference = args.positional.at(1); } - compute() { - let { lastPath, innerReference, innerTag } = this; - let path = this.pathReference.value(); + [UPDATE_REFERENCED_VALUE](value: any) { + let source = this.sourceReference.value(); - if (path !== lastPath) { - innerReference = referenceFromPath(this.sourceReference, path); - update(innerTag, innerReference.tag); - this.innerReference = innerReference; - this.lastPath = path; - } + if (isObject(source)) { + let path = String(this.pathReference.value()); - return innerReference.value(); - } - - [UPDATE](value: any) { - set(this.sourceReference.value() as any, this.pathReference.value(), value); + emberSet(source, path, value); + } } } diff --git a/packages/@ember/-internals/glimmer/lib/helpers/hash.ts b/packages/@ember/-internals/glimmer/lib/helpers/hash.ts index 4a7c124cbf0..16fe06b2e4b 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/hash.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/hash.ts @@ -1,6 +1,5 @@ +import { Dict, VMArguments } from '@glimmer/interfaces'; import { PathReference } from '@glimmer/reference'; -import { Arguments, VM } from '@glimmer/runtime'; -import { Dict, Opaque } from '@glimmer/util'; /** @module ember @@ -45,6 +44,6 @@ import { Dict, Opaque } from '@glimmer/util'; @public */ -export default function(_vm: VM, args: Arguments): PathReference> { +export default function(args: VMArguments): PathReference> { return args.named.capture(); } diff --git a/packages/@ember/-internals/glimmer/lib/helpers/if-unless.ts b/packages/@ember/-internals/glimmer/lib/helpers/if-unless.ts index c278efbac1f..229fdaaccaf 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/if-unless.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/if-unless.ts @@ -3,47 +3,41 @@ */ import { assert } from '@ember/debug'; -import { combine, createUpdatableTag, isConst, UpdatableTag, update } from '@glimmer/reference'; -import { Arguments, PrimitiveReference, VM } from '@glimmer/runtime'; -import { CachedReference, ConditionalReference } from '../utils/references'; - -class ConditionalHelperReference extends CachedReference { - public branchTag: UpdatableTag; - public tag: any; - public cond: any; - public truthy: any; - public falsy: any; - - static create( - _condRef: any, - truthyRef: PrimitiveReference, - falsyRef: PrimitiveReference - ) { - let condRef = ConditionalReference.create(_condRef); - if (isConst(condRef)) { - return condRef.value() ? truthyRef : falsyRef; - } else { - return new ConditionalHelperReference(condRef, truthyRef, falsyRef); - } - } +import { CapturedArguments, VM, VMArguments } from '@glimmer/interfaces'; +import { HelperRootReference } from '@glimmer/reference'; +import toBool from '../utils/to-bool'; - constructor(cond: any, truthy: any, falsy: any) { - super(); +function ifHelper({ positional }: CapturedArguments) { + assert( + 'The inline form of the `if` helper expects two or three arguments, e.g. `{{if trialExpired "Expired" expiryDate}}`.', + positional.length === 3 || positional.length === 2 + ); - this.branchTag = createUpdatableTag(); - this.tag = combine([cond.tag, this.branchTag]); + let condition = positional.at(0); + let truthyValue = positional.at(1); + let falsyValue = positional.at(2); - this.cond = cond; - this.truthy = truthy; - this.falsy = falsy; + if (toBool(condition.value()) === true) { + return truthyValue.value(); + } else { + return falsyValue !== undefined ? falsyValue.value() : undefined; } +} - compute() { - let branch = this.cond.value() ? this.truthy : this.falsy; +function unless({ positional }: CapturedArguments) { + assert( + 'The inline form of the `unless` helper expects two or three arguments, e.g. `{{unless isFirstLogin "Welcome back!"}}`.', + positional.length === 3 || positional.length === 2 + ); - update(this.branchTag, branch.tag); + let condition = positional.at(0); + let truthyValue = positional.at(2); + let falsyValue = positional.at(1); - return branch.value(); + if (toBool(condition.value()) === true) { + return truthyValue !== undefined ? truthyValue.value() : undefined; + } else { + return falsyValue.value(); } } @@ -138,13 +132,8 @@ class ConditionalHelperReference extends CachedReference { @for Ember.Templates.helpers @public */ -export function inlineIf(_vm: VM, { positional }: Arguments) { - assert( - 'The inline form of the `if` helper expects two or three arguments, e.g. ' + - '`{{if trialExpired "Expired" expiryDate}}`.', - positional.length === 3 || positional.length === 2 - ); - return ConditionalHelperReference.create(positional.at(0), positional.at(1), positional.at(2)); +export function inlineIf(args: VMArguments, vm: VM) { + return new HelperRootReference(ifHelper, args.capture(), vm.env); } /** @@ -232,11 +221,6 @@ export function inlineIf(_vm: VM, { positional }: Arguments) { @for Ember.Templates.helpers @public */ -export function inlineUnless(_vm: VM, { positional }: Arguments) { - assert( - 'The inline form of the `unless` helper expects two or three arguments, e.g. ' + - '`{{unless isFirstLogin "Welcome back!"}}`.', - positional.length === 3 || positional.length === 2 - ); - return ConditionalHelperReference.create(positional.at(0), positional.at(2), positional.at(1)); +export function inlineUnless(args: VMArguments, vm: VM) { + return new HelperRootReference(unless, args.capture(), vm.env); } diff --git a/packages/@ember/-internals/glimmer/lib/helpers/log.ts b/packages/@ember/-internals/glimmer/lib/helpers/log.ts index c31a1511828..52b02eeb7c3 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/log.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/log.ts @@ -1,5 +1,5 @@ -import { Arguments, CapturedArguments, VM } from '@glimmer/runtime'; -import { InternalHelperReference } from '../utils/references'; +import { CapturedArguments, VM, VMArguments } from '@glimmer/interfaces'; +import { HelperRootReference } from '@glimmer/reference'; /** @module ember */ @@ -23,6 +23,6 @@ function log({ positional }: CapturedArguments) { /* eslint-enable no-console */ } -export default function(_vm: VM, args: Arguments) { - return new InternalHelperReference(log, args.capture()); +export default function(args: VMArguments, vm: VM) { + return new HelperRootReference(log, args.capture(), vm.env); } diff --git a/packages/@ember/-internals/glimmer/lib/helpers/mut.ts b/packages/@ember/-internals/glimmer/lib/helpers/mut.ts index 5ef02420a04..9b7d5f91b61 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/mut.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/mut.ts @@ -3,8 +3,9 @@ */ import { symbol } from '@ember/-internals/utils'; import { assert } from '@ember/debug'; -import { Arguments, VM } from '@glimmer/runtime'; -import { INVOKE, UPDATE } from '../utils/references'; +import { Environment, VM, VMArguments } from '@glimmer/interfaces'; +import { RootReference, UPDATE_REFERENCED_VALUE, VersionedPathReference } from '@glimmer/reference'; +import { Tag } from '@glimmer/validator'; /** The `mut` helper lets you __clearly specify__ that a child `Component` can update the @@ -85,21 +86,44 @@ import { INVOKE, UPDATE } from '../utils/references'; @for Ember.Templates.helpers @public */ -const MUT_REFERENCE = symbol('MUT'); -const SOURCE = symbol('SOURCE'); +export const INVOKE: unique symbol = symbol('INVOKE') as any; +const SOURCE: unique symbol = symbol('SOURCE') as any; -export function isMut(ref: any): boolean { - return ref && ref[MUT_REFERENCE]; +class MutReference extends RootReference { + public tag: Tag; + public [SOURCE]: VersionedPathReference; + + constructor(protected inner: VersionedPathReference, env: Environment) { + super(env); + this.tag = inner.tag; + this[SOURCE] = inner; + } + + value(): unknown { + return this.inner.value(); + } + + get(key: string): VersionedPathReference { + return this.inner.get(key); + } + + [UPDATE_REFERENCED_VALUE](value: unknown) { + return this.inner[UPDATE_REFERENCED_VALUE](value); + } + + [INVOKE](value: unknown) { + return this.inner[UPDATE_REFERENCED_VALUE](value); + } } -export function unMut(ref: any) { +export function unMut(ref: VersionedPathReference) { return ref[SOURCE] || ref; } -export default function(_vm: VM, args: Arguments) { +export default function(args: VMArguments, vm: VM) { let rawRef = args.positional.at(0); - if (isMut(rawRef)) { + if (typeof rawRef[INVOKE] === 'function') { return rawRef; } @@ -115,13 +139,7 @@ export default function(_vm: VM, args: Arguments) { // // This message is alright for the first case, but could be quite // confusing for the second case. - assert('You can only pass a path to mut', rawRef[UPDATE]); - - let wrappedRef = Object.create(rawRef); - - wrappedRef[SOURCE] = rawRef; - wrappedRef[INVOKE] = rawRef[UPDATE]; - wrappedRef[MUT_REFERENCE] = true; + assert('You can only pass a path to mut', rawRef[UPDATE_REFERENCED_VALUE] !== undefined); - return wrappedRef; + return new MutReference(rawRef, vm.env); } diff --git a/packages/@ember/-internals/glimmer/lib/helpers/query-param.ts b/packages/@ember/-internals/glimmer/lib/helpers/query-param.ts index ffc40e16b84..f12c18f432f 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/query-param.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/query-param.ts @@ -4,8 +4,8 @@ import { QueryParams } from '@ember/-internals/routing'; import { assert } from '@ember/debug'; import { assign } from '@ember/polyfills'; -import { Arguments, CapturedArguments, VM } from '@glimmer/runtime'; -import { InternalHelperReference } from '../utils/references'; +import { CapturedArguments, VM, VMArguments } from '@glimmer/interfaces'; +import { HelperRootReference } from '@glimmer/reference'; /** This is a helper to be used in conjunction with the link-to helper. @@ -38,6 +38,6 @@ function queryParams({ positional, named }: CapturedArguments) { return new QueryParams(assign({}, named.value() as any)); } -export default function(_vm: VM, args: Arguments) { - return new InternalHelperReference(queryParams, args.capture()); +export default function(args: VMArguments, vm: VM) { + return new HelperRootReference(queryParams, args.capture(), vm.env); } diff --git a/packages/@ember/-internals/glimmer/lib/helpers/readonly.ts b/packages/@ember/-internals/glimmer/lib/helpers/readonly.ts index ef1bc21546b..d04214ca715 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/readonly.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/readonly.ts @@ -1,9 +1,10 @@ /** @module ember */ -import { Arguments, VM } from '@glimmer/runtime'; -import { ReadonlyReference } from '../utils/references'; -import { unMut } from './mut'; +import { Environment, VM, VMArguments } from '@glimmer/interfaces'; +import { RootReference, VersionedPathReference } from '@glimmer/reference'; +import { Tag } from '@glimmer/validator'; +import { INVOKE, unMut } from './mut'; /** The `readonly` helper let's you specify that a binding is one-way only, @@ -120,8 +121,29 @@ import { unMut } from './mut'; @for Ember.Templates.helpers @private */ -export default function(_vm: VM, args: Arguments) { +class ReadonlyReference extends RootReference { + public tag: Tag; + + constructor(protected inner: VersionedPathReference, env: Environment) { + super(env); + this.tag = inner.tag; + } + + get [INVOKE](): Function | undefined { + return this.inner[INVOKE]; + } + + value(): unknown { + return this.inner.value(); + } + + get(key: string): VersionedPathReference { + return this.inner.get(key); + } +} + +export default function(args: VMArguments, vm: VM) { let ref = unMut(args.positional.at(0)); - return new ReadonlyReference(ref); + return new ReadonlyReference(ref, vm.env); } diff --git a/packages/@ember/-internals/glimmer/lib/helpers/unbound.ts b/packages/@ember/-internals/glimmer/lib/helpers/unbound.ts index ba25c36e886..4a63a4603aa 100644 --- a/packages/@ember/-internals/glimmer/lib/helpers/unbound.ts +++ b/packages/@ember/-internals/glimmer/lib/helpers/unbound.ts @@ -3,8 +3,8 @@ */ import { assert } from '@ember/debug'; -import { Arguments, VM } from '@glimmer/runtime'; -import { UnboundReference } from '../utils/references'; +import { VM, VMArguments } from '@glimmer/interfaces'; +import { UnboundRootReference } from '../utils/references'; /** The `{{unbound}}` helper disconnects the one-way binding of a property, @@ -34,11 +34,11 @@ import { UnboundReference } from '../utils/references'; @public */ -export default function(_vm: VM, args: Arguments) { +export default function(args: VMArguments, vm: VM) { assert( 'unbound helper cannot be called with multiple params or hash params', args.positional.length === 1 && args.named.length === 0 ); - return UnboundReference.create(args.positional.at(0).value()); + return new UnboundRootReference(args.positional.at(0).value(), vm.env); } diff --git a/packages/@ember/-internals/glimmer/lib/modifiers/action.ts b/packages/@ember/-internals/glimmer/lib/modifiers/action.ts index 10d8b177b40..c3887b94bd8 100644 --- a/packages/@ember/-internals/glimmer/lib/modifiers/action.ts +++ b/packages/@ember/-internals/glimmer/lib/modifiers/action.ts @@ -3,17 +3,17 @@ import { ActionManager, isSimpleClick } from '@ember/-internals/views'; import { assert, deprecate } from '@ember/debug'; import { flaggedInstrument } from '@ember/instrumentation'; import { join } from '@ember/runloop'; -import { Opaque, Simple } from '@glimmer/interfaces'; -import { Tag } from '@glimmer/reference'; import { - Arguments, CapturedNamedArguments, CapturedPositionalArguments, + Destroyable, DynamicScope, ModifierManager, -} from '@glimmer/runtime'; -import { Destroyable } from '@glimmer/util'; -import { INVOKE } from '../utils/references'; + VMArguments, +} from '@glimmer/interfaces'; +import { Tag } from '@glimmer/validator'; +import { SimpleElement } from '@simple-dom/interface'; +import { INVOKE } from '../helpers/mut'; const MODIFIERS = ['alt', 'shift', 'meta', 'ctrl']; const POINTER_EVENT_TYPE_REGEX = /^click|mouse|touch/; @@ -61,7 +61,7 @@ export let ActionHelper = { }; export class ActionState { - public element: Simple.Element; + public element: SimpleElement; public actionId: number; public actionName: any; public actionArgs: any; @@ -73,7 +73,7 @@ export class ActionState { public tag: Tag; constructor( - element: Simple.Element, + element: SimpleElement, actionId: number, actionName: any, actionArgs: any[], @@ -186,11 +186,11 @@ export class ActionState { } // implements ModifierManager -export default class ActionModifierManager implements ModifierManager { +export default class ActionModifierManager implements ModifierManager { create( - element: Simple.Element, - _state: Opaque, - args: Arguments, + element: SimpleElement, + _state: unknown, + args: VMArguments, _dynamicScope: DynamicScope, dom: any ) { diff --git a/packages/@ember/-internals/glimmer/lib/modifiers/custom.ts b/packages/@ember/-internals/glimmer/lib/modifiers/custom.ts index 30413c92952..cba98225897 100644 --- a/packages/@ember/-internals/glimmer/lib/modifiers/custom.ts +++ b/packages/@ember/-internals/glimmer/lib/modifiers/custom.ts @@ -1,11 +1,18 @@ -import { track, untrack } from '@ember/-internals/metal'; import { Factory } from '@ember/-internals/owner'; import { getDebugName } from '@ember/-internals/utils'; import { assert, deprecate } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; -import { Dict, Opaque, Simple } from '@glimmer/interfaces'; -import { combine, CONSTANT_TAG, createUpdatableTag, Tag, update } from '@glimmer/reference'; -import { Arguments, CapturedArguments, ModifierManager } from '@glimmer/runtime'; +import { CapturedArguments, Dict, ModifierManager, VMArguments } from '@glimmer/interfaces'; +import { + combine, + CONSTANT_TAG, + createUpdatableTag, + Tag, + track, + untrack, + update, +} from '@glimmer/validator'; +import { SimpleElement } from '@simple-dom/interface'; import debugRenderMessage from '../utils/debug-render-message'; export interface CustomModifierDefinitionState { @@ -72,7 +79,7 @@ export class CustomModifierState { public tag = createUpdatableTag(); constructor( - public element: Simple.Element, + public element: SimpleElement, public delegate: ModifierManagerDelegate, public modifier: ModifierInstance, public args: CapturedArguments @@ -86,14 +93,14 @@ export class CustomModifierState { // TODO: export ICapturedArgumentsValue from glimmer and replace this export interface Args { - named: Dict; - positional: Opaque[]; + named: Dict; + positional: unknown[]; } export interface ModifierManagerDelegate { capabilities: Capabilities; - createModifier(factory: Opaque, args: Args): ModifierInstance; - installModifier(instance: ModifierInstance, element: Simple.Element, args: Args): void; + createModifier(factory: unknown, args: Args): ModifierInstance; + installModifier(instance: ModifierInstance, element: SimpleElement, args: Args): void; updateModifier(instance: ModifierInstance, args: Args): void; destroyModifier(instance: ModifierInstance, args: Args): void; } @@ -129,9 +136,9 @@ class InteractiveCustomModifierManager CustomModifierDefinitionState > { create( - element: Simple.Element, + element: SimpleElement, definition: CustomModifierDefinitionState, - args: Arguments + args: VMArguments ) { let { delegate, ModifierClass } = definition; const capturedArgs = args.capture(); diff --git a/packages/@ember/-internals/glimmer/lib/modifiers/on.ts b/packages/@ember/-internals/glimmer/lib/modifiers/on.ts index 9bbc5d6e7b5..2c126cea48b 100644 --- a/packages/@ember/-internals/glimmer/lib/modifiers/on.ts +++ b/packages/@ember/-internals/glimmer/lib/modifiers/on.ts @@ -1,9 +1,8 @@ import { assert } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; -import { Opaque, Simple } from '@glimmer/interfaces'; -import { CONSTANT_TAG, Tag } from '@glimmer/reference'; -import { Arguments, CapturedArguments, ModifierManager } from '@glimmer/runtime'; -import { Destroyable } from '@glimmer/util'; +import { CapturedArguments, Destroyable, ModifierManager, VMArguments } from '@glimmer/interfaces'; +import { CONSTANT_TAG, Tag } from '@glimmer/validator'; +import { SimpleElement } from '@simple-dom/interface'; import buildUntouchableThis from '../utils/untouchable-this'; const untouchableContext = buildUntouchableThis('`on` modifier'); @@ -301,7 +300,7 @@ function addEventListener( @public @since 3.11.0 */ -export default class OnModifierManager implements ModifierManager { +export default class OnModifierManager implements ModifierManager { public SUPPORTS_EVENT_OPTIONS: boolean = SUPPORTS_EVENT_OPTIONS; public isInteractive: boolean; @@ -313,7 +312,7 @@ export default class OnModifierManager implements ModifierManager ElementBuilder; @@ -65,41 +82,37 @@ export class DynamicScope implements GlimmerDynamicScope { class RootState { public id: string; - public env: Environment; - public root: Component | OutletView; public result: RenderResult | undefined; - public shouldReflush: boolean; public destroyed: boolean; public render: () => void; constructor( - root: Component | OutletView, - env: Environment, + public root: Component | OutletView, + public runtime: JitRuntimeContext, + context: SyntaxCompilationContext, template: OwnedTemplate, - self: VersionedPathReference, - parentElement: Simple.Element, + self: VersionedPathReference, + parentElement: SimpleElement, dynamicScope: DynamicScope, builder: IBuilder ) { assert(`You cannot render \`${self.value()}\` without a template.`, template !== undefined); this.id = getViewId(root); - this.env = env; - this.root = root; this.result = undefined; - this.shouldReflush = false; this.destroyed = false; this.render = () => { - let layout = template.asLayout(); - let handle = layout.compile(); - let iterator = renderMain( - layout['compiler'].program, - env, + let layout = unwrapTemplate(template).asLayout(); + let handle = layout.compile(context); + + let iterator = renderJitMain( + runtime, + context, self, - dynamicScope, - builder(env, { element: parentElement, nextSibling: null }), - handle + builder(runtime.env, { element: parentElement, nextSibling: null }), + unwrapHandle(handle), + dynamicScope ); let iteratorResult: IteratorResult; do { @@ -113,16 +126,19 @@ class RootState { }; } - isFor(possibleRoot: Opaque): boolean { + isFor(possibleRoot: unknown): boolean { return this.root === possibleRoot; } destroy() { - let { result, env } = this; + let { + result, + runtime: { env }, + } = this; this.destroyed = true; - this.env = undefined as any; + this.runtime = undefined as any; this.root = null as any; this.result = undefined; this.render = undefined as any; @@ -138,18 +154,8 @@ class RootState { * When roots are being destroyed during `Renderer#destroy`, no transaction exists */ - let needsTransaction = !env.inTransaction; - if (needsTransaction) { - env.begin(); - } - try { - result.destroy(); - } finally { - if (needsTransaction) { - env.commit(); - } - } + inTransaction(env, () => result!.destroy()); } } } @@ -236,48 +242,82 @@ backburner.on('begin', loopBegin); backburner.on('end', loopEnd); interface ViewRegistry { - [viewId: string]: Opaque; + [viewId: string]: unknown; } export abstract class Renderer { - private _env: Environment; private _rootTemplate: OwnedTemplate; private _viewRegistry: ViewRegistry; private _destinedForDOM: boolean; - private _destroyed: boolean; private _roots: RootState[]; - private _lastRevision: number; - private _isRenderingRoots: boolean; private _removedRoots: RootState[]; private _builder: IBuilder; + private _inRenderTransaction = false; + + private _context: JitSyntaxCompilationContext; + private _runtime: JitRuntimeContext; + + private _lastRevision = -1; + private _destroyed = false; + + readonly _runtimeResolver: RuntimeResolver; constructor( - env: Environment, + owner: Owner, + document: SimpleDocument, + env: { isInteractive: boolean; hasDOM: boolean }, rootTemplate: TemplateFactory, viewRegistry: ViewRegistry, destinedForDOM = false, builder = clientBuilder ) { - this._env = env; - this._rootTemplate = rootTemplate(env.owner); + this._rootTemplate = rootTemplate(owner); this._viewRegistry = viewRegistry; this._destinedForDOM = destinedForDOM; - this._destroyed = false; this._roots = []; - this._lastRevision = -1; - this._isRenderingRoots = false; this._removedRoots = []; this._builder = builder; + + // resolver is exposed for tests + let runtimeResolver = (this._runtimeResolver = new RuntimeResolver(env.isInteractive)); + let compileTimeResolver = new CompileTimeResolver(runtimeResolver); + + this._context = JitContext(compileTimeResolver); + + populateMacros(this._context.macros); + + let program = new RuntimeProgramImpl( + this._context.program.constants, + this._context.program.heap + ); + + let runtimeEnvironmentDelegate = new EmberEnvironmentDelegate(owner, env.isInteractive); + + this._runtime = JitRuntimeFromProgram( + { + appendOperations: env.hasDOM + ? new DOMTreeConstruction(document) + : new NodeDOMTreeConstruction(document), + updateOperations: new DOMChanges(document), + }, + program, + runtimeResolver, + runtimeEnvironmentDelegate + ); + } + + get debugRenderTree() { + return this._runtime.env.extra.debugRenderTree; } // renderer HOOKS - appendOutletView(view: OutletView, target: Simple.Element) { + appendOutletView(view: OutletView, target: SimpleElement) { let definition = createRootOutlet(view); this._appendDefinition(view, curry(definition), target); } - appendTo(view: Component, target: Simple.Element) { + appendTo(view: Component, target: SimpleElement) { let definition = new RootComponentDefinition(view); this._appendDefinition(view, curry(definition), target); } @@ -285,13 +325,14 @@ export abstract class Renderer { _appendDefinition( root: OutletView | Component, definition: CurriedComponentDefinition, - target: Simple.Element + target: SimpleElement ) { - let self = new UnboundReference(definition); + let self = new UnboundRootReference(definition, this._runtime.env); let dynamicScope = new DynamicScope(null, UNDEFINED_REFERENCE); let rootState = new RootState( root, - this._env, + this._runtime, + this._context, this._rootTemplate, self, target, @@ -328,7 +369,7 @@ export abstract class Renderer { } } - cleanupRootFor(view: Opaque) { + cleanupRootFor(view: unknown) { // no need to cleanup roots if we have already been destroyed if (this._destroyed) { return; @@ -356,11 +397,11 @@ export abstract class Renderer { this._clearAllRoots(); } - abstract getElement(view: Opaque): Option; + abstract getElement(view: unknown): Option; getBounds( view: object - ): { parentElement: Simple.Element; firstNode: Simple.Node; lastNode: Simple.Node } { + ): { parentElement: SimpleElement; firstNode: SimpleNode; lastNode: SimpleNode } { let bounds: Bounds = view[BOUNDS]; assert('object passed to getBounds must have the BOUNDS symbol as a property', Boolean(bounds)); @@ -372,8 +413,8 @@ export abstract class Renderer { return { parentElement, firstNode, lastNode }; } - createElement(tagName: string): Simple.Element { - return this._env.getAppendOperations().createElement(tagName); + createElement(tagName: string): SimpleElement { + return this._runtime.env.getAppendOperations().createElement(tagName); } _renderRoot(root: RootState) { @@ -389,16 +430,15 @@ export abstract class Renderer { } _renderRoots() { - let { _roots: roots, _env: env, _removedRoots: removedRoots } = this; + let { _roots: roots, _runtime: runtime, _removedRoots: removedRoots } = this; let initialRootsLength: number; do { - env.begin(); - try { + initialRootsLength = roots.length; + + inTransaction(runtime.env, () => { // ensure that for the first iteration of the loop // each root is processed - initialRootsLength = roots.length; - for (let i = 0; i < roots.length; i++) { let root = roots[i]; @@ -428,9 +468,7 @@ export abstract class Renderer { } this._lastRevision = value(CURRENT_TAG); - } finally { - env.commit(); - } + }); } while (roots.length > initialRootsLength); // remove any roots that were destroyed during this transaction @@ -447,7 +485,7 @@ export abstract class Renderer { } _renderRootsTransaction() { - if (this._isRenderingRoots) { + if (this._inRenderTransaction) { // currently rendering roots, a new root was added and will // be processed by the existing _renderRoots invocation return; @@ -455,7 +493,7 @@ export abstract class Renderer { // used to prevent calling _renderRoots again (see above) // while we are actively rendering roots - this._isRenderingRoots = true; + this._inRenderTransaction = true; let completedWithoutError = false; try { @@ -464,11 +502,8 @@ export abstract class Renderer { } finally { if (!completedWithoutError) { this._lastRevision = value(CURRENT_TAG); - if (this._env.inTransaction === true) { - this._env.commit(); - } } - this._isRenderingRoots = false; + this._inRenderTransaction = false; } } @@ -507,20 +542,24 @@ export abstract class Renderer { export class InertRenderer extends Renderer { static create({ + [OWNER]: owner, + document, env, rootTemplate, _viewRegistry, builder, }: { - env: Environment; + [OWNER]: Owner; + document: SimpleDocument; + env: { isInteractive: boolean; hasDOM: boolean }; rootTemplate: TemplateFactory; _viewRegistry: any; builder: any; }) { - return new this(env, rootTemplate, _viewRegistry, false, builder); + return new this(owner, document, env, rootTemplate, _viewRegistry, false, builder); } - getElement(_view: Opaque): Option { + getElement(_view: unknown): Option { throw new Error( 'Accessing `this.element` is not allowed in non-interactive environments (such as FastBoot).' ); @@ -529,20 +568,24 @@ export class InertRenderer extends Renderer { export class InteractiveRenderer extends Renderer { static create({ + [OWNER]: owner, + document, env, rootTemplate, _viewRegistry, builder, }: { - env: Environment; + [OWNER]: Owner; + document: SimpleDocument; + env: { isInteractive: boolean; hasDOM: boolean }; rootTemplate: TemplateFactory; _viewRegistry: any; builder: any; }) { - return new this(env, rootTemplate, _viewRegistry, true, builder); + return new this(owner, document, env, rootTemplate, _viewRegistry, true, builder); } - getElement(view: Opaque): Option { + getElement(view: unknown): Option { return getViewElement(view); } } diff --git a/packages/@ember/-internals/glimmer/lib/resolver.ts b/packages/@ember/-internals/glimmer/lib/resolver.ts index 85f8fc4d9c0..31d28c45ae1 100644 --- a/packages/@ember/-internals/glimmer/lib/resolver.ts +++ b/packages/@ember/-internals/glimmer/lib/resolver.ts @@ -1,6 +1,6 @@ import { privatize as P } from '@ember/-internals/container'; import { ENV } from '@ember/-internals/environment'; -import { Factory, FactoryClass, LookupOptions, Owner } from '@ember/-internals/owner'; +import { Factory, FactoryClass, getOwnerById, LookupOptions, Owner } from '@ember/-internals/owner'; import { OwnedTemplateMeta } from '@ember/-internals/views'; import { EMBER_GLIMMER_SET_COMPONENT_TEMPLATE, @@ -11,15 +11,10 @@ import { assert, deprecate } from '@ember/debug'; import { PARTIALS } from '@ember/deprecated-features'; import EmberError from '@ember/error'; import { _instrumentStart } from '@ember/instrumentation'; -import { - ComponentDefinition, - Opaque, - Option, - RuntimeResolver as IRuntimeResolver, -} from '@glimmer/interfaces'; -import { LazyCompiler, Macros, PartialDefinition } from '@glimmer/opcode-compiler'; -import { getDynamicVar, Helper, ModifierDefinition } from '@glimmer/runtime'; -import CompileTimeLookup from './compile-time-lookup'; +import { DEBUG } from '@glimmer/env'; +import { ComponentDefinition, Helper, JitRuntimeResolver, Option } from '@glimmer/interfaces'; +import { PartialDefinition, unwrapTemplate } from '@glimmer/opcode-compiler'; +import { getDynamicVar, ModifierDefinition } from '@glimmer/runtime'; import { CurlyComponentDefinition } from './component-managers/curly'; import { CustomManagerDefinition, ManagerDelegate } from './component-managers/custom'; import InternalComponentManager, { @@ -30,6 +25,7 @@ import { isHelperFactory, isSimpleHelper } from './helper'; import { default as componentAssertionHelper } from './helpers/-assert-implicit-component-helper-argument'; import { default as inputTypeHelper } from './helpers/-input-type'; import { default as normalizeClassHelper } from './helpers/-normalize-class'; +import { default as trackArray } from './helpers/-track-array'; import { default as action } from './helpers/action'; import { default as array } from './helpers/array'; import { default as concat } from './helpers/concat'; @@ -46,14 +42,13 @@ import { default as unbound } from './helpers/unbound'; import ActionModifierManager from './modifiers/action'; import { CustomModifierDefinition, ModifierManagerDelegate } from './modifiers/custom'; import OnModifierManager from './modifiers/on'; -import { populateMacros } from './syntax'; import { mountHelper } from './syntax/mount'; import { outletHelper } from './syntax/outlet'; import { Factory as TemplateFactory, OwnedTemplate } from './template'; import { getComponentTemplate } from './utils/component-template'; import { getModifierManager } from './utils/custom-modifier-manager'; import { getManager } from './utils/managers'; -import { ClassBasedHelperReference, SimpleHelperReference } from './utils/references'; +import { EmberHelperRootReference } from './utils/references'; function instrumentationPayload(name: string) { return { object: `component:${name}` }; @@ -100,10 +95,12 @@ function lookupModuleUnificationComponentPair( ) { localComponent = null; } + // TODO: Remove this when the MU feature flag is removed if ( localLayout !== null && globalLayout !== null && - localLayout.referrer.moduleName === globalLayout.referrer.moduleName + unwrapTemplate(localLayout).referrer.moduleName === + unwrapTemplate(globalLayout).referrer.moduleName ) { localLayout = null; } @@ -251,9 +248,11 @@ const BUILTINS_HELPERS: IBuiltInHelpers = { readonly, unbound, unless: inlineUnless, + '-hash': hash, '-each-in': eachIn, '-input-type': inputTypeHelper, '-normalize-class': normalizeClassHelper, + '-track-array': trackArray, '-get-dynamic-var': getDynamicVar, '-mount': mountHelper, '-outlet': outletHelper, @@ -264,9 +263,8 @@ interface IBuiltInModifiers { [name: string]: ModifierDefinition | undefined; } -export default class RuntimeResolver implements IRuntimeResolver { +export default class RuntimeResolver implements JitRuntimeResolver { public isInteractive: boolean; - public compiler: LazyCompiler; private handles: any[] = [ undefined, // ensure no falsy handle @@ -283,9 +281,6 @@ export default class RuntimeResolver implements IRuntimeResolver(new CompileTimeLookup(this), this, macros); this.isInteractive = isInteractive; this.builtInModifiers = { @@ -300,7 +295,7 @@ export default class RuntimeResolver implements IRuntimeResolver { + lookupComponent(name: string, meta: OwnedTemplateMeta): Option { let handle = this.lookupComponentHandle(name, meta); if (handle === null) { assert( @@ -370,10 +365,14 @@ export default class RuntimeResolver implements IRuntimeResolver { + assert( + `You attempted to overwrite the built-in helper "${_name}" which is not allowed. Please rename the helper.`, + !(this.builtInHelpers[_name] && getOwnerById(meta.ownerId).hasRegistration(`helper:${_name}`)) + ); + const helper = this.builtInHelpers[_name]; if (helper !== undefined) { return helper; } - const { owner, moduleName } = meta; + const { moduleName } = meta; + let owner = getOwnerById(meta.ownerId); let name = _name; let namespace = undefined; @@ -410,19 +415,28 @@ export default class RuntimeResolver implements IRuntimeResolver { + return (args, vm) => { const helper = factory.create(); - if (isSimpleHelper(helper)) { - return SimpleHelperReference.create(helper.compute, args.capture()); + + if (!isSimpleHelper(helper)) { + vm.associateDestroyable(helper); + } else if (DEBUG) { + // Bind to null in case someone accidentally passed an unbound function + // in, and attempts use `this` on it. + // + // TODO: Update buildUntouchableThis to be flexible enough to provide a + // nice error message here. + helper.compute = helper.compute.bind(null); } - vm.newDestroyable(helper); - return ClassBasedHelperReference.create(helper, args.capture()); + + return new EmberHelperRootReference(helper, args.capture(), vm.env); }; } private _lookupPartial(name: string, meta: OwnedTemplateMeta): PartialDefinition { - let templateFactory = lookupPartial(name, meta.owner); - let template = templateFactory(meta.owner); + let owner = getOwnerById(meta.ownerId); + let templateFactory = lookupPartial(name, owner); + let template = templateFactory(owner); return new PartialDefinition(name, template); } @@ -431,10 +445,10 @@ export default class RuntimeResolver implements IRuntimeResolver(`modifier:${name}`); if (modifier !== undefined) { - let managerFactory = getModifierManager>(modifier.class); + let managerFactory = getModifierManager>(modifier.class); let manager = managerFactory!(owner); return new CustomModifierDefinition(name, modifier, manager, this.isInteractive); @@ -458,10 +472,13 @@ export default class RuntimeResolver implements IRuntimeResolver { let name = _name; let namespace = undefined; + let owner = getOwnerById(meta.ownerId); + let { moduleName } = meta; + if (EMBER_MODULE_UNIFICATION) { const parsed = this._parseNameForNamespace(_name); name = parsed.name; @@ -472,7 +489,7 @@ export default class RuntimeResolver implements IRuntimeResolver = null; + let layout: OwnedTemplate | undefined; let key: object; if (pair.component === null) { @@ -486,7 +503,7 @@ export default class RuntimeResolver implements IRuntimeResolver, + factory(owner) as InternalComponentManager, ComponentClass as Factory, layout! ); @@ -526,8 +543,8 @@ export default class RuntimeResolver implements IRuntimeResolver, - layout !== null + factory(owner) as ManagerDelegate, + layout !== undefined ? layout : owner.lookup(P`template:components/-default`)!(owner) ); @@ -539,7 +556,6 @@ export default class RuntimeResolver implements IRuntimeResolver, hash: Option, - builder: OpcodeBuilder -): boolean { - assert( - `You attempted to overwrite the built-in helper "${name}" which is not allowed. Please rename the helper.`, - !( - (builder.compiler['resolver'] as CompileTimeLookup)['resolver']['builtInHelpers'][name] && - builder.referrer.owner.hasRegistration(`helper:${name}`) - ) - ); - - let handle = builder.compiler['resolver'].lookupComponentDefinition(name, builder.referrer); - - if (handle !== null) { - builder.component.static(handle, [params === null ? [] : params, hashToArgs(hash), null, null]); - return true; + context: MacroContext +): StatementCompileActions | Unhandled { + let component = context.resolver.lookupComponent(name, context.meta.referrer); + + if (component !== null) { + return staticComponent(component, [ + params === null ? [] : params, + hashToArgs(hash), + EMPTY_BLOCKS, + ]); } - return false; + return UNHANDLED; } function refineBlockSyntax( name: string, params: Core.Params, hash: Core.Hash, - template: Option, - inverse: Option, - builder: OpcodeBuilder -) { - let handle = builder.compiler['resolver'].lookupComponentDefinition(name, builder.referrer); + blocks: NamedBlocks, + context: MacroContext +): StatementCompileActions { + let handle = context.resolver.lookupComponent(name, context.meta.referrer); if (handle !== null) { - builder.component.static(handle, [params, hashToArgs(hash), template, inverse]); - return true; + return staticComponent(handle, [params, hashToArgs(hash), blocks]); } assert( `A component or helper named "${name}" could not be found`, - builder.referrer.owner.hasRegistration(`helper:${name}`) + getOwnerById((context.meta.referrer as OwnedTemplateMeta).ownerId).hasRegistration( + `helper:${name}` + ) ); assert( `Helpers may not be used in the block form, for example {{#${name}}}{{/${name}}}. Please use a component, or alternatively use the helper in combination with a built-in Ember helper, for example {{#if (${name})}}{{/if}}.`, !(() => { - const resolver = (builder.compiler['resolver'] as CompileTimeLookup)['resolver']; - const { owner, moduleName } = builder.referrer; + const resolver = context.resolver['inner']['resolver']; + const { moduleName } = context.meta.referrer as OwnedTemplateMeta; + const owner = getOwnerById((context.meta.referrer as OwnedTemplateMeta).ownerId); if (name === 'component' || resolver['builtInHelpers'][name]) { return true; } @@ -69,26 +77,13 @@ function refineBlockSyntax( })() ); - return false; -} - -export const experimentalMacros: any[] = []; - -// This is a private API to allow for experimental macros -// to be created in user space. Registering a macro should -// should be done in an initializer. -export function registerMacros(macro: any) { - experimentalMacros.push(macro); + return NONE; } export function populateMacros(macros: Macros) { let { inlines, blocks } = macros; - inlines.add('outlet', outletMacro); - inlines.add('mount', mountMacro); inlines.addMissing(refineInlineSyntax); - - blocks.add('let', blockLetMacro); blocks.addMissing(refineBlockSyntax); for (let i = 0; i < experimentalMacros.length; i++) { diff --git a/packages/@ember/-internals/glimmer/lib/syntax/let.ts b/packages/@ember/-internals/glimmer/lib/syntax/let.ts index ea87e55346b..44da6c1cddd 100644 --- a/packages/@ember/-internals/glimmer/lib/syntax/let.ts +++ b/packages/@ember/-internals/glimmer/lib/syntax/let.ts @@ -1,8 +1,3 @@ -import { OwnedTemplateMeta } from '@ember/-internals/views'; -import { CompilableBlock, Option } from '@glimmer/interfaces'; -import { OpcodeBuilder } from '@glimmer/opcode-compiler'; -import * as WireFormat from '@glimmer/wire-format'; - /** @module ember */ @@ -56,20 +51,3 @@ import * as WireFormat from '@glimmer/wire-format'; @for Ember.Templates.helpers @public */ -export function blockLetMacro( - params: WireFormat.Core.Params, - _hash: WireFormat.Core.Hash, - template: Option, - _inverse: Option, - builder: OpcodeBuilder -) { - if (template !== null) { - if (params !== null) { - builder.compileParams(params); - builder.invokeStaticBlock(template, params.length); - } else { - builder.invokeStatic(template); - } - } - return true; -} diff --git a/packages/@ember/-internals/glimmer/lib/syntax/mount.ts b/packages/@ember/-internals/glimmer/lib/syntax/mount.ts index 559bbdc144f..5f8b790c5cc 100644 --- a/packages/@ember/-internals/glimmer/lib/syntax/mount.ts +++ b/packages/@ember/-internals/glimmer/lib/syntax/mount.ts @@ -1,33 +1,45 @@ /** @module ember */ -import { OwnedTemplateMeta } from '@ember/-internals/views'; import { assert } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; -import { Option } from '@glimmer/interfaces'; -import { OpcodeBuilder } from '@glimmer/opcode-compiler'; -import { Tag, VersionedPathReference } from '@glimmer/reference'; +import { CapturedArguments, Option, VM, VMArguments } from '@glimmer/interfaces'; +import { VersionedPathReference } from '@glimmer/reference'; import { - Arguments, - CapturedArguments, CurriedComponentDefinition, curry, EMPTY_ARGS, UNDEFINED_REFERENCE, - VM, } from '@glimmer/runtime'; -import * as WireFormat from '@glimmer/wire-format'; +import { Tag } from '@glimmer/validator'; import { MODEL_ARG_NAME, MountDefinition } from '../component-managers/mount'; -import Environment from '../environment'; +import { EmberVMEnvironment } from '../environment'; export function mountHelper( - vm: VM, - args: Arguments + args: VMArguments, + vm: VM ): VersionedPathReference { - let env = vm.env as Environment; + let env = vm.env as EmberVMEnvironment; let nameRef = args.positional.at(0); let captured: Option = null; + assert( + 'You can only pass a single positional argument to the {{mount}} helper, e.g. {{mount "chat-engine"}}.', + args.positional.length === 1 + ); + + if (DEBUG && args.named) { + let keys = args.named.names; + let extra = keys.filter(k => k !== 'model'); + + assert( + 'You can only pass a `model` argument to the {{mount}} helper, ' + + 'e.g. {{mount "profile-engine" model=this.profile}}. ' + + `You passed ${extra.join(',')}.`, + extra.length === 0 + ); + } + // TODO: the functionality to create a proper CapturedArgument should be // exported by glimmer, or that it should provide an overload for `curry` // that takes `PreparedArguments` @@ -100,33 +112,6 @@ export function mountHelper( @for Ember.Templates.helpers @public */ -export function mountMacro( - _name: string, - params: Option, - hash: Option, - builder: OpcodeBuilder -) { - assert( - 'You can only pass a single positional argument to the {{mount}} helper, e.g. {{mount "chat-engine"}}.', - params!.length === 1 - ); - - if (DEBUG && hash) { - let keys = hash[0]; - let extra = keys.filter(k => k !== 'model'); - - assert( - 'You can only pass a `model` argument to the {{mount}} helper, ' + - 'e.g. {{mount "profile-engine" model=this.profile}}. ' + - `You passed ${extra.join(',')}.`, - extra.length === 0 - ); - } - - let expr: WireFormat.Expressions.Helper = [WireFormat.Ops.Helper, '-mount', params || [], hash]; - builder.dynamicComponent(expr, null, [], null, false, null, null); - return true; -} class DynamicEngineReference implements VersionedPathReference> { public tag: Tag; @@ -135,7 +120,7 @@ class DynamicEngineReference implements VersionedPathReference, - public env: Environment, + public env: EmberVMEnvironment, public args: Option ) { this.tag = nameRef.tag; @@ -152,10 +137,10 @@ class DynamicEngineReference implements VersionedPathReference; + if (args.positional.length === 0) { nameRef = new ConstReference('main'); } else { nameRef = args.positional.at>(0); } + return new OutletComponentReference( new OutletReference(scope.outletState, nameRef), - vm.env as Environment + vm.env as EmberVMEnvironment ); } -export function outletMacro( - _name: string, - params: Option, - hash: Option, - builder: OpcodeBuilder -) { - let expr: WireFormat.Expressions.Helper = [WireFormat.Ops.Helper, '-outlet', params || [], hash]; - builder.dynamicComponent(expr, null, [], null, false, null, null); - return true; -} - -class OutletModelReference implements VersionedPathReference { +class OutletModelReference extends RootReference { public tag: Tag; - private debugStackLog?: string; constructor( private parent: VersionedPathReference, - private env: Environment + env: EmberVMEnvironment ) { + super(env); this.tag = parent.tag; } - value(): Opaque { + value(): unknown { let state = this.parent.value(); if (state === undefined) { @@ -118,86 +108,26 @@ class OutletModelReference implements VersionedPathReference { return undefined; } - return render.model as Opaque; - } - - get(property: string): VersionedPathReference { - if (DEBUG) { - // We capture the log stack now, as accessing `{{@model}}` directly can't - // cause issues (doesn't autotrack) but accessing subproperties can. We - // don't want to capture the log stack when `value` or `debug` are called, - // because the ref might have been passed downward, so we'd have the - // incorrect context. - // - // TODO: This feels messy, side-effect of the fact that this ref is - // created well before the component itself. - this.debugStackLog = this.env.debugRenderTree.logCurrentRenderStack(); - - // This guarantees that we preserve the `debug()` output below - return new NestedPropertyReference(this, property); - } else { - return PropertyReference.create(this, property); - } + return render.model as unknown; } } if (DEBUG) { - OutletModelReference.prototype['debug'] = function debug(subPath: string): string { - return `${this['debugStackLog']}@model.${subPath}`; - }; + OutletModelReference.prototype['debugLogName'] = '@model'; } class OutletComponentReference implements VersionedPathReference { public tag: Tag; - private args: Option = null; private definition: Option = null; private lastState: Option = null; constructor( private outletRef: VersionedPathReference, - env: Environment + private env: EmberVMEnvironment ) { // The router always dirties the root state. - let tag = (this.tag = outletRef.tag); - - if (EMBER_ROUTING_MODEL_ARG) { - let modelRef = new OutletModelReference(outletRef, env); - let map = dict(); - map.model = modelRef; - - // TODO: the functionality to create a proper CapturedArgument should be - // exported by glimmer, or that it should provide an overload for `curry` - // that takes `PreparedArguments` - this.args = { - tag, - positional: EMPTY_ARGS.positional, - named: { - tag, - map, - names: ['model'], - references: [modelRef], - length: 1, - has(key: string): boolean { - return key === 'model'; - }, - get(key: string): T { - return (key === 'model' ? modelRef : UNDEFINED_REFERENCE) as unsafe; - }, - value(): Dict { - let model = modelRef.value(); - return { model }; - }, - }, - length: 1, - value() { - return { - named: this.named.value(), - positional: this.positional.value(), - }; - }, - }; - } + this.tag = outletRef.tag; } value(): CurriedComponentDefinition | null { @@ -206,10 +136,15 @@ class OutletComponentReference return this.definition; } this.lastState = state; + let definition = null; + if (state !== null) { - definition = curry(new OutletComponentDefinition(state), this.args); + let args = EMBER_ROUTING_MODEL_ARG ? makeArgs(this.outletRef, this.env) : null; + + definition = curry(new OutletComponentDefinition(state), args); } + return (this.definition = definition); } @@ -218,6 +153,48 @@ class OutletComponentReference } } +function makeArgs( + outletRef: VersionedPathReference, + env: EmberVMEnvironment +): CapturedArguments { + let tag = outletRef.tag; + let modelRef = new OutletModelReference(outletRef, env); + let map = dict(); + map.model = modelRef; + + // TODO: the functionailty to create a proper CapturedArgument should be + // exported by glimmer, or that it should provide an overload for `curry` + // that takes `PreparedArguments` + return { + tag, + positional: EMPTY_ARGS.positional, + named: { + tag, + map, + names: ['model'], + references: [modelRef], + length: 1, + has(key: string): boolean { + return key === 'model'; + }, + get(key: string): T { + return (key === 'model' ? modelRef : UNDEFINED_REFERENCE) as unsafe; + }, + value(): Dict { + let model = modelRef.value(); + return { model }; + }, + }, + length: 1, + value() { + return { + named: this.named.value(), + positional: this.positional.value(), + }; + }, + }; +} + function stateFor( ref: VersionedPathReference ): OutletDefinitionState | null { diff --git a/packages/@ember/-internals/glimmer/lib/syntax/utils.ts b/packages/@ember/-internals/glimmer/lib/syntax/utils.ts index 4cdf934920d..8366056b5c6 100644 --- a/packages/@ember/-internals/glimmer/lib/syntax/utils.ts +++ b/packages/@ember/-internals/glimmer/lib/syntax/utils.ts @@ -1,5 +1,4 @@ -import { Option } from '@glimmer/util'; -import { Core } from '@glimmer/wire-format'; +import { Core, Option } from '@glimmer/interfaces'; export function hashToArgs(hash: Option): Option { if (hash === null) return null; diff --git a/packages/@ember/-internals/glimmer/lib/template-compiler.ts b/packages/@ember/-internals/glimmer/lib/template-compiler.ts deleted file mode 100644 index cedd4d93acd..00000000000 --- a/packages/@ember/-internals/glimmer/lib/template-compiler.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Compiler } from '@glimmer/interfaces'; -import RuntimeResolver from './resolver'; - -export interface ICompilerOptions { - environment: { - isInteractive: boolean; - }; -} - -// factory for DI -export default { - create({ environment }: ICompilerOptions): Compiler { - return new RuntimeResolver(environment.isInteractive).compiler; - }, -}; diff --git a/packages/@ember/-internals/glimmer/lib/template.ts b/packages/@ember/-internals/glimmer/lib/template.ts index 301dd6866e0..3a605ca4108 100644 --- a/packages/@ember/-internals/glimmer/lib/template.ts +++ b/packages/@ember/-internals/glimmer/lib/template.ts @@ -1,9 +1,8 @@ -import { privatize as P } from '@ember/-internals/container'; import { Owner } from '@ember/-internals/owner'; +import { guidFor } from '@ember/-internals/utils'; import { OwnedTemplateMeta, StaticTemplateMeta } from '@ember/-internals/views'; -import { Template } from '@glimmer/interfaces'; -import { LazyCompiler, templateFactory } from '@glimmer/opcode-compiler'; -import { SerializedTemplateWithLazyBlock } from '@glimmer/wire-format'; +import { SerializedTemplateWithLazyBlock, Template } from '@glimmer/interfaces'; +import { templateFactory } from '@glimmer/opcode-compiler'; export type StaticTemplate = SerializedTemplateWithLazyBlock; export type OwnedTemplate = Template; @@ -25,19 +24,19 @@ export let counters = { cacheMiss: 0, }; -const TEMPLATE_COMPILER_MAIN = P`template-compiler:main`; - export default function template(json: StaticTemplate): Factory { let glimmerFactory = templateFactory(json); let cache = new WeakMap(); + const meta = glimmerFactory.meta as StaticTemplateMeta; + let factory = ((owner: Owner) => { let result = cache.get(owner); + let ownerId = guidFor(owner); if (result === undefined) { counters.cacheMiss++; - let compiler = owner.lookup>(TEMPLATE_COMPILER_MAIN)!; - result = glimmerFactory.create(compiler, { owner }); + result = glimmerFactory.create(Object.assign({ ownerId }, meta)); cache.set(owner, result); } else { counters.cacheHit++; @@ -47,7 +46,7 @@ export default function template(json: StaticTemplate): Factory { }) as Factory; factory.__id = glimmerFactory.id; - factory.__meta = glimmerFactory.meta; + factory.__meta = meta; return factory; } diff --git a/packages/@ember/-internals/glimmer/lib/templates/outlet.hbs b/packages/@ember/-internals/glimmer/lib/templates/outlet.hbs index e2147cab02d..7a670a61a70 100644 --- a/packages/@ember/-internals/glimmer/lib/templates/outlet.hbs +++ b/packages/@ember/-internals/glimmer/lib/templates/outlet.hbs @@ -1 +1 @@ -{{outlet}} \ No newline at end of file +{{component (-outlet)}} \ No newline at end of file diff --git a/packages/@ember/-internals/glimmer/lib/utils/bindings.ts b/packages/@ember/-internals/glimmer/lib/utils/bindings.ts index 868c1bc7514..caffdbd5407 100644 --- a/packages/@ember/-internals/glimmer/lib/utils/bindings.ts +++ b/packages/@ember/-internals/glimmer/lib/utils/bindings.ts @@ -2,11 +2,20 @@ import { get } from '@ember/-internals/metal'; import { assert, deprecate } from '@ember/debug'; import { EMBER_COMPONENT_IS_VISIBLE } from '@ember/deprecated-features'; import { dasherize } from '@ember/string'; -import { Opaque, Option, Simple } from '@glimmer/interfaces'; -import { CachedReference, combine, map, Reference, Tag } from '@glimmer/reference'; -import { ElementOperations, PrimitiveReference } from '@glimmer/runtime'; +import { DEBUG } from '@glimmer/env'; +import { ElementOperations, Option } from '@glimmer/interfaces'; +import { + Reference, + RootReference, + VersionedPathReference, + VersionedReference, +} from '@glimmer/reference'; +import { PrimitiveReference, UNDEFINED_REFERENCE } from '@glimmer/runtime'; +import { combine, Tag } from '@glimmer/validator'; +import { SimpleElement } from '@simple-dom/interface'; +import { EmberVMEnvironment } from '../environment'; import { Component } from './curly-component-state-bucket'; -import { referenceFromParts, RootReference } from './references'; +import { referenceFromParts } from './references'; import { htmlSafe, isHTMLSafe, SafeString } from './string'; export function referenceForKey(rootRef: RootReference, key: string) { @@ -52,11 +61,11 @@ export const AttributeBinding = { }, install( - _element: Simple.Element, component: Component, rootRef: RootReference, parsed: [string, string, boolean], - operations: ElementOperations + operations: ElementOperations, + env: EmberVMEnvironment ) { let [prop, attribute, isSimple] = parsed; @@ -87,9 +96,10 @@ export const AttributeBinding = { StyleBindingReference !== undefined ) { reference = new StyleBindingReference( + rootRef, reference, referenceForKey(rootRef, 'isVisible'), - component + env ); } @@ -103,28 +113,48 @@ const SAFE_DISPLAY_NONE = htmlSafe(DISPLAY_NONE); let StyleBindingReference: | undefined - | { new (...args: any[]): CachedReference }; + | { + new ( + parent: VersionedPathReference, + inner: Reference, + isVisible: Reference, + env: EmberVMEnvironment + ): VersionedReference; + }; + +export let installIsVisibleBinding: + | undefined + | (( + rootRef: RootReference, + operations: ElementOperations, + environment: EmberVMEnvironment + ) => void); if (EMBER_COMPONENT_IS_VISIBLE) { - StyleBindingReference = class extends CachedReference { + StyleBindingReference = class implements VersionedPathReference { public tag: Tag; constructor( - private inner: Reference, - private isVisible: Reference, - private component: Component + parent: VersionedPathReference, + private inner: Reference, + private isVisible: Reference, + private env: EmberVMEnvironment ) { - super(); - this.tag = combine([inner.tag, isVisible.tag]); + + if (DEBUG) { + env.setTemplatePathDebugContext(this, 'style', parent); + } } - compute(): string | SafeString { + value(): string | SafeString { let value = this.inner.value(); let isVisible = this.isVisible.value(); if (isVisible !== undefined) { deprecate( - `\`isVisible\` is deprecated (from "${this.component._debugContainerKey}")`, + `The \`isVisible\` property on classic component classes is deprecated. Was accessed ${this.env + .getTemplatePathDebugContext(this) + .replace(/^W/, 'w')}`, false, { id: 'ember-component.is-visible', @@ -135,72 +165,42 @@ if (EMBER_COMPONENT_IS_VISIBLE) { } if (isVisible !== false) { - return value; - } - - if (!value) { + return value as string; + } else if (!value) { return SAFE_DISPLAY_NONE; } else { let style = value + ' ' + DISPLAY_NONE; return isHTMLSafe(value) ? htmlSafe(style) : style; } } - }; -} - -export let IsVisibleBinding: - | undefined - | { - install( - element: Simple.Element, - component: Component, - rootRef: RootReference, - operations: ElementOperations - ): void; - mapStyleValue(isVisible: boolean, component: Component): SafeString | null; - }; - -if (EMBER_COMPONENT_IS_VISIBLE) { - IsVisibleBinding = { - install( - _element: Simple.Element, - component: Component, - rootRef: RootReference, - operations: ElementOperations - ) { - let componentMapStyleValue = (isVisible: boolean) => { - return this.mapStyleValue(isVisible, component); - }; - - operations.setAttribute( - 'style', - map(referenceForKey(rootRef, 'isVisible') as any, componentMapStyleValue), - false, - null - ); - // // the upstream type for addDynamicAttribute's `value` argument - // // appears to be incorrect. It is currently a Reference, I - // // think it should be a Reference. - // operations.addDynamicAttribute(element, 'style', ref as any as Reference, false); - }, - mapStyleValue(isVisible: boolean, component: Component) { - if (isVisible !== undefined) { - deprecate(`\`isVisible\` is deprecated (from "${component._debugContainerKey}")`, false, { - id: 'ember-component.is-visible', - until: '4.0.0', - url: 'https://deprecations.emberjs.com/v3.x#toc_ember-component-is-visible', - }); - } + get() { + return UNDEFINED_REFERENCE; + } + }; - return isVisible === false ? SAFE_DISPLAY_NONE : null; - }, + installIsVisibleBinding = ( + rootRef: RootReference, + operations: ElementOperations, + environment: EmberVMEnvironment + ) => { + operations.setAttribute( + 'style', + new StyleBindingReference!( + rootRef, + UNDEFINED_REFERENCE, + rootRef.get('isVisible'), + environment + ), + false, + null + ); }; } export const ClassNameBinding = { install( - _element: Simple.Element, + _element: SimpleElement, rootRef: RootReference, microsyntax: string, operations: ElementOperations @@ -223,28 +223,20 @@ export const ClassNameBinding = { } operations.setAttribute('class', ref, false, null); - // // the upstream type for addDynamicAttribute's `value` argument - // // appears to be incorrect. It is currently a Reference, I - // // think it should be a Reference. - // operations.addDynamicAttribute(element, 'class', ref as any as Reference, false); } }, }; -export class SimpleClassNameBindingReference extends CachedReference> { +export class SimpleClassNameBindingReference implements VersionedReference> { public tag: Tag; private dasherizedPath: Option; - constructor(private inner: Reference, private path: string) { - super(); - + constructor(private inner: Reference, private path: string) { this.tag = inner.tag; - this.inner = inner; - this.path = path; this.dasherizedPath = null; } - compute(): Option { + value(): Option { let value = this.inner.value(); if (value === true) { @@ -258,20 +250,18 @@ export class SimpleClassNameBindingReference extends CachedReference> { +class ColonClassNameBindingReference implements VersionedReference> { public tag: Tag; constructor( - private inner: Reference, + private inner: Reference, private truthy: Option = null, private falsy: Option = null ) { - super(); - this.tag = inner.tag; } - compute(): Option { + value(): Option { let { inner, truthy, falsy } = this; return inner.value() ? truthy : falsy; } diff --git a/packages/@ember/-internals/glimmer/lib/utils/curly-component-state-bucket.ts b/packages/@ember/-internals/glimmer/lib/utils/curly-component-state-bucket.ts index a1ea19e80ea..0197957f24f 100644 --- a/packages/@ember/-internals/glimmer/lib/utils/curly-component-state-bucket.ts +++ b/packages/@ember/-internals/glimmer/lib/utils/curly-component-state-bucket.ts @@ -1,10 +1,10 @@ import { clearElementView, clearViewElement, getViewElement } from '@ember/-internals/views'; -import { Revision, value, VersionedReference } from '@glimmer/reference'; -import { CapturedNamedArguments } from '@glimmer/runtime'; -import { Opaque } from '@glimmer/util'; -import Environment from '../environment'; +import { CapturedNamedArguments } from '@glimmer/interfaces'; +import { ComponentRootReference, VersionedReference } from '@glimmer/reference'; +import { Revision, value } from '@glimmer/validator'; +import { EmberVMEnvironment } from '../environment'; +import { Renderer } from '../renderer'; import { Factory as TemplateFactory, OwnedTemplate } from '../template'; -import { RootReference } from './references'; export interface Component { _debugContainerKey: string; @@ -21,6 +21,7 @@ export interface Component { trigger(event: string): void; destroy(): void; setProperties(props: { [key: string]: any }): void; + renderer: Renderer; } type Finalizer = () => void; @@ -38,12 +39,12 @@ function NOOP() {} @private */ export default class ComponentStateBucket { - public classRef: VersionedReference | null = null; - public rootRef: RootReference; + public classRef: VersionedReference | null = null; + public rootRef: ComponentRootReference; public argsRevision: Revision; constructor( - public environment: Environment, + public environment: EmberVMEnvironment, public component: Component, public args: CapturedNamedArguments | null, public finalizer: Finalizer, @@ -51,10 +52,10 @@ export default class ComponentStateBucket { ) { this.classRef = null; this.argsRevision = args === null ? 0 : value(args.tag); - this.rootRef = new RootReference(component, environment); + this.rootRef = new ComponentRootReference(component, environment); } - destroy() { + willDestroy() { let { component, environment } = this; if (environment.isInteractive) { @@ -69,7 +70,11 @@ export default class ComponentStateBucket { } } - environment.destroyedComponents.push(component); + component.renderer.unregister(component); + } + + destroy() { + this.component.destroy(); } finalize() { diff --git a/packages/@ember/-internals/glimmer/lib/utils/custom-component-manager.ts b/packages/@ember/-internals/glimmer/lib/utils/custom-component-manager.ts index a0900e6bc3f..e4ab147e8cc 100644 --- a/packages/@ember/-internals/glimmer/lib/utils/custom-component-manager.ts +++ b/packages/@ember/-internals/glimmer/lib/utils/custom-component-manager.ts @@ -1,15 +1,14 @@ import { Owner } from '@ember/-internals/owner'; import { deprecate } from '@ember/debug'; import { COMPONENT_MANAGER_STRING_LOOKUP } from '@ember/deprecated-features'; -import { Opaque } from '@glimmer/interfaces'; import { ManagerDelegate } from '../component-managers/custom'; import { getManager, ManagerFactory, setManager } from './managers'; export function setComponentManager( - stringOrFunction: string | ManagerFactory>, + stringOrFunction: string | ManagerFactory>, obj: any ) { - let factory: ManagerFactory; + let factory: ManagerFactory; if (COMPONENT_MANAGER_STRING_LOOKUP && typeof stringOrFunction === 'string') { deprecate( 'Passing the name of the component manager to "setupComponentManager" is deprecated. Please pass a function that produces an instance of the manager.', @@ -24,7 +23,7 @@ export function setComponentManager( return owner.lookup(`component-manager:${stringOrFunction}`); }; } else { - factory = stringOrFunction as ManagerFactory; + factory = stringOrFunction as ManagerFactory; } return setManager({ factory, internal: false, type: 'component' }, obj); diff --git a/packages/@ember/-internals/glimmer/lib/utils/custom-modifier-manager.ts b/packages/@ember/-internals/glimmer/lib/utils/custom-modifier-manager.ts index dc8e734590f..8a31a370720 100644 --- a/packages/@ember/-internals/glimmer/lib/utils/custom-modifier-manager.ts +++ b/packages/@ember/-internals/glimmer/lib/utils/custom-modifier-manager.ts @@ -1,7 +1,6 @@ -import { Opaque } from '@glimmer/util'; import { getManager, ManagerFactory, setManager } from './managers'; -export function setModifierManager(factory: ManagerFactory, obj: any) { +export function setModifierManager(factory: ManagerFactory, obj: any) { return setManager({ factory, internal: false, type: 'modifier' }, obj); } diff --git a/packages/@ember/-internals/glimmer/lib/utils/debug-render-tree.ts b/packages/@ember/-internals/glimmer/lib/utils/debug-render-tree.ts index a69115ebeab..cb877704878 100644 --- a/packages/@ember/-internals/glimmer/lib/utils/debug-render-tree.ts +++ b/packages/@ember/-internals/glimmer/lib/utils/debug-render-tree.ts @@ -1,11 +1,15 @@ import { assert } from '@ember/debug'; -import { Simple } from '@glimmer/interfaces'; -import { Bounds, CapturedArguments } from '@glimmer/runtime'; -import { expect, Option, Stack } from '@glimmer/util'; +import { Bounds, CapturedArguments, Option } from '@glimmer/interfaces'; +import { unwrapTemplate } from '@glimmer/opcode-compiler'; +import { VersionedPathReference } from '@glimmer/reference'; +import { expect, Stack } from '@glimmer/util'; +import { SimpleElement, SimpleNode } from '@simple-dom/interface'; import { OwnedTemplate } from '../template'; export type RenderNodeType = 'outlet' | 'engine' | 'route-template' | 'component'; +export type PathNodeType = 'root' | 'argument' | 'property' | 'iterator'; + export interface RenderNode { type: RenderNodeType; name: string; @@ -17,6 +21,15 @@ export interface RenderNode { interface InternalRenderNode extends RenderNode { bounds: Option; refs: Set>; + paths: Set>; + parent?: InternalRenderNode; +} + +interface InternalPathNode { + name: string; + type: PathNodeType; + parent: InternalPathNode | InternalRenderNode; + paths: Set>; } export interface CapturedRenderNode { @@ -27,15 +40,26 @@ export interface CapturedRenderNode { instance: unknown; template: Option; bounds: Option<{ - parentElement: Simple.Element; - firstNode: Simple.Node; - lastNode: Simple.Node; + parentElement: SimpleElement; + firstNode: SimpleNode; + lastNode: SimpleNode; }>; children: CapturedRenderNode[]; } let GUID = 0; +function isPathNode( + node: RenderNode | InternalPathNode +): node is InternalPathNode { + return ( + node.type === 'root' || + node.type === 'argument' || + node.type === 'property' || + node.type === 'iterator' + ); +} + export class Ref { readonly id: number = GUID++; private value: Option; @@ -78,31 +102,27 @@ function repeatString(str: string, count: number) { return _repeat.call(str, count); } -class StackWithToArray extends Stack { - toArray(): T[] { - // polyfilling feature of modern Glimmer VM - return this['stack']; - } -} - export default class DebugRenderTree { - private stack = new StackWithToArray(); + private stack = new Stack(); private refs = new WeakMap>(); private roots = new Set>(); private nodes = new WeakMap>(); + private pathNodes = new WeakMap>(); begin(): void { this.reset(); } create(state: Bucket, node: RenderNode): void { - this.nodes.set(state, { + let internalNode: InternalRenderNode = { ...node, bounds: null, refs: new Set(), - }); - this.appendChild(state); + paths: new Set(), + }; + this.nodes.set(state, internalNode); + this.appendChild(internalNode, state); this.enter(state); } @@ -133,15 +153,107 @@ export default class DebugRenderTree { return this.captureRefs(this.roots); } - logCurrentRenderStack(): string { - let nodes = this.stack.toArray().map(bucket => this.nodeFor(bucket)); - let message = nodes - .filter(node => node.type !== 'outlet' && node.name !== '-top-level') - .map((node, index) => `${repeatString(' ', index * 2)}${node.name}`); + createPath( + pathRef: VersionedPathReference, + name: string, + type: PathNodeType, + parentRef: Option + ) { + assert( + 'BUG: Attempted to register a path that had already been registered', + !this.pathNodes.has(pathRef) + ); + + let { current } = this.stack; + + if (current === null) { + // Not currently in a rendering context, don't register the node + return; + } + + let currentNode = expect( + this.nodes.get(current), + 'BUG: Attempted to create a path, but there is no current render node' + ); + + let parent: InternalPathNode | InternalRenderNode; + + if (parentRef === null) { + parent = currentNode; + } else { + let { named } = currentNode.args; + let refIndex = named.references.indexOf(parentRef); + + if (refIndex !== -1) { + parent = { + parent: currentNode, + type: 'argument', + name: `@${named.names[refIndex]}`, + paths: new Set(), + }; + } else if (this.pathNodes.has(parentRef)) { + parent = this.pathNodes.get(parentRef)!; + } else { + // Some RootReferences get created before a component context has been + // setup (root, curly). This is mainly because the debugRenderTree is + // tied to the manager hooks, and not built into the VM directly. In + // these cases, we setup the path lazily when the first property is + // accessed. + + this.createPath(parentRef, 'this', 'root', null); + + parent = this.pathNodes.get(parentRef)!; + } + } + + let pathNode: InternalPathNode = { + name, + type, + parent, + paths: new Set(), + }; + + parent.paths.add(pathNode); + + this.pathNodes.set(pathRef, pathNode); + } + + logRenderStackForPath(pathRef: VersionedPathReference): string { + let node: InternalRenderNode | InternalPathNode | undefined = expect( + this.pathNodes.get(pathRef), + 'BUG: Attempted to create a log for a path reference, but no node exist for that reference' + ); + + let pathParts = []; + + while (node !== undefined && isPathNode(node)) { + if (node.type === 'iterator') { + // Iterator items are a combination of their own name (the key of the item) and + // their parent, the iterable itself. + let part = `${node.parent.name}[${node.name}]`; + pathParts.push(part); - message.push(`${repeatString(' ', message.length * 2)}`); + node = node.parent; + } else { + pathParts.unshift(node.name); + } + + node = node.parent; + } + + let messageParts = [pathParts.join('.')]; + + while (node !== undefined) { + if (node.type === 'outlet' || node.name === '-top-level') { + node = node.parent; + continue; + } + + messageParts.unshift(node.name); + node = node.parent; + } - return message.join('\n'); + return messageParts.map((part, index) => `${repeatString(' ', index * 2)}${part}`).join('\n'); } private reset(): void { @@ -172,7 +284,7 @@ export default class DebugRenderTree { return expect(this.nodes.get(state), 'BUG: missing node'); } - private appendChild(state: Bucket): void { + private appendChild(node: InternalRenderNode, state: Bucket): void { assert('BUG: child already appended', !this.refs.has(state)); let parent = this.stack.current; @@ -181,7 +293,9 @@ export default class DebugRenderTree { this.refs.set(state, ref); if (parent) { - this.nodeFor(parent).refs.add(ref); + let parentNode = this.nodeFor(parent); + parentNode.refs.add(ref); + node.parent = parentNode; } else { this.roots.add(ref); } @@ -213,7 +327,7 @@ export default class DebugRenderTree { } private captureTemplate({ template }: InternalRenderNode): Option { - return (template && template.referrer.moduleName) || null; + return (template && unwrapTemplate(template).referrer.moduleName) || null; } private captureBounds(node: InternalRenderNode): CapturedRenderNode['bounds'] { diff --git a/packages/@ember/-internals/glimmer/lib/utils/iterable.ts b/packages/@ember/-internals/glimmer/lib/utils/iterable.ts deleted file mode 100644 index 2110e40313a..00000000000 --- a/packages/@ember/-internals/glimmer/lib/utils/iterable.ts +++ /dev/null @@ -1,486 +0,0 @@ -import { - consume, - get, - isTracking, - objectAt, - tagFor, - tagForProperty, -} from '@ember/-internals/metal'; -import { _contentFor } from '@ember/-internals/runtime'; -import { guidFor, HAS_NATIVE_SYMBOL, isEmberArray, isProxy } from '@ember/-internals/utils'; -import { assert } from '@ember/debug'; -import { - AbstractIterable, - combine, - createUpdatableTag, - IterationItem, - OpaqueIterator, - Tag, - update, - VersionedReference, -} from '@glimmer/reference'; -import { Opaque, Option } from '@glimmer/util'; -import { isEachIn } from '../helpers/each-in'; -import { UpdatableReference } from './references'; - -const ITERATOR_KEY_GUID = 'be277757-bbbe-4620-9fcb-213ef433cca2'; - -// FIXME: export this from Glimmer -type OpaqueIterationItem = IterationItem; -type EmberIterable = AbstractIterable< - Opaque, - Opaque, - OpaqueIterationItem, - UpdatableReference, - UpdatableReference ->; - -export default function iterableFor( - ref: VersionedReference, - keyPath: string | null | undefined -): EmberIterable { - if (isEachIn(ref)) { - return new EachInIterable(ref, keyPath || '@key'); - } else { - return new EachIterable(ref, keyPath || '@identity'); - } -} - -abstract class BoundedIterator implements OpaqueIterator { - private position = 0; - - constructor(private length: number, private keyFor: KeyFor) {} - - isEmpty(): false { - return false; - } - - abstract valueFor(position: number): Opaque; - - memoFor(position: number): Opaque { - return position; - } - - next(): Option { - let { length, keyFor, position } = this; - - if (position >= length) { - return null; - } - - let value = this.valueFor(position); - let memo = this.memoFor(position); - let key = keyFor(value, memo, position); - - this.position++; - - return { key, value, memo }; - } -} - -class ArrayIterator extends BoundedIterator { - static from(array: Opaque[], keyFor: KeyFor): OpaqueIterator { - let { length } = array; - - if (length === 0) { - return EMPTY_ITERATOR; - } else { - return new this(array, length, keyFor); - } - } - - static fromForEachable(object: ForEachable, keyFor: KeyFor): OpaqueIterator { - let array: Opaque[] = []; - object.forEach(item => array.push(item)); - return this.from(array, keyFor); - } - - constructor(private array: Opaque[], length: number, keyFor: KeyFor) { - super(length, keyFor); - } - - valueFor(position: number): Opaque { - return this.array[position]; - } -} - -class EmberArrayIterator extends BoundedIterator { - static from(array: Opaque[], keyFor: KeyFor): OpaqueIterator { - let { length } = array; - - if (length === 0) { - return EMPTY_ITERATOR; - } else { - return new this(array, length, keyFor); - } - } - - constructor(private array: Opaque, length: number, keyFor: KeyFor) { - super(length, keyFor); - } - - valueFor(position: number): Opaque { - return objectAt(this.array as any, position); - } -} - -class ObjectIterator extends BoundedIterator { - static fromIndexable(obj: Indexable, keyFor: KeyFor): OpaqueIterator { - let keys = Object.keys(obj); - let { length } = keys; - - if (length === 0) { - return EMPTY_ITERATOR; - } else { - let values: Opaque[] = []; - for (let i = 0; i < length; i++) { - let value: any; - let key = keys[i]; - - value = obj[key]; - - // Add the tag of the returned value if it is an array, since arrays - // should always cause updates if they are consumed and then changed - if (isTracking()) { - consume(tagForProperty(obj, key)); - - if (Array.isArray(value) || isEmberArray(value)) { - consume(tagForProperty(value, '[]')); - } - } - - values.push(value); - } - return new this(keys, values, length, keyFor); - } - } - - static fromForEachable(obj: ForEachable, keyFor: KeyFor): OpaqueIterator { - let keys: Opaque[] = []; - let values: Opaque[] = []; - let length = 0; - let isMapLike = false; - - obj.forEach((value: Opaque, key: Opaque) => { - isMapLike = isMapLike || arguments.length >= 2; - - if (isMapLike) { - keys.push(key); - } - values.push(value); - - length++; - }); - - if (length === 0) { - return EMPTY_ITERATOR; - } else if (isMapLike) { - return new this(keys, values, length, keyFor); - } else { - return new ArrayIterator(values, length, keyFor); - } - } - - constructor(private keys: Opaque[], private values: Opaque[], length: number, keyFor: KeyFor) { - super(length, keyFor); - } - - valueFor(position: number): Opaque { - return this.values[position]; - } - - memoFor(position: number): Opaque { - return this.keys[position]; - } -} - -interface NativeIteratorConstructor { - new (iterable: Iterator, result: IteratorResult, keyFor: KeyFor): NativeIterator; -} - -abstract class NativeIterator implements OpaqueIterator { - static from( - this: NativeIteratorConstructor, - iterable: Iterable, - keyFor: KeyFor - ): OpaqueIterator { - let iterator = iterable[Symbol.iterator](); - let result = iterator.next(); - let { value, done } = result; - - if (done) { - return EMPTY_ITERATOR; - } else if (Array.isArray(value) && value.length === 2) { - return new this(iterator, result, keyFor); - } else { - return new ArrayLikeNativeIterator(iterator, result, keyFor); - } - } - - private position = 0; - - constructor( - private iterable: Iterator, - private result: IteratorResult, - private keyFor: KeyFor - ) {} - - isEmpty(): false { - return false; - } - - abstract valueFor(result: IteratorResult, position: number): Opaque; - abstract memoFor(result: IteratorResult, position: number): Opaque; - - next(): Option { - let { iterable, result, position, keyFor } = this; - - if (result.done) { - return null; - } - - let value = this.valueFor(result, position); - let memo = this.memoFor(result, position); - let key = keyFor(value, memo, position); - - this.position++; - this.result = iterable.next(); - - return { key, value, memo }; - } -} - -class ArrayLikeNativeIterator extends NativeIterator { - valueFor(result: IteratorResult): Opaque { - return result.value; - } - - memoFor(_result: IteratorResult, position: number): Opaque { - return position; - } -} - -class MapLikeNativeIterator extends NativeIterator<[Opaque, Opaque]> { - valueFor(result: IteratorResult<[Opaque, Opaque]>): Opaque { - return result.value[1]; - } - - memoFor(result: IteratorResult<[Opaque, Opaque]>): Opaque { - return result.value[0]; - } -} - -const EMPTY_ITERATOR: OpaqueIterator = { - isEmpty(): true { - return true; - }, - - next(): null { - assert('Cannot call next() on an empty iterator'); - return null; - }, -}; - -class EachInIterable implements EmberIterable { - public tag: Tag; - private valueTag = createUpdatableTag(); - - constructor(private ref: VersionedReference, private keyPath: string) { - this.tag = combine([ref.tag, this.valueTag]); - } - - iterate(): OpaqueIterator { - let { ref, valueTag } = this; - - let iterable = ref.value(); - let tag = tagFor(iterable); - - if (isProxy(iterable)) { - // this is because the each-in doesn't actually get(proxy, 'key') but bypasses it - // and the proxy's tag is lazy updated on access - iterable = _contentFor(iterable); - } - - update(valueTag, tag); - - if (!isIndexable(iterable)) { - return EMPTY_ITERATOR; - } - - if (Array.isArray(iterable) || isEmberArray(iterable)) { - return ObjectIterator.fromIndexable(iterable, this.keyFor(true)); - } else if (HAS_NATIVE_SYMBOL && isNativeIterable<[Opaque, Opaque]>(iterable)) { - return MapLikeNativeIterator.from(iterable, this.keyFor()); - } else if (hasForEach(iterable)) { - return ObjectIterator.fromForEachable(iterable, this.keyFor()); - } else { - return ObjectIterator.fromIndexable(iterable, this.keyFor(true)); - } - } - - valueReferenceFor(item: OpaqueIterationItem): UpdatableReference { - return new UpdatableReference(item.value); - } - - updateValueReference(ref: UpdatableReference, item: OpaqueIterationItem): void { - ref.update(item.value); - } - - memoReferenceFor(item: OpaqueIterationItem): UpdatableReference { - return new UpdatableReference(item.memo); - } - - updateMemoReference(ref: UpdatableReference, item: OpaqueIterationItem): void { - ref.update(item.memo); - } - - private keyFor(hasUniqueKeys = false): KeyFor { - let { keyPath } = this; - - switch (keyPath) { - case '@key': - return hasUniqueKeys ? ObjectKey : Unique(MapKey); - case '@index': - return Index; - case '@identity': - return Unique(Identity); - default: - assert(`Invalid key: ${keyPath}`, keyPath[0] !== '@'); - return Unique(KeyPath(keyPath)); - } - } -} - -class EachIterable implements EmberIterable { - public tag: Tag; - private valueTag = createUpdatableTag(); - - constructor(private ref: VersionedReference, private keyPath: string) { - this.tag = combine([ref.tag, this.valueTag]); - } - - iterate(): OpaqueIterator { - let { ref, valueTag } = this; - - let iterable = ref.value(); - - update(valueTag, tagForProperty(iterable, '[]')); - - if (iterable === null || typeof iterable !== 'object') { - return EMPTY_ITERATOR; - } - - let keyFor = this.keyFor(); - - if (Array.isArray(iterable)) { - return ArrayIterator.from(iterable, keyFor); - } else if (isEmberArray(iterable)) { - return EmberArrayIterator.from(iterable as Opaque[], keyFor); - } else if (HAS_NATIVE_SYMBOL && isNativeIterable(iterable)) { - return ArrayLikeNativeIterator.from(iterable, keyFor); - } else if (hasForEach(iterable)) { - return ArrayIterator.fromForEachable(iterable, keyFor); - } else { - return EMPTY_ITERATOR; - } - } - - valueReferenceFor(item: OpaqueIterationItem): UpdatableReference { - return new UpdatableReference(item.value); - } - - updateValueReference(ref: UpdatableReference, item: OpaqueIterationItem): void { - ref.update(item.value); - } - - memoReferenceFor(item: OpaqueIterationItem): UpdatableReference { - return new UpdatableReference(item.memo as number); - } - - updateMemoReference(ref: UpdatableReference, item: OpaqueIterationItem): void { - ref.update(item.memo); - } - - private keyFor(): KeyFor { - let { keyPath } = this; - - switch (keyPath) { - case '@index': - return Index; - case '@identity': - return Unique(Identity); - default: - assert(`Invalid key: ${keyPath}`, keyPath[0] !== '@'); - return Unique(KeyPath(keyPath)); - } - } -} - -interface ForEachable { - forEach(callback: (item: Opaque, key: Opaque) => void): void; -} - -function hasForEach(value: object): value is ForEachable { - return typeof value['forEach'] === 'function'; -} - -function isNativeIterable(value: object): value is Iterable { - return typeof value[Symbol.iterator] === 'function'; -} - -interface Indexable { - readonly [key: string]: Opaque; -} - -function isIndexable(value: Opaque): value is Indexable { - return value !== null && (typeof value === 'object' || typeof value === 'function'); -} - -type KeyFor = (value: Opaque, memo: Opaque, position: number) => string; - -// Position in an array is guaranteed to be unique -function Index(_value: Opaque, _memo: Opaque, position: number): string { - return String(position); -} - -// Object.keys(...) is guaranteed to be strings and unique -function ObjectKey(_value: Opaque, memo: Opaque): string { - return memo as string; -} - -// Map keys can be any objects -function MapKey(_value: Opaque, memo: Opaque): string { - return Identity(memo); -} - -function Identity(value: Opaque): string { - switch (typeof value) { - case 'string': - return value as string; - case 'number': - return String(value); - default: - return guidFor(value); - } -} - -function KeyPath(keyPath: string): KeyFor { - return (value: Opaque) => String(get(value as any, keyPath)); -} - -function Unique(func: KeyFor): KeyFor { - let seen = {}; - - return (value: Opaque, memo: Opaque, position: number) => { - let key = func(value, memo, position); - let count = seen[key]; - - if (count === undefined) { - seen[key] = 0; - return key; - } else { - seen[key] = ++count; - return `${key}${ITERATOR_KEY_GUID}${count}`; - } - }; -} diff --git a/packages/@ember/-internals/glimmer/lib/utils/iterator.ts b/packages/@ember/-internals/glimmer/lib/utils/iterator.ts new file mode 100644 index 00000000000..3c47c999284 --- /dev/null +++ b/packages/@ember/-internals/glimmer/lib/utils/iterator.ts @@ -0,0 +1,270 @@ +import { objectAt, tagForProperty } from '@ember/-internals/metal'; +import { _contentFor } from '@ember/-internals/runtime'; +import { EmberArray, HAS_NATIVE_SYMBOL, isEmberArray, isObject } from '@ember/-internals/utils'; +import { Option } from '@glimmer/interfaces'; +import { IteratorDelegate } from '@glimmer/reference'; +import { consume, isTracking } from '@glimmer/validator'; +import { EachInWrapper } from '../helpers/each-in'; + +export default function toIterator(iterable: unknown): Option { + if (iterable instanceof EachInWrapper) { + return toEachInIterator(iterable.inner); + } else { + return toEachIterator(iterable); + } +} + +function toEachInIterator(iterable: unknown) { + if (!isIndexable(iterable)) { + return null; + } + + if (Array.isArray(iterable) || isEmberArray(iterable)) { + return ObjectIterator.fromIndexable(iterable); + } else if (HAS_NATIVE_SYMBOL && isNativeIterable<[unknown, unknown]>(iterable)) { + return MapLikeNativeIterator.from(iterable); + } else if (hasForEach(iterable)) { + return ObjectIterator.fromForEachable(iterable); + } else { + return ObjectIterator.fromIndexable(iterable); + } +} + +function toEachIterator(iterable: unknown) { + if (!isObject(iterable)) { + return null; + } + + if (Array.isArray(iterable)) { + return ArrayIterator.from(iterable); + } else if (isEmberArray(iterable)) { + return EmberArrayIterator.from(iterable); + } else if (HAS_NATIVE_SYMBOL && isNativeIterable(iterable)) { + return ArrayLikeNativeIterator.from(iterable); + } else if (hasForEach(iterable)) { + return ArrayIterator.fromForEachable(iterable); + } else { + return null; + } +} + +abstract class BoundedIterator implements IteratorDelegate { + private position = 0; + + constructor(private length: number) {} + + isEmpty(): false { + return false; + } + + abstract valueFor(position: number): unknown; + + memoFor(position: number): unknown { + return position; + } + + next() { + let { length, position } = this; + + if (position >= length) { + return null; + } + + let value = this.valueFor(position); + let memo = this.memoFor(position); + + this.position++; + + return { value, memo }; + } +} + +class ArrayIterator extends BoundedIterator { + static from(iterable: unknown[]) { + return iterable.length > 0 ? new this(iterable) : null; + } + + static fromForEachable(object: ForEachable) { + let array: unknown[] = []; + object.forEach(item => array.push(item)); + return this.from(array); + } + + constructor(private array: unknown[]) { + super(array.length); + } + + valueFor(position: number): unknown { + return this.array[position]; + } +} + +class EmberArrayIterator extends BoundedIterator { + static from(iterable: EmberArray) { + return iterable.length > 0 ? new this(iterable) : null; + } + + constructor(private array: EmberArray) { + super(array.length); + } + + valueFor(position: number): unknown { + return objectAt(this.array as any, position); + } +} + +class ObjectIterator extends BoundedIterator { + static fromIndexable(obj: Indexable) { + let keys = Object.keys(obj); + let { length } = keys; + + if (length === 0) { + return null; + } else { + let values: unknown[] = []; + for (let i = 0; i < length; i++) { + let value: any; + let key = keys[i]; + + value = obj[key]; + + // Add the tag of the returned value if it is an array, since arrays + // should always cause updates if they are consumed and then changed + if (isTracking()) { + consume(tagForProperty(obj, key)); + + if (Array.isArray(value) || isEmberArray(value)) { + consume(tagForProperty(value, '[]')); + } + } + + values.push(value); + } + return new this(keys, values); + } + } + + static fromForEachable(obj: ForEachable) { + let keys: unknown[] = []; + let values: unknown[] = []; + let length = 0; + let isMapLike = false; + + // Not using an arrow function here so we can get an accurate `arguments` + obj.forEach(function(value: unknown, key: unknown) { + isMapLike = isMapLike || arguments.length >= 2; + + if (isMapLike) { + keys.push(key); + } + values.push(value); + + length++; + }); + + if (length === 0) { + return null; + } else if (isMapLike) { + return new this(keys, values); + } else { + return new ArrayIterator(values); + } + } + + constructor(private keys: unknown[], private values: unknown[]) { + super(values.length); + } + + valueFor(position: number): unknown { + return this.values[position]; + } + + memoFor(position: number): unknown { + return this.keys[position]; + } +} + +interface NativeIteratorConstructor { + new (iterable: Iterator, result: IteratorResult): NativeIterator; +} + +abstract class NativeIterator implements IteratorDelegate { + static from(this: NativeIteratorConstructor, iterable: Iterable) { + let iterator = iterable[Symbol.iterator](); + let result = iterator.next(); + let { done } = result; + + if (done) { + return null; + } else { + return new this(iterator, result); + } + } + + private position = 0; + + constructor(private iterable: Iterator, private result: IteratorResult) {} + + isEmpty(): false { + return false; + } + + abstract valueFor(result: IteratorResult, position: number): unknown; + abstract memoFor(result: IteratorResult, position: number): unknown; + + next() { + let { iterable, result, position } = this; + + if (result.done) { + return null; + } + + let value = this.valueFor(result, position); + let memo = this.memoFor(result, position); + + this.position++; + this.result = iterable.next(); + + return { value, memo }; + } +} + +class ArrayLikeNativeIterator extends NativeIterator { + valueFor(result: IteratorResult): unknown { + return result.value; + } + + memoFor(_result: IteratorResult, position: number): unknown { + return position; + } +} + +class MapLikeNativeIterator extends NativeIterator<[unknown, unknown]> { + valueFor(result: IteratorResult<[unknown, unknown]>): unknown { + return result.value[1]; + } + + memoFor(result: IteratorResult<[unknown, unknown]>): unknown { + return result.value[0]; + } +} + +interface ForEachable { + forEach(callback: (item: unknown, key: unknown) => void): void; +} + +function hasForEach(value: object): value is ForEachable { + return typeof value['forEach'] === 'function'; +} + +function isNativeIterable(value: object): value is Iterable { + return typeof value[Symbol.iterator] === 'function'; +} + +interface Indexable { + readonly [key: string]: unknown; +} + +function isIndexable(value: unknown): value is Indexable { + return value !== null && (typeof value === 'object' || typeof value === 'function'); +} diff --git a/packages/@ember/-internals/glimmer/lib/utils/outlet.ts b/packages/@ember/-internals/glimmer/lib/utils/outlet.ts index a415c5cf1cb..079156bb984 100644 --- a/packages/@ember/-internals/glimmer/lib/utils/outlet.ts +++ b/packages/@ember/-internals/glimmer/lib/utils/outlet.ts @@ -1,13 +1,6 @@ import { Owner } from '@ember/-internals/owner'; -import { Opaque } from '@glimmer/interfaces'; -import { - combine, - createTag, - dirty, - Reference, - Tag, - VersionedPathReference, -} from '@glimmer/reference'; +import { Reference, VersionedPathReference } from '@glimmer/reference'; +import { combine, createTag, dirty, Tag } from '@glimmer/validator'; import { Factory as TemplateFactory, OwnedTemplate } from '../template'; export interface RenderState { @@ -79,7 +72,7 @@ export class RootOutletReference implements VersionedPathReference constructor(public outletState: OutletState) {} - get(key: string): VersionedPathReference { + get(key: string): VersionedPathReference { return new PathReference(this, key); } @@ -112,7 +105,7 @@ export class OutletReference implements VersionedPathReference { + get(key: string): VersionedPathReference { return new PathReference(this, key); } } @@ -121,23 +114,23 @@ export class OutletReference implements VersionedPathReference { - public parent: VersionedPathReference; +class PathReference implements VersionedPathReference { + public parent: VersionedPathReference; public key: string; public tag: Tag; - constructor(parent: VersionedPathReference, key: string) { + constructor(parent: VersionedPathReference, key: string) { this.parent = parent; this.key = key; this.tag = parent.tag; } - get(key: string): VersionedPathReference { + get(key: string): VersionedPathReference { return new PathReference(this, key); } - value(): Opaque { + value(): unknown { let parent = this.parent.value(); - return parent && parent[this.key]; + return parent && (parent as object)[this.key]; } } diff --git a/packages/@ember/-internals/glimmer/lib/utils/process-args.ts b/packages/@ember/-internals/glimmer/lib/utils/process-args.ts index 2f10f68d87d..972f4d2d7ad 100644 --- a/packages/@ember/-internals/glimmer/lib/utils/process-args.ts +++ b/packages/@ember/-internals/glimmer/lib/utils/process-args.ts @@ -1,8 +1,9 @@ import { symbol } from '@ember/-internals/utils'; import { MUTABLE_CELL } from '@ember/-internals/views'; -import { CapturedNamedArguments } from '@glimmer/runtime'; +import { CapturedNamedArguments } from '@glimmer/interfaces'; +import { UPDATE_REFERENCED_VALUE } from '@glimmer/reference'; import { ARGS } from '../component'; -import { ACTION, UPDATE } from './references'; +import { ACTION } from '../helpers/action'; // ComponentArgs takes EvaluatedNamedArgs and converts them into the // inputs needed by CurlyComponents (attrs and props, with mutable @@ -22,7 +23,7 @@ export function processComponentArgs(namedArgs: CapturedNamedArguments) { if (typeof value === 'function' && value[ACTION]) { attrs[name] = value; - } else if (ref[UPDATE]) { + } else if (ref[UPDATE_REFERENCED_VALUE]) { attrs[name] = new MutableCell(ref, value); } @@ -46,6 +47,6 @@ class MutableCell { } update(val: any) { - this[REF][UPDATE](val); + this[REF][UPDATE_REFERENCED_VALUE](val); } } diff --git a/packages/@ember/-internals/glimmer/lib/utils/references.ts b/packages/@ember/-internals/glimmer/lib/utils/references.ts index 2f29abc3fd8..0dd43c129a2 100644 --- a/packages/@ember/-internals/glimmer/lib/utils/references.ts +++ b/packages/@ember/-internals/glimmer/lib/utils/references.ts @@ -1,468 +1,92 @@ -import { - consume, - deprecateMutationsInAutotrackingTransaction, - get, - set, - tagFor, - tagForProperty, - track, -} from '@ember/-internals/metal'; -import { getDebugName, isProxy, symbol } from '@ember/-internals/utils'; -import { assert, debugFreeze } from '@ember/debug'; +import { getDebugName, isObject } from '@ember/-internals/utils'; +import { debugFreeze } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; -import { Dict, Opaque } from '@glimmer/interfaces'; -import { - combine, - ConstReference, - createTag, - createUpdatableTag, - dirty, - DirtyableTag, - isConst, - Revision, - Tag, - UpdatableTag, - update, - validate, - value, - VersionedPathReference, - VersionedReference, -} from '@glimmer/reference'; -import { - CapturedArguments, - ConditionalReference as GlimmerConditionalReference, - PrimitiveReference, - UNDEFINED_REFERENCE, -} from '@glimmer/runtime'; -import { Option } from '@glimmer/util'; -import Environment from '../environment'; -import { HelperFunction, HelperInstance, RECOMPUTE_TAG } from '../helper'; -import debugRenderMessage from './debug-render-message'; -import emberToBool from './to-bool'; - -export const UPDATE = symbol('UPDATE'); -export const INVOKE = symbol('INVOKE'); -export const ACTION = symbol('ACTION'); - -abstract class EmberPathReference implements VersionedPathReference { - abstract tag: Tag; - - get(key: string): VersionedPathReference { - return PropertyReference.create(this, key); - } - - abstract value(): Opaque; -} - -export abstract class CachedReference extends EmberPathReference { - abstract tag: Tag; - private lastRevision: Option; - private lastValue: Opaque; - - constructor() { - super(); - this.lastRevision = null; - this.lastValue = null; - } - - abstract compute(): Opaque; - - value(): Opaque { - let { tag, lastRevision, lastValue } = this; - - if (lastRevision === null || !validate(tag, lastRevision)) { - lastValue = this.lastValue = this.compute(); - this.lastRevision = value(tag); - } - - return lastValue; - } -} - -export class RootReference extends ConstReference - implements VersionedPathReference { - static create(value: T, env?: Environment): VersionedPathReference { - return valueToRef(value, true, env); - } - - private children: Dict>; - - constructor(value: T, private env?: Environment) { - super(value); - this.children = Object.create(null); - } - - get(propertyKey: string): VersionedPathReference { - let ref = this.children[propertyKey]; - - if (ref === undefined) { - ref = this.children[propertyKey] = new RootPropertyReference( - this.inner, - propertyKey, - this.env - ); - } - - return ref; - } -} - -export abstract class PropertyReference extends CachedReference { - abstract tag: Tag; - - static create(parentReference: VersionedPathReference, propertyKey: string) { - if (isConst(parentReference)) { - return valueKeyToRef(parentReference.value(), propertyKey); - } else { - return new NestedPropertyReference(parentReference, propertyKey); - } - } - - get(key: string): VersionedPathReference { - return new NestedPropertyReference(this, key); - } -} - -export class RootPropertyReference extends PropertyReference - implements VersionedPathReference { - public tag: Tag; - private propertyTag: UpdatableTag; - private debugStackLog?: string; - - constructor(private parentValue: object, private propertyKey: string, env?: Environment) { - super(); - - if (DEBUG) { - // Capture the stack when this reference is created, as that is the - // component/context that the component was created _in_. Later, it could - // be accessed from any number of components. - this.debugStackLog = env ? env.debugRenderTree.logCurrentRenderStack() : ''; - } - - this.propertyTag = createUpdatableTag(); - - this.tag = this.propertyTag; - } - - compute(): Opaque { - let { parentValue, propertyKey } = this; - - let ret; - - let tag = track( - () => (ret = get(parentValue, propertyKey)), - DEBUG && debugRenderMessage!(this['debug']()) - ); - - consume(tag); - update(this.propertyTag, tag); - - return ret; - } - - [UPDATE](value: Opaque): void { - set(this.parentValue, this.propertyKey, value); - } -} - -if (DEBUG) { - RootPropertyReference.prototype['debug'] = function debug(subPath?: string): string { - let path = `this.${this['propertyKey']}`; - - if (subPath) { - path += `.${subPath}`; - } - - return `${this['debugStackLog']}${path}`; - }; -} - -export class NestedPropertyReference extends PropertyReference { - public tag: Tag; - private propertyTag: UpdatableTag; +import { CapturedArguments, Environment } from '@glimmer/interfaces'; +import { HelperRootReference, RootReference, VersionedPathReference } from '@glimmer/reference'; +import { PrimitiveReference } from '@glimmer/runtime'; +import { consume, deprecateMutationsInAutotrackingTransaction } from '@glimmer/validator'; +import { HelperInstance, isSimpleHelper, RECOMPUTE_TAG, SimpleHelper } from '../helper'; +export class EmberHelperRootReference extends HelperRootReference { constructor( - private parentReference: VersionedPathReference, - private propertyKey: string + helper: SimpleHelper | HelperInstance, + args: CapturedArguments, + env: Environment ) { - super(); - - let parentReferenceTag = parentReference.tag; - let propertyTag = (this.propertyTag = createUpdatableTag()); - - this.tag = combine([parentReferenceTag, propertyTag]); - } - - compute(): Opaque { - let { parentReference, propertyTag, propertyKey } = this; - - let _parentValue = parentReference.value(); - let parentValueType = typeof _parentValue; - - if (parentValueType === 'string' && propertyKey === 'length') { - return (_parentValue as string).length; - } - - if ((parentValueType === 'object' && _parentValue !== null) || parentValueType === 'function') { - let parentValue = _parentValue as object; - - let ret; - - let tag = track( - () => (ret = get(parentValue, propertyKey)), - DEBUG && debugRenderMessage!(this['debug']()) - ); - - consume(tag); - - update(propertyTag, tag); - - return ret; - } else { - return undefined; - } - } - - [UPDATE](value: Opaque): void { - set( - this.parentReference.value() as object /* let the other side handle the error */, - this.propertyKey, - value - ); - } -} - -if (DEBUG) { - NestedPropertyReference.prototype['debug'] = function debug(subPath?: string): string { - let parent = this['parentReference']; - let path = subPath ? `${this['propertyKey']}.${subPath}` : this['propertyKey']; - - if (typeof parent['debug'] === 'function') { - return parent['debug'](path); - } else { - return `unknownObject.${path}`; - } - }; -} - -export class UpdatableReference extends EmberPathReference { - public tag: DirtyableTag; - private _value: Opaque; - - constructor(value: Opaque) { - super(); - - this.tag = createTag(); - this._value = value; - } - - value(): Opaque { - return this._value; - } - - update(value: Opaque): void { - let { _value } = this; - - if (value !== _value) { - dirty(this.tag); - this._value = value; - } - } -} - -export class ConditionalReference extends GlimmerConditionalReference - implements VersionedReference { - public objectTag: UpdatableTag; - static create(reference: VersionedReference): VersionedReference { - if (isConst(reference)) { - let value = reference.value(); - - if (!isProxy(value)) { - return PrimitiveReference.create(emberToBool(value)); - } - } - - return new ConditionalReference(reference); - } - - constructor(reference: VersionedReference) { - super(reference); - this.objectTag = createUpdatableTag(); - this.tag = combine([reference.tag, this.objectTag]); - } - - toBool(predicate: Opaque): boolean { - if (isProxy(predicate)) { - update(this.objectTag, tagForProperty(predicate, 'isTruthy')); - return Boolean(get(predicate, 'isTruthy')); - } else { - update(this.objectTag, tagFor(predicate)); - return emberToBool(predicate); - } - } -} - -export class SimpleHelperReference extends CachedReference { - static create(helper: HelperFunction, args: CapturedArguments) { - if (isConst(args)) { + let fnWrapper = (args: CapturedArguments) => { let { positional, named } = args; let positionalValue = positional.value(); let namedValue = named.value(); + let ret: T; + if (DEBUG) { debugFreeze(positionalValue); debugFreeze(namedValue); - } - - let result = helper(positionalValue, namedValue); - return valueToRef(result); - } else { - return new SimpleHelperReference(helper, args); - } - } - - private computeTag: UpdatableTag; - public tag: Tag; - - constructor(private helper: HelperFunction, private args: CapturedArguments) { - super(); - - let computeTag = (this.computeTag = createUpdatableTag()); - this.tag = combine([args.tag, computeTag]); - } - - compute(): Opaque { - let { - helper, - computeTag, - args: { positional, named }, - } = this; - - let positionalValue = positional.value(); - let namedValue = named.value(); - - if (DEBUG) { - debugFreeze(positionalValue); - debugFreeze(namedValue); - } - let computedValue; - let combinedTrackingTag = track(() => { - if (DEBUG) { deprecateMutationsInAutotrackingTransaction!(() => { - computedValue = helper(positionalValue, namedValue); + ret = helper.compute(positionalValue, namedValue); }); } else { - computedValue = helper(positionalValue, namedValue); + ret = helper.compute(positionalValue, namedValue); } - }, DEBUG && debugRenderMessage!(`(result of a \`${getDebugName!(helper)}\` helper)`)); - - update(computeTag, combinedTrackingTag); - - return computedValue; - } -} - -export class ClassBasedHelperReference extends CachedReference { - static create(instance: HelperInstance, args: CapturedArguments) { - return new ClassBasedHelperReference(instance, args); - } - - private computeTag: UpdatableTag; - public tag: Tag; - - constructor(private instance: HelperInstance, private args: CapturedArguments) { - super(); - - let computeTag = (this.computeTag = createUpdatableTag()); - this.tag = combine([instance[RECOMPUTE_TAG], args.tag, computeTag]); - } - compute(): Opaque { - let { - instance, - computeTag, - args: { positional, named }, - } = this; + if (helper[RECOMPUTE_TAG]) { + consume(helper[RECOMPUTE_TAG]); + } - let positionalValue = positional.value(); - let namedValue = named.value(); + return ret!; + }; if (DEBUG) { - debugFreeze(positionalValue); - debugFreeze(namedValue); - } + let debugName = isSimpleHelper(helper) + ? getDebugName!(helper.compute) + : getDebugName!(helper); - let computedValue; - let combinedTrackingTag = track(() => { - if (DEBUG) { - deprecateMutationsInAutotrackingTransaction!(() => { - computedValue = instance.compute(positionalValue, namedValue); - }); - } else { - computedValue = instance.compute(positionalValue, namedValue); - } - }, DEBUG && debugRenderMessage!(`(result of a \`${getDebugName!(instance)}\` helper)`)); - - update(computeTag, combinedTrackingTag); - - return computedValue; + super(fnWrapper, args, env, debugName); + } else { + super(fnWrapper, args, env); + } } } -export class InternalHelperReference extends CachedReference { - public tag: Tag; - +export class UnboundRootReference extends RootReference { constructor( - private helper: (args: CapturedArguments) => Opaque, - private args: CapturedArguments + private inner: T, + protected env: Environment, + parent?: VersionedPathReference, + key?: string ) { - super(); - this.tag = args.tag; - } - - compute(): Opaque { - let { helper, args } = this; - return helper(args); - } -} - -export class UnboundReference extends ConstReference { - static create(value: T): VersionedPathReference { - return valueToRef(value, false); - } - - get(key: string): VersionedPathReference { - return valueToRef(this.inner[key], false); - } -} - -export class ReadonlyReference extends CachedReference { - public tag: Tag; + super(env); - constructor(private inner: VersionedPathReference) { - super(); - this.tag = inner.tag; + if (DEBUG) { + env.setTemplatePathDebugContext(this, key || 'this', parent || null); + } } - get [INVOKE](): Function | undefined { - return this.inner[INVOKE]; + value() { + return this.inner; } - compute(): Opaque { - return this.inner.value(); - } + get(key: string): VersionedPathReference { + let value = this.value(); - get(key: string): VersionedPathReference { - return this.inner.get(key); + if (isObject(value)) { + // root of interop with ember objects + return new UnboundPropertyReference(value[key], this.env, this, key); + } else { + return PrimitiveReference.create(value as any); + } } } +export class UnboundPropertyReference extends UnboundRootReference {} + export function referenceFromParts( - root: VersionedPathReference, + root: VersionedPathReference, parts: string[] -): VersionedPathReference { +): VersionedPathReference { let reference = root; for (let i = 0; i < parts.length; i++) { @@ -471,62 +95,3 @@ export function referenceFromParts( return reference; } - -function isObject(value: unknown): value is object { - return value !== null && typeof value === 'object'; -} - -function isFunction(value: unknown): value is Function { - return typeof value === 'function'; -} - -function ensurePrimitive(value: unknown) { - if (DEBUG) { - let label; - - try { - label = ` (was \`${String(value)}\`)`; - } catch (e) { - label = null; - } - - assert( - `This is a fall-through check for typing purposes only! \`value\` must already be a primitive at this point.${label})`, - value === undefined || - value === null || - typeof value === 'boolean' || - typeof value === 'number' || - typeof value === 'string' - ); - } -} - -function valueToRef( - value: T, - bound = true, - env?: Environment -): VersionedPathReference { - if (isObject(value)) { - // root of interop with ember objects - return bound ? new RootReference(value, env) : new UnboundReference(value); - } else if (isFunction(value)) { - // ember doesn't do observing with functions - return new UnboundReference(value); - } else { - ensurePrimitive(value); - return PrimitiveReference.create(value as any); - } -} - -function valueKeyToRef(value: unknown, key: string): VersionedPathReference { - if (isObject(value)) { - // root of interop with ember objects - return new RootPropertyReference(value, key); - } else if (isFunction(value)) { - // ember doesn't do observing with functions - return new UnboundReference(value[key]); - } else { - ensurePrimitive(value); - return UNDEFINED_REFERENCE; - } -} diff --git a/packages/@ember/-internals/glimmer/lib/utils/to-bool.ts b/packages/@ember/-internals/glimmer/lib/utils/to-bool.ts index eec6b59865c..42c29a424c5 100644 --- a/packages/@ember/-internals/glimmer/lib/utils/to-bool.ts +++ b/packages/@ember/-internals/glimmer/lib/utils/to-bool.ts @@ -1,8 +1,11 @@ +import { get } from '@ember/-internals/metal'; import { isArray } from '@ember/-internals/runtime'; -import { Opaque } from '@glimmer/interfaces'; +import { isProxy } from '@ember/-internals/utils'; -export default function toBool(predicate: Opaque): boolean { - if (isArray(predicate)) { +export default function toBool(predicate: unknown): boolean { + if (isProxy(predicate)) { + return Boolean(get(predicate, 'isTruthy')); + } else if (isArray(predicate)) { return (predicate as { length: number }).length !== 0; } else { return Boolean(predicate); diff --git a/packages/@ember/-internals/glimmer/lib/views/outlet.ts b/packages/@ember/-internals/glimmer/lib/views/outlet.ts index ae4eaf7226b..2e871d687cc 100644 --- a/packages/@ember/-internals/glimmer/lib/views/outlet.ts +++ b/packages/@ember/-internals/glimmer/lib/views/outlet.ts @@ -1,7 +1,7 @@ import { OWNER, Owner } from '@ember/-internals/owner'; import { assign } from '@ember/polyfills'; import { schedule } from '@ember/runloop'; -import { Simple } from '@glimmer/interfaces'; +import { SimpleElement } from '@simple-dom/interface'; import { OutletDefinitionState } from '../component-managers/outlet'; import { Renderer } from '../renderer'; import { OwnedTemplate } from '../template'; @@ -71,7 +71,7 @@ export default class OutletView { }; } - appendTo(selector: string | Simple.Element) { + appendTo(selector: string | SimpleElement) { let target; if (this._environment.hasDOM) { diff --git a/packages/@ember/-internals/glimmer/tests/integration/application/debug-render-tree-test.ts b/packages/@ember/-internals/glimmer/tests/integration/application/debug-render-tree-test.ts index a76b364b554..84fc4551ed1 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/application/debug-render-tree-test.ts +++ b/packages/@ember/-internals/glimmer/tests/integration/application/debug-render-tree-test.ts @@ -20,15 +20,15 @@ import Controller from '@ember/controller'; import { captureRenderTree } from '@ember/debug'; import Engine from '@ember/engine'; import EngineInstance from '@ember/engine/instance'; -import { Simple } from '@glimmer/interfaces'; import { expect } from '@glimmer/util'; +import { SimpleElement, SimpleNode } from '@simple-dom/interface'; import { compile } from 'ember-template-compiler'; import { runTask } from 'internal-test-helpers/lib/run'; interface CapturedBounds { - parentElement: Simple.Element; - firstNode: Simple.Node; - lastNode: Simple.Node; + parentElement: SimpleElement; + firstNode: SimpleNode; + lastNode: SimpleNode; } type Expected = T | ((actual: T) => boolean); @@ -51,6 +51,8 @@ if (ENV._DEBUG_RENDER_TREE) { moduleFor( 'Application test: debug render tree', class extends ApplicationTestCase { + _TEMPLATE_ONLY_GLIMMER_COMPONENTS: boolean; + constructor() { super(...arguments); this._TEMPLATE_ONLY_GLIMMER_COMPONENTS = ENV._TEMPLATE_ONLY_GLIMMER_COMPONENTS; @@ -1529,17 +1531,17 @@ if (ENV._DEBUG_RENDER_TREE) { parentElement: (expect( node.parentNode, 'BUG: detached node' - ) as Simple.Node) as Simple.Element, - firstNode: node as Simple.Node, - lastNode: node as Simple.Node, + ) as SimpleNode) as SimpleElement, + firstNode: node as SimpleNode, + lastNode: node as SimpleNode, }; } elementBounds(element: Element): CapturedBounds { return { - parentElement: element as Simple.Element, - firstNode: element.firstChild! as Simple.Node, - lastNode: element.lastChild! as Simple.Node, + parentElement: element as SimpleElement, + firstNode: element.firstChild! as SimpleNode, + lastNode: element.lastChild! as SimpleNode, }; } diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/curly-components-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/curly-components-test.js index 0ab93ca301a..ad29ae343d4 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/curly-components-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/curly-components-test.js @@ -2914,7 +2914,7 @@ moduleFor( this.render(`{{foo-bar id="foo-bar" isVisible=visible}}`, { visible: false, }); - }, '`isVisible` is deprecated (from "component:foo-bar")'); + }, /The `isVisible` property on classic component classes is deprecated. Was accessed while rendering:\n\nfoo-bar/); assertStyle('display: none;'); @@ -2924,7 +2924,7 @@ moduleFor( runTask(() => { set(this.context, 'visible', true); }); - }, '`isVisible` is deprecated (from "component:foo-bar")'); + }, /The `isVisible` property on classic component classes is deprecated. Was accessed while rendering:\n\nfoo-bar/); assertStyle(''); @@ -2932,7 +2932,7 @@ moduleFor( runTask(() => { set(this.context, 'visible', false); }); - }, '`isVisible` is deprecated (from "component:foo-bar")'); + }, /The `isVisible` property on classic component classes is deprecated. Was accessed while rendering:\n\nfoo-bar/); assertStyle('display: none;'); } @@ -2951,7 +2951,7 @@ moduleFor( this.render(`{{foo-bar id="foo-bar" isVisible=visible}}`, { visible: false, }); - }, '`isVisible` is deprecated (from "component:foo-bar")'); + }, /The `isVisible` property on classic component classes is deprecated. Was accessed while rendering:\n\nfoo-bar/); this.assertComponentElement(this.firstChild, { tagName: 'div', @@ -2964,7 +2964,7 @@ moduleFor( runTask(() => { set(this.context, 'visible', true); }); - }, '`isVisible` is deprecated (from "component:foo-bar")'); + }, /The `isVisible` property on classic component classes is deprecated. Was accessed while rendering:\n\nfoo-bar/); this.assertComponentElement(this.firstChild, { tagName: 'div', @@ -2975,7 +2975,7 @@ moduleFor( runTask(() => { set(this.context, 'visible', false); }); - }, '`isVisible` is deprecated (from "component:foo-bar")'); + }, /The `isVisible` property on classic component classes is deprecated. Was accessed while rendering:\n\nfoo-bar/); this.assertComponentElement(this.firstChild, { tagName: 'div', @@ -3011,7 +3011,7 @@ moduleFor( visible: false, foo: 'baz', }); - }, '`isVisible` is deprecated (from "component:foo-bar")'); + }, /The `isVisible` property on classic component classes is deprecated. Was accessed while rendering:\n\nfoo-bar/); assertStyle('display: none;'); @@ -3021,7 +3021,7 @@ moduleFor( runTask(() => { set(this.context, 'visible', true); }); - }, '`isVisible` is deprecated (from "component:foo-bar")'); + }, /The `isVisible` property on classic component classes is deprecated. Was accessed while rendering:\n\nfoo-bar/); assertStyle(''); @@ -3030,7 +3030,7 @@ moduleFor( set(this.context, 'visible', false); set(this.context, 'foo', 'woo'); }); - }, '`isVisible` is deprecated (from "component:foo-bar")'); + }, /The `isVisible` property on classic component classes is deprecated. Was accessed while rendering:\n\nfoo-bar/); assertStyle('display: none;'); assert.equal(this.firstChild.getAttribute('foo'), 'woo'); diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/destroy-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/destroy-test.js deleted file mode 100644 index 89927af28da..00000000000 --- a/packages/@ember/-internals/glimmer/tests/integration/components/destroy-test.js +++ /dev/null @@ -1,35 +0,0 @@ -import { moduleFor, RenderingTestCase, runTask } from 'internal-test-helpers'; - -import { set } from '@ember/-internals/metal'; - -import { Component } from '../../utils/helpers'; - -moduleFor( - 'Component destroy', - class extends RenderingTestCase { - ['@test it correctly releases the destroyed components'](assert) { - let FooBarComponent = Component.extend({}); - - this.registerComponent('foo-bar', { - ComponentClass: FooBarComponent, - template: 'hello', - }); - - this.render('{{#if switch}}{{#foo-bar}}{{foo-bar}}{{/foo-bar}}{{/if}}', { - switch: true, - }); - - this.assertComponentElement(this.firstChild, { content: 'hello' }); - - runTask(() => set(this.context, 'switch', false)); - - this.assertText(''); - - assert.equal( - this.env.destroyedComponents.length, - 0, - 'environment.destroyedComponents should be empty' - ); - } - } -); diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/error-handling-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/error-handling-test.js index b1739b09f01..7b1d6ea15ab 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/error-handling-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/error-handling-test.js @@ -30,7 +30,7 @@ moduleFor( }, /silly mistake in init/); assert.equal( - this.env.inTransaction, + this.renderer._inRenderTransaction, false, 'should not be in a transaction even though an error was thrown' ); @@ -79,7 +79,7 @@ moduleFor( }, /silly mistake in init/); assert.equal( - this.env.inTransaction, + this.renderer._inRenderTransaction, false, 'should not be in a transaction even though an error was thrown' ); @@ -117,7 +117,7 @@ moduleFor( }, /silly mistake/); assert.equal( - this.env.inTransaction, + this.renderer._inRenderTransaction, false, 'should not be in a transaction even though an error was thrown' ); diff --git a/packages/@ember/-internals/glimmer/tests/integration/components/life-cycle-test.js b/packages/@ember/-internals/glimmer/tests/integration/components/life-cycle-test.js index 295a507c72f..f710961adbb 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/components/life-cycle-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/components/life-cycle-test.js @@ -1192,6 +1192,11 @@ class LifeCycleHooksTest extends RenderingTestCase { ['nested-item', 'willRender'], ['nested-item', 'willInsertElement'], + ['nested-item', 'didInsertElement'], + ['nested-item', 'didRender'], + ['no-items', 'didInsertElement'], + ['no-items', 'didRender'], + ['an-item', 'didDestroyElement'], ['nested-item', 'didDestroyElement'], ['an-item', 'didDestroyElement'], @@ -1203,11 +1208,6 @@ class LifeCycleHooksTest extends RenderingTestCase { ['an-item', 'didDestroyElement'], ['nested-item', 'didDestroyElement'], - ['nested-item', 'didInsertElement'], - ['nested-item', 'didRender'], - ['no-items', 'didInsertElement'], - ['no-items', 'didRender'], - ['an-item', 'willDestroy'], ['nested-item', 'willDestroy'], ['an-item', 'willDestroy'], diff --git a/packages/@ember/-internals/glimmer/tests/integration/helpers/custom-helper-test.js b/packages/@ember/-internals/glimmer/tests/integration/helpers/custom-helper-test.js index 33760217485..50934ccb891 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/helpers/custom-helper-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/helpers/custom-helper-test.js @@ -441,7 +441,7 @@ moduleFor( this.assert.throws(() => { this.render(`
`); - }, /Compile Error some-helper is not a modifier: Helpers may not be used in the element form/); + }, /Error: Compile Error: Unexpected Modifier some-helper @ 0..0/); } ['@test class-based helper not usable within element']() { @@ -451,7 +451,7 @@ moduleFor( this.assert.throws(() => { this.render(`
`); - }, /Compile Error some-helper is not a modifier: Helpers may not be used in the element form/); + }, /Error: Compile Error: Unexpected Modifier some-helper @ 0..0/); } ['@test class-based helper is torn down'](assert) { diff --git a/packages/@ember/-internals/glimmer/tests/integration/helpers/tracked-test.js b/packages/@ember/-internals/glimmer/tests/integration/helpers/tracked-test.js index 23e5e077950..fe81f543951 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/helpers/tracked-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/helpers/tracked-test.js @@ -353,7 +353,7 @@ moduleFor( }); let expectedMessage = backtrackingMessageFor('name', '\\(unknown object\\)', { - renderTree: ['\\(result of a `helloWorld` helper\\)'], + renderTree: ['\\(result of a `.*` helper\\)'], }); expectDeprecation(() => { diff --git a/packages/@ember/-internals/glimmer/tests/integration/modifiers/on-test.js b/packages/@ember/-internals/glimmer/tests/integration/modifiers/on-test.js index fc6eb7166a4..2aa92d4b941 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/modifiers/on-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/modifiers/on-test.js @@ -1,6 +1,5 @@ import { moduleFor, RenderingTestCase, runTask } from 'internal-test-helpers'; import { isChrome, isFirefox } from '@ember/-internals/browser-environment'; -import { privatize as P } from '@ember/-internals/container'; import { HAS_NATIVE_PROXY } from '@ember/-internals/utils'; import { Component } from '../../utils/helpers'; @@ -18,9 +17,7 @@ moduleFor( getOnManagerInstance() { // leveraging private APIs, this can be deleted if these APIs change // but it has been useful to verify some internal details - let templateCompiler = this.owner.lookup(P`template-compiler:main`); - - return templateCompiler.resolver.resolver.builtInModifiers.on.manager; + return this.renderer._runtimeResolver.builtInModifiers.on.manager; } assertCounts(expected) { @@ -391,9 +388,7 @@ moduleFor( getOnManagerInstance() { // leveraging private APIs, this can be deleted if these APIs change // but it has been useful to verify some internal details - let templateCompiler = this.owner.lookup(P`template-compiler:main`); - - return templateCompiler.resolver.resolver.builtInModifiers.on.manager; + return this.renderer._runtimeResolver.builtInModifiers.on.manager; } assertCounts(expected) { diff --git a/packages/@ember/-internals/glimmer/tests/integration/syntax/experimental-syntax-test.js b/packages/@ember/-internals/glimmer/tests/integration/syntax/experimental-syntax-test.js index 0e92d188ab8..4e5b237cef3 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/syntax/experimental-syntax-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/syntax/experimental-syntax-test.js @@ -1,6 +1,7 @@ import { moduleFor, RenderingTestCase, strip } from 'internal-test-helpers'; import { _registerMacros, _experimentalMacros } from '@ember/-internals/glimmer'; +import { invokeStaticBlockWithStack } from '@glimmer/opcode-compiler'; moduleFor( 'registerMacros', @@ -9,9 +10,8 @@ moduleFor( let originalMacros = _experimentalMacros.slice(); _registerMacros(blocks => { - blocks.add('-let', (params, hash, _default, inverse, builder) => { - builder.compileParams(params); - builder.invokeStaticBlock(_default, params.length); + blocks.add('-test-block', (params, _hash, blocks) => { + return invokeStaticBlockWithStack(blocks.get('default')); }); }); @@ -29,11 +29,10 @@ moduleFor( ['@test allows registering custom syntax via private API']() { this.render( strip` - {{#-let obj as |bar|}} - {{bar}} - {{/-let}} - `, - { obj: 'hello world!' } + {{#-test-block}} + hello world! + {{/-test-block}} + ` ); this.assertText('hello world!'); diff --git a/packages/@ember/-internals/glimmer/tests/integration/syntax/in-element-test.js b/packages/@ember/-internals/glimmer/tests/integration/syntax/in-element-test.js index 0f593c8bd93..20735288c38 100644 --- a/packages/@ember/-internals/glimmer/tests/integration/syntax/in-element-test.js +++ b/packages/@ember/-internals/glimmer/tests/integration/syntax/in-element-test.js @@ -21,10 +21,10 @@ moduleFor( this.render( strip` - {{#-in-element someElement}} - {{text}} - {{/-in-element}} - `, + {{#-in-element someElement}} + {{text}} + {{/-in-element}} + `, { someElement, text: 'Whoop!', @@ -47,6 +47,55 @@ moduleFor( equalTokens(someElement, 'Whoop!'); } + ['@test allows insertBefore=null']() { + let someElement = document.createElement('div'); + + this.render( + strip` + {{#-in-element someElement insertBefore=null}} + {{text}} + {{/-in-element}} + `, + { + someElement, + text: 'Whoop!', + } + ); + + equalTokens(this.element, ''); + equalTokens(someElement, 'Whoop!'); + + this.assertStableRerender(); + + runTask(() => set(this.context, 'text', 'Huzzah!!')); + + equalTokens(this.element, ''); + equalTokens(someElement, 'Huzzah!!'); + + runTask(() => set(this.context, 'text', 'Whoop!')); + + equalTokens(this.element, ''); + equalTokens(someElement, 'Whoop!'); + } + + ['@test does not allow insertBefore=non-null-value']() { + let someElement = document.createElement('div'); + + expectAssertion(() => { + this.render( + strip` + {{#-in-element someElement insertBefore=".foo"}} + {{text}} + {{/-in-element}} + `, + { + someElement, + text: 'Whoop!', + } + ); + }, /Can only pass a null literal to insertBefore in -in-element, received:/); + } + ['@test components are cleaned up properly'](assert) { let hooks = []; @@ -68,12 +117,12 @@ moduleFor( this.render( strip` - {{#if showModal}} - {{#-in-element someElement}} - {{modal-display text=text}} - {{/-in-element}} - {{/if}} - `, + {{#if showModal}} + {{#-in-element someElement}} + {{modal-display text=text}} + {{/-in-element}} + {{/if}} + `, { someElement, text: 'Whoop!', diff --git a/packages/@ember/-internals/glimmer/tests/unit/runtime-resolver-cache-test.js b/packages/@ember/-internals/glimmer/tests/unit/runtime-resolver-cache-test.js index 6c156e40d4f..00b8ba8d8d7 100644 --- a/packages/@ember/-internals/glimmer/tests/unit/runtime-resolver-cache-test.js +++ b/packages/@ember/-internals/glimmer/tests/unit/runtime-resolver-cache-test.js @@ -214,7 +214,7 @@ moduleFor( } getCacheCounters() { - let { componentDefinitionCount, helperDefinitionCount } = this.runtimeResolver; + let { componentDefinitionCount, helperDefinitionCount } = this.renderer._runtimeResolver; return (this._counters = { templateCacheHits: templateCacheCounters.cacheHit, diff --git a/packages/@ember/-internals/glimmer/tests/utils/shared-conditional-tests.js b/packages/@ember/-internals/glimmer/tests/utils/shared-conditional-tests.js index 46f14523dc8..c6c4022b862 100644 --- a/packages/@ember/-internals/glimmer/tests/utils/shared-conditional-tests.js +++ b/packages/@ember/-internals/glimmer/tests/utils/shared-conditional-tests.js @@ -594,8 +594,10 @@ export class TogglingHelperConditionalsTest extends TogglingConditionalsTest { let template = this.wrappedTemplateFor({ cond: 'cond', - truthy: '(x-truthy)', - falsy: '(x-falsy)', + + // pass values so the helpers don't eagerly compute + truthy: '(x-truthy this.foo)', + falsy: '(x-falsy this.foo)', }); withoutEvaluatingFalsy(() => this.render(template, { cond: this.truthyValue })); diff --git a/packages/@ember/-internals/meta/lib/meta.ts b/packages/@ember/-internals/meta/lib/meta.ts index 1506c824f7c..c632ef159ce 100644 --- a/packages/@ember/-internals/meta/lib/meta.ts +++ b/packages/@ember/-internals/meta/lib/meta.ts @@ -1,7 +1,7 @@ import { symbol, toString } from '@ember/-internals/utils'; import { assert } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; -import { createUpdatableTag, UpdatableTag } from '@glimmer/reference'; +import { UpdatableTag } from '@glimmer/validator'; type ObjMap = { [key: string]: T }; @@ -96,8 +96,6 @@ let currentListenerVersion = 1; export class Meta { _descriptors: Map | undefined; _mixins: any | undefined; - _tag: UpdatableTag | undefined; - _tags: ObjMap | undefined; _flags: MetaFlags; _lazyChains: ObjMap> | undefined; source: object; @@ -120,8 +118,6 @@ export class Meta { this._parent = undefined; this._descriptors = undefined; this._mixins = undefined; - this._tag = undefined; - this._tags = undefined; // initial value for all flags right now is false // see FLAGS const for detailed list of flags used @@ -228,31 +224,6 @@ export class Meta { return false; } - writableTags() { - return this._getOrCreateOwnMap('_tags'); - } - readableTags() { - return this._tags; - } - - writableTag() { - assert( - this.isMetaDestroyed() - ? `Cannot create a new tag for \`${toString(this.source)}\` after it has been destroyed.` - : '', - !this.isMetaDestroyed() - ); - let ret = this._tag; - if (ret === undefined) { - ret = this._tag = createUpdatableTag(); - } - return ret; - } - - readableTag() { - return this._tag; - } - writableLazyChainsFor(key: string) { if (DEBUG) { counters!.writableLazyChainsCalls++; diff --git a/packages/@ember/-internals/meta/tests/meta_test.js b/packages/@ember/-internals/meta/tests/meta_test.js index 16bab8e9692..79c1d3460d9 100644 --- a/packages/@ember/-internals/meta/tests/meta_test.js +++ b/packages/@ember/-internals/meta/tests/meta_test.js @@ -32,20 +32,5 @@ moduleFor( } } } - - ['@test meta.writableTag issues useful error after destroy']() { - let target = { - toString() { - return ''; - }, - }; - let targetMeta = meta(target); - - targetMeta.destroy(); - - expectAssertion(() => { - targetMeta.writableTag(() => {}); - }, 'Cannot create a new tag for `` after it has been destroyed.'); - } } ); diff --git a/packages/@ember/-internals/metal/index.ts b/packages/@ember/-internals/metal/index.ts index 3af160fd625..7b2efad04a6 100644 --- a/packages/@ember/-internals/metal/index.ts +++ b/packages/@ember/-internals/metal/index.ts @@ -53,17 +53,8 @@ export { default as expandProperties } from './lib/expand_properties'; export { addObserver, activateObserver, removeObserver, flushAsyncObservers } from './lib/observer'; export { Mixin, aliasMethod, mixin, observer, applyMixin } from './lib/mixin'; export { default as inject, DEBUG_INJECTION_FUNCTIONS } from './lib/injected_property'; -export { tagForProperty, tagFor, markObjectAsDirty, UNKNOWN_PROPERTY_TAG } from './lib/tags'; -export { - consume, - Tracker, - tracked, - track, - untrack, - isTracking, - runInAutotrackingTransaction, - deprecateMutationsInAutotrackingTransaction, -} from './lib/tracked'; +export { tagForProperty, tagForObject, markObjectAsDirty, UNKNOWN_PROPERTY_TAG } from './lib/tags'; +export { tracked } from './lib/tracked'; export { NAMESPACES, diff --git a/packages/@ember/-internals/metal/lib/alias.ts b/packages/@ember/-internals/metal/lib/alias.ts index 85ca5f1793f..450a60411cd 100644 --- a/packages/@ember/-internals/metal/lib/alias.ts +++ b/packages/@ember/-internals/metal/lib/alias.ts @@ -2,7 +2,15 @@ import { Meta } from '@ember/-internals/meta'; import { inspect } from '@ember/-internals/utils'; import { assert } from '@ember/debug'; import EmberError from '@ember/error'; -import { combine, UpdatableTag, update, validate, value } from '@glimmer/reference'; +import { + combine, + consume, + untrack, + UpdatableTag, + update, + validate, + value, +} from '@glimmer/validator'; import { finishLazyChains, getChainTagsForKey } from './chain-tags'; import { getLastRevisionFor, setLastRevisionFor } from './computed_cache'; import { @@ -16,7 +24,6 @@ import { defineProperty } from './properties'; import { get } from './property_get'; import { set } from './property_set'; import { tagForProperty } from './tags'; -import { consume, untrack } from './tracked'; export type AliasDecorator = Decorator & PropertyDecorator & AliasDecoratorImpl; diff --git a/packages/@ember/-internals/metal/lib/array.ts b/packages/@ember/-internals/metal/lib/array.ts index f3bf0f44847..69142a5d2bc 100644 --- a/packages/@ember/-internals/metal/lib/array.ts +++ b/packages/@ember/-internals/metal/lib/array.ts @@ -1,3 +1,4 @@ +import { EmberArray } from '@ember/-internals/utils'; import { arrayContentDidChange, arrayContentWillChange } from './array_events'; import { addListener, removeListener } from './events'; import { notifyPropertyChange } from './property_events'; @@ -8,13 +9,6 @@ interface ObjectHasArrayObservers { hasArrayObservers?: boolean; } -export interface EmberArray extends ObjectHasArrayObservers { - length: number; - objectAt(index: number): T | undefined; - replace(start: number, deleteCount: number, items: T[]): void; - splice(start: number, deleteCount: number, ...items: T[]): void; -} - export function objectAt(array: T[] | EmberArray, index: number): T | undefined { if (Array.isArray(array)) { return array[index]; diff --git a/packages/@ember/-internals/metal/lib/chain-tags.ts b/packages/@ember/-internals/metal/lib/chain-tags.ts index bc3422be285..682010857ff 100644 --- a/packages/@ember/-internals/metal/lib/chain-tags.ts +++ b/packages/@ember/-internals/metal/lib/chain-tags.ts @@ -1,6 +1,14 @@ import { meta as metaFor, peekMeta } from '@ember/-internals/meta'; import { assert, deprecate } from '@ember/debug'; -import { combine, createUpdatableTag, Tag, update, validate } from '@glimmer/reference'; +import { DEBUG } from '@glimmer/env'; +import { + ALLOW_CYCLES, + combine, + createUpdatableTag, + Tag, + update, + validate, +} from '@glimmer/validator'; import { objectAt } from './array'; import { getLastRevisionFor, peekCacheFor } from './computed_cache'; import { descriptorForProperty } from './descriptor_map'; @@ -241,5 +249,9 @@ export function getChainTagsForKey(obj: any, path: string) { } } + if (DEBUG) { + chainTags.forEach(t => ALLOW_CYCLES!.set(t, true)); + } + return chainTags; } diff --git a/packages/@ember/-internals/metal/lib/computed.ts b/packages/@ember/-internals/metal/lib/computed.ts index 2312b2655d0..86e21e45878 100644 --- a/packages/@ember/-internals/metal/lib/computed.ts +++ b/packages/@ember/-internals/metal/lib/computed.ts @@ -4,12 +4,15 @@ import { assert, deprecate, warn } from '@ember/debug'; import EmberError from '@ember/error'; import { combine, + consume, Tag, + track, + untrack, UpdatableTag, update, validate, value as tagValue, -} from '@glimmer/reference'; +} from '@glimmer/validator'; import { finishLazyChains, getChainTagsForKeys } from './chain-tags'; import { getCachedValueFor, @@ -36,7 +39,6 @@ import { defineProperty } from './properties'; import { beginPropertyChanges, endPropertyChanges, notifyPropertyChange } from './property_events'; import { set } from './property_set'; import { tagForProperty } from './tags'; -import { consume, track, untrack } from './tracked'; export type ComputedPropertyGetter = (keyName: string) => any; export type ComputedPropertySetter = (keyName: string, value: any, cachedValue?: any) => any; diff --git a/packages/@ember/-internals/metal/lib/observer.ts b/packages/@ember/-internals/metal/lib/observer.ts index 4c0ff4b6239..ba9f4ec8e2f 100644 --- a/packages/@ember/-internals/metal/lib/observer.ts +++ b/packages/@ember/-internals/metal/lib/observer.ts @@ -1,7 +1,7 @@ import { ENV } from '@ember/-internals/environment'; import { peekMeta } from '@ember/-internals/meta'; import { schedule } from '@ember/runloop'; -import { combine, CURRENT_TAG, Tag, validate, value } from '@glimmer/reference'; +import { combine, CURRENT_TAG, Tag, validate, value } from '@glimmer/validator'; import { getChainTagsForKey } from './chain-tags'; import changeEvent from './change_event'; import { addListener, removeListener, sendEvent } from './events'; diff --git a/packages/@ember/-internals/metal/lib/property_events.ts b/packages/@ember/-internals/metal/lib/property_events.ts index fc3ec5389f1..d206c3072a7 100644 --- a/packages/@ember/-internals/metal/lib/property_events.ts +++ b/packages/@ember/-internals/metal/lib/property_events.ts @@ -36,9 +36,7 @@ function notifyPropertyChange(obj: object, keyName: string, _meta?: Meta | null) return; } - if (meta !== null) { - markObjectAsDirty(obj, keyName, meta); - } + markObjectAsDirty(obj, keyName); if (deferred <= 0) { flushSyncObservers(); diff --git a/packages/@ember/-internals/metal/lib/property_get.ts b/packages/@ember/-internals/metal/lib/property_get.ts index 2def23f660c..1311d34c972 100644 --- a/packages/@ember/-internals/metal/lib/property_get.ts +++ b/packages/@ember/-internals/metal/lib/property_get.ts @@ -1,12 +1,16 @@ /** @module @ember/object */ -import { HAS_NATIVE_PROXY, isEmberArray, symbol } from '@ember/-internals/utils'; +import { HAS_NATIVE_PROXY, isEmberArray, isProxy, symbol } from '@ember/-internals/utils'; import { assert } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; +import { + consume, + deprecateMutationsInAutotrackingTransaction, + isTracking, +} from '@glimmer/validator'; import { isPath } from './path_cache'; import { tagForProperty } from './tags'; -import { consume, deprecateMutationsInAutotrackingTransaction, isTracking } from './tracked'; export const PROXY_CONTENT = symbol('PROXY_CONTENT'); @@ -113,10 +117,18 @@ export function get(obj: object, keyName: string): any { value = obj[keyName]; } - // Add the tag of the returned value if it is an array, since arrays - // should always cause updates if they are consumed and then changed - if (tracking && (Array.isArray(value) || isEmberArray(value))) { - consume(tagForProperty(value, '[]')); + if (tracking) { + // Add the tag of the returned value if it is an array, since arrays + // should always cause updates if they are consumed and then changed + if (Array.isArray(value) || isEmberArray(value)) { + consume(tagForProperty(value, '[]')); + } + + // Add the value of the content if the value is a proxy. This is because + // content changes the truthiness/falsiness of the proxy. + if (isProxy(value)) { + consume(tagForProperty(value, 'content')); + } } } else { value = obj[keyName]; diff --git a/packages/@ember/-internals/metal/lib/tags.ts b/packages/@ember/-internals/metal/lib/tags.ts index 9cda151358a..43bebc531de 100644 --- a/packages/@ember/-internals/metal/lib/tags.ts +++ b/packages/@ember/-internals/metal/lib/tags.ts @@ -1,80 +1,108 @@ -import { Meta, meta as metaFor } from '@ember/-internals/meta'; -import { setupMandatorySetter, symbol } from '@ember/-internals/utils'; +import { meta as metaFor } from '@ember/-internals/meta'; +import { + getDebugName, + isObject, + setupMandatorySetter, + symbol, + toString, +} from '@ember/-internals/utils'; +import { assert, deprecate } from '@ember/debug'; +import { _WeakSet as WeakSet } from '@ember/polyfills'; import { backburner } from '@ember/runloop'; import { DEBUG } from '@glimmer/env'; -import { CONSTANT_TAG, createUpdatableTag, dirty, Tag } from '@glimmer/reference'; -import { assertTagNotConsumed } from './tracked'; - -export const UNKNOWN_PROPERTY_TAG = symbol('UNKNOWN_PROPERTY_TAG'); - -export function tagForProperty(object: any, propertyKey: string | symbol, _meta?: Meta): Tag { - let objectType = typeof object; - if (objectType !== 'function' && (objectType !== 'object' || object === null)) { - return CONSTANT_TAG; - } - let meta = _meta === undefined ? metaFor(object) : _meta; - - if (!(propertyKey in object) && typeof object[UNKNOWN_PROPERTY_TAG] === 'function') { - return object[UNKNOWN_PROPERTY_TAG](propertyKey); - } +import { + CONSTANT_TAG, + dirtyTagFor, + setAutotrackingTransactionEnv, + setPropertyDidChange, + Tag, + tagFor, +} from '@glimmer/validator'; + +///////// + +// Setup tracking environment + +setPropertyDidChange(() => backburner.ensureInstance()); + +if (DEBUG) { + setAutotrackingTransactionEnv!({ + assert(message) { + assert(message, false); + }, + + deprecate(message) { + deprecate(message, false, { + id: 'autotracking.mutation-after-consumption', + until: '4.0.0', + }); + }, + + debugMessage(obj, keyName) { + let dirtyString = keyName + ? `\`${keyName}\` on \`${getDebugName!(obj)}\`` + : `\`${getDebugName!(obj)}\``; + + return `You attempted to update ${dirtyString}, but it had already been used previously in the same computation. Attempting to update a value after using it in a computation can cause logical errors, infinite revalidation bugs, and performance issues, and is not supported.`; + }, + }); +} - let tags = meta.writableTags(); - let tag = tags[propertyKey]; - if (tag) { - return tag; - } +///////// - let newTag = createUpdatableTag(); +export const UNKNOWN_PROPERTY_TAG = symbol('UNKNOWN_PROPERTY_TAG'); - if (DEBUG) { - setupMandatorySetter!(object, propertyKey); +// This is exported for `@tracked`, but should otherwise be avoided. Use `tagForObject`. +export const SELF_TAG: string = symbol('SELF_TAG'); - (newTag as any)._propertyKey = propertyKey; - } +let SEEN_TAGS: WeakSet | undefined; - return (tags[propertyKey] = newTag); +if (DEBUG) { + SEEN_TAGS = new WeakSet(); } -export function tagFor(object: any | null, _meta?: Meta): Tag { - if (typeof object === 'object' && object !== null) { - let meta = _meta === undefined ? metaFor(object) : _meta; +export function tagForProperty(obj: unknown, propertyKey: string | symbol): Tag { + if (!isObject(obj)) { + return CONSTANT_TAG; + } - if (!meta.isMetaDestroyed()) { - return meta.writableTag(); - } + if (!(propertyKey in obj) && typeof obj[UNKNOWN_PROPERTY_TAG] === 'function') { + return obj[UNKNOWN_PROPERTY_TAG](propertyKey); } - return CONSTANT_TAG; -} + let tag = tagFor(obj, propertyKey); -export function markObjectAsDirty(obj: object, propertyKey: string, _meta?: Meta): void { - let meta = _meta === undefined ? metaFor(obj) : _meta; - let objectTag = meta.readableTag(); + if (DEBUG && !SEEN_TAGS!.has(tag)) { + SEEN_TAGS!.add(tag); - if (objectTag !== undefined) { - if (DEBUG) { - assertTagNotConsumed!(objectTag, obj); - } + setupMandatorySetter!(obj, propertyKey); - dirty(objectTag); + (tag as any)._propertyKey = propertyKey; } - let tags = meta.readableTags(); - let propertyTag = tags !== undefined ? tags[propertyKey] : undefined; + return tag; +} - if (propertyTag !== undefined) { +export function tagForObject(obj: unknown | null): Tag { + if (isObject(obj)) { if (DEBUG) { - assertTagNotConsumed!(propertyTag, obj, propertyKey); + let meta = metaFor(obj); + + assert( + meta.isMetaDestroyed() + ? `Cannot create a new tag for \`${toString(meta.source)}\` after it has been destroyed.` + : '', + !meta.isMetaDestroyed() + ); } - dirty(propertyTag); + return tagFor(obj, SELF_TAG); } - if (objectTag !== undefined || propertyTag !== undefined) { - ensureRunloop(); - } + return CONSTANT_TAG; } -export function ensureRunloop(): void { - backburner.ensureInstance(); +export function markObjectAsDirty(obj: object, propertyKey: string): void { + dirtyTagFor(obj, propertyKey); + dirtyTagFor(obj, SELF_TAG); } diff --git a/packages/@ember/-internals/metal/lib/tracked.ts b/packages/@ember/-internals/metal/lib/tracked.ts index 4d4324ead38..5af772db5de 100644 --- a/packages/@ember/-internals/metal/lib/tracked.ts +++ b/packages/@ember/-internals/metal/lib/tracked.ts @@ -1,216 +1,10 @@ -import { getDebugName, isEmberArray } from '@ember/-internals/utils'; -import { assert, deprecate } from '@ember/debug'; +import { isEmberArray } from '@ember/-internals/utils'; +import { assert } from '@ember/debug'; import { DEBUG } from '@glimmer/env'; -import { combine, CONSTANT_TAG, Tag, UpdatableTag, update } from '@glimmer/reference'; +import { consume, dirtyTagFor, trackedData } from '@glimmer/validator'; import { Decorator, DecoratorPropertyDescriptor, isElementDescriptor } from './decorator'; import { setClassicDecorator } from './descriptor_map'; -import { markObjectAsDirty, tagForProperty } from './tags'; - -type Option = T | null; - -interface AutotrackingTransactionSourceData { - context?: string; - error: Error; -} - -let DEPRECATE_IN_AUTOTRACKING_TRANSACTION = false; -let AUTOTRACKING_TRANSACTION: WeakMap | null = null; - -export let runInAutotrackingTransaction: undefined | ((fn: () => void) => void); -export let deprecateMutationsInAutotrackingTransaction: undefined | ((fn: () => void) => void); - -let debuggingContexts: string[] | undefined; - -export let assertTagNotConsumed: - | undefined - | ((tag: Tag, obj: object, keyName?: string, forHardError?: boolean) => void); - -let markTagAsConsumed: undefined | ((_tag: Tag, sourceError: Error) => void); - -if (DEBUG) { - /** - * Creates a global autotracking transaction. This will prevent any backflow - * in any `track` calls within the transaction, even if they are not - * externally consumed. - * - * `runInAutotrackingTransaction` can be called within itself, and it will add - * onto the existing transaction if one exists. - * - * TODO: Only throw an error if the `track` is consumed. - */ - runInAutotrackingTransaction = (fn: () => void) => { - let previousDeprecateState = DEPRECATE_IN_AUTOTRACKING_TRANSACTION; - let previousTransactionState = AUTOTRACKING_TRANSACTION; - - DEPRECATE_IN_AUTOTRACKING_TRANSACTION = false; - - if (previousTransactionState === null) { - // if there was no transaction start it. Otherwise, the transaction already exists. - AUTOTRACKING_TRANSACTION = new WeakMap(); - } - - try { - fn(); - } finally { - DEPRECATE_IN_AUTOTRACKING_TRANSACTION = previousDeprecateState; - AUTOTRACKING_TRANSACTION = previousTransactionState; - } - }; - - /** - * Switches to deprecating within an autotracking transaction, if one exists. - * If `runInAutotrackingTransaction` is called within the callback of this - * method, it switches back to throwing an error, allowing zebra-striping of - * the types of errors that are thrown. - * - * Does not start an autotracking transaction. - */ - deprecateMutationsInAutotrackingTransaction = (fn: () => void) => { - let previousDeprecateState = DEPRECATE_IN_AUTOTRACKING_TRANSACTION; - DEPRECATE_IN_AUTOTRACKING_TRANSACTION = true; - - try { - fn(); - } finally { - DEPRECATE_IN_AUTOTRACKING_TRANSACTION = previousDeprecateState; - } - }; - - let nthIndex = (str: string, pattern: string, n: number, startingPos = -1) => { - let i = startingPos; - - while (n-- > 0 && i++ < str.length) { - i = str.indexOf(pattern, i); - if (i < 0) break; - } - - return i; - }; - - let makeAutotrackingErrorMessage = ( - sourceData: AutotrackingTransactionSourceData, - obj: object, - keyName?: string - ) => { - let dirtyString = keyName - ? `\`${keyName}\` on \`${getDebugName!(obj)}\`` - : `\`${getDebugName!(obj)}\``; - - let message = [ - `You attempted to update ${dirtyString}, but it had already been used previously in the same computation. Attempting to update a value after using it in a computation can cause logical errors, infinite revalidation bugs, and performance issues, and is not supported.`, - ]; - - if (sourceData.context) { - message.push(`\`${keyName}\` was first used:\n\n${sourceData.context}`); - } - - if (sourceData.error.stack) { - let sourceStack = sourceData.error.stack; - let thirdIndex = nthIndex(sourceStack, '\n', 3); - sourceStack = sourceStack.substr(thirdIndex); - - message.push(`Stack trace for the first usage: ${sourceStack}`); - } - - message.push(`Stack trace for the update:`); - - return message.join('\n\n'); - }; - - debuggingContexts = []; - - markTagAsConsumed = (_tag: Tag, sourceError: Error) => { - if (!AUTOTRACKING_TRANSACTION || AUTOTRACKING_TRANSACTION.has(_tag)) return; - - AUTOTRACKING_TRANSACTION.set(_tag, { - context: debuggingContexts!.map(c => c.replace(/^/gm, ' ').replace(/^ /, '-')).join('\n\n'), - error: sourceError, - }); - - // We need to mark the tag and all of its subtags as consumed, so we need to - // cast in and access its internals. In the future this shouldn't be necessary, - // this is only for computed properties.e - let tag = _tag as any; - - if (tag.subtag) { - markTagAsConsumed!(tag.subtag, sourceError); - } - - if (tag.subtags) { - tag.subtags.forEach((tag: Tag) => markTagAsConsumed!(tag, sourceError)); - } - }; - - assertTagNotConsumed = (tag: Tag, obj: object, keyName?: string, forceHardError = false) => { - if (AUTOTRACKING_TRANSACTION === null) return; - - let sourceData = AUTOTRACKING_TRANSACTION.get(tag); - - if (!sourceData) return; - - if (DEPRECATE_IN_AUTOTRACKING_TRANSACTION && !forceHardError) { - deprecate(makeAutotrackingErrorMessage(sourceData, obj, keyName), false, { - id: 'autotracking.mutation-after-consumption', - until: '4.0.0', - }); - } else { - // This hack makes the assertion message nicer, we can cut off the first - // few lines of the stack trace and let users know where the actual error - // occurred. - try { - assert(makeAutotrackingErrorMessage(sourceData, obj, keyName), false); - } catch (e) { - if (e.stack) { - let updateStackBegin = e.stack.indexOf('Stack trace for the update:'); - - if (updateStackBegin !== -1) { - let start = nthIndex(e.stack, '\n', 1, updateStackBegin); - let end = nthIndex(e.stack, '\n', 4, updateStackBegin); - e.stack = e.stack.substr(0, start) + e.stack.substr(end); - } - } - - throw e; - } - } - }; -} - -/** - An object that that tracks @tracked properties that were consumed. - - @private -*/ -export class Tracker { - private tags = new Set(); - private last: Option = null; - - add(tag: Tag): void { - this.tags.add(tag); - - if (DEBUG) { - markTagAsConsumed!(tag, new Error()); - } - - this.last = tag; - } - - get size(): number { - return this.tags.size; - } - - combine(): Tag { - if (this.tags.size === 0) { - return CONSTANT_TAG; - } else if (this.tags.size === 1) { - return this.last as Tag; - } else { - let tags: Tag[] = []; - this.tags.forEach(tag => tags.push(tag)); - return combine(tags); - } - } -} +import { SELF_TAG, tagForProperty } from './tags'; /** @decorator @@ -354,153 +148,27 @@ function descriptorForField([_target, key, desc]: [ !desc || (!desc.value && !desc.get && !desc.set) ); - let initializer = desc ? desc.initializer : undefined; - let values = new WeakMap(); - let hasInitializer = typeof initializer === 'function'; + let { getter, setter } = trackedData(key, desc ? desc.initializer : undefined); return { enumerable: true, configurable: true, get(): any { - let propertyTag = tagForProperty(this, key) as UpdatableTag; - - consume(propertyTag); - - let value; - - // If the field has never been initialized, we should initialize it - if (hasInitializer && !values.has(this)) { - value = initializer.call(this); - - values.set(this, value); - } else { - value = values.get(this); - } + let value = getter(this); // Add the tag of the returned value if it is an array, since arrays // should always cause updates if they are consumed and then changed if (Array.isArray(value) || isEmberArray(value)) { - update(propertyTag, tagForProperty(value, '[]')); + consume(tagForProperty(value, '[]')); } return value; }, set(newValue: any): void { - if (DEBUG) { - // No matter what, attempting to update a tracked property in an - // autotracking context after it has been read is invalid, even if we - // are otherwise warning, so always assert. - assertTagNotConsumed!(tagForProperty(this, key), this, key, true); - } - - markObjectAsDirty(this, key); - - values.set(this, newValue); - - if (propertyDidChange !== null) { - propertyDidChange(); - } + setter(this, newValue); + dirtyTagFor(this, SELF_TAG); }, }; } - -/** - @private - - Whenever a tracked computed property is entered, the current tracker is - saved off and a new tracker is replaced. - - Any tracked properties consumed are added to the current tracker. - - When a tracked computed property is exited, the tracker's tags are - combined and added to the parent tracker. - - The consequence is that each tracked computed property has a tag - that corresponds to the tracked properties consumed inside of - itself, including child tracked computed properties. -*/ -let CURRENT_TRACKER: Option = null; - -export function track(callback: () => void, debuggingContext?: string | false) { - // Note: debuggingContext is allowed to be false so `DEBUG && 'debug message'` works - - let parent = CURRENT_TRACKER; - let current = new Tracker(); - - CURRENT_TRACKER = current; - - try { - if (DEBUG) { - if (debuggingContext) { - debuggingContexts!.unshift(debuggingContext); - } - runInAutotrackingTransaction!(callback); - } else { - callback(); - } - } finally { - if (DEBUG && debuggingContext) { - debuggingContexts!.shift(); - } - CURRENT_TRACKER = parent; - } - - return current.combine(); -} - -export function consume(tag: Tag) { - if (CURRENT_TRACKER !== null) { - CURRENT_TRACKER.add(tag); - } -} - -export function isTracking() { - return CURRENT_TRACKER !== null; -} - -export function untrack(callback: () => void) { - let parent = CURRENT_TRACKER; - CURRENT_TRACKER = null; - - try { - callback(); - } finally { - CURRENT_TRACKER = parent; - } -} - -export type Key = string; - -export interface Interceptors { - [key: string]: boolean; -} - -let propertyDidChange: (() => void) | null = null; - -export function setPropertyDidChange(cb: () => void): void { - propertyDidChange = cb; -} - -export class UntrackedPropertyError extends Error { - static for(obj: any, key: string): UntrackedPropertyError { - return new UntrackedPropertyError( - obj, - key, - `The property '${key}' on ${obj} was changed after being rendered. If you want to change a property used in a template after the component has rendered, mark the property as a tracked property with the @tracked decorator.` - ); - } - - constructor(public target: any, public key: string, message: string) { - super(message); - } -} - -/** - * Function that can be used in development mode to generate more meaningful - * error messages. - */ -export interface UntrackedPropertyErrorThrower { - (obj: any, key: string): void; -} diff --git a/packages/@ember/-internals/metal/tests/accessors/get_test.js b/packages/@ember/-internals/metal/tests/accessors/get_test.js index a4b42875f0b..27d8a35e661 100644 --- a/packages/@ember/-internals/metal/tests/accessors/get_test.js +++ b/packages/@ember/-internals/metal/tests/accessors/get_test.js @@ -1,8 +1,9 @@ import { ENV } from '@ember/-internals/environment'; import { Object as EmberObject } from '@ember/-internals/runtime'; -import { get, set, track, getWithDefault, Mixin, observer, computed } from '../..'; +import { get, set, getWithDefault, Mixin, observer, computed } from '../..'; import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; import { run } from '@ember/runloop'; +import { track } from '@glimmer/validator'; function aget(x, y) { return x[y]; diff --git a/packages/@ember/-internals/metal/tests/alias_test.js b/packages/@ember/-internals/metal/tests/alias_test.js index 22e66fee28a..940f035d0f8 100644 --- a/packages/@ember/-internals/metal/tests/alias_test.js +++ b/packages/@ember/-internals/metal/tests/alias_test.js @@ -10,7 +10,7 @@ import { } from '..'; import { Object as EmberObject } from '@ember/-internals/runtime'; import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; -import { value, validate } from '@glimmer/reference'; +import { value, validate } from '@glimmer/validator'; let obj, count; diff --git a/packages/@ember/-internals/metal/tests/tracked/classic_classes_test.js b/packages/@ember/-internals/metal/tests/tracked/classic_classes_test.js index 36c42a55999..90286c52401 100644 --- a/packages/@ember/-internals/metal/tests/tracked/classic_classes_test.js +++ b/packages/@ember/-internals/metal/tests/tracked/classic_classes_test.js @@ -1,7 +1,7 @@ import { AbstractTestCase, moduleFor } from 'internal-test-helpers'; -import { defineProperty, tracked, track, nativeDescDecorator } from '../..'; +import { defineProperty, tracked, nativeDescDecorator } from '../..'; -import { value, validate } from '@glimmer/reference'; +import { track, value, validate } from '@glimmer/validator'; moduleFor( '@tracked decorator - classic classes', diff --git a/packages/@ember/-internals/metal/tests/tracked/validation_test.js b/packages/@ember/-internals/metal/tests/tracked/validation_test.js index 42f21b39a53..a1ecefc94c8 100644 --- a/packages/@ember/-internals/metal/tests/tracked/validation_test.js +++ b/packages/@ember/-internals/metal/tests/tracked/validation_test.js @@ -5,13 +5,12 @@ import { set, tagForProperty, tracked, - track, notifyPropertyChange, } from '../..'; import { EMBER_ARRAY } from '@ember/-internals/utils'; import { AbstractTestCase, moduleFor } from 'internal-test-helpers'; -import { value, validate } from '@glimmer/reference'; +import { track, value, validate } from '@glimmer/validator'; moduleFor( '@tracked get validation', @@ -142,12 +141,9 @@ moduleFor( let obj = new Tracked(tom); let tag = track(() => obj.full); - let snapshot = value(tag); - assert.equal(obj.full, 'Tom Dale'); - assert.equal(validate(tag, snapshot), true); - snapshot = value(tag); + let snapshot = value(tag); assert.equal(validate(tag, snapshot), true); set(tom, 'first', 'Thomas'); @@ -200,13 +196,10 @@ moduleFor( let obj = new EmberObject(tom); let tag = tagForProperty(obj, 'full'); - let snapshot = value(tag); - let full = get(obj, 'full'); assert.equal(full, 'Tom Dale'); - assert.equal(validate(tag, snapshot), true); - snapshot = value(tag); + let snapshot = value(tag); assert.equal(validate(tag, snapshot), true); tom.first = 'Thomas'; @@ -260,13 +253,10 @@ moduleFor( let obj = new EmberObject(contact); let tag = tagForProperty(obj, 'full'); - let snapshot = value(tag); - let full = get(obj, 'full'); assert.equal(full, 'Tom Dale'); - assert.equal(validate(tag, snapshot), true); - snapshot = value(tag); + let snapshot = value(tag); assert.equal(validate(tag, snapshot), true); set(tom, 'first', 'Thomas'); @@ -287,17 +277,40 @@ moduleFor( assert.equal(get(obj, 'full'), 'Tizzle Dale'); } - ['@test interaction with arrays'](assert) { + ['@test ember get interaction with arrays'](assert) { + class EmberObject { + array = []; + } + + let obj = new EmberObject(); + let array; + + let tag = track(() => (array = get(obj, 'array'))); + let snapshot = value(tag); + + assert.deepEqual(array, []); + assert.equal(validate(tag, snapshot), true); + + array.push(1); + notifyPropertyChange(array, '[]'); + assert.equal( + validate(tag, snapshot), + false, + 'invalid after pushing an object and notifying on the array' + ); + } + + ['@test native get interaction with arrays'](assert) { class EmberObject { @tracked array = []; } let obj = new EmberObject(); + let array; - let tag = tagForProperty(obj, 'array'); + let tag = track(() => (array = obj.array)); let snapshot = value(tag); - let array = get(obj, 'array'); assert.deepEqual(array, []); assert.equal(validate(tag, snapshot), true); @@ -310,7 +323,30 @@ moduleFor( ); } - ['@test interaction with ember arrays'](assert) { + ['@test ember get interaction with ember arrays'](assert) { + class EmberObject { + emberArray = { + [EMBER_ARRAY]: true, + }; + } + + let obj = new EmberObject(); + let emberArray; + + let tag = track(() => (emberArray = get(obj, 'emberArray'))); + let snapshot = value(tag); + + assert.equal(validate(tag, snapshot), true); + + notifyPropertyChange(emberArray, '[]'); + assert.equal( + validate(tag, snapshot), + false, + 'invalid after setting a property on the object' + ); + } + + ['@test native get interaction with ember arrays'](assert) { class EmberObject { @tracked emberArray = { [EMBER_ARRAY]: true, @@ -318,11 +354,11 @@ moduleFor( } let obj = new EmberObject(); + let emberArray; - let tag = tagForProperty(obj, 'emberArray'); + let tag = track(() => (emberArray = obj.emberArray)); let snapshot = value(tag); - let emberArray = get(obj, 'emberArray'); assert.equal(validate(tag, snapshot), true); notifyPropertyChange(emberArray, '[]'); diff --git a/packages/@ember/-internals/owner/index.ts b/packages/@ember/-internals/owner/index.ts index 2fe90909b60..2fa76c88c4f 100644 --- a/packages/@ember/-internals/owner/index.ts +++ b/packages/@ember/-internals/owner/index.ts @@ -38,8 +38,9 @@ export interface Owner { } import { symbol } from '@ember/-internals/utils'; +import { assert } from '@ember/debug'; -export const OWNER = symbol('OWNER'); +export const OWNER: unique symbol = symbol('OWNER') as any; /** Framework objects in an Ember application (components, services, routes, etc.) @@ -104,3 +105,11 @@ export function getOwner(object: any): Owner { export function setOwner(object: any, owner: Owner): void { object[OWNER] = owner; } + +export const OWNER_MAP = new Map(); + +export function getOwnerById(ownerId: string): Owner { + assert('Attempted to lookup an owner that no longer exists', OWNER_MAP.has(ownerId)); + + return OWNER_MAP.get(ownerId)!; +} diff --git a/packages/@ember/-internals/routing/lib/location/hash_location.ts b/packages/@ember/-internals/routing/lib/location/hash_location.ts index 611bf949e35..d16a5823185 100644 --- a/packages/@ember/-internals/routing/lib/location/hash_location.ts +++ b/packages/@ember/-internals/routing/lib/location/hash_location.ts @@ -38,6 +38,7 @@ import { getHash } from './util'; */ export default class HashLocation extends EmberObject implements EmberLocation { implementation = 'hash'; + _hashchangeHandler?: EventListener; init() { set(this, 'location', this._location || window.location); @@ -135,7 +136,7 @@ export default class HashLocation extends EmberObject implements EmberLocation { callback(path); }); - window.addEventListener('hashchange', this._hashchangeHandler); + window.addEventListener('hashchange', this._hashchangeHandler!); } /** diff --git a/packages/@ember/-internals/routing/lib/location/history_location.ts b/packages/@ember/-internals/routing/lib/location/history_location.ts index 6c12668320a..9fd5206766d 100644 --- a/packages/@ember/-internals/routing/lib/location/history_location.ts +++ b/packages/@ember/-internals/routing/lib/location/history_location.ts @@ -50,6 +50,8 @@ function _uuid() { */ export default class HistoryLocation extends EmberObject implements EmberLocation { implementation = 'history'; + _previousURL?: string; + _popstateHandler?: EventListener; /** Will be pre-pended to path upon state change diff --git a/packages/@ember/-internals/routing/lib/system/route.ts b/packages/@ember/-internals/routing/lib/system/route.ts index 8e85edca302..65bf0c751ca 100644 --- a/packages/@ember/-internals/routing/lib/system/route.ts +++ b/packages/@ember/-internals/routing/lib/system/route.ts @@ -90,8 +90,14 @@ export function hasDefaultSerialize(route: Route) { class Route extends EmberObject implements IRoute { routeName!: string; - _internalName!: string; + fullRouteName!: string; context: {} = {}; + controller!: Controller; + currentModel: unknown; + + _internalName!: string; + _names: unknown; + serialize!: (model: {}, params: string[]) => object | undefined; _router!: EmberRouter; @@ -133,7 +139,7 @@ class Route extends EmberObject implements IRoute { */ _setRouteName(name: string) { this.routeName = name; - this.fullRouteName = getEngineRouteName(getOwner(this), name); + this.fullRouteName = getEngineRouteName(getOwner(this), name)!; } /** @@ -250,7 +256,7 @@ class Route extends EmberObject implements IRoute { let state = transition ? transition[STATE_SYMBOL] : this._router._routerMicrolib.state; let fullName = route.fullRouteName; - let params = assign({}, state!.params[fullName]); + let params = assign({}, state!.params[fullName!]); let queryParams = getQueryParamsFor(route, state!); return Object.keys(queryParams).reduce((params, key) => { @@ -361,7 +367,7 @@ class Route extends EmberObject implements IRoute { */ _internalReset(isExiting: boolean, transition: Transition) { let controller = this.controller; - controller._qpDelegate = get(this, '_qp.states.inactive'); + controller['_qpDelegate'] = get(this, '_qp.states.inactive'); this.resetController(controller, isExiting, transition); } @@ -2598,7 +2604,7 @@ Route.reopen(ActionHandler, Evented, { qpMeta.qps.forEach((qp: QueryParam) => { let routeQpMeta = get(qp.route, '_qp'); let finalizedController = qp.route.controller; - finalizedController._qpDelegate = get(routeQpMeta, 'states.active'); + finalizedController['_qpDelegate'] = get(routeQpMeta, 'states.active'); }); router._qpUpdates.clear(); diff --git a/packages/@ember/-internals/routing/lib/system/router.ts b/packages/@ember/-internals/routing/lib/system/router.ts index 7e7006ea9b3..9a0ae892dd1 100644 --- a/packages/@ember/-internals/routing/lib/system/router.ts +++ b/packages/@ember/-internals/routing/lib/system/router.ts @@ -1,6 +1,8 @@ +import { OutletState as GlimmerOutletState, OutletView } from '@ember/-internals/glimmer'; import { computed, get, notifyPropertyChange, set } from '@ember/-internals/metal'; import { getOwner, Owner } from '@ember/-internals/owner'; import { A as emberA, Evented, Object as EmberObject, typeOf } from '@ember/-internals/runtime'; +import Controller from '@ember/controller'; import { assert, deprecate, info } from '@ember/debug'; import { APP_CTRL_ROUTER_PROPS, ROUTER_EVENTS } from '@ember/deprecated-features'; import EmberError from '@ember/error'; @@ -132,11 +134,15 @@ class EmberRouter extends EmberObject { _qpCache = Object.create(null); _qpUpdates = new Set(); + _queuedQPChanges: { [key: string]: unknown } = {}; + _toplevelView: OutletView | null = null; _handledErrors = new Set(); _engineInstances: { [name: string]: { [id: string]: EngineInstance } } = Object.create(null); _engineInfoByRoute = Object.create(null); + _slowTransitionTimer: unknown; + constructor() { super(...arguments); @@ -455,7 +461,7 @@ class EmberRouter extends EmberObject { } } if (connections.length === 0) { - ownState = representEmptyRoute(liveRoutes!, defaultParentState as OutletState, route!); + ownState = representEmptyRoute(liveRoutes!, defaultParentState! as OutletState, route!); } defaultParentState = ownState!; } @@ -472,12 +478,12 @@ class EmberRouter extends EmberObject { if (!this._toplevelView) { let owner = getOwner(this); let OutletView = owner.factoryFor('view:-outlet')!; - this._toplevelView = OutletView.create(); - this._toplevelView.setOutletState(liveRoutes); + this._toplevelView = OutletView.create() as OutletView; + this._toplevelView.setOutletState(liveRoutes as GlimmerOutletState); let instance: any = owner.lookup('-application-instance:main'); instance.didCreateRootView(this._toplevelView); } else { - this._toplevelView.setOutletState(liveRoutes); + this._toplevelView.setOutletState(liveRoutes as GlimmerOutletState); } } @@ -1463,7 +1469,7 @@ function updatePaths(router: EmberRouter) { set(router, 'currentRouteName', currentRouteName); set(router, 'currentURL', currentURL); - let appController = getOwner(router).lookup('controller:application'); + let appController = getOwner(router).lookup('controller:application'); if (!appController) { // appController might not exist when top-level loading/error diff --git a/packages/@ember/-internals/runtime/lib/mixins/-proxy.js b/packages/@ember/-internals/runtime/lib/mixins/-proxy.js index b54323468cb..57c66f7c60b 100644 --- a/packages/@ember/-internals/runtime/lib/mixins/-proxy.js +++ b/packages/@ember/-internals/runtime/lib/mixins/-proxy.js @@ -8,21 +8,18 @@ import { set, defineProperty, Mixin, - tagFor, + tagForObject, computed, UNKNOWN_PROPERTY_TAG, getChainTagsForKey, } from '@ember/-internals/metal'; import { setProxy } from '@ember/-internals/utils'; import { assert } from '@ember/debug'; -import { combine, update } from '@glimmer/reference'; +import { combine, update } from '@glimmer/validator'; -export function contentFor(proxy, m) { +export function contentFor(proxy) { let content = get(proxy, 'content'); - let tag = (m === undefined ? meta(proxy) : m).readableTag(); - if (tag !== undefined) { - update(tag, tagFor(content)); - } + update(tagForObject(proxy), tagForObject(content)); return content; } @@ -48,8 +45,7 @@ export default Mixin.create({ init() { this._super(...arguments); setProxy(this); - let m = meta(this); - m.writableTag(); + tagForObject(this); }, willDestroy() { @@ -82,7 +78,7 @@ export default Mixin.create({ return value; } - let content = contentFor(this, m); + let content = contentFor(this); assert( `Cannot delegate set('${key}', ${value}) to the \'content\' property of object proxy ${this}: its 'content' is undefined.`, diff --git a/packages/@ember/-internals/runtime/lib/mixins/container_proxy.js b/packages/@ember/-internals/runtime/lib/mixins/container_proxy.js index 0c29413c805..ba0f2221e21 100644 --- a/packages/@ember/-internals/runtime/lib/mixins/container_proxy.js +++ b/packages/@ember/-internals/runtime/lib/mixins/container_proxy.js @@ -3,6 +3,8 @@ import { schedule, join } from '@ember/runloop'; @module ember */ import { Mixin } from '@ember/-internals/metal'; +import { guidFor } from '@ember/-internals/utils'; +import { OWNER_MAP } from '@ember/-internals/owner'; /** ContainerProxyMixin is used to provide public access to specific @@ -12,6 +14,18 @@ import { Mixin } from '@ember/-internals/metal'; @private */ let containerProxyMixin = { + init() { + this._super(...arguments); + + OWNER_MAP.set(guidFor(this), this); + }, + + willDestroy() { + this._super(); + + OWNER_MAP.delete(guidFor(this)); + }, + /** The container stores state. diff --git a/packages/@ember/-internals/runtime/lib/system/array_proxy.js b/packages/@ember/-internals/runtime/lib/system/array_proxy.js index a6514c4f78b..5d3b25e8088 100644 --- a/packages/@ember/-internals/runtime/lib/system/array_proxy.js +++ b/packages/@ember/-internals/runtime/lib/system/array_proxy.js @@ -15,7 +15,7 @@ import { import EmberObject from './object'; import { isArray, MutableArray } from '../mixins/array'; import { assert } from '@ember/debug'; -import { combine, validate, value } from '@glimmer/reference'; +import { combine, validate, value } from '@glimmer/validator'; const ARRAY_OBSERVER_MAPPING = { willChange: '_arrangedContentArrayWillChange', diff --git a/packages/@ember/-internals/runtime/tests/system/object/framework_test.js b/packages/@ember/-internals/runtime/tests/system/object/framework_test.js index 99ca20839ac..188a2145109 100644 --- a/packages/@ember/-internals/runtime/tests/system/object/framework_test.js +++ b/packages/@ember/-internals/runtime/tests/system/object/framework_test.js @@ -1,11 +1,11 @@ import { getOwner } from '@ember/-internals/owner'; import { FrameworkObject } from '../../../index'; -import { moduleFor, AbstractRenderingTestCase } from 'internal-test-helpers'; +import { moduleFor, RenderingTestCase } from 'internal-test-helpers'; import { setFrameworkClass } from '../../../lib/system/core_object'; moduleFor( 'FrameworkObject', - class extends AbstractRenderingTestCase { + class extends RenderingTestCase { ['@test tunnels the owner through to the base constructor for framework classes'](assert) { assert.expect(2); diff --git a/packages/@ember/-internals/utils/index.ts b/packages/@ember/-internals/utils/index.ts index feb4c667397..95427e74017 100644 --- a/packages/@ember/-internals/utils/index.ts +++ b/packages/@ember/-internals/utils/index.ts @@ -29,11 +29,12 @@ export { canInvoke, tryInvoke } from './lib/invoke'; export { default as makeArray } from './lib/make-array'; export { getName, setName } from './lib/name'; export { default as toString } from './lib/to-string'; +export { isObject } from './lib/spec'; export { HAS_NATIVE_SYMBOL } from './lib/symbol-utils'; export { HAS_NATIVE_PROXY } from './lib/proxy-utils'; export { isProxy, setProxy } from './lib/is_proxy'; export { default as Cache } from './lib/cache'; -export { EMBER_ARRAY, isEmberArray } from './lib/ember-array'; +export { EMBER_ARRAY, EmberArray, isEmberArray } from './lib/ember-array'; export { setupMandatorySetter, teardownMandatorySetter, diff --git a/packages/@ember/-internals/utils/lib/ember-array.ts b/packages/@ember/-internals/utils/lib/ember-array.ts index 4efbff614e4..df291262fb4 100644 --- a/packages/@ember/-internals/utils/lib/ember-array.ts +++ b/packages/@ember/-internals/utils/lib/ember-array.ts @@ -2,6 +2,14 @@ import symbol from './symbol'; export const EMBER_ARRAY = symbol('EMBER_ARRAY'); -export function isEmberArray(obj: any) { +export interface EmberArray { + length: number; + hasArrayObservers?: boolean; + objectAt(index: number): T | undefined; + replace(start: number, deleteCount: number, items: T[]): void; + splice(start: number, deleteCount: number, ...items: T[]): void; +} + +export function isEmberArray(obj: any): obj is EmberArray { return obj && obj[EMBER_ARRAY]; } diff --git a/packages/@ember/-internals/utils/lib/get-debug-name.ts b/packages/@ember/-internals/utils/lib/get-debug-name.ts index f4c65e40d68..620055cf360 100644 --- a/packages/@ember/-internals/utils/lib/get-debug-name.ts +++ b/packages/@ember/-internals/utils/lib/get-debug-name.ts @@ -12,7 +12,7 @@ if (DEBUG) { functionName = (match && match[1]) || ''; } - return functionName; + return functionName.replace(/^bound /, ''); }; let getObjectName = (obj: object) => { diff --git a/packages/@ember/-internals/views/index.d.ts b/packages/@ember/-internals/views/index.d.ts index 143017ca5ae..28f6944ae4f 100644 --- a/packages/@ember/-internals/views/index.d.ts +++ b/packages/@ember/-internals/views/index.d.ts @@ -1,7 +1,7 @@ -import { Simple, Template, Option } from '@glimmer/interfaces'; -import { Opaque } from '@glimmer/util'; +import { Template, Option } from '@glimmer/interfaces'; import { Factory, Owner } from '@ember/-internals/owner'; import { TemplateFactory } from '@ember/-internals/glimmer'; +import { SimpleElement } from '@simple-dom/interface'; export interface StaticTemplateMeta { moduleName: string; @@ -9,7 +9,7 @@ export interface StaticTemplateMeta { } export interface OwnedTemplateMeta extends StaticTemplateMeta { - owner: Owner; + ownerId: string; moduleName: string; managerId?: string; } @@ -22,14 +22,14 @@ export const ViewMixin: any; export const ViewStateSupport: any; export const TextSupport: any; -export function getElementView(element: Simple.Element): Opaque; -export function getViewElement(view: Opaque): Option; -export function setElementView(element: Simple.Element, view: Opaque): void; -export function setViewElement(view: Opaque, element: Simple.Element): void; -export function clearElementView(element: Simple.Element): void; -export function clearViewElement(view: Opaque): void; +export function getElementView(element: SimpleElement): unknown; +export function getViewElement(view: unknown): Option; +export function setElementView(element: SimpleElement, view: unknown): void; +export function setViewElement(view: unknown, element: SimpleElement): void; +export function clearElementView(element: SimpleElement): void; +export function clearViewElement(view: unknown): void; -export function addChildView(parent: Opaque, child: Opaque): void; +export function addChildView(parent: unknown, child: unknown): void; export function isSimpleClick(event: Event): boolean; diff --git a/packages/@ember/-internals/views/lib/views/states/in_dom.js b/packages/@ember/-internals/views/lib/views/states/in_dom.js index 244449bc970..35b5ebdb599 100644 --- a/packages/@ember/-internals/views/lib/views/states/in_dom.js +++ b/packages/@ember/-internals/views/lib/views/states/in_dom.js @@ -28,10 +28,6 @@ const inDOM = assign({}, hasElement, { }); } }, - - exit(view) { - view.renderer.unregister(view); - }, }); export default Object.freeze(inDOM); diff --git a/packages/@ember/application/tests/application_instance_test.js b/packages/@ember/application/tests/application_instance_test.js index 128b13217e4..f2c5682d6bc 100644 --- a/packages/@ember/application/tests/application_instance_test.js +++ b/packages/@ember/application/tests/application_instance_test.js @@ -175,7 +175,7 @@ moduleFor( } ['@test can build and boot a registered engine'](assert) { - assert.expect(11); + assert.expect(10); let ChatEngine = Engine.extend({ Resolver: ModuleBasedTestResolver, @@ -193,7 +193,7 @@ moduleFor( return chatEngineInstance.boot().then(() => { assert.ok(true, 'boot successful'); - let registrations = ['route:basic', 'service:-routing', 'service:-glimmer-environment']; + let registrations = ['route:basic', 'service:-routing']; registrations.forEach(key => { assert.strictEqual( diff --git a/packages/@ember/application/tests/application_test.js b/packages/@ember/application/tests/application_test.js index cdf8fdfea64..321febb4909 100644 --- a/packages/@ember/application/tests/application_test.js +++ b/packages/@ember/application/tests/application_test.js @@ -178,24 +178,6 @@ moduleFor( verifyRegistration(assert, application, 'container-debug-adapter:main'); verifyRegistration(assert, application, 'component-lookup:main'); - verifyRegistration(assert, application, 'service:-glimmer-environment'); - verifyRegistration(assert, application, 'service:-dom-changes'); - verifyRegistration(assert, application, 'service:-dom-tree-construction'); - verifyInjection( - assert, - application, - 'service:-glimmer-environment', - 'appendOperations', - 'service:-dom-tree-construction' - ); - verifyInjection( - assert, - application, - 'service:-glimmer-environment', - 'updateOperations', - 'service:-dom-changes' - ); - verifyInjection(assert, application, 'renderer', 'env', 'service:-glimmer-environment'); verifyRegistration(assert, application, 'view:-outlet'); verifyRegistration(assert, application, 'renderer:-dom'); verifyRegistration(assert, application, 'renderer:-inert'); diff --git a/packages/@ember/debug/lib/capture-render-tree.ts b/packages/@ember/debug/lib/capture-render-tree.ts index e423e3a1199..5e02ac0c469 100644 --- a/packages/@ember/debug/lib/capture-render-tree.ts +++ b/packages/@ember/debug/lib/capture-render-tree.ts @@ -1,4 +1,4 @@ -import { CapturedRenderNode, Environment } from '@ember/-internals/glimmer'; +import { CapturedRenderNode, Renderer } from '@ember/-internals/glimmer'; import { Owner } from '@ember/-internals/owner'; import { expect } from '@glimmer/util'; @@ -20,9 +20,16 @@ import { expect } from '@glimmer/util'; */ export default function captureRenderTree(app: Owner): CapturedRenderNode[] { let env = expect( - app.lookup('service:-glimmer-environment'), - 'BUG: owner is missing service:-glimmer-environment' + app.lookup<{ isInteractive: boolean }>('-environment:main'), + 'BUG: owner is missing -environment:main' ); - return env.debugRenderTree.capture(); + let rendererType = env.isInteractive ? 'renderer:-dom' : 'renderer:-inert'; + + let renderer = expect( + app.lookup(rendererType), + `BUG: owner is missing ${rendererType}` + ); + + return renderer.debugRenderTree.capture(); } diff --git a/packages/@ember/engine/instance.js b/packages/@ember/engine/instance.js index e3dcb24f0d7..da9a4f658a0 100644 --- a/packages/@ember/engine/instance.js +++ b/packages/@ember/engine/instance.js @@ -2,7 +2,6 @@ @module @ember/engine */ -import { guidFor } from '@ember/-internals/utils'; import { Object as EmberObject, ContainerProxyMixin, @@ -37,8 +36,6 @@ const EngineInstance = EmberObject.extend(RegistryProxyMixin, ContainerProxyMixi init() { this._super(...arguments); - guidFor(this); - let base = this.base; if (!base) { @@ -172,7 +169,7 @@ const EngineInstance = EmberObject.extend(RegistryProxyMixin, ContainerProxyMixi cloneParentDependencies() { let parent = getEngineParent(this); - let registrations = ['route:basic', 'service:-routing', 'service:-glimmer-environment']; + let registrations = ['route:basic', 'service:-routing']; registrations.forEach(key => this.register(key, parent.resolveRegistration(key))); @@ -185,7 +182,6 @@ const EngineInstance = EmberObject.extend(RegistryProxyMixin, ContainerProxyMixi '-view-registry:main', `renderer:-${env.isInteractive ? 'dom' : 'inert'}`, 'service:-document', - P`template-compiler:main`, ]; if (env.isInteractive) { diff --git a/packages/@ember/engine/tests/engine_test.js b/packages/@ember/engine/tests/engine_test.js index 3ee5a4f2466..e3fb1e7cc04 100644 --- a/packages/@ember/engine/tests/engine_test.js +++ b/packages/@ember/engine/tests/engine_test.js @@ -103,14 +103,6 @@ moduleFor( verifyRegistration(assert, engine, 'container-debug-adapter:main'); verifyRegistration(assert, engine, 'component-lookup:main'); - verifyInjection(assert, engine, 'service:-dom-changes', 'document', 'service:-document'); - verifyInjection( - assert, - engine, - 'service:-dom-tree-construction', - 'document', - 'service:-document' - ); verifyRegistration(assert, engine, 'view:-outlet'); verifyRegistration(assert, engine, P`template:components/-default`); verifyRegistration(assert, engine, 'template:-outlet'); diff --git a/packages/@ember/object/compat.ts b/packages/@ember/object/compat.ts index 6a44a30663e..68399170b10 100644 --- a/packages/@ember/object/compat.ts +++ b/packages/@ember/object/compat.ts @@ -1,15 +1,13 @@ import { Meta } from '@ember/-internals/meta'; import { - consume, Decorator, DecoratorPropertyDescriptor, isElementDescriptor, setClassicDecorator, tagForProperty, - track, } from '@ember/-internals/metal'; import { assert } from '@ember/debug'; -import { UpdatableTag, update } from '@glimmer/reference'; +import { consume, track, UpdatableTag, update } from '@glimmer/validator'; let wrapGetterSetter = function(_target: object, key: string, desc: PropertyDescriptor) { let { get: originalGet } = desc; diff --git a/packages/ember-template-compiler/lib/plugins/assert-if-helper-without-arguments.ts b/packages/ember-template-compiler/lib/plugins/assert-if-helper-without-arguments.ts index 312048f85a7..a988b7eb151 100644 --- a/packages/ember-template-compiler/lib/plugins/assert-if-helper-without-arguments.ts +++ b/packages/ember-template-compiler/lib/plugins/assert-if-helper-without-arguments.ts @@ -1,16 +1,18 @@ +import { StaticTemplateMeta } from '@ember/-internals/views'; import { assert } from '@ember/debug'; import { AST, ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax'; import calculateLocationDisplay from '../system/calculate-location-display'; +import { isPath } from './utils'; export default function assertIfHelperWithoutArguments(env: ASTPluginEnvironment): ASTPlugin { - let { moduleName } = env.meta; + let { moduleName } = env.meta as StaticTemplateMeta; return { name: 'assert-if-helper-without-arguments', visitor: { BlockStatement(node: AST.BlockStatement) { - if (isInvalidBlockIf(node)) { + if (isPath(node.path) && isInvalidBlockIf(node.path, node.params)) { assert( `${blockAssertMessage(node.path.original)} ${calculateLocationDisplay( moduleName, @@ -21,7 +23,7 @@ export default function assertIfHelperWithoutArguments(env: ASTPluginEnvironment }, MustacheStatement(node: AST.MustacheStatement) { - if (isInvalidInlineIf(node)) { + if (isPath(node.path) && isInvalidInlineIf(node.path, node.params)) { assert( `${inlineAssertMessage(node.path.original as string)} ${calculateLocationDisplay( moduleName, @@ -32,7 +34,7 @@ export default function assertIfHelperWithoutArguments(env: ASTPluginEnvironment }, SubExpression(node: AST.SubExpression) { - if (isInvalidInlineIf(node)) { + if (isPath(node.path) && isInvalidInlineIf(node.path, node.params)) { assert( `${inlineAssertMessage(node.path.original)} ${calculateLocationDisplay( moduleName, @@ -53,13 +55,12 @@ function inlineAssertMessage(original: string) { return `The inline form of the '${original}' helper expects two or three arguments.`; } -function isInvalidInlineIf(node: AST.BlockStatement | AST.MustacheStatement | AST.SubExpression) { +function isInvalidInlineIf(path: AST.PathExpression, params: AST.Expression[]) { return ( - node.path.original === 'if' && - (!node.params || node.params.length < 2 || node.params.length > 3) + isPath(path) && path.original === 'if' && (!params || params.length < 2 || params.length > 3) ); } -function isInvalidBlockIf(node: AST.BlockStatement | AST.MustacheStatement | AST.SubExpression) { - return node.path.original === 'if' && (!node.params || node.params.length !== 1); +function isInvalidBlockIf(path: AST.PathExpression, params: AST.Expression[]) { + return isPath(path) && path.original === 'if' && (!params || params.length !== 1); } diff --git a/packages/ember-template-compiler/lib/plugins/assert-input-helper-without-block.ts b/packages/ember-template-compiler/lib/plugins/assert-input-helper-without-block.ts index 4345d411d57..ef913ff209c 100644 --- a/packages/ember-template-compiler/lib/plugins/assert-input-helper-without-block.ts +++ b/packages/ember-template-compiler/lib/plugins/assert-input-helper-without-block.ts @@ -1,20 +1,20 @@ +import { StaticTemplateMeta } from '@ember/-internals/views'; import { assert } from '@ember/debug'; import { AST, ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax'; import calculateLocationDisplay from '../system/calculate-location-display'; +import { isPath } from './utils'; export default function errorOnInputWithContent(env: ASTPluginEnvironment): ASTPlugin { - let { moduleName } = env.meta; + let { moduleName } = env.meta as StaticTemplateMeta; return { name: 'assert-input-helper-without-block', visitor: { BlockStatement(node: AST.BlockStatement) { - if (node.path.original !== 'input') { - return; + if (isPath(node.path) && node.path.original === 'input') { + assert(assertMessage(moduleName, node)); } - - assert(assertMessage(moduleName, node)); }, }, }; diff --git a/packages/ember-template-compiler/lib/plugins/assert-local-variable-shadowing-helper-invocation.ts b/packages/ember-template-compiler/lib/plugins/assert-local-variable-shadowing-helper-invocation.ts index 80d75d924d1..00a475a8466 100644 --- a/packages/ember-template-compiler/lib/plugins/assert-local-variable-shadowing-helper-invocation.ts +++ b/packages/ember-template-compiler/lib/plugins/assert-local-variable-shadowing-helper-invocation.ts @@ -1,38 +1,24 @@ +import { StaticTemplateMeta } from '@ember/-internals/views'; import { assert } from '@ember/debug'; import { AST, ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax'; import calculateLocationDisplay from '../system/calculate-location-display'; +import { isPath, trackLocals } from './utils'; export default function assertLocalVariableShadowingHelperInvocation( env: ASTPluginEnvironment ): ASTPlugin { - let { moduleName } = env.meta; - let locals: string[][] = []; + let { moduleName } = env.meta as StaticTemplateMeta; + let { hasLocal, node } = trackLocals(); return { name: 'assert-local-variable-shadowing-helper-invocation', visitor: { - Program: { - enter(node: AST.Program) { - locals.push(node.blockParams); - }, - - exit() { - locals.pop(); - }, - }, + Program: node, ElementNode: { keys: { - children: { - enter(node: AST.ElementNode) { - locals.push(node.blockParams); - }, - - exit() { - locals.pop(); - }, - }, + children: node, }, }, @@ -43,50 +29,46 @@ export default function assertLocalVariableShadowingHelperInvocation( assert( `${messageFor(name, type)} ${calculateLocationDisplay(moduleName, node.loc)}`, - !isLocalVariable(node.path, locals) + !isLocalVariable(node.path, hasLocal) ); } }, SubExpression(node: AST.SubExpression) { - let name = node.path.parts[0]; - let type = 'helper'; + if (isPath(node.path)) { + let name = node.path.parts[0]; + let type = 'helper'; - assert( - `${messageFor(name, type)} ${calculateLocationDisplay(moduleName, node.loc)}`, - !isLocalVariable(node.path, locals) - ); + assert( + `${messageFor(name, type)} ${calculateLocationDisplay(moduleName, node.loc)}`, + !isLocalVariable(node.path, hasLocal) + ); + } }, ElementModifierStatement(node: AST.ElementModifierStatement) { - let name = node.path.parts[0]; - let type = 'modifier'; + if (isPath(node.path)) { + let name = node.path.parts[0]; + let type = 'modifier'; - assert( - `${messageFor(name, type)} ${calculateLocationDisplay(moduleName, node.loc)}`, - !isLocalVariable(node.path, locals) - ); + assert( + `${messageFor(name, type)} ${calculateLocationDisplay(moduleName, node.loc)}`, + !isLocalVariable(node.path, hasLocal) + ); + } }, }, }; } -function isLocalVariable(node: AST.PathExpression, locals: string[][]): boolean { - return !node.this && node.parts.length === 1 && hasLocalVariable(node.parts[0], locals); -} - -function hasLocalVariable(name: string, locals: string[][]): boolean { - return locals.some(names => names.indexOf(name) !== -1); +function isLocalVariable(node: AST.PathExpression, hasLocal: (k: string) => boolean): boolean { + return !node.this && node.parts.length === 1 && hasLocal(node.parts[0]); } function messageFor(name: string, type: string): string { return `Cannot invoke the \`${name}\` ${type} because it was shadowed by a local variable (i.e. a block param) with the same name. Please rename the local variable to resolve the conflict.`; } -function isPath(node: AST.Node): node is AST.PathExpression { - return node.type === 'PathExpression'; -} - function hasArguments(node: AST.MustacheStatement): boolean { return node.params.length > 0 || node.hash.pairs.length > 0; } diff --git a/packages/ember-template-compiler/lib/plugins/assert-reserved-named-arguments.ts b/packages/ember-template-compiler/lib/plugins/assert-reserved-named-arguments.ts index 98100b0bb91..b84a045e74e 100644 --- a/packages/ember-template-compiler/lib/plugins/assert-reserved-named-arguments.ts +++ b/packages/ember-template-compiler/lib/plugins/assert-reserved-named-arguments.ts @@ -1,9 +1,10 @@ +import { StaticTemplateMeta } from '@ember/-internals/views'; import { assert } from '@ember/debug'; import { AST, ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax'; import calculateLocationDisplay from '../system/calculate-location-display'; export default function assertReservedNamedArguments(env: ASTPluginEnvironment): ASTPlugin { - let { moduleName } = env.meta; + let { moduleName } = env.meta as StaticTemplateMeta; return { name: 'assert-reserved-named-arguments', diff --git a/packages/ember-template-compiler/lib/plugins/assert-splattribute-expression.ts b/packages/ember-template-compiler/lib/plugins/assert-splattribute-expression.ts index 384b93d158a..bb5a443318d 100644 --- a/packages/ember-template-compiler/lib/plugins/assert-splattribute-expression.ts +++ b/packages/ember-template-compiler/lib/plugins/assert-splattribute-expression.ts @@ -1,9 +1,10 @@ +import { StaticTemplateMeta } from '@ember/-internals/views'; import { assert } from '@ember/debug'; import { ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax'; import calculateLocationDisplay from '../system/calculate-location-display'; export default function assertSplattributeExpressions(env: ASTPluginEnvironment): ASTPlugin { - let { moduleName } = env.meta; + let { moduleName } = env.meta as StaticTemplateMeta; return { name: 'assert-splattribute-expressions', diff --git a/packages/ember-template-compiler/lib/plugins/deprecate-send-action.ts b/packages/ember-template-compiler/lib/plugins/deprecate-send-action.ts index f12242ab461..644497a0e7c 100644 --- a/packages/ember-template-compiler/lib/plugins/deprecate-send-action.ts +++ b/packages/ember-template-compiler/lib/plugins/deprecate-send-action.ts @@ -1,7 +1,9 @@ +import { StaticTemplateMeta } from '@ember/-internals/views'; import { deprecate } from '@ember/debug'; import { SEND_ACTION } from '@ember/deprecated-features'; import { AST, ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax'; import calculateLocationDisplay from '../system/calculate-location-display'; +import { isPath } from './utils'; const EVENTS = [ 'insert-newline', @@ -16,7 +18,7 @@ const EVENTS = [ export default function deprecateSendAction(env: ASTPluginEnvironment): ASTPlugin | undefined { if (SEND_ACTION) { - let { moduleName } = env.meta; + let { moduleName } = env.meta as StaticTemplateMeta; let deprecationMessage = (node: AST.Node, eventName: string, actionName: string) => { let sourceInformation = calculateLocationDisplay(moduleName, node.loc); @@ -64,7 +66,7 @@ export default function deprecateSendAction(env: ASTPluginEnvironment): ASTPlugi }, MustacheStatement(node: AST.MustacheStatement) { - if (node.path.original !== 'input') { + if (!isPath(node.path) || node.path.original !== 'input') { return; } diff --git a/packages/ember-template-compiler/lib/plugins/index.ts b/packages/ember-template-compiler/lib/plugins/index.ts index b445b61d2de..09ead25c502 100644 --- a/packages/ember-template-compiler/lib/plugins/index.ts +++ b/packages/ember-template-compiler/lib/plugins/index.ts @@ -8,17 +8,20 @@ import TransformActionSyntax from './transform-action-syntax'; import TransformAttrsIntoArgs from './transform-attrs-into-args'; import TransformComponentInvocation from './transform-component-invocation'; import TransformEachInIntoEach from './transform-each-in-into-each'; +import TransformEachTrackArray from './transform-each-track-array'; import TransformHasBlockSyntax from './transform-has-block-syntax'; import TransformInElement from './transform-in-element'; import TransformLinkTo from './transform-link-to'; import TransformOldClassBindingSyntax from './transform-old-class-binding-syntax'; import TransformQuotedBindingsIntoJustBindings from './transform-quoted-bindings-into-just-bindings'; +import TransformWrapMountAndOutlet from './transform-wrap-mount-and-outlet'; import { SEND_ACTION } from '@ember/deprecated-features'; import { ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax'; export type APluginFunc = (env: ASTPluginEnvironment) => ASTPlugin | undefined; +// order of plugins is important const transforms: Array = [ TransformComponentInvocation, TransformOldClassBindingSyntax, @@ -34,6 +37,8 @@ const transforms: Array = [ TransformInElement, AssertIfHelperWithoutArguments, AssertSplattributeExpressions, + TransformEachTrackArray, + TransformWrapMountAndOutlet, ]; if (SEND_ACTION) { diff --git a/packages/ember-template-compiler/lib/plugins/transform-action-syntax.ts b/packages/ember-template-compiler/lib/plugins/transform-action-syntax.ts index 712adb2b87b..c74f6f64186 100644 --- a/packages/ember-template-compiler/lib/plugins/transform-action-syntax.ts +++ b/packages/ember-template-compiler/lib/plugins/transform-action-syntax.ts @@ -1,5 +1,6 @@ import { AST, ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax'; import { Builders } from '../types'; +import { isPath } from './utils'; /** @module ember @@ -55,7 +56,7 @@ export default function transformActionSyntax({ syntax }: ASTPluginEnvironment): } function isAction(node: AST.ElementModifierStatement | AST.MustacheStatement | AST.SubExpression) { - return node.path.original === 'action'; + return isPath(node.path) && node.path.original === 'action'; } function insertThisAsFirstParam( diff --git a/packages/ember-template-compiler/lib/plugins/transform-attrs-into-args.ts b/packages/ember-template-compiler/lib/plugins/transform-attrs-into-args.ts index cf191fac575..31286ab7420 100644 --- a/packages/ember-template-compiler/lib/plugins/transform-attrs-into-args.ts +++ b/packages/ember-template-compiler/lib/plugins/transform-attrs-into-args.ts @@ -45,7 +45,7 @@ export default function transformAttrsIntoArgs(env: ASTPluginEnvironment): ASTPl PathExpression(node: AST.PathExpression): AST.Node | void { if (isAttrs(node, stack[stack.length - 1])) { - let path = b.path(node.original.substr(6)); + let path = b.path(node.original.substr(6)) as AST.PathExpression; path.original = `@${path.original}`; path.data = true; return path; diff --git a/packages/ember-template-compiler/lib/plugins/transform-component-invocation.ts b/packages/ember-template-compiler/lib/plugins/transform-component-invocation.ts index 134694e564a..0a2c3f9e0a3 100644 --- a/packages/ember-template-compiler/lib/plugins/transform-component-invocation.ts +++ b/packages/ember-template-compiler/lib/plugins/transform-component-invocation.ts @@ -1,6 +1,8 @@ +import { StaticTemplateMeta } from '@ember/-internals/views'; import { AST, ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax'; import calculateLocationDisplay from '../system/calculate-location-display'; import { Builders } from '../types'; +import { isPath, trackLocals } from './utils'; /** Transforms unambigious invocations of closure components to be wrapped with @@ -121,24 +123,18 @@ import { Builders } from '../types'; @class TransFormComponentInvocation */ export default function transformComponentInvocation(env: ASTPluginEnvironment): ASTPlugin { - let { moduleName } = env.meta; + let { moduleName } = env.meta as StaticTemplateMeta; let { builders: b } = env.syntax; - let locals: string[][] = []; + + let { hasLocal, node } = trackLocals(); + let isAttrs = false; return { name: 'transform-component-invocation', visitor: { - Program: { - enter(node: AST.Program) { - locals.push(node.blockParams); - }, - - exit() { - locals.pop(); - }, - }, + Program: node, ElementNode: { keys: { @@ -152,26 +148,18 @@ export default function transformComponentInvocation(env: ASTPluginEnvironment): }, }, - children: { - enter(node: AST.ElementNode) { - locals.push(node.blockParams); - }, - - exit() { - locals.pop(); - }, - }, + children: node, }, }, BlockStatement(node: AST.BlockStatement) { - if (isBlockInvocation(node, locals)) { + if (isBlockInvocation(node, hasLocal)) { wrapInComponent(moduleName, node, b); } }, MustacheStatement(node: AST.MustacheStatement): AST.Node | void { - if (!isAttrs && isInlineInvocation(node, locals)) { + if (!isAttrs && isInlineInvocation(node, hasLocal)) { wrapInComponent(moduleName, node, b); } }, @@ -179,17 +167,16 @@ export default function transformComponentInvocation(env: ASTPluginEnvironment): }; } -function isInlineInvocation(node: AST.MustacheStatement, locals: string[][]): boolean { +function isInlineInvocation( + node: AST.MustacheStatement, + hasLocal: (k: string) => boolean +): boolean { let { path } = node; - return isPath(path) && isIllegalName(path, locals) && hasArguments(node); + return isPath(path) && isIllegalName(path, hasLocal) && hasArguments(node); } -function isPath(node: AST.PathExpression | AST.Literal): node is AST.PathExpression { - return node.type === 'PathExpression'; -} - -function isIllegalName(node: AST.PathExpression, locals: string[][]): boolean { - return isThisPath(node) || isDotPath(node) || isNamedArg(node) || isLocalVariable(node, locals); +function isIllegalName(node: AST.PathExpression, hasLocal: (k: string) => boolean): boolean { + return isThisPath(node) || isDotPath(node) || isNamedArg(node) || isLocalVariable(node, hasLocal); } function isThisPath(node: AST.PathExpression): boolean { @@ -204,20 +191,16 @@ function isNamedArg(node: AST.PathExpression): boolean { return node.data === true; } -function isLocalVariable(node: AST.PathExpression, locals: string[][]): boolean { - return !node.this && hasLocalVariable(node.parts[0], locals); -} - -function hasLocalVariable(name: string, locals: string[][]): boolean { - return locals.some(names => names.indexOf(name) !== -1); +function isLocalVariable(node: AST.PathExpression, hasLocal: (k: string) => boolean): boolean { + return !node.this && hasLocal(node.parts[0]); } function hasArguments(node: AST.MustacheStatement): boolean { return node.params.length > 0 || node.hash.pairs.length > 0; } -function isBlockInvocation(node: AST.BlockStatement, locals: string[][]): boolean { - return isIllegalName(node.path, locals); +function isBlockInvocation(node: AST.BlockStatement, hasLocal: (k: string) => boolean): boolean { + return isPath(node.path) && isIllegalName(node.path, hasLocal); } function wrapInAssertion(moduleName: string, node: AST.PathExpression, b: Builders) { diff --git a/packages/ember-template-compiler/lib/plugins/transform-each-in-into-each.ts b/packages/ember-template-compiler/lib/plugins/transform-each-in-into-each.ts index 1cf1342574f..e661375ddb5 100644 --- a/packages/ember-template-compiler/lib/plugins/transform-each-in-into-each.ts +++ b/packages/ember-template-compiler/lib/plugins/transform-each-in-into-each.ts @@ -1,4 +1,5 @@ import { AST, ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax'; +import { isPath } from './utils'; /** @module ember @@ -8,13 +9,13 @@ import { AST, ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax'; A Glimmer2 AST transformation that replaces all instances of ```handlebars - {{#each-in iterableThing as |key value|}} + {{#each-in iterableThing as |key value|}} ``` with ```handlebars - {{#each (-each-in iterableThing) as |value key|}} + {{#each (-each-in iterableThing) as |value key|}} ``` @private @@ -28,7 +29,7 @@ export default function transformEachInIntoEach(env: ASTPluginEnvironment): ASTP visitor: { BlockStatement(node: AST.BlockStatement): AST.Node | void { - if (node.path.original === 'each-in') { + if (isPath(node.path) && node.path.original === 'each-in') { node.params[0] = b.sexpr(b.path('-each-in'), [node.params[0]]); let blockParams = node.program.blockParams; diff --git a/packages/ember-template-compiler/lib/plugins/transform-each-track-array.ts b/packages/ember-template-compiler/lib/plugins/transform-each-track-array.ts new file mode 100644 index 00000000000..5df9fd54f46 --- /dev/null +++ b/packages/ember-template-compiler/lib/plugins/transform-each-track-array.ts @@ -0,0 +1,57 @@ +import { AST, ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax'; +import { isPath } from './utils'; + +/** + @module ember +*/ + +/** + A Glimmer2 AST transformation that replaces all instances of + + ```handlebars + {{#each iterableThing as |key value|}} + ``` + + with + + ```handlebars + {{#each (-track-array iterableThing) as |key value|}} + ``` + + @private + @class TransformHasBlockSyntax +*/ +export default function transformEachTrackArray(env: ASTPluginEnvironment): ASTPlugin { + let { builders: b } = env.syntax; + + return { + name: 'transform-each-track-array', + + visitor: { + BlockStatement(node: AST.BlockStatement): AST.Node | void { + if (isPath(node.path) && node.path.original === 'each') { + let firstParam = node.params[0]; + + if ( + firstParam.type === 'SubExpression' && + firstParam.path.type === 'PathExpression' && + firstParam.path.original === '-each-in' + ) { + return; + } + + node.params[0] = b.sexpr(b.path('-track-array'), [node.params[0]]); + + return b.block( + b.path('each'), + node.params, + node.hash, + node.program, + node.inverse, + node.loc + ); + } + }, + }, + }; +} diff --git a/packages/ember-template-compiler/lib/plugins/transform-has-block-syntax.ts b/packages/ember-template-compiler/lib/plugins/transform-has-block-syntax.ts index 5254e5cdf6c..cff677493a2 100644 --- a/packages/ember-template-compiler/lib/plugins/transform-has-block-syntax.ts +++ b/packages/ember-template-compiler/lib/plugins/transform-has-block-syntax.ts @@ -1,4 +1,5 @@ import { AST, ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax'; +import { isPath } from './utils'; /** @module ember */ @@ -38,7 +39,7 @@ export default function transformHasBlockSyntax(env: ASTPluginEnvironment): ASTP } }, MustacheStatement(node: AST.MustacheStatement): AST.Node | void { - if (typeof node.path.original === 'string' && TRANSFORMATIONS[node.path.original]) { + if (isPath(node.path) && TRANSFORMATIONS[node.path.original]) { return b.mustache( b.path(TRANSFORMATIONS[node.path.original]), node.params, @@ -49,7 +50,7 @@ export default function transformHasBlockSyntax(env: ASTPluginEnvironment): ASTP } }, SubExpression(node: AST.SubExpression): AST.Node | void { - if (TRANSFORMATIONS[node.path.original]) { + if (isPath(node.path) && TRANSFORMATIONS[node.path.original]) { return b.sexpr(b.path(TRANSFORMATIONS[node.path.original]), node.params, node.hash); } }, diff --git a/packages/ember-template-compiler/lib/plugins/transform-in-element.ts b/packages/ember-template-compiler/lib/plugins/transform-in-element.ts index a02a3461ff8..b71f65c74f5 100644 --- a/packages/ember-template-compiler/lib/plugins/transform-in-element.ts +++ b/packages/ember-template-compiler/lib/plugins/transform-in-element.ts @@ -1,6 +1,8 @@ +import { StaticTemplateMeta } from '@ember/-internals/views'; import { assert } from '@ember/debug'; import { AST, ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax'; import calculateLocationDisplay from '../system/calculate-location-display'; +import { isPath } from './utils'; /** @module ember @@ -45,7 +47,7 @@ import calculateLocationDisplay from '../system/calculate-location-display'; @class TransformHasBlockSyntax */ export default function transformInElement(env: ASTPluginEnvironment): ASTPlugin { - let { moduleName } = env.meta; + let { moduleName } = env.meta as StaticTemplateMeta; let { builders: b } = env.syntax; let cursorCount = 0; @@ -54,6 +56,8 @@ export default function transformInElement(env: ASTPluginEnvironment): ASTPlugin visitor: { BlockStatement(node: AST.BlockStatement) { + if (!isPath(node.path)) return; + if (node.path.original === 'in-element') { assert(assertMessage(moduleName, node)); } else if (node.path.original === '-in-element') { @@ -62,11 +66,18 @@ export default function transformInElement(env: ASTPluginEnvironment): ASTPlugin // replicate special hash arguments added here: // https://github.com/glimmerjs/glimmer-vm/blob/ba9b37d44b85fa1385eeeea71910ff5798198c8e/packages/%40glimmer/syntax/lib/parser/handlebars-node-visitors.ts#L340-L363 - let hasNextSibling = false; + let hasInsertBefore = false; let hash = node.hash; hash.pairs.forEach(pair => { - if (pair.key === 'nextSibling') { - hasNextSibling = true; + if (pair.key === 'insertBefore') { + assert( + `Can only pass a null literal to insertBefore in -in-element, received: ${JSON.stringify( + pair.value + )}`, + pair.value.type === 'NullLiteral' + ); + + hasInsertBefore = true; } }); @@ -74,9 +85,9 @@ export default function transformInElement(env: ASTPluginEnvironment): ASTPlugin let guidPair = b.pair('guid', guid); hash.pairs.unshift(guidPair); - if (!hasNextSibling) { - let nullLiteral = b.literal('NullLiteral', null); - let nextSibling = b.pair('nextSibling', nullLiteral); + if (!hasInsertBefore) { + let undefinedLiteral = b.literal('UndefinedLiteral', undefined); + let nextSibling = b.pair('insertBefore', undefinedLiteral); hash.pairs.push(nextSibling); } } diff --git a/packages/ember-template-compiler/lib/plugins/transform-link-to.ts b/packages/ember-template-compiler/lib/plugins/transform-link-to.ts index c5c2c1c18fc..a2dec50c710 100644 --- a/packages/ember-template-compiler/lib/plugins/transform-link-to.ts +++ b/packages/ember-template-compiler/lib/plugins/transform-link-to.ts @@ -1,22 +1,20 @@ +import { StaticTemplateMeta } from '@ember/-internals/views'; import { assert } from '@ember/debug'; import { AST, ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax'; import calculateLocationDisplay from '../system/calculate-location-display'; import { Builders } from '../types'; +import { isPath, isSubExpression } from './utils'; function isInlineLinkTo(node: AST.MustacheStatement): boolean { - return node.path.original === 'link-to'; + return isPath(node.path) && node.path.original === 'link-to'; } function isBlockLinkTo(node: AST.BlockStatement): boolean { - return node.path.original === 'link-to'; -} - -function isSubExpression(node: AST.Expression): node is AST.SubExpression { - return node.type === 'SubExpression'; + return isPath(node.path) && node.path.original === 'link-to'; } function isQueryParams(node: AST.Expression): node is AST.SubExpression { - return isSubExpression(node) && node.path.original === 'query-params'; + return isSubExpression(node) && isPath(node.path) && node.path.original === 'query-params'; } function transformInlineLinkToIntoBlockForm( @@ -29,7 +27,12 @@ function transformInlineLinkToIntoBlockForm( 'link-to', node.params.slice(1), node.hash, - buildProgram(b, node.params[0], node.escaped, node.loc), + b.blockItself( + [buildStatement(b, node.params[0], node.escaped, node.loc)], + undefined, + false, + node.loc + ), null, node.loc ); @@ -40,7 +43,7 @@ function transformPositionalLinkToIntoNamedArguments( node: AST.BlockStatement ): AST.BlockStatement { let { builders: b } = env.syntax; - let { moduleName } = env.meta; + let { moduleName } = env.meta as StaticTemplateMeta; let { params, hash: { pairs }, @@ -128,7 +131,11 @@ function transformPositionalLinkToIntoNamedArguments( ); pairs.push( - b.pair('query', b.sexpr(b.path('hash', query.path.loc), [], query.hash, query.loc), query.loc) + b.pair( + 'query', + b.sexpr(b.path('-hash', query.path.loc), [], query.hash, query.loc), + query.loc + ) ); } @@ -160,10 +167,6 @@ function transformPositionalLinkToIntoNamedArguments( ); } -function buildProgram(b: Builders, content: AST.Node, escaped: boolean, loc: AST.SourceLocation) { - return b.program([buildStatement(b, content, escaped, loc)], undefined, loc); -} - function buildStatement(b: Builders, content: AST.Node, escaped: boolean, loc: AST.SourceLocation) { switch (content.type) { case 'PathExpression': diff --git a/packages/ember-template-compiler/lib/plugins/transform-wrap-mount-and-outlet.ts b/packages/ember-template-compiler/lib/plugins/transform-wrap-mount-and-outlet.ts new file mode 100644 index 00000000000..a2e01ef36c8 --- /dev/null +++ b/packages/ember-template-compiler/lib/plugins/transform-wrap-mount-and-outlet.ts @@ -0,0 +1,66 @@ +import { AST, ASTPlugin, ASTPluginEnvironment } from '@glimmer/syntax'; +import { isPath, trackLocals } from './utils'; + +/** + @module ember +*/ + +/** + A Glimmer2 AST transformation that replaces all instances of + + ```handlebars + {{mount "engine" model=this.model}} + ``` + + with + + ```handlebars + {{component (-mount "engine" model=this.model)}} + ``` + + and + + ```handlebars + {{outlet}} + ``` + + with + + ```handlebars + {{component (-outlet)}} + ``` + + @private + @class TransformHasBlockSyntax +*/ +export default function transformWrapMountAndOutlet(env: ASTPluginEnvironment): ASTPlugin { + let { builders: b } = env.syntax; + + let { hasLocal, node } = trackLocals(); + + return { + name: 'transform-wrap-mount-and-outlet', + + visitor: { + Program: node, + ElementNode: node, + + MustacheStatement(node: AST.MustacheStatement): AST.Node | void { + if ( + isPath(node.path) && + (node.path.original === 'mount' || node.path.original === 'outlet') && + !hasLocal(node.path.original) + ) { + let subexpression = b.sexpr( + b.path(`-${node.path.original}`), + node.params, + node.hash, + node.loc + ); + + return b.mustache(b.path('component'), [subexpression], b.hash(), undefined, node.loc); + } + }, + }, + }; +} diff --git a/packages/ember-template-compiler/lib/plugins/utils.ts b/packages/ember-template-compiler/lib/plugins/utils.ts new file mode 100644 index 00000000000..fa310f00e31 --- /dev/null +++ b/packages/ember-template-compiler/lib/plugins/utils.ts @@ -0,0 +1,39 @@ +import { AST } from '@glimmer/syntax'; + +export function isPath(node: AST.Node): node is AST.PathExpression { + return node.type === 'PathExpression'; +} + +export function isSubExpression(node: AST.Node): node is AST.SubExpression { + return node.type === 'SubExpression'; +} + +export function trackLocals() { + let locals = new Map(); + + let node = { + enter(node: AST.Program | AST.ElementNode) { + for (let param of node.blockParams) { + let value = locals.get(param) || 0; + locals.set(param, value + 1); + } + }, + + exit(node: AST.Program | AST.ElementNode) { + for (let param of node.blockParams) { + let value = locals.get(param) - 1; + + if (value === 0) { + locals.delete(param); + } else { + locals.set(param, value); + } + } + }, + }; + + return { + hasLocal: (key: string) => locals.has(key), + node, + }; +} diff --git a/packages/ember-template-compiler/lib/system/bootstrap.ts b/packages/ember-template-compiler/lib/system/bootstrap.ts index c8673dbf807..1c0108c487a 100644 --- a/packages/ember-template-compiler/lib/system/bootstrap.ts +++ b/packages/ember-template-compiler/lib/system/bootstrap.ts @@ -5,7 +5,7 @@ import compile from './compile'; export interface BootstrapOptions { - context?: NodeSelector; + context?: Document | HTMLElement; hasTemplate(templateName: string): boolean; setTemplate(templateName: string, template: string): void; } diff --git a/packages/ember-template-compiler/tests/plugins/transform-link-to-test.js b/packages/ember-template-compiler/tests/plugins/transform-link-to-test.js index 6c3a4444c9d..087a72d35f7 100644 --- a/packages/ember-template-compiler/tests/plugins/transform-link-to-test.js +++ b/packages/ember-template-compiler/tests/plugins/transform-link-to-test.js @@ -286,29 +286,31 @@ moduleFor( } ['@test query params']() { + QUnit.dump.maxDepth = 100; + this.assertTransformed( `{{#link-to (query-params)}}Foo{{/link-to}}`, - `{{#link-to query=(hash)}}Foo{{/link-to}}` + `{{#link-to query=(-hash)}}Foo{{/link-to}}` ); this.assertTransformed( `{{#link-to (query-params foo='bar' baz=bat)}}Foo{{/link-to}}`, - `{{#link-to query=(hash foo='bar' baz=bat)}}Foo{{/link-to}}` + `{{#link-to query=(-hash foo='bar' baz=bat)}}Foo{{/link-to}}` ); this.assertTransformed( `{{#link-to 'foo' (query-params foo='bar' baz=bat)}}Foo{{/link-to}}`, - `{{#link-to query=(hash foo='bar' baz=bat) route='foo'}}Foo{{/link-to}}` + `{{#link-to query=(-hash foo='bar' baz=bat) route='foo'}}Foo{{/link-to}}` ); this.assertTransformed( `{{#link-to 'foo' 'bar' (query-params foo='bar' baz=bat)}}Foo{{/link-to}}`, - `{{#link-to query=(hash foo='bar' baz=bat) route='foo' model='bar'}}Foo{{/link-to}}` + `{{#link-to query=(-hash foo='bar' baz=bat) route='foo' model='bar'}}Foo{{/link-to}}` ); this.assertTransformed( `{{#link-to 'foo' 'bar' 'baz' 'bat' 'wat' (query-params foo='bar' baz=bat)}}Foo{{/link-to}}`, - `{{#link-to query=(hash foo='bar' baz=bat) route='foo' models=(array 'bar' 'baz' 'bat' 'wat')}}Foo{{/link-to}}` + `{{#link-to query=(-hash foo='bar' baz=bat) route='foo' models=(array 'bar' 'baz' 'bat' 'wat')}}Foo{{/link-to}}` ); } } diff --git a/packages/internal-test-helpers/index.js b/packages/internal-test-helpers/index.js index 6c9837ac6e8..46b5ef163c2 100644 --- a/packages/internal-test-helpers/index.js +++ b/packages/internal-test-helpers/index.js @@ -15,7 +15,6 @@ export { default as AbstractTestCase } from './lib/test-cases/abstract'; export { default as AbstractApplicationTestCase } from './lib/test-cases/abstract-application'; export { default as ApplicationTestCase } from './lib/test-cases/application'; export { default as QueryParamTestCase } from './lib/test-cases/query-param'; -export { default as AbstractRenderingTestCase } from './lib/test-cases/abstract-rendering'; export { default as RenderingTestCase } from './lib/test-cases/rendering'; export { default as RouterTestCase } from './lib/test-cases/router'; export { default as AutobootApplicationTestCase } from './lib/test-cases/autoboot-application'; diff --git a/packages/internal-test-helpers/lib/test-cases/abstract-rendering.js b/packages/internal-test-helpers/lib/test-cases/abstract-rendering.js deleted file mode 100644 index cd2fb59524b..00000000000 --- a/packages/internal-test-helpers/lib/test-cases/abstract-rendering.js +++ /dev/null @@ -1,214 +0,0 @@ -import { assign } from '@ember/polyfills'; -import { compile } from 'ember-template-compiler'; -import { EventDispatcher } from '@ember/-internals/views'; -import { helper, Helper, Component, _resetRenderers } from '@ember/-internals/glimmer'; -import { ModuleBasedResolver } from '../test-resolver'; - -import AbstractTestCase from './abstract'; -import buildOwner from '../build-owner'; -import { runAppend, runDestroy } from '../run'; - -const TextNode = window.Text; - -export default class AbstractRenderingTestCase extends AbstractTestCase { - constructor() { - super(...arguments); - let bootOptions = this.getBootOptions(); - - let owner = (this.owner = buildOwner({ - ownerOptions: this.getOwnerOptions(), - resolver: this.getResolver(), - bootOptions, - })); - - owner.register('-view-registry:main', Object.create(null), { instantiate: false }); - owner.register('event_dispatcher:main', EventDispatcher); - - // TODO: why didn't buildOwner do this for us? - owner.inject('view', '_viewRegistry', '-view-registry:main'); - owner.inject('renderer', '_viewRegistry', '-view-registry:main'); - - this.renderer = this.owner.lookup('renderer:-dom'); - this.element = document.querySelector('#qunit-fixture'); - this.component = null; - - if (!bootOptions || bootOptions.isInteractive !== false) { - owner.lookup('event_dispatcher:main').setup(this.getCustomDispatcherEvents(), this.element); - } - } - - compile() { - return compile(...arguments); - } - - getCustomDispatcherEvents() { - return {}; - } - - getOwnerOptions() {} - getBootOptions() {} - - get resolver() { - return this.owner.__registry__.fallback.resolver; - } - - getResolver() { - return new ModuleBasedResolver(); - } - - add(specifier, factory) { - this.resolver.add(specifier, factory); - } - - addTemplate(templateName, templateString) { - if (typeof templateName === 'string') { - this.resolver.add( - `template:${templateName}`, - this.compile(templateString, { - moduleName: templateName, - }) - ); - } else { - this.resolver.add( - templateName, - this.compile(templateString, { - moduleName: templateName.moduleName, - }) - ); - } - } - - addComponent(name, { ComponentClass = null, template = null }) { - if (ComponentClass) { - this.resolver.add(`component:${name}`, ComponentClass); - } - - if (typeof template === 'string') { - this.resolver.add( - `template:components/${name}`, - this.compile(template, { - moduleName: `components/${name}`, - }) - ); - } - } - - afterEach() { - try { - if (this.component) { - runDestroy(this.component); - } - if (this.owner) { - runDestroy(this.owner); - } - } finally { - _resetRenderers(); - } - } - - get context() { - return this.component; - } - - render(templateStr, context = {}) { - let { owner } = this; - - owner.register( - 'template:-top-level', - this.compile(templateStr, { - moduleName: '-top-level', - }) - ); - - let attrs = assign({}, context, { - tagName: '', - layoutName: '-top-level', - }); - - owner.register('component:-top-level', Component.extend(attrs)); - - this.component = owner.lookup('component:-top-level'); - - runAppend(this.component); - } - - rerender() { - this.component.rerender(); - } - - registerHelper(name, funcOrClassBody) { - let type = typeof funcOrClassBody; - - if (type === 'function') { - this.owner.register(`helper:${name}`, helper(funcOrClassBody)); - } else if (type === 'object' && type !== null) { - this.owner.register(`helper:${name}`, Helper.extend(funcOrClassBody)); - } else { - throw new Error(`Cannot register ${funcOrClassBody} as a helper`); - } - } - - registerPartial(name, template) { - let owner = this.env.owner || this.owner; - if (typeof template === 'string') { - owner.register( - `template:${name}`, - this.compile(template, { moduleName: `my-app/templates/-${name}.hbs` }) - ); - } - } - - registerComponent(name, { ComponentClass = Component, template = null }) { - let { owner } = this; - - if (ComponentClass) { - owner.register(`component:${name}`, ComponentClass); - } - - if (typeof template === 'string') { - owner.register( - `template:components/${name}`, - this.compile(template, { - moduleName: `my-app/templates/components/${name}.hbs`, - }) - ); - } - } - - registerModifier(name, ModifierClass) { - let { owner } = this; - - owner.register(`modifier:${name}`, ModifierClass); - } - - registerComponentManager(name, manager) { - let owner = this.env.owner || this.owner; - owner.register(`component-manager:${name}`, manager); - } - - registerTemplate(name, template) { - let { owner } = this; - if (typeof template === 'string') { - owner.register( - `template:${name}`, - this.compile(template, { - moduleName: `my-app/templates/${name}.hbs`, - }) - ); - } else { - throw new Error(`Registered template "${name}" must be a string`); - } - } - - registerService(name, klass) { - this.owner.register(`service:${name}`, klass); - } - - assertTextNode(node, text) { - if (!(node instanceof TextNode)) { - throw new Error(`Expecting a text node, but got ${node}`); - } - - this.assert.strictEqual(node.textContent, text, 'node.textContent'); - } -} diff --git a/packages/internal-test-helpers/lib/test-cases/rendering.js b/packages/internal-test-helpers/lib/test-cases/rendering.js index f70bdb7ca4b..405bfa028ca 100644 --- a/packages/internal-test-helpers/lib/test-cases/rendering.js +++ b/packages/internal-test-helpers/lib/test-cases/rendering.js @@ -1,14 +1,214 @@ -import AbstractRenderingTestCase from './abstract-rendering'; -import { privatize as P } from '@ember/-internals/container'; +import { assign } from '@ember/polyfills'; +import { compile } from 'ember-template-compiler'; +import { EventDispatcher } from '@ember/-internals/views'; +import { helper, Helper, Component, _resetRenderers } from '@ember/-internals/glimmer'; +import { ModuleBasedResolver } from '../test-resolver'; -export default class RenderingTestCase extends AbstractRenderingTestCase { +import AbstractTestCase from './abstract'; +import buildOwner from '../build-owner'; +import { runAppend, runDestroy } from '../run'; + +const TextNode = window.Text; + +export default class RenderingTestCase extends AbstractTestCase { constructor() { super(...arguments); + let bootOptions = this.getBootOptions(); + + let owner = (this.owner = buildOwner({ + ownerOptions: this.getOwnerOptions(), + resolver: this.getResolver(), + bootOptions, + })); + + owner.register('-view-registry:main', Object.create(null), { instantiate: false }); + owner.register('event_dispatcher:main', EventDispatcher); + + // TODO: why didn't buildOwner do this for us? + owner.inject('view', '_viewRegistry', '-view-registry:main'); + owner.inject('renderer', '_viewRegistry', '-view-registry:main'); + + this.renderer = this.owner.lookup('renderer:-dom'); + this.element = document.querySelector('#qunit-fixture'); + this.component = null; + + if (!bootOptions || bootOptions.isInteractive !== false) { + owner.lookup('event_dispatcher:main').setup(this.getCustomDispatcherEvents(), this.element); + } + } + + compile() { + return compile(...arguments); + } + + getCustomDispatcherEvents() { + return {}; + } + + getOwnerOptions() {} + getBootOptions() {} + + get resolver() { + return this.owner.__registry__.fallback.resolver; + } + + getResolver() { + return new ModuleBasedResolver(); + } + + add(specifier, factory) { + this.resolver.add(specifier, factory); + } + + addTemplate(templateName, templateString) { + if (typeof templateName === 'string') { + this.resolver.add( + `template:${templateName}`, + this.compile(templateString, { + moduleName: templateName, + }) + ); + } else { + this.resolver.add( + templateName, + this.compile(templateString, { + moduleName: templateName.moduleName, + }) + ); + } + } + + addComponent(name, { ComponentClass = null, template = null }) { + if (ComponentClass) { + this.resolver.add(`component:${name}`, ComponentClass); + } + + if (typeof template === 'string') { + this.resolver.add( + `template:components/${name}`, + this.compile(template, { + moduleName: `components/${name}`, + }) + ); + } + } + + afterEach() { + try { + if (this.component) { + runDestroy(this.component); + } + if (this.owner) { + runDestroy(this.owner); + } + } finally { + _resetRenderers(); + } + } + + get context() { + return this.component; + } + + render(templateStr, context = {}) { let { owner } = this; - this.env = owner.lookup('service:-glimmer-environment'); - this.templateOptions = owner.lookup(P`template-compiler:main`); - this.compileTimeLookup = this.templateOptions.resolver; - this.runtimeResolver = this.compileTimeLookup.resolver; + owner.register( + 'template:-top-level', + this.compile(templateStr, { + moduleName: '-top-level', + }) + ); + + let attrs = assign({}, context, { + tagName: '', + layoutName: '-top-level', + }); + + owner.register('component:-top-level', Component.extend(attrs)); + + this.component = owner.lookup('component:-top-level'); + + runAppend(this.component); + } + + rerender() { + this.component.rerender(); + } + + registerHelper(name, funcOrClassBody) { + let type = typeof funcOrClassBody; + + if (type === 'function') { + this.owner.register(`helper:${name}`, helper(funcOrClassBody)); + } else if (type === 'object' && type !== null) { + this.owner.register(`helper:${name}`, Helper.extend(funcOrClassBody)); + } else { + throw new Error(`Cannot register ${funcOrClassBody} as a helper`); + } + } + + registerPartial(name, template) { + let owner = this.owner; + if (typeof template === 'string') { + owner.register( + `template:${name}`, + this.compile(template, { moduleName: `my-app/templates/-${name}.hbs` }) + ); + } + } + + registerComponent(name, { ComponentClass = Component, template = null }) { + let { owner } = this; + + if (ComponentClass) { + owner.register(`component:${name}`, ComponentClass); + } + + if (typeof template === 'string') { + owner.register( + `template:components/${name}`, + this.compile(template, { + moduleName: `my-app/templates/components/${name}.hbs`, + }) + ); + } + } + + registerModifier(name, ModifierClass) { + let { owner } = this; + + owner.register(`modifier:${name}`, ModifierClass); + } + + registerComponentManager(name, manager) { + let owner = this.owner; + owner.register(`component-manager:${name}`, manager); + } + + registerTemplate(name, template) { + let { owner } = this; + if (typeof template === 'string') { + owner.register( + `template:${name}`, + this.compile(template, { + moduleName: `my-app/templates/${name}.hbs`, + }) + ); + } else { + throw new Error(`Registered template "${name}" must be a string`); + } + } + + registerService(name, klass) { + this.owner.register(`service:${name}`, klass); + } + + assertTextNode(node, text) { + if (!(node instanceof TextNode)) { + throw new Error(`Expecting a text node, but got ${node}`); + } + + this.assert.strictEqual(node.textContent, text, 'node.textContent'); } } diff --git a/tsconfig.json b/tsconfig.json index 6f0b5952b3b..28014d261f1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,16 +21,24 @@ "newLine": "LF", "noEmit": true, - "allowJs": false + "allowJs": false, + + // TODO: remove this once router_js types have been fixed + // https://github.com/tildeio/router.js/issues/290 + "skipLibCheck": true, + + "paths": { + "@glimmer/*": ["../node_modules/@glimmer/*"] + } }, "include": [ - "packages/**/*.ts" + "packages/**/*.ts", ], "exclude": [ "dist", + "node_modules", "tmp", - "node_modules" ] } diff --git a/yarn.lock b/yarn.lock index 1a39095ec3e..d777f096883 100644 --- a/yarn.lock +++ b/yarn.lock @@ -801,122 +801,138 @@ resolved "https://registry.yarnpkg.com/@ember/edition-utils/-/edition-utils-1.1.1.tgz#d5732c3da593f202e6e1ac6dbee56a758242403f" integrity sha512-GEhri78jdQp/xxPpM6z08KlB0wrHfnfrJ9dmQk7JeQ4XCiMzXsJci7yooQgg/IcTKCM/PxE/IkGCQAo80adMkw== -"@glimmer/compiler@0.38.5-alpha.3": - version "0.38.5-alpha.3" - resolved "https://registry.yarnpkg.com/@glimmer/compiler/-/compiler-0.38.5-alpha.3.tgz#1e5c5e6779c95dac77e81dc96822bef035a68c57" - integrity sha512-ih35kzH9iCl9RP8awREVVr25+27HxcbkDQuPdApmeDTuc+7tTvr40OBcEG9GLLPfdWXd5bP5eq0hITlAQvVW4g== - dependencies: - "@glimmer/interfaces" "^0.38.5-alpha.3" - "@glimmer/syntax" "^0.38.5-alpha.3" - "@glimmer/util" "^0.38.5-alpha.3" - "@glimmer/wire-format" "^0.38.5-alpha.3" +"@glimmer/compiler@0.45.3": + version "0.45.3" + resolved "https://registry.yarnpkg.com/@glimmer/compiler/-/compiler-0.45.3.tgz#111f87eb9cc4af4e3846bb132186cccf18227dd3" + integrity sha512-NmlmQDHBxmz5j98FMwXExYya005vv89AE2ORUj3N0ciNfZm1VqM5MzPeyJla5jNhu/pRT/yh6FPsmakTha3q+w== + dependencies: + "@glimmer/interfaces" "^0.45.3" + "@glimmer/syntax" "^0.45.3" + "@glimmer/util" "^0.45.3" + "@glimmer/wire-format" "^0.45.3" + "@simple-dom/interface" "^1.4.0" -"@glimmer/encoder@^0.38.5-alpha.3": - version "0.38.5-alpha.3" - resolved "https://registry.yarnpkg.com/@glimmer/encoder/-/encoder-0.38.5-alpha.3.tgz#a6572e09a3e56f507a6503f2921df8fcc794981c" - integrity sha512-T4vfEG24nIoVWLavzjaUAAP9oPenIakKcqvXQNoj5P+2aLKLJau2jAzU/vMLzv4rS+hZWD5bpz0IVWoiKiV09w== +"@glimmer/encoder@^0.45.3": + version "0.45.3" + resolved "https://registry.yarnpkg.com/@glimmer/encoder/-/encoder-0.45.3.tgz#3ec8496cf9855eb69c9edbdb15b6dcb8e1c179bb" + integrity sha512-cJ6GbPkMhA5i2o51gfzNt7qeRGNvIvynPTGHFri7baSUzlPJg6ieoc7flxZ+jmVVh8BwlbN9/HgEoCMneaqMhg== dependencies: - "@glimmer/interfaces" "^0.38.5-alpha.3" - "@glimmer/vm" "^0.38.5-alpha.3" + "@glimmer/interfaces" "^0.45.3" + "@glimmer/vm" "^0.45.3" -"@glimmer/env@^0.1.7": +"@glimmer/env@0.1.7", "@glimmer/env@^0.1.7": version "0.1.7" resolved "https://registry.yarnpkg.com/@glimmer/env/-/env-0.1.7.tgz#fd2d2b55a9029c6b37a6c935e8c8871ae70dfa07" integrity sha1-/S0rVakCnGs3psk16MiHGucN+gc= -"@glimmer/interfaces@0.38.5-alpha.3", "@glimmer/interfaces@^0.38.5-alpha.3": - version "0.38.5-alpha.3" - resolved "https://registry.yarnpkg.com/@glimmer/interfaces/-/interfaces-0.38.5-alpha.3.tgz#e68a51ad2900ee3929fda76d42c0b581356d8ca7" - integrity sha512-VvnzMeZa6+UUdDsjEHe/zjGQywMjyegKmFA45xa0fjD6bL22GoKFXucIe9oi72BWEukM2+hNd9bXYioUg8mkOA== - dependencies: - "@glimmer/wire-format" "^0.38.5-alpha.3" - "@simple-dom/interface" "1.4.0" - -"@glimmer/low-level@^0.38.5-alpha.3": - version "0.38.5-alpha.3" - resolved "https://registry.yarnpkg.com/@glimmer/low-level/-/low-level-0.38.5-alpha.3.tgz#4269003a4bc665da9bd4236f16803b197c518c5c" - integrity sha512-t+erJ598CKw43ZatsLPaLMQAySYEB00dJk/EUVBmJOCZS8413g1mnuuQJkrHiQ8nE7Zklyc2vEzKaPYgjxp67Q== - -"@glimmer/node@0.38.5-alpha.3": - version "0.38.5-alpha.3" - resolved "https://registry.yarnpkg.com/@glimmer/node/-/node-0.38.5-alpha.3.tgz#e3d1386b4b4b1170524f6c554ef7f75de7b3d890" - integrity sha512-KSCXYhl1dv3UgKDQMy4pqB+BsV/BBo7Bd0N45+5OrVU0cSvXOG0Ak512UXwiVS68PQ2qz5u8vh/+chFh5wLa2A== - dependencies: - "@glimmer/interfaces" "^0.38.5-alpha.3" - "@glimmer/runtime" "^0.38.5-alpha.3" - -"@glimmer/opcode-compiler@0.38.5-alpha.3": - version "0.38.5-alpha.3" - resolved "https://registry.yarnpkg.com/@glimmer/opcode-compiler/-/opcode-compiler-0.38.5-alpha.3.tgz#48d287f5d0342162f2afcb29dc55e6b87695a94c" - integrity sha512-CrqmYvVVe1460ownzS48NOXI7nlfvMTfM1rrzGw7hv3h2UwQ7EHedO81Mg7Hsb/fc/C4UxvMpfiicKeQVbzh5g== - dependencies: - "@glimmer/encoder" "^0.38.5-alpha.3" - "@glimmer/interfaces" "^0.38.5-alpha.3" - "@glimmer/program" "^0.38.5-alpha.3" - "@glimmer/reference" "^0.38.5-alpha.3" - "@glimmer/util" "^0.38.5-alpha.3" - "@glimmer/vm" "^0.38.5-alpha.3" - "@glimmer/wire-format" "^0.38.5-alpha.3" - -"@glimmer/program@0.38.5-alpha.3", "@glimmer/program@^0.38.5-alpha.3": - version "0.38.5-alpha.3" - resolved "https://registry.yarnpkg.com/@glimmer/program/-/program-0.38.5-alpha.3.tgz#55a32da10cb33c1fd436f35c77f7750a332008d4" - integrity sha512-Neyi4jGS7/igXMc+bpIETRQWLgDQuU6vxvwbIpOWAtf8MFInBl42Bo6rekcVktPmsNxPPBpjEDnapucteYE8gA== - dependencies: - "@glimmer/encoder" "^0.38.5-alpha.3" - "@glimmer/interfaces" "^0.38.5-alpha.3" - "@glimmer/util" "^0.38.5-alpha.3" - -"@glimmer/reference@0.38.5-alpha.3", "@glimmer/reference@^0.38.5-alpha.3": - version "0.38.5-alpha.3" - resolved "https://registry.yarnpkg.com/@glimmer/reference/-/reference-0.38.5-alpha.3.tgz#f5291bc7f5adfaa8583b00696a7fdab3889ad781" - integrity sha512-F7rZha3h3/xuR2xLDYRJY0GlWh5ctSNI8OXQ9+L3E9DYVpqtqGQUfWAUfPVBmqXRyJzppIS2BceCTT4vaXpHKg== - dependencies: - "@glimmer/util" "^0.38.5-alpha.3" - -"@glimmer/runtime@0.38.5-alpha.3", "@glimmer/runtime@^0.38.5-alpha.3": - version "0.38.5-alpha.3" - resolved "https://registry.yarnpkg.com/@glimmer/runtime/-/runtime-0.38.5-alpha.3.tgz#74255bbedea684b9460ab7be86ba31c9be8a0a6d" - integrity sha512-ofMBR984oNfvCR6z2RTSzZr3e897JW95xK/MQcfr1P3UCJr7+zdKECm861cNVMOmaCH2bv2rQxoplWs1SEULXg== - dependencies: - "@glimmer/interfaces" "^0.38.5-alpha.3" - "@glimmer/low-level" "^0.38.5-alpha.3" - "@glimmer/program" "^0.38.5-alpha.3" - "@glimmer/reference" "^0.38.5-alpha.3" - "@glimmer/util" "^0.38.5-alpha.3" - "@glimmer/vm" "^0.38.5-alpha.3" - "@glimmer/wire-format" "^0.38.5-alpha.3" +"@glimmer/interfaces@0.45.3", "@glimmer/interfaces@^0.45.3": + version "0.45.3" + resolved "https://registry.yarnpkg.com/@glimmer/interfaces/-/interfaces-0.45.3.tgz#9a54b6cc3f9d5887fc26c39750fd180d9dc650c0" + integrity sha512-RsjpflPimJUgkJwzhKWhu/CJ8HdRwqBFcSNPL9Fu8cElE1DyERdcqJbSCfw7pkFornVrhoU8gTNxzo24ly1H3g== + dependencies: + "@simple-dom/interface" "^1.4.0" + +"@glimmer/low-level@^0.45.3": + version "0.45.3" + resolved "https://registry.yarnpkg.com/@glimmer/low-level/-/low-level-0.45.3.tgz#98324038a364fe54d4f62f9441a478e28b3b87fa" + integrity sha512-cf3XyivURLhSwOnYJJgeHQllLvhE+OMnhI3AQ9fH/8SZgXQW1rbO4BPYRZwzBYyNhAJ2+HryFC1ml5XLdu1r4Q== + +"@glimmer/node@0.45.3": + version "0.45.3" + resolved "https://registry.yarnpkg.com/@glimmer/node/-/node-0.45.3.tgz#228016a312495f632a1f7a0d5be9cc1de25550d8" + integrity sha512-nOMTUFtP9bh8UmOWtq8a74rmnKxC+i2bW92iePGm0TYPu5DYnDlHRkzYrtuVVvlgr10BaKyXhH33F6Em+i+vcQ== + dependencies: + "@glimmer/interfaces" "^0.45.3" + "@glimmer/runtime" "^0.45.3" + "@simple-dom/document" "^1.4.0" + "@simple-dom/interface" "^1.4.0" + +"@glimmer/opcode-compiler@0.45.3": + version "0.45.3" + resolved "https://registry.yarnpkg.com/@glimmer/opcode-compiler/-/opcode-compiler-0.45.3.tgz#1481b60b59c27282a8f8d223e326f1f9954b2baf" + integrity sha512-BLWGtt6ZHrdS16SH+GAeZQ5AS5XkQ6yk779JznKY07kOzy9ZtXUFUGD0+F5oH2gjvu8SzJt2r6BkNU+dLmkJuw== + dependencies: + "@glimmer/encoder" "^0.45.3" + "@glimmer/interfaces" "^0.45.3" + "@glimmer/program" "^0.45.3" + "@glimmer/reference" "^0.45.3" + "@glimmer/util" "^0.45.3" + "@glimmer/vm" "^0.45.3" + "@glimmer/wire-format" "^0.45.3" + +"@glimmer/program@0.45.3", "@glimmer/program@^0.45.3": + version "0.45.3" + resolved "https://registry.yarnpkg.com/@glimmer/program/-/program-0.45.3.tgz#b68b3ec794301045495acb9f94215023dc9c881d" + integrity sha512-4ns6VGxK+9aRr0AiOfItQeA6CZi88dHUjFWBp9nZ93odjwbWvFheoGqrs0OylELUQ+DYCxKeOCa/tSqUysSdbQ== + dependencies: + "@glimmer/encoder" "^0.45.3" + "@glimmer/interfaces" "^0.45.3" + "@glimmer/util" "^0.45.3" + +"@glimmer/reference@0.45.3", "@glimmer/reference@^0.45.3": + version "0.45.3" + resolved "https://registry.yarnpkg.com/@glimmer/reference/-/reference-0.45.3.tgz#f0223271de9babda66f3e04db4649af532684cef" + integrity sha512-v8yio70XCkYaDRjf7Nj9nfLVX9p/1HqevjN6tdUaInyj2ymCHTYpZ9zjNtWu+7cM5Lh9I5n0FcQo5NRk+VKQOA== + dependencies: + "@glimmer/env" "^0.1.7" + "@glimmer/util" "^0.45.3" + "@glimmer/validator" "^0.45.3" + +"@glimmer/runtime@0.45.3", "@glimmer/runtime@^0.45.3": + version "0.45.3" + resolved "https://registry.yarnpkg.com/@glimmer/runtime/-/runtime-0.45.3.tgz#bbbe0bb407ff4031dabf23c2151b521fe02e47fd" + integrity sha512-wb2xfWajUvITFmZOWLm0eTyzs53xE9/vFX1Nxnof4OjX7JOdzCHMl6WLEcrfd7rvQKy/GEcLeKlgiNZ2JWqS9w== + dependencies: + "@glimmer/env" "0.1.7" + "@glimmer/interfaces" "^0.45.3" + "@glimmer/low-level" "^0.45.3" + "@glimmer/program" "^0.45.3" + "@glimmer/reference" "^0.45.3" + "@glimmer/util" "^0.45.3" + "@glimmer/validator" "^0.45.3" + "@glimmer/vm" "^0.45.3" + "@glimmer/wire-format" "^0.45.3" "@simple-dom/interface" "^1.4.0" -"@glimmer/syntax@^0.38.5-alpha.3": - version "0.38.5-alpha.3" - resolved "https://registry.yarnpkg.com/@glimmer/syntax/-/syntax-0.38.5-alpha.3.tgz#da6c1daba1ada18493a1fc0879b2e8d1313b35af" - integrity sha512-hpYevmtgXrJxqJ9/07VAUNx86FGzoCcZi5SzVdFmN/YUuWEdjZjx2bNOCUDSx8SwssWpJJnxizHx77YII0Ftbw== +"@glimmer/syntax@^0.45.3": + version "0.45.3" + resolved "https://registry.yarnpkg.com/@glimmer/syntax/-/syntax-0.45.3.tgz#298c1eff98661726eb8df0806a241e7b43ac662b" + integrity sha512-olTpMV4FiF7ylLYWTHzlNGiirQvCPGSVFDtohDcrNgHVkXwYYpaCF6QUgsHQMPCrY2Zu2Ohqv4PpZo1cJXY0ug== dependencies: - "@glimmer/interfaces" "^0.38.5-alpha.3" - "@glimmer/util" "^0.38.5-alpha.3" - handlebars "^4.0.6" + "@glimmer/interfaces" "^0.45.3" + "@glimmer/util" "^0.45.3" + handlebars "^4.5.1" simple-html-tokenizer "^0.5.8" -"@glimmer/util@^0.38.5-alpha.3": - version "0.38.5-alpha.3" - resolved "https://registry.yarnpkg.com/@glimmer/util/-/util-0.38.5-alpha.3.tgz#8d8084cef9ebbac1a757ac1b5bc6ba4948bd7e68" - integrity sha512-0gu7hN37tYhsGy+eHrAEYcielPNwDXsMo3IvD02K8MPJNHfhitofV5kFYJ8DpkNvZ5gMhD2As/461XKrwUdQKw== +"@glimmer/util@^0.45.3": + version "0.45.3" + resolved "https://registry.yarnpkg.com/@glimmer/util/-/util-0.45.3.tgz#da902da3c5e36a8dfc79c9993718044d88eacc76" + integrity sha512-2lf+f3ND5OiKIFbjeHMq/1me0xymqAanvJoVjxHMd//s74o6GVsjFUSVx7FJ53E8VE2gmtdL4m/Yz5smAJHD4A== + dependencies: + "@simple-dom/interface" "^1.4.0" + +"@glimmer/validator@0.45.3", "@glimmer/validator@^0.45.3": + version "0.45.3" + resolved "https://registry.yarnpkg.com/@glimmer/validator/-/validator-0.45.3.tgz#266bf069adf2cca6c5d44aac8e7aa7b83d2e0463" + integrity sha512-xmBQEC5minbGhHyEIGNzbY0Cip+zRn9RiI2Xk3UtmdKCQt05dc+ta9KgxIPwlkcOXxjuHtb4QlPA2J9pyLMhdg== + dependencies: + "@glimmer/env" "^0.1.7" -"@glimmer/vm@^0.38.5-alpha.3": - version "0.38.5-alpha.3" - resolved "https://registry.yarnpkg.com/@glimmer/vm/-/vm-0.38.5-alpha.3.tgz#d2a3477db184b7bbcb04270d3dc25a271d3c9f5c" - integrity sha512-ODW/hIzksJGm4aMjWb6QA1QeLqkLxfkbdvyvoH+WMhXvKkjsf9fQA9TYYPJD7ATe8KkaQWqyRatvqYS+3OoHng== +"@glimmer/vm@^0.45.3": + version "0.45.3" + resolved "https://registry.yarnpkg.com/@glimmer/vm/-/vm-0.45.3.tgz#3b4fcc8f2004e191fe147f5299fb2151d2778b18" + integrity sha512-YmX1qzAYjZPaVrtJcU9wLI3zgnaMk42qEeFXaj7LFbK3+STsJRnwSX7nHOiPB38aEtcwBQTda4G6DMoeyCIzJA== dependencies: - "@glimmer/interfaces" "^0.38.5-alpha.3" - "@glimmer/util" "^0.38.5-alpha.3" + "@glimmer/interfaces" "^0.45.3" + "@glimmer/util" "^0.45.3" -"@glimmer/wire-format@^0.38.5-alpha.3": - version "0.38.5-alpha.3" - resolved "https://registry.yarnpkg.com/@glimmer/wire-format/-/wire-format-0.38.5-alpha.3.tgz#f79cc3b886a0fa322d8743820eb88d25a7f8304f" - integrity sha512-HQaNJCDTHNQTrTwCSKNB2vAsHW/gbYl+C7hPyZSbSYsEz3y+YOf6eZC8GSUMprErBeWkSLdXAa0RYUavUEEzLQ== +"@glimmer/wire-format@^0.45.3": + version "0.45.3" + resolved "https://registry.yarnpkg.com/@glimmer/wire-format/-/wire-format-0.45.3.tgz#df2404767a4f303c6e93c1ade54c90b381e66ffc" + integrity sha512-GPqQPmjXmU9OoydGGV74Gw/FezUsjAkLz/OC0XmFK7pIFUyirKQLqaxz0egAEAPPwFxN2caNK5Uf+EVp9DymeA== dependencies: - "@glimmer/util" "^0.38.5-alpha.3" + "@glimmer/interfaces" "^0.45.3" + "@glimmer/util" "^0.45.3" "@simple-dom/document@^1.4.0": version "1.4.0" @@ -925,7 +941,7 @@ dependencies: "@simple-dom/interface" "^1.4.0" -"@simple-dom/interface@1.4.0", "@simple-dom/interface@^1.4.0": +"@simple-dom/interface@^1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@simple-dom/interface/-/interface-1.4.0.tgz#e8feea579232017f89b0138e2726facda6fbb71f" integrity sha512-l5qumKFWU0S+4ZzMaLXFU8tQZsicHEMEyAxI5kDFGhJsRqDwe0a7/iPA/GdxlGyDKseQQAgIz5kzU7eXTrlSpA== @@ -2036,10 +2052,10 @@ broccoli-string-replace@^0.1.2: broccoli-persistent-filter "^1.1.5" minimatch "^3.0.3" -broccoli-typescript-compiler@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/broccoli-typescript-compiler/-/broccoli-typescript-compiler-4.1.0.tgz#8511b73a7b019f6b6267679df64e211ca65ba036" - integrity sha512-pz+hQMlfwvklezPB1K4COYdf5SIQX6Dl4bdLX/R0uTNEJAwVa3Is/4YSXXm2L+LQdfMCZSXzqjbC5AGgvdbB6A== +broccoli-typescript-compiler@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/broccoli-typescript-compiler/-/broccoli-typescript-compiler-4.2.0.tgz#b0c75898c75b897f40d3f627d548a64e0bb3d9f2" + integrity sha512-Fk17EapT3Yye5pUNC3lzxq78KdFTWGC7m8Zi8BnIfwD6ZPzVT+uEYMEcYXsfCtMD//jHSIXDQi9YlWZcHVjiDw== dependencies: broccoli-funnel "^2.0.1" broccoli-merge-trees "^3.0.0" @@ -2047,7 +2063,7 @@ broccoli-typescript-compiler@^4.1.0: fs-tree-diff "^0.5.7" heimdalljs "0.3.3" md5-hex "^2.0.0" - typescript "~3.2.1" + typescript "~3.5.3" walk-sync "^0.3.2" broccoli-uglify-sourcemap@^3.1.1: @@ -4431,7 +4447,7 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= -handlebars@^4.0.11, handlebars@^4.0.4, handlebars@^4.0.6: +handlebars@^4.0.11, handlebars@^4.0.4, handlebars@^4.5.1: version "4.5.3" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482" integrity sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA== @@ -8361,10 +8377,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@~3.2.1: - version "3.2.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.4.tgz#c585cb952912263d915b462726ce244ba510ef3d" - integrity sha512-0RNDbSdEokBeEAkgNbxJ+BLwSManFy9TeXz8uW+48j/xhEXv1ePME60olyzw2XzUqUBNAYFeJadIqAgNqIACwg== +typescript@~3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" + integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== uc.micro@^1.0.0, uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6"