Grunt with babel and browserify - javascript

I have a simple JavaScript project that uses Babel to transpile ECMAScript 6 to ES5 and then needs Browserify to take advantage of ES6's Modules.
As so, I came up with this Gruntfile.js to compile it:
module.exports = function(grunt) {
"use strict";
grunt.loadNpmTasks('grunt-babel');
grunt.loadNpmTasks('grunt-browserify');
grunt.initConfig({
"babel": {
options: {
sourceMap: true
},
dist: {
files: {
"lib/pentagine.js": "lib/pentagine_babel.js",
"demos/helicopter_game/PlayState.js": "demos/helicopter_game/PlayState_babel.js"
}
}
},
"browserify": {
dist: {
files: {
"lib/pentagine.js": "lib/pentagine_babel.js",
"demos/helicopter_game/PlayState.js": "demos/helicopter_game/PlayState_babel.js"
}
}
}
});
grunt.registerTask("default", ["babel", "browserify"]);
};
grunt runs just fine without any errors. However, I get the following errors:
Uncaught SyntaxError: Unexpected reserved word on export
Uncaught SyntaxError: Unexpected reserved word on import
Basically what I'm doing in the main file is the following:
export class Game {
...
}
And then importing it like:
import {Sprite, Game} from "lib/pentagine";
I'm doing all the code according to ECMAScript 6. However, the export/import does not seem to be working and is instead colliding with JavaScript reserved words (despite me having browserify.js working).

Shouldn't you browserify the files created after the babel task? Note that the property name is the destination file and the value after the : is the source file. (I assume that your ES6 files are called filename.js instead of filename_babel.js)
files: {
"destination_file": "src_file"
}
Which leads to:
grunt.initConfig({
"babel": {
options: {
sourceMap: true
},
dist: {
files: {
"lib/pentagine_babel.js": "lib/pentagine.js",
"demos/helicopter_game/PlayState_babel.js": "demos/helicopter_game/PlayState.js"
}
}
},
"browserify": {
dist: {
files: {
"lib/pentagine_browserified.js": "lib/pentagine_babel.js",
"demos/helicopter_game/PlayState_browserified.js": "demos/helicopter_game/PlayState_babel.js"
}
}
}
});
or just lib/pentagine_babel.js": "lib/pentagine_babel.js" to browserify the same file.

Related

grunt-terser Giving SyntaxError: "VARIABLE_NAME" is redeclared

I'm using grunt-terser to minify my es6 files.
I have two files.
file-1.js
file-2.js
In both files I have required a module with same variable name like this:
const VARIABLE_NAME = require('MODULE_NAME');
Here is my grunt-terser task:
terser: {
main: {
options: { compress: true, toplevel: true },
files: {
'./dist/app.js':
['file-1.js', 'file-2.js']
}
}
}
When I run
npx grunt terser
I get the following error:
Running "terser:main" (terser) task
>> SyntaxError: "VARIABLE_NAME" is redeclared

Grunt concats JS file multiple times

