Background:
I have several vanilla JS projects with files up to 25 000 lines / single file in which I would like to use ES6 capabilities + require so I can make the code more readable. OS X guy by the way
Possible solution:
I could make up a package.json and use a webpack for each project, but I would prefer if I do not do that.
What I have done so far:
Install browserify globally
sudo npm install -g browserify
setup a watcher in PhpStorm
global browserify path
/usr/local/lib/node_modules/browserify/bin/cmd.js
arguments
$FilePath$
-o
$FileDir$/$FileNameWithoutAllExtensions$.js
all works fine, and if I have a require in my code
var foo = require('./inc/_dependency-functionality');
✅ it will bundle just right and the required external files will get bundle inside a single file
BUT, ideally, I would like to have it so that I can use ES6 import / export functions, which may say I need babelify
doing this
sudo npm install -g browserify
and using
$FilePath$
-o
$FileDir$/$FileNameWithoutAllExtensions$.js
-t [ /usr/local/lib/node_modules/babelify --presets [/usr/local/lib/node_modules/#babel/preset-env ] ]
❌ will not actually babelify the script
I'm kind of stuck
Ok, I managed to do it
$FilePath$
-o
$FileDir$/$FileNameWithoutAllExtensions$.js
-t [ /usr/local/lib/node_modules/babelify/index.js --presets [/usr/local/lib/node_modules/#babel/preset-env ] ]
Babelify should be appended by /index.js
and then you can actually lose the presets, if you have .babelrc in the home of your user library ( babel searches for .babelrc recursively on top )
$FilePath$
-o
$FileDir$/$FileNameWithoutAllExtensions$.js
-t [ /usr/local/lib/node_modules/babelify/index.js ]
Also, make sure your files are named file.source.js not file.babel as I had them, as the transpiling will not start for unknown files
Related
I am trying to use react-vis library in my project. In their readme file, they have asked to import library by adding following lines in non-node environment:
If you're working in a non-node environment, you can also directly include the bundle and compiled style using basic html tags.
<link rel="stylesheet" href="https://unpkg.com/react-vis/dist/style.css">
<script type="text/javascript" src="https://unpkg.com/react-vis/dist/dist.min.js"></script>
The global reactVis object will now be available for you to play around.
When I did that, I got following error:
Uncaught TypeError: Cannot call a class as a function
at _classCallCheck (dist.min.js:formatted:23789:27)
at LabelSeries (dist.min.js:formatted:23823:21)
at Constructor.render (<anonymous>:1866:23)
at Constructor.<anonymous> (react.js:6029:34)
at Constructor._renderValidatedComponent (react.js:11403:21)
at Constructor.<anonymous> (react.js:5582:14)
at Constructor.mountComponent (react.js:11403:21)
at Constructor.mountChildren (react.js:10913:42)
at Constructor._createContentMarkup (react.js:6812:32)
at Constructor.<anonymous> (react.js:6734:14)
After a lot of digging, I realised issue was that the react-vis library follows ES6, while my project is in ES5. So, I thought to transpile react-vis library to ES5. After digging a bit in the react-vis repo, I found that it uses browserify.
So to compile ES6 porject to single ES5 bundle file using browserify itself, I added --presets [ #babel/preset-env ] on this line itself as suggested here:
"build:browser": "browserify src/index.js -t [ babelify --presets [ #babel/preset-env ] ] --standalone reactVis | uglifyjs > dist/dist.min.js",
I also tried adding #babel/preset-react:
"build:browser": "browserify src/index.js -t [ babelify --presets [ #babel/preset-env #babel/preset-react] --rootMode upward ] --standalone reactVis | uglifyjs > dist/dist.min.js",
But in both cases I ended up getting same error: Cannot call a class as a function.
What I am missing?
Question: How can I write my gulp file in ES6 so I can use import instead of require and use => syntax over function()?
I can use io.js or node any version.
gulpfile.js:
import gulp from "./node_modules/gulp/index.js";
gulp.task('hello-world', =>{
console.log('hello world');
});
Errors:
import gulp from "./node_modules/gulp/index.js";
^^^^^^
SyntaxError: Unexpected reserved word
gulp.task('hello-world', =>{
^^
SyntaxError: Unexpected token =>
Inside the node_modules/gulp/bin/gulp.js i've changed the first line to #!/usr/bin/env node --harmony as asked in this stack
Yes, you can by using babel.
Make sure you've got the latest version of the gulp-cli.
npm install -g gulp-cli
Install babel as a dependency of the project.
npm install --save-dev babel
Rename gulpfile.js to gulpfile.babel.js
Your gulpfile might look something like this:
import gulp from 'gulp';
gulp.task('default', () => {
// do something
});
Update for Babel 6.0+
As correctly pointed out by Eric Bronniman, there are a few extra steps involved in getting this to work with the latest version of babel. Here are those instructions:
Again, make sure you've got the latest version of gulp-cli
npm install -g gulp-cli
Then install gulp, babel core, and the es2015 presets
npm install --save-dev gulp babel-core babel-preset-es2015
Then, either add the following to a .babelrc file or to your package.json
"babel": {
"presets": [
"es2015"
]
}
Your gulpfile.js should be named gulpfile.babel.js
Note you can now use many/most ES6 features in Node.js v4.0.0 without babel. However apparently 'import' is still not supported. See: https://nodejs.org/en/docs/es6/
Edit: Most of the popular ES6 features (including destructuring and spread) are supported by default in NodeJS 5.0 (see above link.) The only major missing feature appears to be ES6 modules as far as I can tell.
If you have the latest versions of gulp & node, you can simply create a gulpfile as gulpfile.mjs instead of gulpfile.js and it should work without needing to use Babel or any other transpiler.
.mjs is a special format used by node which allows usage of ES Modules.
References :-
https://nodejs.org/api/esm.html#esm_enabling
https://nodejs.org/api/packages.html#packages_determining_module_system
Example :-
// Filename: gulpfile.mjs
import gulp from 'gulp';
export default task;
function task()
{
return (
gulp.src(`./src/js/**/*.js`)
.pipe(gulp.dest(`./dist/Static/js`)
);
}
I use babel-node and native gulp.
Install babel and gulp as devDependencies.
Write gulpfile.js with ES6 syntax.
Use command ./node_modules/.bin/babel-node ./node_modules/.bin/gulp to run gulp
In package.json scripts section, you can skip the first ./node_modules/.bin/ part - as babel-node ./node_modules/.bin/gulp.
The advantage of this appoach is, one day when the node.js support all ES6 features one day, all you need to opt-out babel runtime is to replace babel-node with node. That is all.
If you're using the most modern version of Gulp and the Gulp CLI, you can just do Gulpfile.babel.js and it will understand and transpile your ES6 gulpfile with BabelJS by default.
It is also important to have the BabelJS transpiler installed under devDependencies as is Gulp:
npm install --save-dev babel
Also note that to require gulp in this context, you do not have to import the index.js, you can just:
import gulp from 'gulp';
Basically, what you need to install using npm is gulp, gulp-babel and babel-resent-env, add "env" to your .babelrc presets array, and use a gulpfile.babel.js file.
npm install gulp-babel --save-dev
Some of the answers mentioned babel-core, babel-preset-es2015, etc. The Babel official setup guide with Gulp is to use gulp-babel only, while gulp-babel has dependencies modules including babel-core so you don't need to install it separately.
About preset, you need to use a preset to make Babel actually do something, which is called Preset Env that automatically determines the Babel plugins you need based on your supported environments.
npm install babel-preset-env --save-dev
and in .babelrc file
{
"presets": ["env"]
}
/*
* Steps
* 1. Rename your gulpfile.js to gulpfile.babel.js
* 2. Add babel to your package.json (npm install -D babel)
* 3. Start writing ES6 in your gulpfile!
*/
import gulp from 'gulp'; // ES6 imports!
import sass from 'gulp-sass';
const sassOpts = { outputStyle: 'compressed', errLogToConsole: true }; // "let" and "const"!!
gulp.task('sass', () = > { // Arrow functions!!
gulp.src('./**/*.scss')
.pipe(sass(sassOpts))
.pipe(gulp.dest('./'));
});
gulp.task('default', ['sass'], () => { // Arrow functions!!
gulp.watch('./src/sass/**/*.scss', ['sass'])
.on('change', (e) => { // Arrow functions!!
console.log(`File ${e.path} was ${e.type}, running Sass task...`); // Template strings and interpolation!!
});
});
Steps I followed for developing the code for gulpfile in es6:
npm install gulp && sudo npm install gulp -g.
Please make sure that you you are using the updated version of Gulp.
The current version at the time of writing this answer was 3.9.1. To check which version of gulp is installed, type gulp -v
npm install babel-core babel-preset-es2015-without-strict --save-dev
Type touch .babelrc in the terminal
In the .babelrc file, add this code
{
"presets": ["es2015-without-strict"]
}
Created the gulp config file with the name gulpfile.babel.js
Voila!!! You can now write the config code for gulp in ES6.
Source: Using ES6 with Gulp - Mark Goodyear
I have just had the same problem and
solved as following:
Windows 10
node version: 14.15.4
npm version: 6.14.10
gulp version: 4.0.2
using yarn v1
Renamed gulpfile.js as gulpfile.babel.js
Added these packages as devdependency:
"#babel/cli": "^7.12.10",
"#babel/core": "^7.12.10",
"#babel/preset-env": "^7.12.11",
"#babel/register": "^7.12.10",
"gulp-babel": "^8.0.0",
Added babel.config.json
{
"presets": [
[
"#babel/preset-env",
{
"targets": {
"ie": "10",
"edge": "17",
"firefox": "60",
"chrome": "70",
"safari": "11.1"
}
}
]
]
}
Finally I deleted yarn.lock file and node_modules folder and installed all packages.
yarn install
This is how my gulpfile.babel.js file looks like:
import { src, dest, parallel, series, watch } from 'gulp';
import autoprefixer from 'autoprefixer';
import cssnano from 'cssnano';
import concat from 'gulp-concat';
import postcss from 'gulp-postcss';
import replace from 'gulp-replace';
import sass from 'gulp-sass';
import { init, write } from 'gulp-sourcemaps';
import uglify from 'gulp-uglify';
import babel from "gulp-babel";
//....
const _default = series(
parallel(scssTask, jsTask),
cacheBustTask,
watchTask
);
export { _default as default };
Note:
yarn gulp command runs properly but I still have that warning:
Requiring external module #babel/register
I import html template files into my javascript components using babel-plugin-transform-html-import-to-string.
When I use watchify, it doesn't update if an html has been changed. Only Javascript file changes. The npm script goes something like this:
watchify -p browserify-hmr -t [babelify ext .js .html] src/index.js -o public/bundle.js
Since this doesn't work I am using watch instead, as shown below, but my builds are at least 5 seconds slower than before, when they were instant.
watch 'npm run browserify' src/ -d --interval=1
where the browserify script is
browserify -t [babelify ext .js] src/index.js -o public/bundle.js
Anyone know how I can run browserify on html file changes without sacrificing fast rebuilds?
The problem is that browserify never sees the references to the .html files, as babelify is replacing them with variables and HTML strings.
If you want watchify to watch the .html files and to rebuild the bundle if they change, you should allow babelify to transpile statements like import some from './some.html' to var some = require('./some.html') and should use a browserify-based transform (like stringify) to transform the required content:
watchify \
-p browserify-hmr \
-t [babelify ext .js] \
-t [stringify ext .html] \
src/index.js -o public/bundle.js
Browserify/watchify will then see the .html files as being included in the bundle and will watch them for changes.
I'm writing code in Javascript for Espruino (an embedded platform) and there is a restriction: Only one file is allowed to be saved into the device.
For the following code to work:
var x = new require("Something");
x.hello()
Something module should be reachable from main.js file. The Something module is a separate file and looks like this:
function Something() {
....
}
Something.prototype.hello = function() {
console.log("Hello there!");
}
exports = Something;
Question is; How can I concatenate these two files and still use the Something module as require("Something"); format?
The answer is found in Espruino forums:
# create bundle
BUNDLE="init.min.js"
echo "" > ${BUNDLE}
# Register modules via `Modules.addCached()` method
# see: http://forum.espruino.com/comments/12899741/
MODULE_NAME="FlashEEPROM"
MODULE_STR=$(uglifyjs -c -m -- ${MODULE_NAME}.js | sed 's/\"/\\\"/g' | sed "s/\n//g")
echo "Modules.addCached(\"${MODULE_NAME}\", \"${MODULE_STR}\");" >> ${BUNDLE}
uglifyjs -c -m -- init.js >> ${BUNDLE}
Why don't you use webpack to bundle the modules files into one file.
I suppose that your current directory is your project directory
First install nodejs at:
https://nodejs.org/en/download/
or sudo apt-get install nodejs
Second, install webpack using this command
npm i -g webpack
bundle your module
To build your bundle run this command:
webpack ./main.js bundle.js
Note: your bundle.js will include all the module files that you have required in main.js
There is https://www.npmjs.com/package/espruino tool that has function to prepare code (concatenate + minimize) for uploading to your board.
I use it in my build proc that compiles typescript code to javascript and then prepares for board upload. Tutorial is here https://github.com/espruino/EspruinoDocs/blob/master/tutorials/Typescript%20and%20Visual%20Studio%20Code%20IDE.md
Yes like it's mentioned above, you need to uglify the code. You cannot use webpack or grunt here. They will transpile the code which will break your require libraries.
Here is how I achieved this in my case:
I put all my files inside a folder (like /src). Don't import one file from another. Then minify all files in the folder into a single file (eg. index.js)
uglifyjs src/* --compress --mangle -o ./index.js
Then just upload this index.js into espruino. You need to install this library globally first.
npm install -g espruino
espruino -p /dev/cu.wchusbserial1420 -b 115200 --board ESP8266_4MB.json -e 'save()' index.js
I made a simple example here:
https://github.com/aliustaoglu/espruino-http-server
Im experimenting with ES6, and Im using gulp to build and babel to transpile to ES5. The output is not being run in node, just linked to from a .htm file with a tag. Im thinking I need to add
<script src='require.js'></script>
or something like that.
Im trying to import / export.
////////////////scripts.js
import {Circle} from 'shapes';
c = new Circle(4);
console.log(c.area());
/////////////////shapes.js
export class Circle {
circle(radius) {
this.radius = radius;
}
area() {
return this.radius * this.radius * Math.PI;
}
}
Error is
Uncaught ReferenceError: require is not defined
Refers to this (after .pipe(babel()) in gulp)
var _shapes = require('shapes');
Do I need require js when I use babel?
You might need some module loader, but it is not necessary RequireJS. You have several options. The following will help you to get started.
rollup.js with rollup-plugin-babel
Rollup is a next-generation JavaScript module bundler. It understands ES2015 modules natively, and will produce a bundle that doesn't need any module loader to operate. Unused exports will be trimmed from the output, it's called tree-shaking.
Now I personally recommend using rollupjs, as it produces the clearest output, and is easy to setup, however, it gives a different aspect to the answer. All the other approaches do the following:
Compile the ES6 code with babel, use the module format of your choice
Concatenate the compiled modules alongside with a module loader OR use a bundler that will traverse the dependencies for you.
With rollupjs things doesn't really work this way. Here, rollup is the first step, instead of babel. It only understands ES6 modules by default. You must give an entry module of which the dependencies will be traversed and concatenated. As ES6 allows multiple named exports in a module, rollupjs is smart enough to strip unused exports, thus shrinking bundle size. Unfortunately rollupjs-s parser doesn't understand >ES6 syntax, so ES7 modules have to be compiled before rollup parses them, but the compilation should not affect the ES6 imports. It is done by using the rollup-plugin-babel plugin with the babel-preset-es2015-rollup preset (this preset is the same as the es2015 one, except the module transformer and the external-helpers plugin). So rollup will do the following with your modules if correctly set up:
Reads your ES6-7 module from the filesystem
The babel plugin compiles it to ES6 in memory
rollup parses the ES6 code for imports and exports (using acorn parser, compiled into rollup)
it traverses the whole graph, and creates a single bundle (which still might have external dependencies, and the entry's exports might be exported, in a format of your choice)
Example nodejs build:
// setup by `npm i rollup rollup-plugin-babel babel-preset-es2015 babel-plugin-external-helpers --save-dev`
// build.js:
require("rollup").rollup({
entry: "./src/main.js",
plugins: [
require("rollup-plugin-babel")({
"presets": [["es2015", { "modules": false }]],
"plugins": ["external-helpers"]
})
]
}).then(bundle => {
var result = bundle.generate({
// output format - 'amd', 'cjs', 'es6', 'iife', 'umd'
format: 'iife'
});
require("fs").writeFileSync("./dist/bundle.js", result.code);
// sourceMaps are supported too!
}).then(null, err => console.error(err));
Example grunt build with grunt-rollup
// setup by `npm i grunt grunt-rollup rollup-plugin-babel babel-preset-es2015 babel-plugin-external-helpers --save-dev`
// gruntfile.js
module.exports = function(grunt) {
grunt.loadNpmTasks("grunt-rollup");
grunt.initConfig({
"rollup": {
"options": {
"format": "iife",
"plugins": [
require("rollup-plugin-babel")({
"presets": [["es2015", { "modules": false }]],
"plugins": ["external-helpers"]
})
]
},
"dist": {
"files": {
"./dist/bundle.js": ["./src/main.js"]
}
}
}
});
}
Example gulp build with gulp-rollup
// setup by `npm i gulp gulp-rollup rollup-plugin-babel babel-preset-es2015 babel-plugin-external-helpers --save-dev`
// gulpfile.js
var gulp = require('gulp'),
rollup = require('gulp-rollup');
gulp.task('bundle', function() {
gulp.src('./src/**/*.js')
// transform the files here.
.pipe(rollup({
// any option supported by Rollup can be set here.
"format": "iife",
"plugins": [
require("rollup-plugin-babel")({
"presets": [["es2015", { "modules": false }]],
"plugins": ["external-helpers"]
})
],
entry: './src/main.js'
}))
.pipe(gulp.dest('./dist'));
});
Babelify + Browserify
Babel has a neat package called babelify. It's usage is simple and straightforward:
$ npm install --save-dev babelify babel-preset-es2015 babel-preset-react
$ npm install -g browserify
$ browserify src/script.js -o bundle.js \
-t [ babelify --presets [ es2015 react ] ]
or you can use it from node.js:
$ npm install --save-dev browserify babelify babel-preset-es2015 babel-preset-react
...
var fs = require("fs");
var browserify = require("browserify");
browserify(["./src/script.js"])
.transform("babelify", {presets: ["es2015", "react"]})
.bundle()
.pipe(fs.createWriteStream("bundle.js"));
This will transpile and concatenate your code at once. Browserify's .bundle will include a nice little CommonJS loader, and will organize your transpiled modules into functions. You can even have relative imports.
Example:
// project structure
.
+-- src/
| +-- library/
| | \-- ModuleA.js
| +-- config.js
| \-- script.js
+-- dist/
\-- build.js
...
// build.js
var fs = require("fs");
var browserify = require("browserify");
browserify(["./src/script.js"])
.transform("babelify", {presets: ["es2015", "react"]})
.bundle()
.pipe(fs.createWriteStream("dist/bundle.js"));
// config.js
export default "Some config";
// ModuleA.js
import config from '../config';
export default "Some nice export: " + config;
// script.js
import ModuleA from './library/ModuleA';
console.log(ModuleA);
To compile just run node build.js in your project root.
Babel + WebPack
Compile all your code using babel. I recommend you to use the amd module transformer (called babel-plugin-transform-es2015-modules-amd in babel 6). After that bundle your compiled sources with WebPack.
WebPack 2 is out! It understands native ES6 modules, and will perform (or rather simulate) tree shaking using babili-s builtin dead code elimination. For now (September 2016) I would still suggest to use rollup with babel, although my opinion might change with the first release of WebPack 2. Feel free to discuss your opinions in the comments.
Custom compilation pipeline
Sometimes you want to have more control over the compilation process. You can implement your own pipeline like this:
First, you have to configure babel to use amd modules. By default babel transpiles to CommonJS modules, which is a little complicated to handle in the browser, although browserify manages to handle them in a nice way.
Babel 5: use { modules: 'amdStrict', ... } option
Babel 6: use the es2015-modules-amd plugin
Don't forget to turn on the moduleIds: true option.
Check the transpiled code for generated modul names, there are often mismatches between defined and required modules. See sourceRoot and moduleRoot.
Finally, you have to have some kind of module loader, but it isn't necessairy requirejs. There is almondjs, a tiny require shim that works well. You can even implement your own:
var __modules = new Map();
function define(name, deps, factory) {
__modules.set(name, { n: name, d: deps, e: null, f: factory });
}
function require(name) {
const module = __modules.get(name);
if (!module.e) {
module.e = {};
module.f.apply(null, module.d.map(req));
}
return module.e;
function req(name) {
return name === 'exports' ? module.e : require(name);
}
}
At the end, you can just concatenate the loader shim and the compiled modules together, and running an uglify on that.
Babel's boilerplate code is duplicated in every module
By default, most of the above methods compile each module with babel individually, and then concatenate them together. That's what babelify does too. But if you look at the compiled code, you see that babel inserts lots of boilerplate at the beginning of each file, most of them are duplicated across all files.
To prevent this you can use the babel-plugin-transform-runtime plugin.
barebones webpack 2
1) If this is your root directory:
index.html
<html>
...
<script src="./bundle.js"></script>
...
</html>
scripts.js
import { Circle } from './shapes.js';
...
shapes.js
export class Circle {
...
}
2) have node installed node
3) run the following command in your terminal:
$ npm install -g webpack
5) in your root directory run the following:
$ webpack scripts.js bundle.js
You should now have a file called bundle.js in your root directory which will be the file your index.html will consume. This is a minimalistic bundling feature from webpack. You can learn more here
require does not exist in the browser, so this error is expected. You need to use something like require.js or Browserify.