-
Notifications
You must be signed in to change notification settings - Fork 29
Build System: Bundles and Tabs
To run module code, js-slang would have to have all the dependencies of every single module, which would make building it tedious and bloated and also introduce an undesirable dependency between modules and js-slang. Instead, Source Modules are bundled before use.
Bundling refers to the process of combining all of a module's dependencies into a single file. You can refer to other projects that require bundling for more information.
Dependencies available at runtime aren't bundled and are handled differently (refer to later sections for more information)
Currently, there are several bundlers available such as RollupJS, Babel and Webpack. These bundlers trade speed for high configurability, giving the user a wide range of configuration options and plugins to customize the bundling process. Most of these options are unnecessary for bundling source modules.
esbuild
is a Javascript bundler that trades configurability for speed. It is magnitudes faster than most other bundlers and suits the modules repository just fine. We use it to transpile module code from Typescript to Javascript and perform bundling.
Each Source module bundle is passed through esbuild
, which converts it into an IIFE. Here is the curve
module passed through esbuild
:
// All of the code within src/bundles/curve is combined into a single file
// build/bundles/curve.js
var globalName = (() => {
// Code from mat4
function create() { /* implemntation details */ }
function clone(a) { /* implemntation details */ }
function copy(out, a) { /* implemntation details */ }
// ... and other implementation details
// The module's exports are returned as a single object
return {
draw_connected_2d,
make_point,
// etc...
}
})();
Bundles are transpiled with esbuild using the following option set:
bundle: true,
external: ['js-slang*'],
format: 'iife',
globalName: 'module',
define: JSON.stringify({
process: {
NODE_ENV: 'production',
}
}),
loader: {
'.ts': 'ts',
'.tsx': 'tsx',
},
platform: 'browser',
target: 'es6',
write: false,
Tell esbuild
to bundle the code into a single file.
Because the frontend is built using React, it is unnecessary to bundle React with the code for tabs. Similarly, js-slang/context
is an import provided by js-slang
at runtime, so it is not bundled with the code for bundles.
If you have any dependencies that are provided at runtime, use this option to externalize it.
Tell esbuild
to output the code as an IIFE.
By default, esbuild
's IIFE output doesn't return its exports:
(function() {
var exports = {}
exports.add_one = function(x) {
return x + 1;
}
})()
By specifying a globalName
, the generated code instead becomes:
var module = (function() {
var exports = {}
exports.add_one = function(x) {
return x + 1;
}
return exports;
})()
It is then possible to extract the inner IIFE and use it to retreive the exports.
Module code that requires constructs such as process.env
which are unavailable in the browser environment will cause the Source program to crash.
The define
option tells esbuild to replace instances with process.env
with { NODE_ENV; 'production' }
, making that environment variable available at runtime
Tell esbuild
how to load source files.
Tell esbuild
that we are bundling for the browser, and that we need to compile code down to the ES6 standard, which is the Javascript standard targeted by Source.
write: false
causes esbuild
to its compiled code into memory instead of to disk, which is necessary to finish building the bundle or tab.
After esbuild bundling, both bundles and tabs are parsed using acorn to produce an AST. Esbuild will produce an IIFE that looks like the following:
var module = (function() {
var exports = {}
exports.add_one = function(x) {
return x + 1;
}
return exports;
})()
Bundles then get transformed to
require => {
var exports = {}
exports.add_one = function(x) {
return x + 1;
}
return exports;
}
Tabs get transformed to:
require => () => {
// tab code...
}()['default']
When bundles and tabs are loaded, the IIFE is called with a function that simulates the require()
function in CommonJS to provide the dependencies marked as external (that have to be provided at runtime). External dependencies are not bundled with the tab or bundle code.
For bundles, the only packages provided at runtime (by js-slang) are:
js-slang/index
js-slang/dist/types
js-slang/dist/stdlib
For tabs, the packages provided at runtime (by the frontend are):
js-slang
js-slang/dist
-
react
andreact/jsx-runtime
(Both are needed to maintain compatibility across JSX transforms) react-dom
react-ace
@blueprintjs/core
@blueprintjs/icons
@blueprintjs/popover2
- Home
- Overview
- System Implementation
-
Development Guide
- Getting Started
- Repository Structure
-
Creating a New Module
- Creating a Bundle
- Creating a Tab
- Writing Documentation
- Developer Documentation (TODO)
- Build System
- Source Modules
- FAQs
Try out Source Academy here.
Check out the Source Modules generated API documentation here.