I'm working on a Node.js website and I'm using Grunt to concat and minify my CSS and JS files. However, after running the grunt command I'm getting the error message:
fullPage: Fullpage.js can only be initialized once and you are doing it multiple times!
Here's my grunt file:
/*global module */
module.exports = function (grunt) {
"use strict";
grunt.initConfig({
// read in the project settings from the package.json file into the pkg property
pkg: grunt.file.readJSON("package.json"),
// Install only the bower packages that we need
bower: {
install: {
options: {
"targetDir": "./public/lib",
"copy": true,
"cleanup": true,
"install": true
}
}
},
concat: {
css: {
src: ["public/lib/css/**/*.css", "public/css/cts.css"],
dest: "public/lib/dist/main.css"
},
js: {
src: ["public/lib/**/jquery.js", "public/lib/**/*.js", "public/js/cts.js"],
dest: "public/lib/dist/main.js"
}
},
cssmin: {
target: {
files: {
"public/lib/dist/main.min.css": "public/lib/dist/main.css"
}
}
},
uglify : {
js: {
files: {
"public/lib/dist/main.min.js": "public/lib/dist/main.js"
}
}
},
copy: {
files: {
expand: true,
flatten: true,
src: ["public/lib/fonts/**/*"],
dest: "public/lib/fonts/",
filter: "isFile"
}
}
});
// Add all plugins that your project needs here
grunt.loadNpmTasks("grunt-bower-task");
grunt.loadNpmTasks("grunt-contrib-concat");
grunt.loadNpmTasks("grunt-contrib-copy");
grunt.loadNpmTasks("grunt-contrib-cssmin");
grunt.loadNpmTasks("grunt-contrib-uglify");
grunt.loadNpmTasks("grunt-contrib-watch");
// this would be run by typing "grunt test" on the command line
// the array should contains the names of the tasks to run
grunt.registerTask("test", []);
// define the default task that can be run just by typing "grunt" on the command line
// the array should contains the names of the tasks to run
grunt.registerTask("default", [ "bower", "concat", "cssmin", "uglify", "copy"]);
grunt.registerInitTask("install", ["bower"]);
};
If anything I would have thought jQuery would be the one that's getting concatenated multiple times but it's not. Any suggestions what I might be doing wrong?
EDIT: Here's my upgraded grunt file with all 3rd party libraries listed in the concat.src.
/// <binding BeforeBuild='default' />
/*global module */
module.exports = function (grunt) {
"use strict";
grunt.initConfig({
// read in the project settings from the package.json file into the pkg property
pkg: grunt.file.readJSON("package.json"),
// Install only the bower packages that we need
bower: {
install: {
options: {
"targetDir": "./public/lib",
"copy": true,
"cleanup": true,
"install": true
}
}
},
concat: {
css: {
src: ["public/lib/css/**/*.css", "public/css/cts.css"],
dest: "public/lib/dist/main.css"
},
js: {
src: [
"public/lib/js/jquery/jquery.js",
"public/lib/js/bootstrap/bootstrap.js",
"public/lib/js/fullpage.js/jquery.fullpage.js",
"public/lib/js/jquery-easing-original/jquery.easing.js",
"public/lib/js/slimscroll/jquery.slimscroll.js",
"public/lib/js/wow/wow.js",
"public/js/cts.js"
],
dest: "public/lib/dist/main.js"
}
},
cssmin: {
target: {
files: {
"public/lib/dist/main.min.css": "public/lib/dist/main.css"
}
}
},
uglify : {
js: {
files: {
"public/lib/dist/main.min.js": "public/lib/dist/main.js"
}
}
},
copy: {
files: {
expand: true,
flatten: true,
src: ["public/lib/fonts/**/*"],
dest: "public/lib/fonts/",
filter: "isFile"
}
}
});
// Add all plugins that your project needs here
grunt.loadNpmTasks("grunt-bower-task");
grunt.loadNpmTasks("grunt-contrib-concat");
grunt.loadNpmTasks("grunt-contrib-copy");
grunt.loadNpmTasks("grunt-contrib-cssmin");
grunt.loadNpmTasks("grunt-contrib-uglify");
grunt.loadNpmTasks("grunt-contrib-watch");
// this would be run by typing "grunt test" on the command line
// the array should contains the names of the tasks to run
grunt.registerTask("test", []);
// define the default task that can be run just by typing "grunt" on the command line
// the array should contains the names of the tasks to run
grunt.registerTask("default", [ "bower", "concat", "cssmin", "uglify", "copy"]);
grunt.registerTask("combine", [ "concat", "cssmin", "uglify", "copy"]);
grunt.registerInitTask("install", ["bower"]);
};
Your issue seems to be in concate.js.src
src: ["public/lib/**/jquery.js", "public/lib/**/*.js", "public/js/cts.js"]
This will have your files added multiple times as there might some files common among the paths mentioned in src.
You should probably move all your vendor files like jquery out of the public directory and put in a different one, say vendor.
Your src should then look something like
src: ["vendor/**/*.js", "public/**/*.js"]
As you see now there are no common files among these two paths.
Also its a good practice to always have 3rd party code outside your app directory as a sibling folder and not inside it.
EDIT:
Ah! I see whats your problem. You want to have jquery first among the other vendor files.
public/lib/**/jquery.js and public/lib/**/*.js together might be causing files added twice.
Try this
src: ["public/lib/jquery/jquery.js", "public/lib/**/*.js", "!public/lib/jquery/jquery.js", public/js/cts.js"]
Put the full path of jquery first public/lib/jquery/jquery.js and then the !public/lib/jquery/jquery.js should prevent jquery being added again as part of public/lib/**/*.js
Got the above pattern from here http://gruntjs.com/configuring-tasks#globbing-patterns
If this still doesn't work, then another option is to add all paths in the src array individually. If you have a requirejs config just copy the paths from there, as jquery might not be the only dependency issue you face in future.

Exported TypeScript Class not included in WebPack bundle if not used directly

I'm converting a javascript project with Angular 1.x to WebPack and TypeScript (using ts-loader). I got it mostly working, but I'm running into trouble when ts-loader seems to be optimizing my scripts out of the bundle when the exports are not directly used.
Here's a sample project demonstrating the issue (npm install, webpack, then load index.html and watch the console).
https://github.com/bbottema/webpack-typescript
The logging from ClassA is showing up, but angular is reporting ClassB missing (provider). If you look in bundle.js you'll notice ClassB missing entirely. The difference is ClassA begin use directly after importing, and ClassB is only referenced by type for compilation.
Is it a bug, or is there a way to force ClassB to be included? Or am I going about it wrong? Angular 2 would probably solve this issue, but that's a step too large right now.
Relevant scripts from the project above:
package.json
{
"devDependencies": {
"typescript": "^1.7.5",
"ts-loader": "^0.8.1"
},
"dependencies": {
"angular": "1.4.9"
}
}
webpack.config.js
var path = require('path');
module.exports = {
entry: {
app: './src/entry.ts'
},
output: {
filename: './dist/bundle.js'
},
resolve: {
root: [
path.resolve('./src/my_modules'),
path.resolve('node_modules')
],
extensions: ['', '.ts', '.js']
},
module: {
loaders: [{
test: /\.tsx?$/,
loader: 'ts-loader'
}]
}
};
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs"
},
"exclude": [
"node_modules"
]
}
index.html
<!doctype html>
<html ng-app="myApp">
<body>
<script src="dist/bundle.js"></script>
</body>
</html>
entry.js
declare var require: any;
'use strict';
import ClassA = require('ClassA');
import ClassB = require('ClassB');
var a:ClassA = new ClassA(); // direct use, this works
var angular = require('angular');
angular.module('myApp', []).
// this compiles as it should, but in runtime the provider will not be packaged and angular will throw an error
run(function(myProvider: ClassB) {
}
);
ClassA.ts
// this line will be logged just fine
console.log('ClassA.ts: if you see this, then ClassA.ts was packaged properly');
class ClassA {
}
export = ClassA;
ClassB.ts
declare var require: any;
// this line is never logged
console.log('ClassB.ts: if you see this, then ClassB.ts was packaged properly');
class ClassB {
}
var angular = require(angular);
angular.module('myApp').service(new ClassB());
export = ClassB;
Turns out you have to signal WebPack to explicitly include a module by adding an extra require call without import statement.
I'm not ready to mangle my .ts files by adding duplicate imports, so I made a generic solution for that using the preprocessor loader:
{
"line": false,
"file": true,
"callbacks": {
"fileName": "all",
"scope": "line",
"callback": "(function fixTs(line, fileName, lineNumber) { return line.replace(/^(import.*(require\\(.*?\\)))/g, '$2;$1'); })"
}]
}
As a proof of concept, this regex version is very limited it only support the following format:
import ClassA = require('ClassA');
// becomes
require('ClassA');import ClassA = require('ClassA');
But it works for me. Similarly, I'm adding the require shim:
{
"fileName": "all",
"scope": "source",
"callback": "(function fixTs(source, fileName) { return 'declare var require: any;' + source; })"
}
I made a sample project with this solution.

