Skip to content

Commit f8a5ed8

Browse files
authored
Merge pull request #80 from opichals/add-rollup
Add Rollup.js tree-shaking and terser minification
2 parents 8982162 + b520965 commit f8a5ed8

File tree

13 files changed

+375
-44
lines changed

13 files changed

+375
-44
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
.settings
33
*~
44
node_modules
5+
package-lock.json
56
tmp

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
CONTRIBUTING.md
33
index.html
44
libs/esprima
5+
libs/rollup/espruino-rollup.browser.js
56
libs/targz.js
67
libs/utf8.js
78
plugins/_examplePlugin.js

bin/espruino-cli.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,10 @@ function setupConfig(Espruino, callback) {
254254
process.exit(1);
255255
//Espruino.Core.Config.getSection(sectionName);
256256
}
257+
if (args.file) {
258+
var env = Espruino.Core.Env.getData();
259+
env.FILE = args.file;
260+
}
257261
if (args.board) {
258262
log("Explicit board JSON supplied: "+JSON.stringify(args.board));
259263
Espruino.Config.ENV_ON_CONNECT = false;

core/modules.js

Lines changed: 84 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,24 @@
6161

6262
// When code is sent to Espruino, search it for modules and add extra code required to load them
6363
Espruino.addProcessor("transformForEspruino", function(code, callback) {
64+
if (Espruino.Config.ROLLUP) {
65+
return loadModulesRollup(code, callback);
66+
}
6467
loadModules(code, callback);
6568
});
69+
70+
// Append the 'getModule' processor as the last (plugins get initialized after Espruino.Core modules)
71+
Espruino.Plugins.CoreModules = {
72+
init: function() {
73+
Espruino.addProcessor("getModule", function(data, callback) {
74+
if (data.moduleCode!==undefined) { // already provided be previous getModule processor
75+
return callback(data);
76+
}
77+
78+
fetchGetModule(data, callback);
79+
});
80+
}
81+
};
6682
}
6783

6884
function isBuiltIn(module) {
@@ -105,6 +121,50 @@
105121
return modules;
106122
};
107123

124+
/** Download modules from MODULE_URL/.. */
125+
function fetchGetModule(data, callback) {
126+
var fullModuleName = data.moduleName;
127+
128+
// try and load the module the old way...
129+
console.log("loadModule("+fullModuleName+")");
130+
131+
var urls = []; // Array of where to look for this module
132+
var modName; // Simple name of the module
133+
if(Espruino.Core.Utils.isURL(fullModuleName)) {
134+
modName = fullModuleName.substr(fullModuleName.lastIndexOf("/") + 1).split(".")[0];
135+
urls = [ fullModuleName ];
136+
} else {
137+
modName = fullModuleName;
138+
Espruino.Config.MODULE_URL.split("|").forEach(function (url) {
139+
url = url.trim();
140+
if (url.length!=0)
141+
Espruino.Config.MODULE_EXTENSIONS.split("|").forEach(function (extension) {
142+
urls.push(url + "/" + fullModuleName + extension);
143+
})
144+
});
145+
};
146+
147+
// Recursively go through all the urls
148+
(function download(urls) {
149+
if (urls.length==0) {
150+
return callback(data);
151+
}
152+
var dlUrl = urls[0];
153+
Espruino.Core.Utils.getURL(dlUrl, function (code) {
154+
if (code!==undefined) {
155+
// we got it!
156+
data.moduleCode = code;
157+
data.isMinified = dlUrl.substr(-7)==".min.js";
158+
return callback(data);
159+
} else {
160+
// else try next
161+
download(urls.slice(1));
162+
}
163+
});
164+
})(urls);
165+
}
166+
167+
108168
/** Called from loadModule when a module is loaded. Parse it for other modules it might use
109169
* and resolve dfd after all submodules have been loaded */
110170
function moduleLoaded(resolve, requires, modName, data, loadedModuleData, alreadyMinified){
@@ -144,51 +204,16 @@
144204
return new Promise(function(resolve, reject) {
145205
// First off, try and find this module using callProcessor
146206
Espruino.callProcessor("getModule",
147-
{ moduleName:fullModuleName, moduleCode:undefined },
207+
{ moduleName:fullModuleName, moduleCode:undefined, isMinified:false },
148208
function(data) {
149-
if (data.moduleCode!==undefined) {
150-
// great! it found something. Use it.
151-
moduleLoaded(resolve, requires, fullModuleName, data.moduleCode, loadedModuleData, false);
152-
} else {
153-
// otherwise try and load the module the old way...
154-
console.log("loadModule("+fullModuleName+")");
155-
156-
var urls = []; // Array of where to look for this module
157-
var modName; // Simple name of the module
158-
if(Espruino.Core.Utils.isURL(fullModuleName)) {
159-
modName = fullModuleName.substr(fullModuleName.lastIndexOf("/") + 1).split(".")[0];
160-
urls = [ fullModuleName ];
161-
} else {
162-
modName = fullModuleName;
163-
Espruino.Config.MODULE_URL.split("|").forEach(function (url) {
164-
url = url.trim();
165-
if (url.length!=0)
166-
Espruino.Config.MODULE_EXTENSIONS.split("|").forEach(function (extension) {
167-
urls.push(url + "/" + fullModuleName + extension);
168-
})
169-
});
170-
};
171-
172-
// Recursively go through all the urls
173-
(function download(urls) {
174-
if (urls.length==0) {
175-
Espruino.Core.Notifications.warning("Module "+fullModuleName+" not found");
176-
return resolve();
177-
}
178-
var dlUrl = urls[0];
179-
Espruino.Core.Utils.getURL(dlUrl, function (data) {
180-
if (data!==undefined) {
181-
// we got it!
182-
moduleLoaded(resolve, requires, fullModuleName, data, loadedModuleData, dlUrl.substr(-7)==".min.js");
183-
} else {
184-
// else try next
185-
download(urls.slice(1));
186-
}
187-
});
188-
})(urls);
209+
if (data.moduleCode===undefined) {
210+
Espruino.Core.Notifications.warning("Module "+fullModuleName+" not found");
211+
return resolve();
189212
}
190-
});
191213

214+
// great! it found something. Use it.
215+
moduleLoaded(resolve, requires, fullModuleName, data.moduleCode, loadedModuleData, data.isMinified);
216+
});
192217
});
193218
}
194219

