@beyond-js/bundles-sdk
Version:
BeyondJS Bundles SDK
288 lines (227 loc) • 7.18 kB
JavaScript
const DynamicProcessor = require('@beyond-js/dynamic-processor')();
const ipc = require('@beyond-js/ipc/main');
const Diagnostics = require('../../diagnostics');
const Meta = require('./meta');
module.exports = class extends DynamicProcessor {
get dp() {
return 'packager.compiler';
}
#packager;
get packager() {
return this.#packager;
}
get id() {
return this.#packager.id;
}
get dependencies() {
return this.#packager.processor.dependencies;
}
get specs() {
return this.#packager.processor.specs;
}
#CompiledSource = require('../../source/compiled');
get CompiledSource() {
return this.#CompiledSource;
}
get analyzer() {
return this.#children.analyzer;
}
get options() {
return this.#children.options;
}
get sources() {
return {
files: this.#children.files,
extensions: this.#children.extensions,
overwrites: this.#children.overwrites,
};
}
get extended() {
return this.#children.extended;
}
#files = new Map();
get files() {
return this.#files;
}
#extensions = new Map();
get extensions() {
return this.#extensions;
}
#overwrites = new Map();
get overwrites() {
return this.#overwrites;
}
#meta;
get meta() {
return this.#meta;
}
#diagnostics;
get diagnostics() {
return this.#diagnostics;
}
get valid() {
return this.#diagnostics.valid;
}
#cache;
#children;
#hashes;
get hashes() {
return this.#hashes;
}
get synchronized() {
return this.#hashes.synchronized;
}
get updated() {
return this.#hashes.updated;
}
get path() {
const { files } = this.#packager.sources;
return files.path;
}
_notify() {
let [processorId, distributionId] = this.id.split('///');
let id = processorId.split('//');
id = id.slice(0, id.length - 2).join('//');
ipc.notify('data-notification', {
type: 'record/update',
table: 'packagers-compilers',
id: `${id}///${distributionId}`,
});
}
constructor(packager, Children) {
super();
this.#packager = packager;
Children = Children ? Children : require('./children');
this.#children = new Children(this);
this.#hashes = new (require('./hashes'))(this);
this.#cache = new (require('./cache'))(this);
super.setup(new Map([['processor.hashes', { child: packager.processor.hashes }]]));
}
async _begin() {
const cached = await this.#cache.load();
// Allow the hydrate method to by async.
// (The sass processor requires the hydrate method to be async)
cached && (await this.hydrate(cached));
}
_prepared(require) {
const ph = this.children.get('processor.hashes').child;
if (!ph.synchronized) return 'processor hashes are not synchronized';
if (this.updated && !this.#children.disposed) return;
// When the processor is updated, the data is taken from the cache, otherwise the children must be set.
this.#children.dispose();
this.sources.files?.forEach(source => require(source));
this.sources.overwrites?.forEach(source => require(source));
const { analyzer, dependencies, extended } = this;
if (analyzer && (!require(analyzer) || !analyzer.synchronized)) return 'analyzer is not synchronized';
if (dependencies && (!require(dependencies) || !dependencies.synchronized))
return 'dependencies are not synchronized';
if (extended && (!require(extended) || !extended.synchronized))
return 'extended compilers are not synchronized';
dependencies?.forEach(dependency => require(dependency));
}
async _process(request) {
const { processor } = this.#packager;
if (this.updated) {
this.#hashes.update();
// It is not required to update the compilation code, but the root hashes have changed
this.#cache.save().catch(exc => console.log(exc.stack));
return { notify: false };
}
const meta = new Meta();
const diagnostics = new Diagnostics();
const updated = { files: new Map(), extensions: new Map(), overwrites: new Map() };
const done = () => {
this.#hashes.update();
this.#meta = meta;
this.#diagnostics = diagnostics;
this.#files.clear();
this.#extensions.clear();
this.#overwrites.clear();
updated.files.forEach((value, key) => this.#files.set(key, value));
updated.extensions.forEach((value, key) => this.#extensions.set(key, value));
updated.overwrites.forEach((value, key) => this.#overwrites.set(key, value));
// Save the new compilation into cache
this.#cache.save().catch(exc => console.log(exc.stack));
};
const { extender } = processor;
if (extender && !extender?.preprocessor.valid) {
diagnostics.set(extender.preprocessor.diagnostics);
return done();
}
if (!processor.valid) {
diagnostics.set({ general: processor.errors });
return done();
}
if (this.options && !this.options.valid) {
diagnostics.set({ general: this.options.errors });
return done();
}
if (this.analyzer && !this.analyzer.valid) {
diagnostics.set(this.analyzer.diagnostics);
return done();
}
if (this.extended && !this.extended.valid) {
this.extended.errors.forEach(error => diagnostics.general.push(error));
return done();
}
// Check for errors in the dependencies before trying to compile
const errors = (() => {
const { dependencies } = this;
if (!dependencies) return [];
if (!dependencies.valid) {
dependencies.errors.forEach(error => diagnostics.general.push(error));
return done();
}
const errors = [];
dependencies.forEach(
({ specifier, valid }) => !valid && errors.push(`Dependency "${specifier}" is invalid`),
);
return errors;
})();
if (errors.length) {
errors.forEach(error => diagnostics.general.push(error));
return done();
}
await this._compile(updated, diagnostics, meta, request);
if (request !== this._request) return;
done();
}
// Hydrate from cache
hydrate(cached) {
// Convert raw data object into source objects
let { hashes, files, extensions, overwrites, diagnostics, meta } = cached;
this.#hashes.hydrate(hashes);
const hydrate = (is, cached) => {
const { processor, distribution } = this.#packager;
const source = new this.CompiledSource(processor, distribution, is);
source.hydrate(cached);
return source;
};
files = new Map(files);
files.forEach((cached, key) => files.set(key, hydrate('source', cached)));
files.forEach((source, key) => this.#files.set(key, source));
extensions = new Map(extensions);
extensions.forEach((cached, key) => extensions.set(key, hydrate('source', cached)));
extensions.forEach((source, key) => this.#extensions.set(key, source));
overwrites = new Map(overwrites);
overwrites.forEach((cached, key) => overwrites.set(key, hydrate('overwrite', cached)));
overwrites.forEach((source, key) => this.#overwrites.set(key, source));
this.#diagnostics = new Diagnostics();
this.#diagnostics.hydrate(diagnostics);
this.#meta = new Meta();
this.#meta.hydrate(meta);
}
toJSON() {
const { files, extensions, overwrites } = this;
const diagnostics = this.diagnostics.toJSON();
const meta = this.meta;
return {
hashes: this.#hashes.toJSON(),
files: [...files],
extensions: [...extensions],
overwrites: [...overwrites],
diagnostics,
meta,
};
}
};