Skip to content

Instantly share code, notes, and snippets.

@huozhi
Created October 24, 2020 04:20
Show Gist options
  • Save huozhi/b72d26de6e1a4f56027e6737451a2a80 to your computer and use it in GitHub Desktop.
Save huozhi/b72d26de6e1a4f56027e6737451a2a80 to your computer and use it in GitHub Desktop.
webpack5 compile integration test of twilio.js
const path = require('path');
const webpack = require('webpack');
const fs = require('fs');
// Based on the NodeWatchFileSystem class at:
// - https://github.com/webpack/webpack/blob/master/lib/node/NodeWatchFileSystem.js
// which in turn exposes:
// - https://www.npmjs.com/package/watchpack
class CustomWatchFileSystem {
constructor(watchStart, watchEnd) {
this.closed = false;
// paused allows the watchers to stay open for the next build
this.paused = false;
this.changeCallback = undefined;
this.watchStart = watchStart;
this.watchEnd = watchEnd;
// Webpack requires us to track this stuff
this.files = undefined;
this.dirs = undefined;
this.missing = undefined;
this.timestamps = new Map();
// this will be populated for us by ncc
this.inputFileSystem = undefined;
}
triggerChanges (changed, removed) {
if (!this.paused) {
const newTime = +Date.now();
for (const file of changed)
this.timestamps.set(file, {
safeTime: newTime + 10,
accuracy: 10,
timestamp: newTime
});
for (const file of removed)
this.timestamps.set(file, null);
for (const file of changed)
this.inputFileSystem.purge(file);
for (const file of removed)
this.inputFileSystem.purge(file);
this.changeCallback(
null,
this.timestamps,
this.timestamps,
removed
);
}
}
// This is called on every rebuild
watch (files, dirs, missing, startTime, options, changeCallback) {
this.files = new Set(files);
this.dirs = new Set(dirs);
this.missing = new Set(missing);
// empty object indicates "unknown" timestamp
// (that is, not cached)
for (const item of files)
this.timestamps.set(item, {});
for (const item of dirs)
this.timestamps.set(item, {});
// null represents "no file"
for (const item of missing)
this.timestamps.set(item, null);
this.paused = false;
this.changeCallback = changeCallback;
// ...Start watching files, dirs, mising
setImmediate(() => {
this.watchStart(files, dirs, missing);
});
return {
close: () => {
this.watchEnd();
},
pause: () => {
this.paused = true;
},
getFileTimestamps: () => {
return this.timestamps;
},
getContextTimestamps: () => {
return this.timestamps;
}
};
}
}
async function main() {
let buildCnt = 0;
const buildFile = path.resolve('./twilio.js');
await new Promise((resolve, reject) => {
const watcher = new CustomWatchFileSystem(function watchStart (files, dirs, missing) {
console.log('db:files', files._set.size)
console.log('db:missing', missing._set.size)
if (buildCnt < 3) {
setTimeout(() => {
// NOTE: We actually have to make the change for the rebuild to happen!
fs.writeFileSync(buildFile, fs.readFileSync(buildFile).toString() + '\n');
watcher.triggerChanges([buildFile], []);
}, 100);
}
}, function watchEnd () {
resolve();
});
console.time('First Build');
const compiler = webpack({
entry: buildFile,
})
compiler.watchFileSystem = watcher;
watcher.inputFileSystem = compiler.inputFileSystem;
let cachedResult;
let watchHandler, rebuildHandler;
const returnedWatcher = compiler.watch({}, (err, stats) => {
if (err) {
console.error('db:err', err)
return watchHandler({ err });
}
if (stats.hasErrors()) {
console.error('db:err', stats.toString())
return watchHandler({ err: stats.toString() });
}
const returnValue = finalizeHandler(stats);
if (watchHandler)
watchHandler(returnValue);
else
cachedResult = returnValue;
});
let closed = false;
const { handler, rebuild, close } = {
close () {
if (!returnedWatcher) {
console.error('db:err', 'No watcher to close.')
throw new Error('No watcher to close.');
}
if (closed) {
console.error('db:err', 'Watcher already closed.')
throw new Error('Watcher already closed.');
}
closed = true;
returnedWatcher.close();
},
handler (handler) {
if (watchHandler) {
console.error('db:err', 'Watcher handler already provided.')
throw new Error('Watcher handler already provided.');
}
watchHandler = handler;
if (cachedResult) {
handler(cachedResult);
cachedResult = null;
}
},
rebuild (handler) {
if (rebuildHandler) {
console.error('db:err', 'Rebuild handler already provided.')
throw new Error('Rebuild handler already provided.');
}
rebuildHandler = handler;
}
};
handler(({ err, code, map, assets, permissions }) => {
if (err) return reject(err);
buildCnt++;
console.log('db:buildCnt', buildCnt)
if (buildCnt === 1) {
console.timeEnd('First Build');
}
else {
console.timeEnd('Watched Build');
}
if (buildCnt === 3) {
close();
fs.writeFileSync(buildFile, fs.readFileSync(buildFile).toString().slice(0, -2));
}
});
rebuild(() => {
console.time('Watched Build');
});
});
}
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment