gitlab-org--gitlab-foss/config/helpers/incremental_webpack_compiler.js

131 lines
3.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* eslint-disable max-classes-per-file, no-underscore-dangle */
const fs = require('fs');
const path = require('path');
const log = (msg, ...rest) => console.log(`IncrementalWebpackCompiler: ${msg}`, ...rest);
// If we force a recompile immediately, the page reload doesn't seem to work.
// Five seconds seem to work fine and the user can read the message
const TIMEOUT = 5000;
/* eslint-disable class-methods-use-this */
class NoopCompiler {
constructor() {
this.enabled = false;
}
filterEntryPoints(entryPoints) {
return entryPoints;
}
logStatus() {}
setupMiddleware() {}
}
/* eslint-enable class-methods-use-this */
class IncrementalWebpackCompiler {
constructor(historyFilePath) {
this.enabled = true;
this.history = {};
this.compiledEntryPoints = new Set([
// Login page
'pages.sessions.new',
// Explore page
'pages.root',
]);
this.historyFilePath = historyFilePath;
this._loadFromHistory();
}
filterEntryPoints(entrypoints) {
return Object.fromEntries(
Object.entries(entrypoints).map(([key, val]) => {
if (this.compiledEntryPoints.has(key)) {
return [key, val];
}
return [key, ['./webpack_non_compiled_placeholder.js']];
}),
);
}
logStatus(totalCount) {
const current = this.compiledEntryPoints.size;
log(`Currently compiling route entrypoints: ${current} of ${totalCount}`);
}
setupMiddleware(app, server) {
app.use((req, res, next) => {
const fileName = path.basename(req.url);
/**
* We are only interested in files that have a name like `pages.foo.bar.chunk.js`
* because those are the ones corresponding to our entry points.
*
* This filters out hot update files that are for example named "pages.foo.bar.[hash].hot-update.js"
*/
if (fileName.startsWith('pages.') && fileName.endsWith('.chunk.js')) {
const chunk = fileName.replace(/\.chunk\.js$/, '');
this._addToHistory(chunk);
if (!this.compiledEntryPoints.has(chunk)) {
log(`First time we are seeing ${chunk}. Adding to compilation.`);
this.compiledEntryPoints.add(chunk);
setTimeout(() => {
server.middleware.invalidate(() => {
if (server.sockets) {
server.sockWrite(server.sockets, 'content-changed');
}
});
}, TIMEOUT);
}
}
next();
});
}
// private methods
_addToHistory(chunk) {
if (!this.history[chunk]) {
this.history[chunk] = { lastVisit: null, count: 0 };
}
this.history[chunk].lastVisit = Date.now();
this.history[chunk].count += 1;
try {
fs.writeFileSync(this.historyFilePath, JSON.stringify(this.history), 'utf8');
} catch (e) {
log('Warning Could not write to history', e.message);
}
}
_loadFromHistory() {
try {
this.history = JSON.parse(fs.readFileSync(this.historyFilePath, 'utf8'));
const entryPoints = Object.keys(this.history);
log(`Successfully loaded history containing ${entryPoints.length} entry points`);
/*
TODO: Let's ask a few folks to give us their history file after a milestone of usage
Then we can make smarter decisions on when to throw out rather than rendering everything
Something like top 20/30/40 entries visited in the last 7/10/15 days might be sufficient
*/
this.compiledEntryPoints = new Set([...this.compiledEntryPoints, ...entryPoints]);
} catch (e) {
log(`No history found...`);
}
}
}
module.exports = (enabled, historyFilePath) => {
log(`Status ${enabled ? 'enabled' : 'disabled'}`);
if (enabled) {
return new IncrementalWebpackCompiler(historyFilePath);
}
return new NoopCompiler();
};