@ima/core
Version:
IMA.js framework for isomorphic javascript application
206 lines (205 loc) • 7.07 kB
JavaScript
import { GenericError } from '../error/GenericError';
/**
* Regular expression used to match and remove the starting and trailing
* forward slashes from a path expression or a URL path.
*
* @const
* @type {RegExp}
*/ export const LOOSE_SLASHES_REGEXP = /^\/|\/$|\/(?=\?)/g;
/**
* Utility for representing and manipulating a single route in the router's
* configuration.
*/ export class AbstractRoute {
/**
* The unique name of this route, identifying it among the rest of the
* routes in the application.
*/ _name;
/**
* Path expression used in route matching, to generate valid path with
* provided params and parsing params from current path.
*/ _pathExpression;
/**
* The full name of Object Container alias identifying the controller
* associated with this route.
*/ _controller;
/**
* The full name or Object Container alias identifying the view class
* associated with this route.
*/ _view;
/**
* The route additional options.
*/ _options;
/**
* Initializes the route.
*
* @param name The unique name of this route, identifying it among
* the rest of the routes in the application.
* @param pathExpression Path expression used in route matching, to generate
* valid path with provided params and parsing params from current path.
* @param controller The full name of Object Container alias
* identifying the controller associated with this route.
* @param view The full name or Object Container alias identifying
* the view class associated with this route.
* @param options The route additional options.
*/ constructor(name, pathExpression, controller, view, options){
this._name = name;
this._pathExpression = pathExpression;
this._controller = {
resolved: !this.isAsync(controller),
controller: controller,
cached: null
};
this._view = {
resolved: !this.isAsync(view),
view: view,
cached: null
};
/**
* Init options with defaults.
*/ this._options = {
...{
autoScroll: true,
documentView: null,
managedRootView: null,
onlyUpdate: false,
viewAdapter: null,
middlewares: []
},
...options
};
}
/**
* Returns the unique identifying name of this route.
*
* @return The name of the route, identifying it.
*/ getName() {
return this._name;
}
/**
* Checks if given argument is an async handler.
*/ isAsync(module) {
return module?.constructor.name === 'AsyncFunction' || module instanceof Promise;
}
/**
* Returns Controller class/alias/constant associated with this route.
* Internally caches async calls for dynamically imported controllers,
* meaning that once they're loaded, you get the same promise for
* subsequent calls.
*
* @return The Controller class/alias/constant.
*/ getController() {
if (!this._controller.cached) {
this._controller.cached = !this._controller.resolved ? this._controller.controller().then((module)=>{
this._controller.resolved = true;
return module.default ?? module;
}) : this._controller.controller;
}
return this._controller.cached;
}
/**
* Returns true for resolved controller. This is always true
* for sync route views.
*/ isControllerResolved() {
return this._controller.resolved;
}
/**
* Returns View class/alias/constant associated with this route.
* Internally caches async calls for dynamically imported views,
* meaning that once they're loaded, you get the same promise for
* subsequent calls.
*
* @return The View class/alias/constant.
*/ getView() {
if (!this._view.cached) {
this._view.cached = !this._view.resolved ? this._view.view().then((module)=>{
this._view.resolved = true;
return module.default ?? module;
}) : this._view.view;
}
return this._view.cached;
}
/**
* Returns true for resolved view. This is always true
* for sync route views.
*/ isViewResolved() {
return this._view.resolved;
}
/**
* Return route additional options.
*/ getOptions() {
return this._options;
}
/**
* Path expression used in route matching, to generate valid path with
* provided params and parsing params from current path.
*
* @return The path expression.
*/ getPathExpression() {
return this._pathExpression;
}
/**
* Trims the trailing forward slash from the provided URL path.
*
* @param path The path to trim.
* @return Trimmed path.
*/ getTrimmedPath(path) {
return `/${path.replace(LOOSE_SLASHES_REGEXP, '')}`;
}
/**
* Preloads dynamically imported view and controller.
*
* @return Promise.All resolving to [view, controller] tuple.
*/ async preload() {
return Promise.all([
this.getController(),
this.getView()
]);
}
/**
* Creates the URL and query parts of a URL by substituting the route's
* parameter placeholders by the provided parameter value.
*
* The extraneous parameters that do not match any of the route's
* placeholders will be appended as the query string.
*
* @param params The route
* parameter values.
* @return Path and, if necessary, query parts of the URL
* representing this route with its parameters replaced by the
* provided parameter values.
*/ toPath(params) {
throw new GenericError('The ima.core.router.AbstractRoute.toPath method is abstract ' + 'and must be overridden', {
params
});
}
/**
* Tests whether the provided URL path matches this route. The provided
* path may contain the query.
*
* @param path The URL path.
* @return `true` if the provided path matches this route.
*/ matches(path) {
throw new GenericError('The ima.core.router.AbstractRoute.matches method is abstract ' + 'and must be overridden', {
path
});
}
/**
* Extracts the parameter values from the provided path. The method
* extracts both the in-path parameters and parses the query, allowing the
* query parameters to override the in-path parameters.
*
* The method returns an empty hash object if the path does not match this
* route.
*
* @param path Currently routed path.
* @param baseUrl Currently routed baseUrl.
* @return Map of parameter names to parameter
* values.
*/ extractParameters(path, baseUrl) {
throw new GenericError('The ima.core.router.AbstractRoute.extractParameters method is abstract ' + 'and must be overridden', {
path,
baseUrl
});
}
}
//# sourceMappingURL=AbstractRoute.js.map