diff --git a/.npmignore b/.npmignore
index ee62c8a2a7b..9115c4d428c 100644
--- a/.npmignore
+++ b/.npmignore
@@ -1,12 +1,11 @@
build/*
-
!build/plotcss.js
!build/ploticon.js
!build/README.md
devtools
test
-dist/extras/mathjax
+dist/extras
circle.yml
docker-compose.yml
diff --git a/lib/index-basic.js b/lib/index-basic.js
new file mode 100644
index 00000000000..f56ffe8c6b6
--- /dev/null
+++ b/lib/index-basic.js
@@ -0,0 +1,18 @@
+/**
+* Copyright 2012-2016, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var Plotly = require('./core');
+
+Plotly.register([
+ require('./bar'),
+ require('./pie')
+]);
+
+module.exports = Plotly;
diff --git a/lib/index-cartesian.js b/lib/index-cartesian.js
new file mode 100644
index 00000000000..1bd25000b40
--- /dev/null
+++ b/lib/index-cartesian.js
@@ -0,0 +1,25 @@
+/**
+* Copyright 2012-2016, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var Plotly = require('./core');
+
+Plotly.register([
+ require('./bar'),
+ require('./box'),
+ require('./heatmap'),
+ require('./histogram'),
+ require('./histogram2d'),
+ require('./histogram2dcontour'),
+ require('./pie'),
+ require('./contour'),
+ require('./scatterternary')
+]);
+
+module.exports = Plotly;
diff --git a/lib/index-geo.js b/lib/index-geo.js
new file mode 100644
index 00000000000..731e14789f5
--- /dev/null
+++ b/lib/index-geo.js
@@ -0,0 +1,18 @@
+/**
+* Copyright 2012-2016, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var Plotly = require('./core');
+
+Plotly.register([
+ require('./scattergeo'),
+ require('./choropleth')
+]);
+
+module.exports = Plotly;
diff --git a/lib/index-gl2d.js b/lib/index-gl2d.js
new file mode 100644
index 00000000000..b5e0df006a4
--- /dev/null
+++ b/lib/index-gl2d.js
@@ -0,0 +1,19 @@
+/**
+* Copyright 2012-2016, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var Plotly = require('./core');
+
+Plotly.register([
+ require('./scattergl'),
+ require('./heatmapgl'),
+ require('./contourgl')
+]);
+
+module.exports = Plotly;
diff --git a/lib/index-gl3d.js b/lib/index-gl3d.js
new file mode 100644
index 00000000000..c77a840b61a
--- /dev/null
+++ b/lib/index-gl3d.js
@@ -0,0 +1,19 @@
+/**
+* Copyright 2012-2016, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var Plotly = require('./core');
+
+Plotly.register([
+ require('./scatter3d'),
+ require('./surface'),
+ require('./mesh3d')
+]);
+
+module.exports = Plotly;
diff --git a/lib/index-mapbox.js b/lib/index-mapbox.js
new file mode 100644
index 00000000000..ffe777f98b1
--- /dev/null
+++ b/lib/index-mapbox.js
@@ -0,0 +1,17 @@
+/**
+* Copyright 2012-2016, Plotly, Inc.
+* All rights reserved.
+*
+* This source code is licensed under the MIT license found in the
+* LICENSE file in the root directory of this source tree.
+*/
+
+'use strict';
+
+var Plotly = require('./core');
+
+Plotly.register([
+ require('./scattermapbox')
+]);
+
+module.exports = Plotly;
diff --git a/lib/index.js b/lib/index.js
index 631fed56656..a870b3897c5 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -8,14 +8,9 @@
'use strict';
-/*
- * This file is browserify'ed into a standalone 'Plotly' object.
- */
+var Plotly = require('./core');
-var Core = require('./core');
-
-// Load all trace modules
-Core.register([
+Plotly.register([
require('./bar'),
require('./box'),
require('./heatmap'),
@@ -33,4 +28,4 @@ Core.register([
require('./scatterternary')
]);
-module.exports = Core;
+module.exports = Plotly;
diff --git a/package.json b/package.json
index 9f8b9bf10b2..172a138727b 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,8 @@
"preprocess": "node tasks/preprocess.js",
"bundle": "node tasks/bundle.js",
"header": "node tasks/header.js",
- "build": "npm run preprocess && npm run bundle && npm run header",
+ "stats": "node tasks/stats.js",
+ "build": "npm run preprocess && npm run bundle && npm run header && npm run stats",
"cibuild": "npm run preprocess && node tasks/cibundle.js",
"watch": "node tasks/watch_plotly.js",
"lint": "eslint . || true",
@@ -95,6 +96,7 @@
"fs-extra": "^0.30.0",
"fuse.js": "^2.2.0",
"glob": "^7.0.0",
+ "gzip-size": "^3.0.0",
"jasmine-core": "^2.4.1",
"karma": "^1.1.0",
"karma-browserify": "^5.0.1",
diff --git a/tasks/bundle.js b/tasks/bundle.js
index 45de5f03649..a3b8da2211d 100644
--- a/tasks/bundle.js
+++ b/tasks/bundle.js
@@ -1,4 +1,5 @@
var fs = require('fs');
+var path = require('path');
var browserify = require('browserify');
var UglifyJS = require('uglify-js');
@@ -28,48 +29,76 @@ try {
}
catch(e) {
throw new Error([
- 'build/ is missing a or more files',
+ 'build/ is missing one or more files',
'Please run `npm run preprocess` first'
].join('\n'));
}
// Browserify plotly.js
-browserify(constants.pathToPlotlyIndex, {
- debug: DEV,
+_bundle(constants.pathToPlotlyIndex, constants.pathToPlotlyDist, {
standalone: 'Plotly',
- transform: [compressAttributes]
-})
-.bundle(function(err, buf) {
- if(err) throw err;
-
- // generate plotly.min.js
- if(!DEV) {
- fs.writeFile(
- constants.pathToPlotlyDistMin,
- UglifyJS.minify(buf.toString(), constants.uglifyOptions).code
- );
- }
-})
-.pipe(fs.createWriteStream(constants.pathToPlotlyDist));
-
+ debug: DEV,
+ pathToMinBundle: constants.pathToPlotlyDistMin
+});
// Browserify the geo assets
-browserify(constants.pathToPlotlyGeoAssetsSrc, {
+_bundle(constants.pathToPlotlyGeoAssetsSrc, constants.pathToPlotlyGeoAssetsDist, {
standalone: 'PlotlyGeoAssets'
-})
-.bundle(function(err) {
- if(err) throw err;
-})
-.pipe(fs.createWriteStream(constants.pathToPlotlyGeoAssetsDist));
-
+});
// Browserify the plotly.js with meta
-browserify(constants.pathToPlotlyIndex, {
- debug: DEV,
- standalone: 'Plotly'
-})
-.bundle(function(err) {
- if(err) throw err;
-})
-.pipe(fs.createWriteStream(constants.pathToPlotlyDistWithMeta));
+_bundle(constants.pathToPlotlyIndex, constants.pathToPlotlyDistWithMeta, {
+ standalone: 'Plotly',
+ debug: DEV
+});
+
+// Browserify the plotly.js partial bundles
+constants.partialBundlePaths.forEach(function(pathObj) {
+ _bundle(pathObj.index, pathObj.dist, {
+ standalone: 'Plotly',
+ debug: DEV,
+ pathToMinBundle: pathObj.distMin
+ });
+});
+
+function _bundle(pathToIndex, pathToBundle, opts) {
+ opts = opts || {};
+
+ // do we output a minified file?
+ var pathToMinBundle = opts.pathToMinBundle,
+ outputMinified = !!pathToMinBundle && !opts.debug;
+
+ var browserifyOpts = {};
+ browserifyOpts.standalone = opts.standalone;
+ browserifyOpts.debug = opts.debug;
+ browserifyOpts.transform = outputMinified ? [compressAttributes] : [];
+
+ var b = browserify(pathToIndex, browserifyOpts),
+ bundleWriteStream = fs.createWriteStream(pathToBundle);
+
+ bundleWriteStream.on('finish', function() {
+ logger(pathToBundle);
+ });
+
+ b.bundle(function(err, buf) {
+ if(err) throw err;
+
+ if(outputMinified) {
+ var minifiedCode = UglifyJS.minify(buf.toString(), constants.uglifyOptions).code;
+
+ fs.writeFile(pathToMinBundle, minifiedCode, function(err) {
+ if(err) throw err;
+
+ logger(pathToMinBundle);
+ });
+ }
+ })
+ .pipe(bundleWriteStream);
+}
+
+function logger(pathToOutput) {
+ var log = 'ok ' + path.basename(pathToOutput);
+
+ console.log(log);
+}
diff --git a/tasks/header.js b/tasks/header.js
index 9733d9b1bf2..a877b3f26b2 100644
--- a/tasks/header.js
+++ b/tasks/header.js
@@ -7,77 +7,95 @@ var glob = require('glob');
var constants = require('./util/constants');
+// main
+addHeadersInDistFiles();
+updateHeadersInSrcFiles();
// add headers to dist files
+function addHeadersInDistFiles() {
+ function _prepend(path, header) {
+ prependFile(path, header + '\n', function(err) {
+ if(err) throw err;
+ });
+ }
+
+ // add header to main dist bundles
+ var pathsDist = [
+ constants.pathToPlotlyDistMin,
+ constants.pathToPlotlyDist,
+ constants.pathToPlotlyDistWithMeta,
+ constants.pathToPlotlyGeoAssetsDist
+ ];
+ pathsDist.forEach(function(path) {
+ _prepend(path, constants.licenseDist);
+ });
-var pathsDist = [
- constants.pathToPlotlyDistMin,
- constants.pathToPlotlyDist,
- constants.pathToPlotlyDistWithMeta,
- constants.pathToPlotlyGeoAssetsDist
-];
+ // add header and bundle name to partial bundle
+ constants.partialBundlePaths.forEach(function(pathObj) {
+ var headerDist = constants.licenseDist
+ .replace('plotly.js', 'plotly.js (' + pathObj.name + ')');
+ _prepend(pathObj.dist, headerDist);
-function headerLicense(path) {
- prependFile(path, constants.licenseDist + '\n', function(err) {
- if(err) throw err;
+ var headerDistMin = constants.licenseDist
+ .replace('plotly.js', 'plotly.js (' + pathObj.name + ' - minified)');
+ _prepend(pathObj.distMin, headerDistMin);
});
}
-pathsDist.forEach(headerLicense);
-
+// add or update header to src/ lib/ files
+function updateHeadersInSrcFiles() {
+ var srcGlob = path.join(constants.pathToSrc, '**/*.js');
+ var libGlob = path.join(constants.pathToLib, '**/*.js');
-// add or update header to src files
+ // remove leading '/*' and trailing '*/' for comparison with falafel output
+ var licenseSrc = constants.licenseSrc;
+ var licenseStr = licenseSrc.substring(2, licenseSrc.length - 2);
-// remove leading '/*' and trailing '*/' for comparison with falafel output
-var licenseSrc = constants.licenseSrc;
-var licenseStr = licenseSrc.substring(2, licenseSrc.length - 2);
+ glob('{' + srcGlob + ',' + libGlob + '}', function(err, files) {
+ files.forEach(function(file) {
+ fs.readFile(file, 'utf-8', function(err, code) {
-var srcGlob = path.join(constants.pathToSrc, '**/*.js');
-var libGlob = path.join(constants.pathToLib, '**/*.js');
-glob('{' + srcGlob + ',' + libGlob + '}', function(err, files) {
- files.forEach(function(file) {
- fs.readFile(file, 'utf-8', function(err, code) {
+ // parse through code string while keeping track of comments
+ var comments = [];
+ falafel(code, {onComment: comments, locations: true}, function() {});
- // parse through code string while keeping track of comments
- var comments = [];
- falafel(code, {onComment: comments, locations: true}, function() {});
+ var header = comments[0];
- var header = comments[0];
+ // error out if no header is found
+ if(!header || header.loc.start.line > 1) {
+ throw new Error(file + ' : has no header information.');
+ }
- // error out if no header is found
- if(!header || header.loc.start.line > 1) {
- throw new Error(file + ' : has no header information.');
- }
+ // if header and license are the same, do nothing
+ if(isCorrect(header)) return;
- // if header and license are the same, do nothing
- if(isCorrect(header)) return;
+ // if header and license only differ by date, update header
+ else if(hasWrongDate(header)) {
+ var codeLines = code.split('\n');
- // if header and license only differ by date, update header
- else if(hasWrongDate(header)) {
- var codeLines = code.split('\n');
+ codeLines.splice(header.loc.start.line - 1, header.loc.end.line);
- codeLines.splice(header.loc.start.line - 1, header.loc.end.line);
+ var newCode = licenseSrc + '\n' + codeLines.join('\n');
- var newCode = licenseSrc + '\n' + codeLines.join('\n');
-
- fs.writeFile(file, newCode, function(err) {
- if(err) throw err;
- });
- }
- else {
- // otherwise, throw an error
- throw new Error(file + ' : has wrong header information.');
- }
+ fs.writeFile(file, newCode, function(err) {
+ if(err) throw err;
+ });
+ }
+ else {
+ // otherwise, throw an error
+ throw new Error(file + ' : has wrong header information.');
+ }
+ });
});
});
-});
-function isCorrect(header) {
- return (header.value === licenseStr);
-}
+ function isCorrect(header) {
+ return (header.value === licenseStr);
+ }
-function hasWrongDate(header) {
- var regex = /Copyright 20[0-9][0-9]-20[0-9][0-9]/g;
+ function hasWrongDate(header) {
+ var regex = /Copyright 20[0-9][0-9]-20[0-9][0-9]/g;
- return (header.value.replace(regex, '') === licenseStr.replace(regex, ''));
+ return (header.value.replace(regex, '') === licenseStr.replace(regex, ''));
+ }
}
diff --git a/tasks/stats.js b/tasks/stats.js
new file mode 100644
index 00000000000..ac90e6b3de6
--- /dev/null
+++ b/tasks/stats.js
@@ -0,0 +1,206 @@
+var path = require('path');
+var fs = require('fs');
+
+var falafel = require('falafel');
+var gzipSize = require('gzip-size');
+var prettySize = require('prettysize');
+
+var constants = require('./util/constants');
+var pkg = require('../package.json');
+
+var pathDistREADME = path.join(constants.pathToDist, 'README.md');
+var cdnRoot = 'https://cdn.plot.ly/plotly-';
+var coreModules = ['scatter'];
+
+var ENC = 'utf-8';
+var JS = '.js';
+var MINJS = '.min.js';
+
+
+// general info about distributed files
+var infoContent = [
+ '# Using distributed files',
+ '',
+ 'All plotly.js dist bundles inject an object `Plotly` into the global scope.',
+ '',
+ 'Import plotly.js as:',
+ '',
+ '```html',
+ '',
+ '```',
+ '',
+ 'or the un-minified version as:',
+ '',
+ '```html',
+ '',
+ '```',
+ '',
+ 'To support IE9, put:',
+ '',
+ '```html',
+ '',
+ '```',
+ '',
+ 'before the plotly.js script tag.',
+ '',
+ 'To add MathJax, put',
+ '',
+ '```html',
+ '',
+ '```',
+ '',
+ 'before the plotly.js script tag. You can grab the relevant MathJax files in `./dist/extras/mathjax/`.',
+ ''
+];
+
+// add bundle content/size info
+var mainSizes = findSizes({
+ dist: constants.pathToPlotlyDist,
+ distMin: constants.pathToPlotlyDistMin,
+ withMeta: constants.pathToPlotlyDistWithMeta
+});
+var bundleInfo = [
+ '# Bundle information',
+ '',
+ 'The main plotly.js bundle includes all the official (non-beta) trace modules.',
+ '',
+ 'It be can imported as minified javascript',
+ '- using dist file `dist/plotly.min.js`',
+ '- using CDN URL ' + cdnRoot + 'plotly-latest.min.js OR ' + cdnRoot + 'plotly-' + pkg.version + MINJS,
+ '',
+ 'or as raw javascript:',
+ '- using dist file `dist/plotly.js`',
+ '- using CDN URL ' + cdnRoot + 'plotly-latest.js OR ' + cdnRoot + 'plotly-' + pkg.version + JS,
+ '- using CommonJS with `require(\'plotly.js\')`',
+ '',
+ 'If you would like to have access to the attribute meta information ' +
+ '(including attribute descriptions as on the [schema reference page](https://plot.ly/javascript/reference/)), ' +
+ 'use dist file `dist/plotly-with-meta.js`',
+ '',
+ 'The main plotly.js bundle weights in at:',
+ '',
+ '| plotly.js | plotly.min.js | plotly.min.js + gzip | plotly-with-meta.js |',
+ '|-----------|---------------|----------------------|---------------------|',
+ '| ' + mainSizes.raw + ' | ' + mainSizes.minified + ' | ' + mainSizes.gzipped + ' | ' + mainSizes.withMeta + ' |',
+ '',
+ '## Partial bundles',
+ '',
+ 'Starting in `v1.15.0`, plotly.js also ships with several _partial_ bundles:',
+ '',
+ constants.partialBundlePaths.map(makeBundleHeaderInfo).join('\n'),
+ ''
+]
+.concat(
+ constants.partialBundlePaths.map(makeBundleInfo)
+);
+
+// footer info
+var footer = [
+ '----------------',
+ '',
+ '_This file is auto-generated by `npm run stats`. ' +
+ 'Please do not edit this file directly._'
+];
+
+var content = infoContent.concat(bundleInfo).concat(footer);
+
+fs.writeFile(pathDistREADME, content.join('\n'), function(err) {
+ if(err) throw err;
+});
+
+function makeBundleHeaderInfo(pathObj) {
+ var name = pathObj.name;
+ return '- [' + name + '](#plotlyjs-' + name + ')';
+}
+
+function makeBundleInfo(pathObj) {
+ var name = pathObj.name;
+ var sizes = findSizes(pathObj);
+ var moduleList = coreModules.concat(scrapeContent(pathObj));
+
+ return [
+ '### plotly.js ' + name,
+ '',
+ formatBundleInfo(name, moduleList),
+ '',
+ '| Way to import | Location |',
+ '|---------------|----------|',
+ '| dist bundle | ' + '`dist/plotly-' + name + JS + '` |',
+ '| dist bundle (minified) | ' + '`dist/plotly-' + name + MINJS + '` |',
+ '| CDN URL (latest) | ' + cdnRoot + name + '-latest' + JS + ' |',
+ '| CDN URL (latest minified) | ' + cdnRoot + name + '-latest' + MINJS + ' |',
+ '| CDN URL (tagged) | ' + cdnRoot + name + '-' + pkg.version + JS + ' |',
+ '| CDN URL (tagged minified) | ' + cdnRoot + name + '-' + pkg.version + MINJS + ' |',
+ '| CommonJS | ' + '`require(\'plotly.js/lib/' + 'index-' + name + '\')`' + ' |',
+ '',
+ '| Raw size | Minified size | Minified + gzip size |',
+ '|------|-----------------|------------------------|',
+ '| ' + sizes.raw + ' | ' + sizes.minified + ' | ' + sizes.gzipped + ' | ',
+ ''
+ ].join('\n');
+}
+
+function findSizes(pathObj) {
+ var codeDist = fs.readFileSync(pathObj.dist, ENC),
+ codeDistMin = fs.readFileSync(pathObj.distMin, ENC);
+
+ var sizes = {
+ raw: prettySize(codeDist.length),
+ minified: prettySize(codeDistMin.length),
+ gzipped: prettySize(gzipSize.sync(codeDistMin))
+ };
+
+ if(pathObj.withMeta) {
+ var codeWithMeta = fs.readFileSync(pathObj.withMeta, ENC);
+ sizes.withMeta = prettySize(codeWithMeta.length);
+ }
+
+ return sizes;
+}
+
+function scrapeContent(pathObj) {
+ var code = fs.readFileSync(pathObj.index, ENC);
+ var moduleList = [];
+
+ falafel(code, function(node) {
+ if(isModuleNode(node)) {
+ var moduleName = node.value.replace('./', '');
+ moduleList.push(moduleName);
+ }
+ });
+
+ return moduleList;
+}
+
+function isModuleNode(node) {
+ return (
+ node.type === 'Literal' &&
+ node.parent &&
+ node.parent.type === 'CallExpression' &&
+ node.parent.callee &&
+ node.parent.callee.type === 'Identifier' &&
+ node.parent.callee.name === 'require' &&
+ node.parent.parent &&
+ node.parent.parent.type === 'ArrayExpression'
+ );
+}
+
+function formatBundleInfo(bundleName, moduleList) {
+ var enumeration = moduleList.map(function(moduleName, i) {
+ var len = moduleList.length,
+ ending;
+
+ if(i === len - 2) ending = 'and';
+ else if(i < len - 1) ending = ',';
+ else ending = '';
+
+ return '`' + moduleName + '`' + ending;
+ });
+
+ return [
+ 'The', '`' + bundleName + '`',
+ 'partial bundle contains the',
+ enumeration.join(' '),
+ 'trace modules.'
+ ].join(' ');
+}
diff --git a/tasks/util/constants.js b/tasks/util/constants.js
index f4a190e6192..d597e4e041e 100644
--- a/tasks/util/constants.js
+++ b/tasks/util/constants.js
@@ -1,5 +1,4 @@
var path = require('path');
-
var pkg = require('../../package.json');
var pathToRoot = path.join(__dirname, '../../');
@@ -13,6 +12,19 @@ var pathToTopojsonSrc = path.join(
path.dirname(require.resolve('sane-topojson')), 'dist/'
);
+var partialBundleNames = [
+ 'basic', 'cartesian', 'geo', 'gl3d', 'gl2d', 'mapbox'
+];
+
+var partialBundlePaths = partialBundleNames.map(function(name) {
+ return {
+ name: name,
+ index: path.join(pathToLib, 'index-' + name + '.js'),
+ dist: path.join(pathToDist, 'plotly-' + name + '.js'),
+ distMin: path.join(pathToDist, 'plotly-' + name + '.min.js')
+ };
+});
+
var year = (new Date()).getFullYear();
module.exports = {
@@ -20,6 +32,7 @@ module.exports = {
pathToSrc: pathToSrc,
pathToLib: pathToLib,
pathToBuild: pathToBuild,
+ pathToDist: pathToDist,
pathToPlotlyIndex: path.join(pathToLib, 'index.js'),
pathToPlotlyCore: path.join(pathToSrc, 'core.js'),
@@ -28,6 +41,9 @@ module.exports = {
pathToPlotlyDistMin: path.join(pathToDist, 'plotly.min.js'),
pathToPlotlyDistWithMeta: path.join(pathToDist, 'plotly-with-meta.js'),
+ partialBundleNames: partialBundleNames,
+ partialBundlePaths: partialBundlePaths,
+
pathToTopojsonSrc: pathToTopojsonSrc,
pathToTopojsonDist: path.join(pathToDist, 'topojson/'),
pathToPlotlyGeoAssetsSrc: path.join(pathToSrc, 'assets/geo_assets.js'),