I have a project that I've set up using gulp and babel. Everything is working fine, except when I create a module and import it once it's converted from ES6 to ES6 it doesn't work. I get an error:
Error: Cannot find module 'hello.js'
at Function.Module._resolveFilename (module.js:440:15)
at Function.Module._load (module.js:388:25)
at Module.require (module.js:468:17)
Here's my gulpfile.babel.js:
import gulp from "gulp";
import babel from "gulp-babel"
import concat from "gulp-concat"
const dirs = {
src: "src",
dest: "build"
}
gulp.task("build", () => {
return gulp.src(dirs.src + "/**/*.js")
.pipe(babel())
.pipe(concat("build.js"))
.pipe(gulp.dest(dirs.dest))
});
gulp.task("default", ["build"]);
During build everything is concatenated into one file. Under src/ I have:
app.js
hellojs
app.js
import hello from './hello.js'
console.log(hello());
hello.js
export default () => {
return 'Hey from hello.js';
};
And I run like so:
npm start
Which basically calls node ./build/build.js.
I think it's because it's concatenating the ES6 into ES5 and the bundle.js still contains the require for hello.js. It wont find it though because its concatenated. Is that possible?
It is incorrect to concatenate two module files and expect the program to work properly, even when transpiled to ES5. Bundling involves more than concatenating the scripts: each module needs a closure for registering exports and resolving the contents of other modules.
You must instead use a bundling tool such as Browserify, Webpack or Rollup. Here's how one would bundle with Browserify (which in this case, it is easier to rely on the Babelify transform rather than gulp-babel):
var browserify = require('browserify');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var babelify = require('babelify');
gulp.task('browserify', function() {
return browserify({
entries: './src/app.js'
})
.transform(babelify)
.bundle()
.pipe(source('bundle.js'))
.pipe(gulp.dest('./build/'));
});
Related
im about to write a complex Incoming WebHook for Rocket.Chat. To avoid a mess in one single file i took Typescript. Rocket.Chat requires a class named Script with some predefined methods like process_incoming_request (one simple example: https://rocket.chat/docs/administrator-guides/integrations/).
my current project setup looks like:
tsconfig.ts
{
"files": [
"src/main.ts"
],
"compilerOptions": {
"noImplicitAny": true,
"target": "es2015"
}
}
gulpfile.js
var gulp = require("gulp");
var browserify = require("browserify");
var source = require("vinyl-source-stream");
var tsify = require("tsify");
var uglify = require("gulp-uglify");
var buffer = require("vinyl-buffer");
gulp.task(
"default",
function () {
return browserify({
basedir: ".",
debug: true,
entries: ["src/main.ts"],
cache: {},
packageCache: {}
})
.plugin(tsify)
.transform("babelify", {
presets: ["es2015"],
extensions: [".ts"]
})
.bundle()
.pipe(source("bundle.js"))
.pipe(buffer())
.pipe(uglify())
.pipe(gulp.dest("dist"));
}
);
main.ts
import {RequestInterface} from "./Interface/RequestInterface";
class Script {
process_incoming_request(request: RequestInterface) {
// some code
}
}
The yarn gulp process runs smoothly without errors but when using the generated code inside the script part of the webhook it results in an error:
Incoming WebHook.error script.js:1
ReferenceError: module is not defined
at script.js:1:4307
at Script.runInContext (vm.js:127:20)
at Script.runInNewContext (vm.js:133:17)
at getIntegrationScript (app/integrations/server/api/api.js:70:12)
at Object.executeIntegrationRest (app/integrations/server/api/api.js:166:13)
at app/api/server/api.js:343:82
at Meteor.EnvironmentVariable.EVp.withValue (packages/meteor.js:1234:12)
at Object._internalRouteActionHandler [as action] (app/api/server/api.js:343:39)
at Route.share.Route.Route._callEndpoint (packages/nimble_restivus/lib/route.coffee:150:32)
at packages/nimble_restivus/lib/route.coffee:59:33
Im not that familiar with Typescript, Node and all the stuff. So the main question is, how can i achive that the process generates a class (or a script which exposes a class) named Script with the method process_incoming_request. Im also not sure if my script generates the error or the RocketChat part.
Thanks!
I guess the problem is that Gulp (or some of it's plugins) generates a scaffolding code, necessary for JS's (non-existent) module system, and it often implies wrapping the compiler output into weird multi-layered anonymous functions.
If you don't need any kind of module system and just want your multiple TS files translated directly to a single JS file (which supposedly goes to the RocketChat), then I'd suggest ditching Gulp altogether, letting TSC to compile your code as usual, then bundling the resulting .js files into a single one with a script.
So, the overall setup would be as follows (assuming src is a source code folder):
tsconfig.json
{
"include": [
"src/**/*.ts"
],
"compilerOptions": {
"noImplicitAny": true,
"target": "es2016"
}
}
build.sh
#!/bin/bash
# TSC puts compiled JS files along with their TS sources.
node_modules/typescript/bin/tsc
# Creating an empty bundle file.
echo "">dist/app.js
# Bundling all the JS together.
# sed removes the 'export' keywords & 'import' statements.
while read p; do
cat $p | sed -E "s/^export\s+(class|function|async|const|var)/\1/" | sed -E "s/import.*$//" >> dist/app.js
done <<< $(find src -type f -name "*.js")
So you program your thing as usual, build it with ./build.sh, get the dist/app.js file and use it in RocketChat.
There must be a way to do something along these lines in Gulp, but I'm not familiar with it, and don't think a full-blown build system is really needed here.
I'm using gulp for concat my plugins (bootstrap, jquery, etc) in one big file, this is my code:
const gulp = require('gulp');
const csso = require('gulp-csso');
const concat = require('gulp-concat');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
gulp.task('build', function() {
gulp.src(["./bower_components/bootstrap/dist/css/bootstrap.min.css",
"./bower_components/ekko-lightbox/dist/ekko-lightbox.css",
"./bower_components/owl.carousel/dist/assets/owl.carousel.min.css",
"./bower_components/owl.carousel/dist/assets/owl.theme.default.css",
"./client/css/core.css",
"./client/css/responsive.css"])
.pipe(csso())
.pipe(concat("main.css"))
.pipe(gulp.dest("./build"));
gulp.src(["./bower_components/jquery/dist/jquery.js",
"./bower_components/bootstrap/dist/js/bootstrap.bundle.js",
"./bower_components/ekko-lightbox/dist/ekko-lightbox.min.js",
"./bower_components/lazysizes/lazysizes.min.js",
"./bower_components/owl.carousel/dist/owl.carousel.min.js",
"./bower_components/moment/min/moment.min.js",
"./bower_components/moment/min/locales.min.js",
"./client/js/script.js" ])
.pipe(babel({
presets: ['es2015']
}))
.pipe(uglify())
.pipe(concat("main.js"))
.pipe(gulp.dest('./build'));
});
And it's doing OK. But when i'm including this script on my project, console throws my error: Uncaught TypeError: Cannot set property 'bootstrap' of undefined.
I tried just concat it without es2015 or uglify, but this error (or another, like moment is not defined) still persists. What am i doing wrong?
Ok, i got it.
npm install --save-dev babel-plugin-proposal-object-rest-spread
npm install --save-dev babel-plugin-transform-object-rest-spread
Then pipe
.pipe(babel({
presets: [['env', {
loose: true,
modules: false,
exclude: ['transform-es2015-typeof-symbol']
}]],
plugins: ['transform-es2015-modules-strip', 'transform-object-rest-spread']
}))
And it works
I've installed three.js library through NPM to get advantage of the new ES6 modular architecture which should let you to import just the modules you need, as explained here: Threejs - Import via modules.
I am using gulp, browserify and babel for bundling and transpiling, like so:
gulp.task("build_js", () => {
return browserify({
entries: "./public/app/app.js",
cache: {},
dev: true
})
.transform(babelify, {presets: ["env"], plugins: ["syntax-async-functions", "transform-async-to-generator"], sourceMaps: true})
.bundle()
.pipe(source("app.bundle.min.js"))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: mode}))
.pipe(uglify())
.pipe(sourcemaps.write("./"))
.pipe(gulp.dest(config.build.js))
});
I want to import only the modules I need and keep the bundle size small, but I noticed that the bundle generated by browserify has the same size regardless if I import all the modules or just one.
If in app.js I import all the modules I got a bundle size of about 500Kb:
// app.js
import * as THREE from 'three'; // about 500 Kb size
But if I try to import just a specific module using ES6 syntax I got the same bundle size (it is importing again all the modules):
// app.js
import { Vector3 } from 'three'; // about 500 Kb size, same as before example
I've also tried the following:
// app.js
import { Vector3 } from "three/build/three.module.js";
But I got the following error:
SyntaxError: 'import' and 'export' may only appear at the top level (45590:0) while parsing /Users/revy/proj001/node_modules/three/build/three.module.js
My question: how can I properly import only the modules I need and keep the bundle size small?
You are missing the concept of Tree Shaking.
When you import a modules by name the other modules are not automatically removed from the bundle. The bundler always includes every module in the code and ignores what you have specified as import names.
The other unused modules, which you did not import, are considered dead code because they are in the bundle however they are not called by your code.
So to remove this unused code from the bundle and thus make the bundle smaller you need a minifier that supports dead code removal.
Check out this popular tree shaking plugin for browserify - it should get you started:
https://github.com/browserify/common-shakeify
Solved using rollupify inside browserify transform. It will perform tree shaking and remove dead code:
gulp.task("build_js", () => {
return browserify({
entries: "./public/app/app.js",
cache: {},
dev: true
})
.transform(rollupify, {config: {}}) // <---
.transform(babelify, {presets: ["env"], plugins: ["syntax-async-functions", "transform-async-to-generator"], sourceMaps: true})
.bundle()
.pipe(source("app.bundle.min.js"))
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: mode}))
.pipe(uglify())
.pipe(sourcemaps.write("./"))
.pipe(gulp.dest(config.build.js))
});
}
Still I would appreciated an explanation on why ES6 module import works like this..
When I run gulp I get the following error:
[12:54:14] { [GulpUglifyError: unable to minify JavaScript]
cause:
{ [SyntaxError: Unexpected token: operator (>)]
message: 'Unexpected token: operator (>)',
filename: 'bundle.js',
line: 3284,
col: 46,
pos: 126739 },
plugin: 'gulp-uglify',
fileName: 'C:\\servers\\vagrant\\workspace\\awesome\\web\\tool\\bundle.js',
showStack: false }
The offending line contains an arrow function:
let zeroCount = numberArray.filter(v => v === 0).length
I know I can replace it with the following to remedy the minification error by abandoning ES6 syntax:
let zeroCount = numberArray.filter(function(v) {return v === 0;}).length
How can I minify code containing ES6 features via gulp?
You can leverage gulp-babel as such...
const gulp = require('gulp');
const babel = require('gulp-babel');
const uglify = require('gulp-uglify');
gulp.task('minify', () => {
return gulp.src('src/**/*.js')
.pipe(babel({
presets: ['es2015']
}))
.pipe(uglify())
// [...]
});
This will transpile your es6 early in the pipeline and churn out as widely supported "plain" javascript by the time you minify.
May be important to note - as pointed out in comments - the core babel compiler ships as a peer dependency in this plugin. In case the core lib is not being pulled down via another dep in your repo, ensure this is installed on your end.
Looking at the peer dependency in gulp-babel the author is specifying #babel/core (7.x). Though, the slightly older babel-core (6.x) will work as well. My guess is the author (who is the same for both projects) is in the midsts of reorganizing their module naming. Either way, both npm installation endpoints point to https://github.com/babel/babel/tree/master/packages/babel-core, so you'll be fine with either of the following...
npm install babel-core --save-dev
or
npm install #babel/core --save-dev
The accepted answer doesn't really answer how to minify straight es6. If you want to minify es6 without transpiling, gulp-uglify v3.0.0 makes that possible:
Update March 2019
Using my original answer, you definitely want to replace the uglify-es package with terser, as it seems uglify-es is no longer being maintained.
Original answer, still works:
1.) First, upgrade your gulp-uglify package to > 3.0.0 If you're using yarn and want to update to the latest version:
yarn upgrade gulp-uglify --latest
2.) Now you can use uglify-es, the "es6 version" of uglify, as so:
const uglifyes = require('uglify-es');
const composer = require('gulp-uglify/composer');
const uglify = composer(uglifyes, console);
gulp.task('compress', function () {
return gulp.src('src/*.js')
.pipe(uglify())
.pipe(gulp.dest('dist'))
});
For more info: https://www.npmjs.com/package/gulp-uglify
You actually can uglify ES6 code without transpilation. Instead of gulp-uglify plugin, use gulp-uglifyes plugin.
var gulp = require('gulp');
var rename = require('gulp-rename');
var uglify = require('gulp-uglifyes');
var plumber = require('gulp-plumber');
var plumberNotifier = require('gulp-plumber-notifier');
var sourcemaps = require('gulp-sourcemaps');
var runSequence = require('run-sequence').use(gulp);
gulp.task('minjs', function () {
return gulp.src(['/dist/**/*.js', '!/dist/**/*.min.js'])
.pipe(plumberNotifier())
.pipe(sourcemaps.init())
.pipe(uglify({
mangle: false,
ecma: 6
}))
.pipe(rename(function (path) {
path.extname = '.min.js';
}))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest('/dist'));
});
gulp-uglify:
For ES6 and newer.
install: npm install --save-dev gulp-uglify
install: npm install --save-dev gulp-babel #babel/core #babel/preset-env
Usage:
const gulp = require('gulp');
const uglify = require('gulp-uglify');
const babel = require('gulp-babel');
gulp.task('script', () => {
return gulp.src('src/**/*.js')
.pipe(babel({
presets: ['#babel/env']
}))
.pipe(uglify())
.pipe(gulp.dest('src/dist'))
});
I worked at this for a while before getting it to work. As other answers have stated the problem is that gulp-uglify doesn't support ES6. gulp-uglify-es does, however if is no longer maintained. Terser is recommended by others, but it doesn't play well with gulp and using it with pipe().
If you use gulp-uglify as I do your gulpfile.js looks something like:
var uglify = require('gulp-uglify');
const html2js = () => {
var source = gulp.src(config.appFiles.templates);
return source
.pipe(concat('templates-app.js'))
.pipe(uglify())
.pipe(gulp.dest(config.buildDir));
};
You can however use the gulp-terser package, which is very easy to just replace and get the same functionality:
var terser = require('gulp-terser');
const html2js = () => {
var source = gulp.src(config.appFiles.templates);
return source
.pipe(concat('templates-app.js'))
.pipe(terser())
.pipe(gulp.dest(config.buildDir));
};
Using gulp-uglify-es instead of gulp-uglify helped me perfectly to accomplish same as you're asking for
The current (Nov 2021) easiest way to transpile and uglify is to use gulp-terser.
If you're already using gulp-uglify then just install gulp-terser and change "uglify" with "terser" and you're done.
const uglifyes = require('terser');
gulp.task('compress', function () {
return gulp.src('src/*.js')
.pipe(terser())
.pipe(gulp.dest('dist'))
});
unfortunately, as per now, you can't use uglify with es-next code,
you can:
Transpile to ES5using Babel
Use Babili instead of Uglify.
module.exports = {
...
optimization: {
minimize: true
},
...
}
webpack can do the job
I'm new to UMD and AMD. I've written a JS library in Browserify and I've just came across UMD. My understanding is if I include a piece of code for every module, my module should be able to be used in CommonJs and AMD.
Here's my sample Module.
./src/main.js
import Cookie from 'js-cookie'; // from npm install js-cookie
import lib1 from './lib/lib1';
window.MyModule = function MyModule(options) {
let lib1;
function methodA() {
}
return {
methodA: methodA
};
(function init() {
lib1 = lib1();
// Some initialization code.
})();
};
module.exports = window.MyModule;
./lib/lib1.js
module.exports = (options) => {
function func1() {
}
return {
func1: func1
};
}
And this is how I pack everything using browserify
browserify src/main.js --outfile dist/main.js --debug
And when I want to use this module I just do.
<script src="//main.js"></script>
My question is, how do I convert my module to be UMD so it can be included in both CommonJS and AMD.
You can use deamdify, es6ify and deglobalify to do what you're after.
npm install deamdify es6ify deglobalify
browserify -t deamdify -t es6ify -t deglobalify main.js -o bundle.js
Source: http://dontkry.com/posts/code/browserify-and-the-universal-module-definition.html