@beyond-js/bundles-sdk
Version:
BeyondJS Bundles SDK
216 lines (172 loc) • 5.7 kB
JavaScript
const SourceMap = require('@beyond-js/bundles-sdk/source-map');
const DynamicProcessor = require('@beyond-js/dynamic-processor')();
const ipc = require('@beyond-js/ipc/main');
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 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 };
}
};