Skip to content

Commit

Permalink
feat: add Rollup.js tree-shaking and minification
Browse files Browse the repository at this point in the history
Uses `opichals/rollup-plugin-espruino-modules`

* Add Config.ROLLUP which replaces `loadModules`
  The core/modules.js transformForEspruino directly use the
  `espruinoRollup.loadModulesRollup` instead of `loadModules`

* Separately add TERSER minification option

* Srap with UMD preamble for browser compat
  • Loading branch information
opichals committed Dec 4, 2018
1 parent f0f8965 commit f530c74
Show file tree
Hide file tree
Showing 11 changed files with 269 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -2,4 +2,5 @@
.settings
*~
node_modules
package-lock.json
tmp
4 changes: 4 additions & 0 deletions bin/espruino-cli.js
Expand Up @@ -254,6 +254,10 @@ function setupConfig(Espruino, callback) {
process.exit(1);
//Espruino.Core.Config.getSection(sectionName);
}
if (args.file) {
var env = Espruino.Core.Env.getData();
env.FILE = args.file;
}
if (args.board) {
log("Explicit board JSON supplied: "+JSON.stringify(args.board));
Espruino.Config.ENV_ON_CONNECT = false;
Expand Down
21 changes: 20 additions & 1 deletion core/modules.js
Expand Up @@ -61,6 +61,9 @@

// When code is sent to Espruino, search it for modules and add extra code required to load them
Espruino.addProcessor("transformForEspruino", function(code, callback) {
if (Espruino.Config.ROLLUP) {
return loadModulesRollup(code, callback);
}
loadModules(code, callback);
});
}
Expand Down Expand Up @@ -211,8 +214,24 @@
callback(loadedModuleData.join("\n") + "\n" + code);
});
}
};
}

function loadModulesRollup(code, callback) {
rollupTools.loadModulesRollup(code)
.then(generated => {
const minified = generated.code;
console.log('rollup: '+minified.length+' bytes');

// FIXME: needs warnings?
Espruino.Core.Notifications.info('Rollup no errors. Bundling ' + code.length + ' bytes to ' + minified.length + ' bytes');
callback(minified);
})
.catch(err => {
console.log('rollup:error', err);
Espruino.Core.Notifications.error("Rollup errors - Bundling failed: " + String(err).trim());
callback(code);
});
}