@@ -211,8 +236,24 @@
211236
callback(loadedModuleData.join("\n") + "\n" + code);
212237
});
213238
}
214-
};
239+
}
240+
241+
function loadModulesRollup(code, callback) {
242+
rollupTools.loadModulesRollup(code)
243+
.then(generated => {
244+
const minified = generated.code;
245+
console.log('rollup: '+minified.length+' bytes');
215246

247+
// FIXME: needs warnings?
248+
Espruino.Core.Notifications.info('Rollup no errors. Bundling ' + code.length + ' bytes to ' + minified.length + ' bytes');
249+
callback(minified);
250+
})
251+
.catch(err => {
252+
console.log('rollup:error', err);
253+
Espruino.Core.Notifications.error("Rollup errors - Bundling failed: " + String(err).trim());
254+
callback(code);
255+
});
256+
}
216257

217258
Espruino.Core.Modules = {
218259
init : init

index.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ function init(callback) {
9494
// Various plugins
9595
loadDir(__dirname+"/plugins");
9696

97+
try {
98+
global.espruinoRollup = require("./libs/rollup/espruino-rollup.js");
99+
global.rollupTools = require("./libs/rollup/index.js");
100+
} catch(e) {
101+
console.log("espruinoRollup library not found - you'll need it to minify code");
102+
}
103+
97104
// Bodge up notifications
98105
Espruino.Core.Notifications = {
99106
success : function(e) { console.log(e); },

libs/rollup/debug-shim.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default () => () => undefined

libs/rollup/espruino-rollup.browser.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

libs/rollup/espruino-rollup.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
const { writeFileSync, vol } = require('fs');
2+
const rollup = require('rollup');
3+
const espruinoModules = require('rollup-plugin-espruino-modules');
4+
5+
function bundle(options) {
6+
const opts = { ...options };
7+
8+
if (typeof vol !== 'undefined') { // only in browser (fs = memfs)
9+
vol.fromJSON({'/modules': null});
10+
11+
if (opts.modules) {
12+
try {
13+
opts.modules.forEach(([name, code]) => writeFileSync(name, code));
14+
} catch (err) {
15+
console.error('Write file failed:', err);
16+
}
17+
delete opts.modules;
18+
}
19+
}
20+
21+
const warnings = [];
22+
opts.onwarn = warning => (warnings.push( warning ), (options.onwarn && options.onwarn(warning)));
23+
24+
const config = espruinoModules.buildRollupConfig(opts);
25+
26+
return rollup.rollup(config).then(bundle =>
27+
bundle.generate(config.output).then(generated => {
28+
generated.warnings = warnings;
29+
return generated;
30+
})
31+
);
32+
}
33+
34+
function minify(code, options) {
35+
return new Promise((resolve, reject) => {
36+
try {
37+
const minifyOptions = espruinoModules.buildMinifyConfig(options)
38+
const generated = espruinoModules.minify(code, minifyOptions);
39+
resolve(generated);
40+
} catch(e) {
41+
reject(e);
42+
}
43+
});
44+
}
45+
46+
module.exports = {
47+
bundle,
48+
minify
49+
}

libs/rollup/index.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
(function (root, factory) {
2+
'use strict';
3+
4+
if (typeof define === 'function' && define.amd) {
5+
define(['exports'], factory);
6+
} else if (typeof exports !== 'undefined') {
7+
factory(exports);
8+
} else {
9+
factory((root.rollupTools = {}));
10+
}
11+
}(this, function(exports) {
12+
13+
// =========================================================
14+
15+
function loadModulesRollup(code) {
16+
var board = Espruino.Core.Env.getBoardData();
17+
var env = Espruino.Core.Env.getData();
18+
var modules = [];
19+
20+
var entryFilename = env.FILE;
21+
22+
// the env.FILE is only present in the espruino-cli
23+
if (!entryFilename) {
24+
// the 'modules' contents is written the filesystem in the espruinoRollup()
25+
// for in-browser setup with filesystem simulation
26+
entryFilename = 'main.js';
27+
modules.push([entryFilename, code]);
28+
}
29+
30+
var job = Espruino.Config;
31+
var minify = job.MINIFICATION_LEVEL === 'TERSER';
32+
var minifyModules = job.MODULE_MINIFICATION_LEVEL === 'TERSER';
33+
34+
return espruinoRollup.bundle({
35+
modules,
36+
input: entryFilename,
37+
output: {
38+
format: 'cjs'
39+
},
40+
espruino: {
41+
job,
42+
43+
externals: {
44+
// for proxy and offline support
45+
getURL: url => new Promise((resolve, reject) => {
46+
Espruino.Core.Utils.getURL(url, data => data!==undefined ? resolve(data) : reject(null));
47+
}),
48+
// for project sandbox chrome app
49+
getModule: moduleName => new Promise((resolve, reject) => {
50+
Espruino.callProcessor("getModule",
51+
{ moduleName, moduleCode:undefined, isMinified:false },
52+
data => data.moduleCode!==undefined ? resolve(data.moduleCode) : reject(null));
53+
})
54+
},
55+
56+
board: board.BOARD ? board : env,
57+
mergeModules: job.MODULE_MERGE,
58+
minify: minify ? buildEspruinoMinifyOptions() : false,
59+
minifyModules
60+
}
61+
});
62+
}
63+
64+
function buildEspruinoMinifyOptions() {
65+
var job = Espruino.Config;
66+
67+
var options = {};
68+
if (job.MINIFICATION_Mangle === false) {
69+
options.mangle = false;
70+
}
71+
if (job.MINIFICATION_Unused === false) {
72+
options.compress = options.compress || {};
73+
options.compress.unused = false;
74+
}
75+
if (job.MINIFICATION_DeadCode === false) {
76+
options.compress = options.compress || {};
77+
options.compress.dead_code = false;
78+
}
79+
if (job.MINIFICATION_Unreachable === false) {
80+
options.compress = options.compress || {};
81+
options.compress.dead_code = false; // in Terser dead_code ~ unreachable
82+
}
83+
if (job.MINIFICATION_Literal === false) {
84+
options.compress = options.compress || {};
85+
options.compress.reduce_vars = false;
86+
}
87+
88+
return options;
89+
}
90+
91+
function minifyCodeTerser(code) {
92+
return espruinoRollup.minify(code, buildEspruinoMinifyOptions());
93+
}
94+
95+
exports.loadModulesRollup = loadModulesRollup
96+
exports.minifyCodeTerser = minifyCodeTerser;
97+
98+
}));

libs/rollup/package.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "espruino-rollup",
3+
"version": "0.1.0",
4+
"description": "Espruino rollup bundling pipeline",
5+
"main": "espruino-rollup.js",
6+
"scripts": {
7+
"build": "rollup -c"
8+
},
9+
"keywords": [],
10+
"author": "Standa Opichal",
11+
"license": "MIT",
12+
"dependencies": {
13+
"memfs": "=2.12.1",
14+
"rollup": "^0.67.4",
15+
"rollup-plugin-espruino-modules": "opichals/rollup-plugin-espruino-modules"
16+
},
17+
"devDependencies": {
18+
"rollup-plugin-alias": "^1.4.0",
19+
"rollup-plugin-commonjs": "^9.1.8",
20+
"rollup-plugin-json": "^3.1.0",
21+
"rollup-plugin-node-builtins": "^2.1.2",
22+
"rollup-plugin-node-globals": "^1.4.0",
23+
"rollup-plugin-node-resolve": "^3.4.0",
24+
"rollup-plugin-terser": "^3.0.0"
25+
}
26+
}

0 commit comments

Comments
 (0)