2021-04-17 20:07:23 +05:30
|
|
|
|
/* eslint-disable max-classes-per-file, no-underscore-dangle */
|
2021-03-11 19:13:27 +05:30
|
|
|
|
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;
|
|
|
|
|
|
2021-04-17 20:07:23 +05:30
|
|
|
|
/* eslint-disable class-methods-use-this */
|
2021-03-11 19:13:27 +05:30
|
|
|
|
class NoopCompiler {
|
|
|
|
|
constructor() {
|
|
|
|
|
this.enabled = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filterEntryPoints(entryPoints) {
|
|
|
|
|
return entryPoints;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
logStatus() {}
|
|
|
|
|
|
|
|
|
|
setupMiddleware() {}
|
|
|
|
|
}
|
2021-04-17 20:07:23 +05:30
|
|
|
|
/* eslint-enable class-methods-use-this */
|
2021-03-11 19:13:27 +05:30
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
};
|