beyond
Version:
The Full Stack Universal Typescript Framework
212 lines (168 loc) • 6.77 kB
JavaScript
const DynamicProcessor = global.utils.DynamicProcessor();
const {ipc} = global.utils;
module.exports = class extends DynamicProcessor {
get dp() {
return 'bundler.bundle.packager.code';
}
get id() {
return this.#packager.id;
}
#packager;
get packager() {
return this.#packager;
}
#extname;
get extname() {
return this.#extname;
}
#hash;
#cache;
#errors;
get errors() {
this.__update();
return this.#errors ? this.#errors : [];
}
get valid() {
return this.processed && !this.errors.length;
}
#processorsCount;
get processorsCount() {
this.__update();
return this.#processorsCount;
}
#sourcemaps = {};
code(hmr) {
this.__update(hmr);
return this.#sourcemaps[hmr ? 'hmr' : 'code']?.code;
}
map(hmr) {
this.__update(hmr);
return this.#sourcemaps[hmr ? 'hmr' : 'code']?.map;
}
_notify() {
const {bundle, distribution, language} = this.#packager;
const message = {
type: 'change',
specifier: bundle.specifier,
vspecifier: bundle.vspecifier,
extname: this.#extname,
distribution: distribution.key,
language
};
ipc.notify('bundles', message);
}
constructor(extname, packager) {
if (!['.js', '.css'].includes(extname)) throw new Error('Invalid parameters');
super();
this.#extname = extname;
this.#packager = packager;
this.#cache = new (require('./cache'))(this);
super.setup(new Map([['hash', {child: packager.hash}]]));
}
async _begin() {
const cached = await this.#cache.load();
cached && this.hydrate(cached);
await this.#packager.ready;
}
get updated() {
const hash = this.children.get('hash').child;
return hash.value === this.#hash;
}
_prepared(require) {
if (this.updated) return;
const ext = this.#extname === '.js' ? 'js' : 'css';
if (!this.children.has('processors')) {
const packager = this.#packager;
const children = new Map();
children.set('processors', {child: packager.processors});
this.children.register(children);
}
const processors = this.children.get('processors').child;
if (!require(processors)) return;
// Check that all processors packagers are prepared and synchronized
const synchronized = (() => {
let synchronized = true;
processors.forEach(processor => {
const packager = processor.packager?.[ext];
if (!packager) return;
if (!require(packager)) return;
synchronized = synchronized && packager.synchronized;
});
return synchronized;
})();
if (!synchronized) return 'processors packagers are not synchronized';
}
_process() {
if (this.updated && !this.#sourcemaps.code && !this.#sourcemaps.hmr && !this.#errors?.length) {
throw new Error('Sourcemap or errors should be defined');
}
if (this.updated) return false;
this.#hash = this.#errors = void 0;
this.#sourcemaps = {};
}
// This method is overwritten by the js and css code processors
_update(hmr) {
void (hmr);
throw new Error('This method must be overridden');
}
__update(hmr) {
let sourcemap = hmr ? this.#sourcemaps.hmr : this.#sourcemaps.code;
if (sourcemap || this.#errors?.length) return; // Already processed
if (!this.processed) {
throw new Error('Processor is not ready. Wait for the .ready property before accessing its state.');
}
const done = ({sourcemap, errors}) => {
this.#hash = this.children.get('hash').child.value;
this.#sourcemaps[hmr ? 'hmr' : 'code'] = sourcemap;
this.#errors = errors ? errors : [];
// Save the code into cache
!hmr && this.#cache.save();
}
if (hmr && !this.children.has('processors')) {
const message = 'HMR code or map cannot be requested if the processor is up-to-date ' +
'and it has been previously obtained from cache';
const sourcemap = new global.SourceMap();
sourcemap.concat(`// ${message}`);
return done({sourcemap});
}
let processors = this.children.get('processors').child;
// Filter the processors that implement the corresponding code extension (.js or .css)
const ext = this.#extname === '.js' ? 'js' : 'css';
// Check if any of the processors is not in a valid state
let errors = [];
let count = this.#processorsCount = 0;
for (const [name, {packager}] of processors) {
if (!packager) continue;
if (!packager[ext]) continue; // Packager does not support the extname of the bundle being processed
// Legacy processors "scss" and "less" injects the css code in the .js bundle, not .css bundle is supported
// if (['scss', 'less'].includes(name) && this.#extname === '.css') continue;
count++;
!packager[ext].valid && errors.push(`Processor "${name}" has been compiled with errors.`);
}
this.#processorsCount = count;
if (errors.length) return done({errors});
// Update the code
({sourcemap, errors} = this._update(hmr));
if (sourcemap && errors?.length) throw new Error('Only sourcemap or errors should be returned from processor code');
if (!sourcemap && !errors?.length) throw new Error('Processor code packager should return a sourcemap or errors');
done({sourcemap, errors});
}
hydrate(cached) {
const {hash, sourcemap, processorsCount, errors} = cached;
this.#hash = hash;
this.#sourcemaps.code = sourcemap;
this.#processorsCount = processorsCount;
this.#errors = errors;
}
toJSON() {
const hash = this.#hash;
const sourcemap = (() => {
if (!this.#sourcemaps.code) return;
const {code, map} = this.#sourcemaps.code;
return {code, map};
})();
const errors = this.#errors;
const processorsCount = this.#processorsCount;
return {hash, sourcemap, processorsCount, errors}
}
}