UNPKG

7.31 kBJavaScriptView Raw
1import 'aurelia-polyfills';
2import {PLATFORM, isInitialized} from 'aurelia-pal';
3
4let bootstrapPromises = [];
5let startResolve;
6
7const startPromise = new Promise(resolve => startResolve = resolve);
8const host = PLATFORM.global;
9const isNodeLike = typeof process !== 'undefined' && !process.browser;
10
11function ready() {
12 if (!host.document || host.document.readyState === 'complete') {
13 return Promise.resolve();
14 }
15
16 return new Promise(resolve => {
17 host.document.addEventListener('DOMContentLoaded', completed);
18 host.addEventListener('load', completed);
19
20 function completed() {
21 host.document.removeEventListener('DOMContentLoaded', completed);
22 host.removeEventListener('load', completed);
23 resolve();
24 }
25 });
26}
27
28function createLoader() {
29 // Note: Please do NOT add any PLATFORM.moduleName annotation in this method,
30 // for example around 'aurelia-loader-default'.
31 // Each import has been carefully written so that it is picked up by
32 // its respective bundler and is ignored by others.
33 // Adding moduleName() would add a static dependency, which we don't
34 // want as the correct loader is determined at build time.
35
36 // Custom Loader Support
37 if (PLATFORM.Loader) {
38 return Promise.resolve(new PLATFORM.Loader());
39 }
40
41 if (typeof AURELIA_WEBPACK_2_0 === 'undefined') {
42 // Webpack Loader Support
43 if (typeof __webpack_require__ !== 'undefined') {
44 // Webpack needs the require to be top level to parse the request.
45 // However, we don't want to use require or that will cause the Babel
46 // transpiler to detect an incorrect dependency.
47 const m = __webpack_require__(require.resolve('aurelia-loader-webpack'));
48 return Promise.resolve(new m.WebpackLoader());
49 }
50
51 // SystemJS Loader Support
52 if (host.System && typeof host.System.config === 'function') {
53 return host.System.normalize('aurelia-bootstrapper').then(bsn => {
54 return host.System.normalize('aurelia-loader-default', bsn);
55 }).then(loaderName => {
56 return host.System.import(loaderName).then(m => new m.DefaultLoader());
57 });
58 }
59
60 // AMD Module Loader Support
61 if (typeof host.require === 'function' && typeof host.require.version === 'string') {
62 return new Promise((resolve, reject) => host.require(['aurelia-loader-default'], m => resolve(new m.DefaultLoader()), reject));
63 }
64
65 // Node.js and Electron Support
66 if (isNodeLike && typeof module !== 'undefined' && typeof module.require !== 'undefined') {
67 // note: we use a scoped module.require() instead of simply require()
68 // so that Webpack's parser does not automatically include this loader as a dependency,
69 // similarly to the non-global call to System.import() above
70 const m = module.require('aurelia-loader-nodejs');
71 return Promise.resolve(new m.NodeJsLoader());
72 }
73 } // endif AURELIA_WEBPACK_2_0
74
75 return Promise.reject('No PLATFORM.Loader is defined and there is neither a System API (ES6) or a Require API (AMD) globally available to load your app.');
76}
77
78function initializePal(loader) {
79 let type;
80
81 const isRenderer = isNodeLike && (process.type === 'renderer' || process.versions['node-webkit']);
82
83 if (isNodeLike && !isRenderer) {
84 type = 'nodejs';
85 } else if (typeof window !== 'undefined') {
86 type = 'browser';
87 } else if (typeof self !== 'undefined') {
88 type = 'worker';
89 } else {
90 throw new Error('Could not determine platform implementation to load.');
91 }
92
93 // Note: Please do NOT try to add PLATFORM.moduleName() annotations here.
94 // This would create a static dependency between bootstrapper and a PAL, which we don't want.
95 // The correct PAL to bundle must be determined by the bundling tool at build time.
96 return loader.loadModule('aurelia-pal-' + type)
97 .then(palModule => type === 'nodejs' && !isInitialized && palModule.globalize() || palModule.initialize());
98}
99
100function preparePlatform(loader) {
101 const map = (moduleId, relativeTo) =>
102 loader.normalize(moduleId, relativeTo)
103 .then(normalized => {
104 loader.map(moduleId, normalized);
105 return normalized;
106 });
107
108 return initializePal(loader)
109 .then(() => loader.normalize('aurelia-bootstrapper'))
110 .then(bootstrapperName => {
111 // aurelia-framework re-exports pretty much everything.
112 // As can be seen at the end of this method, the only field accessed by bootstrapper is `Aurelia`,
113 // so we document that to enable tree shaking on all other exported members.
114 const frameworkPromise = map(PLATFORM.moduleName('aurelia-framework', { exports: ['Aurelia'] }),
115 bootstrapperName);
116 // Please do NOT add PLATFORM.moduleName() around any of those modules.
117 // They are not actually loaded here, only mapped.
118 return Promise.all([
119 frameworkPromise,
120 frameworkPromise.then(frameworkName => map('aurelia-dependency-injection', frameworkName)),
121 map('aurelia-router', bootstrapperName),
122 map('aurelia-logging-console', bootstrapperName)
123 ]);
124 })
125 .then(([frameworkName]) => loader.loadModule(frameworkName))
126 .then(fx => startResolve(() => new fx.Aurelia(loader)));
127}
128
129function config(appHost, configModuleId, aurelia) {
130 aurelia.host = appHost;
131 aurelia.configModuleId = configModuleId || null;
132
133 if (configModuleId) {
134 return aurelia.loader
135 .loadModule(configModuleId)
136 .then(customConfig => {
137 if (!customConfig.configure) {
138 throw new Error(`Cannot initialize module '${configModuleId}' without a configure function.`);
139 }
140
141 return customConfig.configure(aurelia);
142 });
143 }
144
145 aurelia.use
146 .standardConfiguration()
147 .developmentLogging();
148
149 return aurelia.start().then(() => aurelia.setRoot());
150}
151
152function run() {
153 return ready()
154 .then(createLoader)
155 .then(preparePlatform)
156 .then(() => {
157 const appHosts = host.document.querySelectorAll('[aurelia-app],[data-aurelia-app]');
158 for (let i = 0, ii = appHosts.length; i < ii; ++i) {
159 const appHost = appHosts[i];
160 const moduleId = appHost.getAttribute('aurelia-app') || appHost.getAttribute('data-aurelia-app');
161 bootstrap(config.bind(null, appHost, moduleId));
162 }
163
164 // This can't be moved before preparePlatform.
165 // In old IE the console object only exists after F12 tools have been opened and PAL creates a substitute.
166 const toConsole = console.error.bind(console);
167 const bootstraps = bootstrapPromises.map(p => p.catch(toConsole));
168 bootstrapPromises = null;
169 return Promise.all(bootstraps);
170 });
171}
172
173/**
174 * Manually bootstraps an application.
175 * @param configure A callback which passes an Aurelia instance to the developer to manually configure and start up the app.
176 * @return A Promise that completes when configuration is done.
177 */
178export function bootstrap(configure: Function): Promise<void> {
179 const p = startPromise.then(factory => configure(factory()));
180 if (bootstrapPromises) bootstrapPromises.push(p);
181 return p;
182}
183
184/**
185 * A promise that represents the bootstrapper's startup process.
186 * It resolves when the process has finished starting.
187 */
188export const starting = run();