Setting up Karma with Browserify to test React (ES6) components

I'm having trouble setting up a test config with Karma + Browserify for some React components. Mentioning code is written in ES6 and I've upgraded to latest Babel version (6+), which I assume is the root of all evil in this config.
Since Babel is now split and has this plugin-based approach (presets), I'm not sure how I should specify this in the karma.conf file.
My current config looks like this:
module.exports = function(config) {
config.set({
basePath: '',
browsers: ['PhantomJS'],
frameworks: ['browserify', 'jasmine'],
files: [
'app/js/**/*',
'app/__tests__/**/*'
],
preprocessors: {
'app/js/**/*': ['browserify'],
'app/__tests__/**/*': ['browserify']
},
browserify: {
debug: true,
transform: ['babelify']
},
singleRun: true
});
};
However this fails with a bundle error (Unexpected token while parsing file...). Also I get You need to include some adapter that implements __karma__.start method! error message.
It's funny that this happens for some very simple components.
Eg simple React file:
import React from 'react';
class FooterComponent extends React.Component {
render() {
return (
<footer>
This is the application's footer
</footer>
);
}
}
export default FooterComponent;
And the test file doesn't even import it. It's just an always passing test like:
describe('Testing `Footer` component.', () => {
describe('Method: none', function() {
it('Should be a passing test', function() {
expect(true).toBe(true);
});
});
});
The Babel/Browserify related packages in package.json are:
{
"babel-preset-es2015": "^6.0.15",
"babel-preset-react": "^6.0.15",
"babelify": "^7.2.0",
"browserify": "^12.0.1",
}
Any ideas are appreciated. Especially since this used to work before upgrading to Babel 6+.
Cheers!
Add a .babelrc file to your root directory:
{
presets: ['es2015', 'react']
}

