beyond
Version:
The Full Stack Universal Typescript Framework
214 lines (173 loc) • 6.55 kB
JavaScript
const qs = require('querystring');
module.exports = class {
#application;
get application() {
return this.#application;
}
#pathname;
get pathname() {
return this.#pathname;
}
#query;
get query() {
return this.#query;
}
#found = false;
get found() {
return this.#found;
}
// Can be 'bundle' or 'transversal'
#is;
get is() {
return this.#is;
}
#qs;
#hmr;
get hmr() {
return this.#hmr;
}
#info;
get info() {
return this.#info;
}
#extname;
get extname() {
return this.#extname;
}
#language;
get language() {
return this.#language;
}
#bundle;
get bundle() {
return this.#bundle;
}
#pkg;
get pkg() {
return this.#pkg;
}
// Is it a bundle contained in a package that is not the current project
#foreign;
get foreign() {
return this.#foreign;
}
#error;
get error() {
return this.#error;
}
get valid() {
return !this.#error;
}
/**
* Pathname parser constructor
*
* @param application {object} The application object
* @param pathname {string} The pathname
* @param query {string} The querystring
*/
constructor(application, pathname, query) {
this.#application = application;
this.#pathname = pathname;
this.#query = query;
}
async process(distribution) {
const application = this.#application;
const pathname = this.#pathname;
this.#qs = qs.parse(this.#query);
this.#hmr = this.#qs.hmr !== void 0;
this.#info = this.#qs.info !== void 0;
const extname = this.#extname = (() => {
const dts = pathname.endsWith('.d.ts') ? '.d.ts' : void 0;
return dts ? dts : require('path').extname(pathname);
})();
const split = pathname.substr(1, pathname.length - 1 - extname.length).split('/');
// If the resource do not starts with '/packages', then it is a bundle contained in the application package
// Otherwise it should be a foreign bundle
if (split[0] === 'packages') {
split.shift(); // Remove the 'packages' entry
this.#foreign = true;
this.#pkg = split[0].startsWith('@') ? `${split.shift()}/${split.shift()}` : split.shift();
if (!split.length) return;
}
else {
this.#foreign = false;
this.#pkg = application.package;
}
// unknown can have the language and or the name of the bundle
const [filename, ...unknown] = split.pop().split('.');
if (unknown.length > 2) return;
// Check if resource is a transversal bundle
const transversal = await (async () => {
if (split.length) return; // It cannot be a transversal bundle
await global.bundles.ready;
if (!global.bundles.has(filename)) return;
const specs = global.bundles.get(filename);
if (!specs.transversal) return;
// unknown should only have the language
if (unknown.length > 1) return;
const {transversals} = application;
if (!transversals.has(filename)) return;
if (specs.multilanguage && !unknown.length) return;
this.#found = true;
this.#is = 'transversal';
this.#bundle = transversals.get(filename);
this.#language = unknown.length ? unknown[0] : void 0;
})();
if (transversal) return;
// Check if the resource is a module
const {modules} = application;
await modules.ready;
let resource = split.length ? `${split.join('/')}/${filename}` : filename;
resource = `${this.#pkg}/${resource}`;
const {platform} = distribution;
const key = `${resource}//${platform}`;
if (!modules.platforms.has(key)) {
if (modules.resources.has(resource)) {
this.#error = `Module "${resource}" does not support "${platform}" platform`;
}
return;
}
const module = modules.platforms.get(key);
// At this point, alternatives of unknown are, expressed with examples:
// home.js => [] a module with only one bundle that is not multilanguage
// home.en.js => ['en'] home only has a txt bundle, then 'en' is the language
// home.txt.js => ['txt'] home has more than one bundle, and it is not multilanguage, then 'txt' is the bundle
// home.txt.en.js => ['txt', 'en'] home has more than one bundle, and it is not multilanguage, then 'txt' is the bundle
await module.ready;
await module.bundles.ready;
const {bundle, language} = await (async () => {
if (module.bundles.size === 1) {
// Module has only one bundle, so unknown should be empty,
// or only contain the language if the bundle is multilanguage
if (unknown.length > 1) return {};
const bundle = module.bundles.get([...module.bundles.keys()][0]);
return {bundle, language: unknown[0]};
}
else {
// unknown should at least specify the bundle name
// and if the bundle is multilanguage, then it must specify the language too
if (!unknown.length) return {};
const name = unknown.shift();
let bundle;
// Find the bundle in the collection of bundles of the module
for (const b of module.bundles.values()) {
await b.ready;
if (b.iname === name) {
bundle = b;
break;
}
}
if (!bundle) return {};
return {bundle, language: unknown[0]};
}
})();
if (!bundle) return;
await bundle.ready;
if (!bundle.multilanguage && language) return;
if (bundle.multilanguage && !language) return;
this.#found = true;
this.#is = 'bundle';
this.#bundle = bundle;
this.#language = language;
}
}