zotero/scripts/babel-worker.js
fletcherhaz 76ae5d9f59
Switch back to SingleFile from SingleFileZ (#1904)
Our SingleFileZ integration would save images inside directories following the
SingleFileZ format. However, Zotero does not support syncing sub-directories of
attachments. This commit switch back to a single HTML file with base64 encoded
resources. We think that the 33% increase in resources will be offset by the
compression of HTML and removal of JavaScript and unused CSS.

This commit does not fix past snapshots that were saved using SingleFileZ.
2020-10-23 19:39:07 -04:00

124 lines
No EOL
4.2 KiB
JavaScript

/* global onmessage: true, postMessage: false */
'use strict';
const fs = require('fs-extra');
const path = require('path');
const babel = require('@babel/core');
const multimatch = require('multimatch');
const options = JSON.parse(fs.readFileSync('.babelrc'));
const cluster = require('cluster');
const { comparePaths } = require('./utils');
/* exported onmessage */
async function babelWorker(ev) {
const t1 = Date.now();
const sourcefile = ev.file;
const localOptions = {
filename: sourcefile
};
const outfile = path.join('build', sourcefile.replace('.jsx', '.js'));
const postError = (error) => {
process.send({
sourcefile,
outfile,
error
});
};
var isSkipped = false;
var transformed;
try {
let contents = await fs.readFile(sourcefile, 'utf8');
// Patch react
if (comparePaths(sourcefile, 'resource/react.js')) {
transformed = contents.replace('instanceof Error', '.constructor.name == "Error"')
}
// Patch react-dom
else if (comparePaths(sourcefile, 'resource/react-dom.js')) {
transformed = contents.replace(/ ownerDocument\.createElement\((.*?)\)/gi, 'ownerDocument.createElementNS(HTML_NAMESPACE, $1)')
.replace('element instanceof win.HTMLIFrameElement',
'typeof element != "undefined" && element.tagName.toLowerCase() == "iframe"')
.replace("isInputEventSupported = false", 'isInputEventSupported = true');
}
// Patch react-virtualized
else if (comparePaths(sourcefile, 'resource/react-virtualized.js')) {
transformed = contents.replace('scrollDiv = document.createElement("div")', 'scrollDiv = document.createElementNS("http://www.w3.org/1999/xhtml", "div")')
.replace('document.body.appendChild(scrollDiv)', 'document.documentElement.appendChild(scrollDiv)')
.replace('document.body.removeChild(scrollDiv)', 'document.documentElement.removeChild(scrollDiv)');
}
// Patch content-frame-tree
// In Chrome sometimes frames would not have access to the browser object. I could
// not replicate this in firefox so is possibly a bug with injected content_scripts
// in Chrome that was easier to work around than track down. SingleFile has this
// backup mechanism for message so we simply remove the check that implies that if
// the top window has the browser object the frame will as well.
else if (sourcefile === 'resource/SingleFile/lib/single-file/processors/frame-tree/content/content-frame-tree.js') {
transformed = contents
.replace('} else if ((!browser || !browser.runtime) && message.method == INIT_RESPONSE_MESSAGE) {',
'} else if (message.method == INIT_RESPONSE_MESSAGE) {');
}
// Patch single-file
else if (sourcefile === 'resource/SingleFile/lib/single-file/single-file.js') {
// We need to add this bit that is done for the cli implementation of singleFile
// See resource/SingleFile/cli/back-ends/common/scripts.js
const WEB_SCRIPTS = [
"lib/single-file/processors/hooks/content/content-hooks-web.js",
"lib/single-file/processors/hooks/content/content-hooks-frames-web.js"
];
let basePath = 'resource/SingleFile/';
function readScriptFile(path, basePath) {
return new Promise((resolve, reject) =>
fs.readFile(basePath + path, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data.toString() + "\n");
}
})
);
}
const webScripts = {};
await Promise.all(
WEB_SCRIPTS.map(async path => webScripts[path] = await readScriptFile(path, basePath))
);
transformed = contents + '\n\n'
+ "this.singlefile.lib.getFileContent = filename => (" + JSON.stringify(webScripts) + ")[filename];\n";
}
else if ('ignore' in options && options.ignore.some(ignoreGlob => multimatch(sourcefile, ignoreGlob).length)) {
transformed = contents;
isSkipped = true;
} else {
try {
({ code: transformed } = await babel.transformAsync(
contents,
Object.assign(
localOptions,
options
)
));
} catch (error) { return postError(`Babel error: ${error}`);}
}
await fs.outputFile(outfile, transformed);
const t2 = Date.now();
process.send({
isSkipped,
sourcefile,
outfile,
processingTime: t2 - t1
});
} catch (error) { return postError(`I/O error: ${error}`); }
}
module.exports = babelWorker;
if (cluster.isWorker) {
process.on('message', babelWorker);
}