Espruino.Core.Modules = {
init : init
Expand Down
7 changes: 7 additions & 0 deletions index.js
Expand Up @@ -94,6 +94,13 @@ function init(callback) {
// Various plugins
loadDir(__dirname+"/plugins");

try {
global.espruinoRollup = require("./libs/rollup/espruino-rollup.js");
global.rollupTools = require("./libs/rollup/index.js");
} catch(e) {
console.log("espruinoRollup library not found - you'll need it to minify code");
}

// Bodge up notifications
Espruino.Core.Notifications = {
success : function(e) { console.log(e); },
Expand Down
1 change: 1 addition & 0 deletions libs/rollup/debug-shim.js
@@ -0,0 +1 @@
export default () => () => undefined
49 changes: 49 additions & 0 deletions libs/rollup/espruino-rollup.js
@@ -0,0 +1,49 @@
const { writeFileSync, vol } = require('fs');
const rollup = require('rollup');
const espruinoModules = require('rollup-plugin-espruino-modules');

function bundle(options) {
const opts = { ...options };

if (typeof vol !== 'undefined') { // only in browser (fs = memfs)
vol.fromJSON({'/modules': null});

if (opts.modules) {
try {
opts.modules.forEach(([name, code]) => writeFileSync(name, code));
} catch (err) {
console.error('Write file failed:', err);
}
delete opts.modules;
}
}

const warnings = [];
opts.onwarn = warning => (warnings.push( warning ), (options.onwarn && options.onwarn(warning)));

const config = espruinoModules.buildRollupConfig(opts);

return rollup.rollup(config).then(bundle =>
bundle.generate(config.output).then(generated => {
generated.warnings = warnings;
return generated;
})
);
}

function minify(code, options) {
return new Promise((resolve, reject) => {
try {
const minifyOptions = espruinoModules.buildMinifyConfig(options)
const generated = espruinoModules.minify(code, minifyOptions);
resolve(generated);
} catch(e) {
reject(e);
}
});
}

module.exports = {
bundle,
minify
}
58 changes: 58 additions & 0 deletions libs/rollup/index.js
@@ -0,0 +1,58 @@
(function (root, factory) {
'use strict';

if (typeof define === 'function' && define.amd) {
define(['exports'], factory);
} else if (typeof exports !== 'undefined') {
factory(exports);
} else {
factory((root.rollupTools = {}));
}
}(this, function(exports) {

// =========================================================

function loadModulesRollup(code, callback) {
var board = Espruino.Core.Env.getBoardData();
var env = Espruino.Core.Env.getData();
var modules = [];

var entryFilename = env.FILE;

// the env.FILE is only present in the espruino-cli
if (!entryFilename) {
// the 'modules' contents is written the filesystem in the espruinoRollup()
// for in-browser setup with filesystem simulation
entryFilename = 'main.js';
modules.push([entryFilename, code]);
}

return espruinoRollup.bundle({
modules,
input: entryFilename,
output: {
format: 'cjs'
},
espruino: {
job: Espruino.Config,

board: board.BOARD,
mergeModules: Espruino.Config.MODULE_MERGE,
minify: !!Espruino.Config.MINIFICATION_LEVEL,
minifyModules: !!Espruino.Config.MODULE_MINIFICATION_LEVEL

// TODO: handle opts MINIFICATION_Xyz
}
})
}

function minifyCodeTerser(code, callback) {
return espruinoRollup.minify(code, {
// TODO: handle opts MINIFICATION_Xyz
})
}

exports.loadModulesRollup = loadModulesRollup
exports.minifyCodeTerser = minifyCodeTerser;

}));
26 changes: 26 additions & 0 deletions libs/rollup/package.json
@@ -0,0 +1,26 @@
{
"name": "espruino-rollup",
"version": "0.1.0",
"description": "Espruino rollup bundling pipeline",
"main": "espruino-rollup.js",
"scripts": {
"build": "rollup -c"
},
"keywords": [],
"author": "Standa Opichal",
"license": "MIT",
"dependencies": {
"memfs": "=2.12.1",
"rollup": "^0.67.4",
"rollup-plugin-espruino-modules": "opichals/rollup-plugin-espruino-modules"
},
"devDependencies": {
"rollup-plugin-alias": "^1.4.0",
"rollup-plugin-commonjs": "^9.1.8",
"rollup-plugin-json": "^3.1.0",
"rollup-plugin-node-builtins": "^2.1.2",
"rollup-plugin-node-globals": "^1.4.0",
"rollup-plugin-node-resolve": "^3.4.0",
"rollup-plugin-terser": "^3.0.0"
}
}
61 changes: 61 additions & 0 deletions libs/rollup/rollup.config.js
@@ -0,0 +1,61 @@
import json from 'rollup-plugin-json';
import alias from 'rollup-plugin-alias';
import resolve from 'rollup-plugin-node-resolve';
import builtins from 'rollup-plugin-node-builtins';
import globals from 'rollup-plugin-node-globals';
import commonjs from 'rollup-plugin-commonjs';
import { terser } from 'rollup-plugin-terser';

const buildPlugins = opts => [
json(),
resolve({
preferBuiltins: true,
...opts.resolve
}),
commonjs({
namedExports: {
'node_modules/resolve/index.js': [ 'sync' ],
'node_modules/async/dist/async.js': [ 'eachSeries' ],
...opts.commonjs.namedExports
}
}),
];

const config = {
input : 'espruino-rollup.js',
output : {
file: 'espruino-rollup.browser.js',
name: 'espruinoRollup',
format: 'umd',
},
plugins: [
alias({
fs: require.resolve('memfs'),
debug: require.resolve('./debug-shim')
}),
...buildPlugins({
resolve: {
browser: true,
},
commonjs: {
namedExports: {
'node_modules/memfs/lib/index.js': [
'statSync', 'lstatSync', 'realpathSync',
'mkdirSync', 'readdirSync',
'readFileSync',
'writeFile', 'writeFileSync',
'watch',
]
}
}
}),
builtins(),
globals({
dirname: false
}),
terser(),
]
};

// console.log( config );
export default config;
4 changes: 3 additions & 1 deletion package.json
Expand Up @@ -7,7 +7,7 @@
"espruino": "./bin/espruino-cli.js"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"build": "cd libs/rollup && npm install && npm run build"
},
"repository": {
"type": "git",
Expand All @@ -19,6 +19,8 @@
"escodegen": "",
"esmangle": "",
"esprima": "",
"rollup": "^0.66.2",
"rollup-plugin-espruino-modules": "opichals/rollup-plugin-espruino-modules",
"request": "^2.74.0",
"tar.gz": "^1.0.7",
"utf8": "^2.1.2"
Expand Down
39 changes: 39 additions & 0 deletions plugins/minify.js
Expand Up @@ -29,6 +29,7 @@
name : "Minification",
description : "Automatically minify code from the Editor window?",
type : { "":"No Minification",
"TERSER":"Terser (uglify-es)",
"ESPRIMA":"Esprima (offline)",
"WHITESPACE_ONLY":"Closure (online) - Whitespace Only",
"SIMPLE_OPTIMIZATIONS":"Closure (online) - Simple Optimizations",
Expand All @@ -40,13 +41,28 @@
name : "Module Minification",
description : "Automatically minify modules? Only modules with a .js extension will be minified - if a file with a .min.js extension exists then it will be used instead.",
type : { "":"No Minification",
"TERSER":"Terser (uglify-es)",
"ESPRIMA":"Esprima (offline)",
"WHITESPACE_ONLY":"Closure (online) - Whitespace Only",
"SIMPLE_OPTIMIZATIONS":"Closure (online) - Simple Optimizations",
"ADVANCED_OPTIMIZATIONS":"Closure (online) - Advanced Optimizations (not recommended)"},
defaultValue : "ESPRIMA"
});

Espruino.Core.Config.add("ROLLUP",{
section : "Rollup",
name : "Use Rollup",
description : "Uses rollup.js along with rollup-plugin-espruino-modules for bundling",
type : "boolean",
defaultValue : false
});
Espruino.Core.Config.add("MODULE_MERGE",{
section : "Rollup",
name : "Unwrap modules using rollup.js",
description : "Uses rollup.js wihout the Modules.addCache",
type : "boolean",
defaultValue : true
});

Espruino.Core.Config.add("MINIFICATION_Mangle",{
section : "Minification",
Expand Down Expand Up @@ -246,7 +262,29 @@
}
}

function minifyCodeTerser(code, callback, description){
rollupTools.minifyCodeTerser(code)
.then(generated => {
var minified = generated.code;

// FIXME: needs warnings?
Espruino.Core.Notifications.info('Terser no errors'+description+'. Minifying ' + code.length + ' bytes to ' + minified.length + ' bytes');
callback(minified);
})
.catch(err => {
Espruino.Core.Notifications.warning("Terser errors"+description+" - sending unminified code.");
Espruino.Core.Notifications.error(String(err).trim());
callback(code);
});
}


function minify(code, callback, level, isModule, description) {
if (Espruino.Config.ROLLUP) {
// already minified by the ROLLUP pipeline
return callback(code);
}

var minifyCode = code;
var minifyCallback = callback;
if (isModule) {
Expand All @@ -265,6 +303,7 @@
case "SIMPLE_OPTIMIZATIONS":
case "ADVANCED_OPTIMIZATIONS": minifyCodeGoogle(code, callback, level, description); break;
case "ESPRIMA": minifyCodeEsprima(code, callback, description); break;
case "TERSER": minifyCodeTerser(code, callback, description); break;
default: callback(code); break;
}
}
Expand Down

0 comments on commit f530c74

Please sign in to comment.