React JSX + Titaniumifier

Our current infrastructure relies on Titanium for native. I am trying to convert my node project into CommonJS using https://www.npmjs.com/package/grunt-titaniumifier . However, it fails on the JSX:
return (
<div className="App">
<EnergyChart width="1000" data={JSONData.data} customerID={customerID} lineColor='#FF0' height="500" marginTop="20" marginLeft="50" marginBottom="20" marginRight="20" />
</div>
);
Browserify works fine, as it uses the transform like so:
browserify: {
app: {
src: 'app/App.js', dest: 'dist/bundle.js',
options: {
transform: ['grunt-less-browserify', require('grunt-react').browserify],
}
},
}
For titaniumifier, I have the following:
"titaniumifier": {
"module": {
files: {
// The package is in "." and the zipfile will be written in "."
".": "."
},
},
options: {
transform: [ require('grunt-react').browserify],
}
}
However running grunt titaniumifier:module returns
Fatal error: Parsing file
/Users/rduckworth/Projects/titanium-d3/app/App.js: Unexpected token
(23:6)
which is the JSX code above.
Is there anyway I can get Titaniumifier to compile the JSX like browserify does?
#rickyduck Creator and maintainer of titaniumifier here. I’m so sorry I missed this question.
You can enable Babel transpilation for you package/module by using the same syntax I show on the Wiki for enabling it for your app.
Long story short:
Add Babel as a dependency
Use the titaniumifier.transforms object of your package.json
An example:
{
"devDependencies": { ".." },
"titaniumifier": {
"transforms": {
"babel": { "presets": [ "es2015" ] }
}
}
}

Categories