ember-source
Version:
A JavaScript framework for creating ambitious web applications
199 lines (196 loc) • 6.64 kB
JavaScript
import '../../debug/index.js';
import { isDevelopingApp } from '@embroider/macros';
import { assert } from '../../debug/lib/assert.js';
let uuid = 0;
function isCallback(value) {
return typeof value === 'function';
}
function isOptions(value) {
return value !== null && typeof value === 'object';
}
class DSLImpl {
parent;
// eslint-disable-next-line @typescript-eslint/no-wrapper-object-types
matches;
enableLoadingSubstates;
explicitIndex = false;
options;
constructor(name = null, options) {
this.parent = name;
this.enableLoadingSubstates = Boolean(options && options.enableLoadingSubstates);
this.matches = [];
this.options = options;
}
route(name, _options, _callback) {
let options;
let callback = null;
let dummyErrorRoute = `/_unused_dummy_error_path_route_${name}/:error`;
if (isCallback(_options)) {
(isDevelopingApp() && !(arguments.length === 2) && assert('Unexpected arguments', arguments.length === 2));
options = {};
callback = _options;
} else if (isCallback(_callback)) {
(isDevelopingApp() && !(arguments.length === 3) && assert('Unexpected arguments', arguments.length === 3));
(isDevelopingApp() && !(isOptions(_options)) && assert('Unexpected arguments', isOptions(_options)));
options = _options;
callback = _callback;
} else {
options = _options || {};
}
(isDevelopingApp() && !((() => {
if (options.overrideNameAssertion === true) {
return true;
}
return ['basic', 'application'].indexOf(name) === -1;
})()) && assert(`'${name}' cannot be used as a route name.`, (() => {
if (options.overrideNameAssertion === true) {
return true;
}
return ['basic', 'application'].indexOf(name) === -1;
})()));
(isDevelopingApp() && !(name.indexOf(':') === -1) && assert(`'${name}' is not a valid route name. It cannot contain a ':'. You may want to use the 'path' option instead.`, name.indexOf(':') === -1));
if (this.enableLoadingSubstates) {
createRoute(this, `${name}_loading`, {
resetNamespace: options.resetNamespace
});
createRoute(this, `${name}_error`, {
resetNamespace: options.resetNamespace,
path: dummyErrorRoute
});
}
if (callback) {
let fullName = getFullName(this, name, options.resetNamespace);
let dsl = new DSLImpl(fullName, this.options);
createRoute(dsl, 'loading');
createRoute(dsl, 'error', {
path: dummyErrorRoute
});
callback.call(dsl);
createRoute(this, name, options, dsl.generate());
} else {
createRoute(this, name, options);
}
}
push(url, name, callback,
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
serialize) {
let parts = name.split('.');
if (this.options.engineInfo) {
let localFullName = name.slice(this.options.engineInfo.fullName.length + 1);
let routeInfo = Object.assign({
localFullName
}, this.options.engineInfo);
if (serialize) {
routeInfo.serializeMethod = serialize;
}
this.options.addRouteForEngine(name, routeInfo);
} else if (serialize) {
throw new Error(`Defining a route serializer on route '${name}' outside an Engine is not allowed.`);
}
if (url === '' || url === '/' || parts[parts.length - 1] === 'index') {
this.explicitIndex = true;
}
this.matches.push(url, name, callback);
}
generate() {
let dslMatches = this.matches;
if (!this.explicitIndex) {
this.route('index', {
path: '/'
});
}
return match => {
for (let i = 0; i < dslMatches.length; i += 3) {
match(dslMatches[i]).to(dslMatches[i + 1], dslMatches[i + 2]);
}
};
}
mount(_name, options = {}) {
let engineRouteMap = this.options.resolveRouteMap(_name);
let name = _name;
if (options.as) {
name = options.as;
}
let fullName = getFullName(this, name, options.resetNamespace);
let engineInfo = {
name: _name,
instanceId: uuid++,
mountPoint: fullName,
fullName
};
let path = options.path;
if (typeof path !== 'string') {
path = `/${name}`;
}
let callback;
let dummyErrorRoute = `/_unused_dummy_error_path_route_${name}/:error`;
if (engineRouteMap) {
let shouldResetEngineInfo = false;
let oldEngineInfo = this.options.engineInfo;
if (oldEngineInfo) {
shouldResetEngineInfo = true;
this.options.engineInfo = engineInfo;
}
let optionsForChild = Object.assign({
engineInfo
}, this.options);
let childDSL = new DSLImpl(fullName, optionsForChild);
createRoute(childDSL, 'loading');
createRoute(childDSL, 'error', {
path: dummyErrorRoute
});
engineRouteMap.class.call(childDSL);
callback = childDSL.generate();
if (shouldResetEngineInfo) {
this.options.engineInfo = oldEngineInfo;
}
}
let localFullName = 'application';
let routeInfo = Object.assign({
localFullName
}, engineInfo);
if (this.enableLoadingSubstates) {
// These values are important to register the loading routes under their
// proper names for the Router and within the Engine's registry.
let substateName = `${name}_loading`;
let localFullName = `application_loading`;
let routeInfo = Object.assign({
localFullName
}, engineInfo);
createRoute(this, substateName, {
resetNamespace: options.resetNamespace
});
this.options.addRouteForEngine(substateName, routeInfo);
substateName = `${name}_error`;
localFullName = `application_error`;
routeInfo = Object.assign({
localFullName
}, engineInfo);
createRoute(this, substateName, {
resetNamespace: options.resetNamespace,
path: dummyErrorRoute
});
this.options.addRouteForEngine(substateName, routeInfo);
}
this.options.addRouteForEngine(fullName, routeInfo);
this.push(path, fullName, callback);
}
}
function canNest(dsl) {
return dsl.parent !== 'application';
}
function getFullName(dsl, name, resetNamespace) {
if (canNest(dsl) && resetNamespace !== true) {
return `${dsl.parent}.${name}`;
} else {
return name;
}
}
function createRoute(dsl, name, options = {}, callback) {
let fullName = getFullName(dsl, name, options.resetNamespace);
if (typeof options.path !== 'string') {
options.path = `/${name}`;
}
dsl.push(options.path, fullName, callback, options.serialize);
}
export { DSLImpl as default };