diff --git a/packages/@glimmer/application/Brocfile.js b/packages/@glimmer/application/Brocfile.js index 8cea115ed..a29b1fd26 100644 --- a/packages/@glimmer/application/Brocfile.js +++ b/packages/@glimmer/application/Brocfile.js @@ -1,14 +1,37 @@ const build = require('@glimmer/build'); const buildVendorPackage = require('@glimmer/build/lib/build-vendor-package'); +const funnel = require('broccoli-funnel'); +const path = require('path'); let buildOptions = {}; if (process.env.BROCCOLI_ENV === 'tests') { buildOptions.vendorTrees = [ - buildVendorPackage('@glimmer/di', { + buildVendorPackage('@glimmer/compiler', { + external: ['babel-helpers', '@glimmer/syntax', '@glimmer/wire-format', '@glimmer/util'] }), + buildVendorPackage('@glimmer/component', { + external: ['babel-helpers', '@glimmer/di', '@glimmmer/reference', '@glimmer/runtime', '@glimmer/object-reference', '@glimmer/util'] }), + buildVendorPackage('@glimmer/di', { external: ['babel-helpers', '@glimmer/util'] }), - buildVendorPackage('@glimmer/util', { - external: ['babel-helpers'] }) + buildVendorPackage('@glimmer/object-reference', { + external: ['babel-helpers', '@glimmer/util', '@glimmer/reference'] }), + buildVendorPackage('@glimmer/reference', { + external: ['babel-helpers', '@glimmer/util'] }), + buildVendorPackage('@glimmer/runtime', { + external: ['babel-helpers', + '@glimmer/util', + '@glimmer/reference', + '@glimmer/wire-format', + '@glimmer/syntax']}), + buildVendorPackage('@glimmer/syntax', { + external: ['babel-helpers', 'handlebars', 'simple-html-tokenizer'] }), + buildVendorPackage('@glimmer/util', { + external: ['babel-helpers'] }), + buildVendorPackage('@glimmer/wire-format', { + external: ['@glimmer/util'] }), + buildVendorPackage('simple-html-tokenizer'), + funnel(path.dirname(require.resolve('handlebars/package')), { + include: ['dist/handlebars.amd.js'] }) ]; } diff --git a/packages/@glimmer/application/package.json b/packages/@glimmer/application/package.json index af7ec6885..81d8bdebf 100644 --- a/packages/@glimmer/application/package.json +++ b/packages/@glimmer/application/package.json @@ -18,6 +18,7 @@ "test": "testem ci" }, "dependencies": { + "@glimmer/component": "^0.1.0", "@glimmer/di": "^0.1.7", "@glimmer/util": "^0.21.0" }, diff --git a/packages/@glimmer/application/src/application.ts b/packages/@glimmer/application/src/application.ts index b337721ca..4e8de57bb 100644 --- a/packages/@glimmer/application/src/application.ts +++ b/packages/@glimmer/application/src/application.ts @@ -8,20 +8,41 @@ import { RegistrationOptions, setOwner } from '@glimmer/di'; +import { + DynamicScope, + Environment +} from '@glimmer/component'; +import { + Simple, + templateFactory +} from '@glimmer/runtime'; function isTypeSpecifier(specifier: string) { return specifier.indexOf(':') === -1; } +export interface ApplicationOptions { + rootName: string; + rootElement?: Simple.Element; + resolver?: Resolver; +} + export default class Application implements Owner { + public rootName: string; + public rootElement: any; public resolver: Resolver; + public env: Environment; private _registry: Registry; private _container: Container; + private _renderResult: any; // TODO - type + + constructor(options: ApplicationOptions) { + this.rootName = options.rootName; + this.rootElement = options.rootElement; + this.resolver = options.resolver; - constructor(resolver?: Resolver) { - this.resolver = resolver; this._registry = new Registry(); - this._container = new Container(this._registry, resolver); + this._container = new Container(this._registry, this.resolver); // Inject `this` (the app) as the "owner" of every object instantiated // by its container. @@ -30,8 +51,52 @@ export default class Application implements Owner { setOwner(hash, this); return hash; } + + this.initRegistrations(); } + initRegistrations(): void { + this.register(`environment:/${this.rootName}/main/main`, Environment); + this.registerOption('template', 'instantiate', false); + } + + boot(): void { + this.env = this.lookup(`environment:/${this.rootName}/main/main`); + + if (!this.rootElement) { + this.rootElement = this.env.getDOM().createElement('div'); + self.document.body.append(this.rootElement); + } + + this.render(); + } + + render() { + this.env.begin(); + + let mainTemplate = this.lookup(`template:/${this.rootName}/components/main`); + let mainLayout = templateFactory(mainTemplate).create(this.env); + let result = mainLayout.render(null, this.rootElement, new DynamicScope()); + + this.env.commit(); + + this._renderResult = result; + } + + rerender() { + this.env.begin(); + this._renderResult.rerender(); + this.env.commit(); + } + + /** + * Registry accessor methods that normalize specifiers. + * + * TODO: consider converting Registry to be an interface instead of a class + * and then extract these methods to a separate accessor class that implements + * Registry. + */ + register(specifier: string, factory: any, options?: RegistrationOptions): void { let normalizedSpecifier = this._toAbsoluteSpecifier(specifier); this._registry.register(normalizedSpecifier, factory, options); @@ -73,6 +138,10 @@ export default class Application implements Owner { this._registry.registerInjection(normalizedSpecifier, property, normalizedInjection); } + /** + * Owner interface implementation + */ + identify(specifier: string, referrer?: string): string { return this._toAbsoluteSpecifier(specifier, referrer); } @@ -87,6 +156,10 @@ export default class Application implements Owner { return this._container.lookup(absoluteSpecifier); } + /** + * Private methods + */ + private _toAbsoluteSpecifier(specifier: string, referrer?: string): string { if (isSpecifierStringAbsolute(specifier)) { return specifier; diff --git a/packages/@glimmer/application/src/index.ts b/packages/@glimmer/application/src/index.ts index 64ca7eeed..1ab252bdc 100644 --- a/packages/@glimmer/application/src/index.ts +++ b/packages/@glimmer/application/src/index.ts @@ -1 +1 @@ -export { default as Application } from './application'; +export { default as Application, ApplicationOptions } from './application'; diff --git a/packages/@glimmer/application/test/application-container-test.ts b/packages/@glimmer/application/test/application-container-test.ts index 0d3a6f60a..a40e5d80d 100644 --- a/packages/@glimmer/application/test/application-container-test.ts +++ b/packages/@glimmer/application/test/application-container-test.ts @@ -6,7 +6,7 @@ const { module, test } = QUnit; module('Application - Container interface'); test('#identify - returns an absolute specifier unchanged', function(assert) { - let app = new Application(); + let app = new Application({ rootName: 'app' }); let absSpecifier = 'component:/app/components/date-picker'; assert.equal(app.identify(absSpecifier), absSpecifier, 'specifier was returned unchanged'); }); @@ -19,9 +19,10 @@ test('#identify - uses a resolver to convert a relative specifier to an absolute assert.equal(specifier, 'component:date-picker', 'FakeResolver#identify was invoked'); return 'component:/app/components/date-picker'; } + retrieve(specifier: string): any {} } let resolver = new FakeResolver(); - let app = new Application(resolver); + let app = new Application({ rootName: 'app', resolver }); let specifier = 'component:date-picker'; assert.equal(app.identify(specifier, 'component:/app/components/form-controls'), 'component:/app/components/date-picker', 'absolute specifier was returned'); }); @@ -31,7 +32,7 @@ test('#factoryFor - returns a registered factory', function(assert) { static create() { return { foo: 'bar' }; } } - let app = new Application(); + let app = new Application({ rootName: 'app' }); app.register('component:/app/components/date-picker', DatePicker); assert.strictEqual(app.factoryFor('component:/app/components/date-picker'), DatePicker, 'expected factory was returned'); @@ -56,7 +57,7 @@ test('#factoryFor - will use a resolver to locate a factory', function(assert) { } let resolver = new FakeResolver(); - let app = new Application(resolver); + let app = new Application({ rootName: 'app', resolver }); assert.strictEqual(app.factoryFor('component:date-picker'), DatePicker, 'expected factory was returned'); }); @@ -83,7 +84,7 @@ test('#factoryFor - will use a resolver to locate a factory, even if one is regi } let resolver = new FakeResolver(); - let app = new Application(resolver); + let app = new Application({ rootName: 'app', resolver }); app.register('foo:/app/foos/bar', Foo); assert.strictEqual(app.factoryFor('foo:bar'), FooBar, 'factory from resolver was returned'); }); @@ -101,7 +102,7 @@ test('#lookup - returns an instance created by the factory', function(assert) { } } - let app = new Application(); + let app = new Application({ rootName: 'app' }); app.register('foo:/app/foos/bar', FooBar); let foobar = app.lookup('foo:/app/foos/bar'); @@ -120,7 +121,7 @@ test('#lookup - caches looked up instances by default', function(assert) { } } - let app = new Application(); + let app = new Application({ rootName: 'app' }); app.register('foo:/app/foos/bar', FooBar); let foo1 = app.lookup('foo:/app/foos/bar'); @@ -142,7 +143,7 @@ test('#lookup - will not cache lookups specified as non-singletons', function(as } } - let app = new Application(); + let app = new Application({ rootName: 'app' }); app.register('foo:/app/foos/bar', FooBar, { singleton: false }); let foo1 = app.lookup('foo:/app/foos/bar'); @@ -159,7 +160,7 @@ test('#lookup - returns the factory when registrations specify instantiate: fals let factory = {}; - let app = new Application(); + let app = new Application({ rootName: 'app' }); app.register('foo:/app/foos/bar', factory, { instantiate: false }); let foo1 = app.lookup('foo:/app/foos/bar'); @@ -185,7 +186,7 @@ test('#lookup - uses the resolver to locate a registration', function(assert) { } let resolver = new FakeResolver(); - let app = new Application(resolver); + let app = new Application({ rootName: 'app', resolver }); let foo1 = app.lookup('foo:bar'); assert.deepEqual(foo1, { foo: 'bar' }, 'expected factory was invoked'); @@ -213,7 +214,7 @@ test('#lookup - injects references registered by name', function(assert) { } } - let app = new Application(); + let app = new Application({ rootName: 'app' }); app.register('foo:/app/foos/bar', FooBar); app.register('router:/app/root/main', Router); app.registerInjection('foo:/app/foos/bar', 'router', 'router:/app/root/main'); @@ -243,7 +244,7 @@ test('#lookup - injects references registered by type', function(assert) { } } - let app = new Application(); + let app = new Application({ rootName: 'app' }); app.register('foo:/app/foos/bar', FooBar); app.register('router:/app/root/main', Router); app.registerInjection('foo:/app/foos/bar', 'router', 'router:/app/root/main'); diff --git a/packages/@glimmer/application/test/application-registry-test.ts b/packages/@glimmer/application/test/application-registry-test.ts index 22270235f..1640e8ed9 100644 --- a/packages/@glimmer/application/test/application-registry-test.ts +++ b/packages/@glimmer/application/test/application-registry-test.ts @@ -5,17 +5,12 @@ const { module, test } = QUnit; module('Application - Registry interface'); -test('can be instantiated', function(assert) { - let app = new Application(); - assert.ok(app, 'app exists'); -}); - test('#register - registers a factory', function(assert) { class Foo { static create() { return { foo: 'bar' }; } } - let app = new Application(); + let app = new Application({ rootName: 'app' }); assert.strictEqual(app.registration('foo:/app/foos/bar'), undefined, 'factory has not yet been registered'); app.register('foo:/app/foos/bar', Foo); @@ -27,7 +22,7 @@ test('#register - can register options together with a factory', function(assert static create() { return { foo: 'bar' }; } } - let app = new Application(); + let app = new Application({ rootName: 'app' }); assert.strictEqual(app.registration('foo:/app/foos/bar'), undefined, 'factory has not yet been registered'); app.register('foo:/app/foos/bar', Foo, { instantiate: false }); @@ -40,7 +35,7 @@ test('#registration - returns a factory has been registered', function(assert) { static create() { return { foo: 'bar' }; } } - let app = new Application(); + let app = new Application({ rootName: 'app' }); assert.strictEqual(app.registration('foo:/app/foos/bar'), undefined, 'factory has not yet been registered'); app.register('foo:/app/foos/bar', Foo); @@ -52,7 +47,7 @@ test('#unregister - unregisters a factory', function(assert) { static create() { return { foo: 'bar' }; } } - let app = new Application(); + let app = new Application({ rootName: 'app' }); app.register('foo:/app/foos/bar', Foo); assert.strictEqual(app.registration('foo:/app/foos/bar'), Foo, 'factory has been registered'); @@ -65,7 +60,7 @@ test('#registerOption, #registeredOptions, #registeredOption, #unregisterOption' static create() { return { foo: 'bar' }; } } - let app = new Application(); + let app = new Application({ rootName: 'app' }); app.register('foo:/app/foos/bar', Foo); assert.strictEqual(app.registeredOptions('foo:/app/foos/bar'), undefined); @@ -85,7 +80,7 @@ test('Options registered by full name supercede those registered by type', funct static create() { return { foo: 'bar' }; } } - let app = new Application(); + let app = new Application({ rootName: 'app' }); app.register('foo:/app/foos/bar', Foo); diff --git a/packages/@glimmer/application/test/application-test.ts b/packages/@glimmer/application/test/application-test.ts index 9ad970a9b..2372d2540 100644 --- a/packages/@glimmer/application/test/application-test.ts +++ b/packages/@glimmer/application/test/application-test.ts @@ -5,6 +5,6 @@ const { module, test } = QUnit; module('Application'); test('can be instantiated', function(assert) { - let app = new Application(); + let app = new Application({ rootName: 'app' }); assert.ok(app, 'app exists'); });