Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] Move webpack configuration to project root #382

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/perspective-viewer/src/config/themes.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ module.exports = Object.assign({}, common(), {
});

for (const file of fs.readdirSync("src/themes")) {
const extract = new ExtractTextPlugin({filename: file.replace("less", "css")});
const extract = new ExtractTextPlugin({
filename: file.replace("less", "css")
});
module.exports.module.rules.push({
test: new RegExp(file),
loader: extract.extract({
Expand Down
2 changes: 1 addition & 1 deletion packages/perspective-viewer/src/config/view.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const path = require("path");
const common = require("@jpmorganchase/perspective/src/config/common.config.js");
const common = require("../../../../scripts/webpack/common.config.js");

module.exports = Object.assign({}, common(), {
entry: "./src/js/viewer.js",
Expand Down
31 changes: 31 additions & 0 deletions scripts/webpack/common.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const UglifyJSPlugin = require("uglifyjs-webpack-plugin");
const webpack = require("webpack");
const PerspectivePlugin = require("./webpack-plugin.js");

const plugins = [
new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /(en|es|fr)$/)
];

module.exports = function({ build_worker, no_minify } = {}) {
if (!process.env.PSP_NO_MINIFY && !process.env.PSP_DEBUG && !no_minify) {
plugins.push(
new UglifyJSPlugin({
sourceMap: true,
mangle: false,
exclude: /(asmjs\.worker\.js)$/,
output: {
ascii_only: true
}
})
);
}

plugins.push(new PerspectivePlugin({ build_worker: build_worker }));
return {
plugins: plugins,
devtool: "source-map",
node: {
fs: "empty"
}
};
};
110 changes: 110 additions & 0 deletions scripts/webpack/loader/blob_worker_loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/******************************************************************************
*
* Copyright (c) 2017, the Perspective Authors.
*
* This file is part of the Perspective library, distributed under the terms of
* the Apache License 2.0. The full license can be found in the LICENSE file.
*
*/

const loaderUtils = require("loader-utils");
const validateOptions = require("@webpack-contrib/schema-utils");

const NodeTargetPlugin = require("webpack/lib/node/NodeTargetPlugin");
const SingleEntryPlugin = require("webpack/lib/SingleEntryPlugin");
const WebWorkerTemplatePlugin = require("webpack/lib/webworker/WebWorkerTemplatePlugin");

class BlobWorkerLoaderError extends Error {
constructor(err) {
super(err);

this.name = err.name || "Loader Error";
this.message = `${err.name}\n\n${err.message}\n`;
this.stack = false;
}
}

const schema = {
type: "object",
properties: {
name: {
type: "string"
}
},
additionalProperties: false
};

exports.default = function loader() {};

exports.pitch = function pitch(request) {
const options = loaderUtils.getOptions(this) || {};

validateOptions({name: "Blob Worker Loader", schema, target: options});

if (!this.webpack) {
throw new BlobWorkerLoaderError({
name: "Blob Worker Loader",
message: "This loader is only usable with webpack"
});
}

this.cacheable(false);

const filename = loaderUtils.interpolateName(this, options.name || "[hash].worker.js", {
context: options.context || this.rootContext || this.options.context,
regExp: options.regExp
});

if (process.env.PSP_DEBUG && filename.indexOf("asmjs") > -1) {
return;
}

const cb = this.async();

const worker = {};

worker.options = {
filename,
chunkFilename: `[id].${filename}`,
namedChunkFilename: null
};

worker.compiler = this._compilation.createChildCompiler("worker", worker.options);

new WebWorkerTemplatePlugin(worker.options).apply(worker.compiler);

if (this.target !== "webworker" && this.target !== "web") {
new NodeTargetPlugin().apply(worker.compiler);
}

new SingleEntryPlugin(this.context, `!!${request}`, "main").apply(worker.compiler);

const subCache = `subcache ${__dirname} ${request}`;

worker.compilation = compilation => {
if (compilation.cache) {
if (!compilation.cache[subCache]) {
compilation.cache[subCache] = {};
}
compilation.cache = compilation.cache[subCache];
}
};

if (worker.compiler.hooks) {
const plugin = {name: "BlobWorkerLoader"};
worker.compiler.hooks.compilation.tap(plugin, worker.compilation);
} else {
worker.compiler.plugin("compilation", worker.compilation);
}

worker.compiler.runAsChild((err, entries) => {
if (err) return cb(err);

if (entries[0]) {
worker.file = entries[0].files[0];
return cb(null, this._compilation.assets[worker.file].children[0]._value);
}

return cb(null, null);
});
};
53 changes: 53 additions & 0 deletions scripts/webpack/loader/cross_origin_file_loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/******************************************************************************
*
* Copyright (c) 2017, the Perspective Authors.
*
* This file is part of the Perspective library, distributed under the terms of
* the Apache License 2.0. The full license can be found in the LICENSE file.
*
*/

var path = require("path");

var loaderUtils = require("loader-utils");
var validateOptions = require("schema-utils");
var fs = require("fs");

var schema = {
type: "object",
properties: {
name: {},
regExp: {},
context: {
type: "string"
}
},
additionalProperties: true
};

exports.default = function loader() {};

exports.pitch = function pitch(request) {
var options = loaderUtils.getOptions(this) || {};
validateOptions(schema, options, "Cross Origin File Loader");

var context = options.context || this.rootContext || (this.options && this.options.context);
var content = fs.readFileSync(request.replace("src/js", "build").replace("wasm.js", "wasm"));
var url = loaderUtils.interpolateName(this, options.name, {
context,
content,
regExp: options.regExp
});

var outputPath = url;
var publicPath = `__webpack_public_path__ + ${JSON.stringify(outputPath)}`;
this.emitFile(outputPath, content);

const utils_path = JSON.stringify(`!!${path.join(__dirname, "utils.js")}`);
return `
var utils = require(${utils_path});
module.exports = (utils.path + ${publicPath});
`;
};

exports.raw = true;
90 changes: 90 additions & 0 deletions scripts/webpack/loader/file_worker_loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/******************************************************************************
*
* Copyright (c) 2017, the Perspective Authors.
*
* This file is part of the Perspective library, distributed under the terms of
* the Apache License 2.0. The full license can be found in the LICENSE file.
*
*/

const path = require("path");

const loaderUtils = require("loader-utils");
const validateOptions = require("schema-utils");

const fs = require("fs");

const schema = {
type: "object",
properties: {
name: {},
regExp: {},
compiled: {
type: "boolean"
},
inline: {
type: "boolean"
},
context: {
type: "string"
}
},
additionalProperties: true
};

exports.default = function loader(content) {
if (process.env.PSP_DEBUG && content && content.indexOf("asmjs") > -1) {
return "module.exports = function() {};";
}
const options = loaderUtils.getOptions(this) || {};
validateOptions(schema, options, "File Worker Loader");
const context = options.context || this.rootContext || (this.options && this.options.context);
const url = loaderUtils.interpolateName(this, options.name, {
context,
content,
regExp: options.regExp
});
const outputPath = url.replace(/\.js/, ".worker.js");

if (!options.compiled) {
var inputPath = this.resourcePath;
if (!options.inline) {
inputPath = inputPath
.replace(path.join("perspective", "build"), "perspective")
.replace(/\.js/, ".worker.js")
.replace(/(src\/js)/, "build");
}
content = fs.readFileSync(inputPath).toString();
if (!options.compiled) {
this.emitFile(outputPath, "" + content);
const map_file = `${inputPath}.map`;
if (fs.existsSync(map_file)) {
const map_content = fs.readFileSync(map_file).toString();
this.emitFile(`${outputPath}.map`, "" + map_content);
}
}
}

const publicPath = `__webpack_public_path__ + ${JSON.stringify(outputPath)}`;
const utils_path = JSON.stringify(`!!${path.join(__dirname, "utils.js")}`);

if (options.inline) {
const worker_text = JSON.stringify(content.toString())
.replace(/\u2028/g, "\\u2028")
.replace(/\u2029/g, "\\u2029");

return `module.exports = function() {
var utils = require(${utils_path});
return new Promise(function(resolve) { utils.BlobWorker(${worker_text}, resolve); });
};`;
}

return `module.exports = function() {
var utils = require(${utils_path});
if (window.location.origin === utils.host.slice(0, window.location.origin.length)) {
return new Promise(function(resolve) { resolve(new Worker(utils.path + ${publicPath})); });
} else {
return new Promise(function(resolve) { utils.XHRWorker(utils.path + ${publicPath}, resolve); });
}
};`;
};
78 changes: 78 additions & 0 deletions scripts/webpack/loader/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/******************************************************************************
*
* Copyright (c) 2017, the Perspective Authors.
*
* This file is part of the Perspective library, distributed under the terms of
* the Apache License 2.0. The full license can be found in the LICENSE file.
*
*/

/**
* An Object for capturing details of the invoking script's origin.
*
* Returns
* -------
* An instance of a ScriptPath object. Interesting methods on this object
* include:
* fullPath : The complete path of this script.
* path : The path (no host).
* host : The host (no path).
* file : The file name itself.
*/
function ScriptPath() {
var pathParts;
try {
throw new Error();
} catch (e) {
var stackLines = e.stack.split("\n");
var callerIndex = 0;
for (var i in stackLines) {
if (!stackLines[i].match(/http[s]?:\/\//)) continue;
callerIndex = Number(i);
break;
}
pathParts = stackLines[callerIndex].match(/((http[s]?:\/\/.+\/)([^\/]+\.(js|html))).*?:/);
}

this.fullPath = function() {
return pathParts ? pathParts[1] : typeof window !== "undefined" ? window.location.origin + window.location.pathname : "";
};
this.path = function() {
return pathParts ? pathParts[2] : typeof window !== "undefined" ? window.location.pathname : "";
};
this.host = function() {
var x = this.path().match(/.+?\/\/.+?\//);
return x ? x[0] : typeof window !== "undefined" ? window.location.hostname : "";
};
this.file = function() {
return pathParts ? pathParts[3] : "";
};
}

var __SCRIPT_PATH__ = new ScriptPath();

module.exports.host = __SCRIPT_PATH__.host();

module.exports.path = __SCRIPT_PATH__.path();

module.exports.BlobWorker = function(responseText, ready) {
var blob = new Blob([responseText]);
var obj = window.URL.createObjectURL(blob);
var worker = new Worker(obj);
if (ready) {
ready(worker);
}
};

module.exports.XHRWorker = function XHRWorker(url, ready) {
var oReq = new XMLHttpRequest();
oReq.addEventListener(
"load",
function() {
module.exports.BlobWorker(oReq.responseText, ready);
},
oReq
);
oReq.open("get", url, true);
oReq.send();
};
Loading