How do I resolve dependencies when linting single javascript files with grunt? - javascript

I want to separate my application logic into multiple Javascript files for sanity and developer friendliness, stored in the /src folder. These files should be linted and concatenated into /dist/app.js during the build process. I am using grunt for my build process, as it already comes with handy lint and concat tasks.
+
|- grunt.js
|- readme
|-vendors
|-backbone.js
|- src
|- core.js
|- user.js
|- dist
|-app.js
I am running into an annoying problem. I use backbone.js for application structure, and most of my source files start by defining models by extending Backbone.Model. When linting these files, JSHint complains that Backbone is not defined, and rightly so - backbone resides outside in its own directory. Including all necessary scripts in the right order is something I assume is done in the html. Each individual source file should only know about itself.
I know that I can suppress these undefined warnings by setting lint's undef flag in grunt.js to false but I want to keep it set to true in order to be warned about other undefined variables in my application, as it is a common pointer to typos. Is there a clean way to tell grunt (or lint) what files to include prior to linting them? Am I doing something wrong with my build process, or with my application architecture? Or is this simply something I have to live with?

The jshint options allow you to specify a list of globals that come from other libraries you are using, in your grunt.js file:
jshint: {
options: {
curly: true,
eqeqeq: true,
immed: false,
latedef: true,
newcap: true,
noarg: true,
sub: true,
undef: true,
boss: true,
eqnull: true,
browser: true
},
globals: {
jQuery: true,
Backbone: true,
_: true
}
},
note the globals setting at the bottom. This allows JSHint to ignore these variables, but still run your undef: true setting (as shown above).

An alternative approach (that doesn't rely on grunt) is to add a jshint comment into your js files:
/* global Backbone, jQuery, _ */

Related

Typescript Modules Into Single JS file?

Ok, I am complete lost with this. I have just started using Typescript with Grunt JS and need some help.
I have a working Grunt file thats runs my TS and then a uglify process for site ready files, here is the config:
ts: {
default: {
files: {
'/js/builds/main.js': ['/typescript/main/build.ts'],
'/js/builds/public.js': ['/typescript/public/build.ts']
}
},
options: {
target: 'ES5',
fast: 'never',
sourceMap: false,
allowJs: true,
declaration: false,
module: 'amd'
},
},
'uglify': {
options: {
preserveComments: 'some',
},
my_target: {
files: {
'src/js/main.js': ['/js/builds/main.js'],
'src/js/public.js': ['/js/builds/public.js']
}
}
},
watch: {
'JS': {
files: [
'/js/**/*.js',
'/typescript/**/*.ts',
'/typescript/**/**/*.ts'
],
tasks: ['ts', 'uglify'],
options: {
spawn: false,
},
}
}
So now I am working on my Typescript files, but I having a lot of issues, I want to use Typescript as a module system but to output into a single file, right now its set to AMD, which needs Require JS of which I dont know.
Right now I don't have the time to learn Typescript and Require JS. So where is what I have got right now,
test.js:
export class testMe {
constructor() { }
LogingData() {
console.log( 'Data being logged...' );
}
}
Then in my build ts file,
import {testMe} from "./clients/clients";
However this needs Require JS or module loading system in order to get it to run? I have tried using commonJs but it seems support for that was removed in Typescript 1.8 (I am using 2.0).
So how do I get my Grunt / Typescript into a single standard ES5 complied code, while using modules for Typescript?
Many thanks
UPDATE
This question & answer, Typescript compile to single file does not give an answer for how to setup grunt to use Typescript into a single file! Plus the answer states that Typescript 1.8+ will fix that issue - But the version I am using does not.
I am using Grunt to compile the TS code into one standard javascript file, without the use of System or Require JS. So I can use that within my HTML code.
The end goal would be to have two single files. To explain I have lots of single .ts files for two sections, one main and the other public - I have not work on the public section, just the main one, so all my tests I been on that section.
So to layout my file/folder path:
js/
builds/
main.js < targer end file
public.js <- target end file
typescript
main/
settings/
classA.ts
somethingelse.ts
othersection/
something.ts
buildMain.ts <- *1
*1 This file then takes all the ts files, via imports (not sure if thats the correct way) and then builds the complete standard single .js file.
I hope that explains my query in more detail.
Thanks
UPDATE 2:
I would just like to add that I am getting a single file, e.g. main.js but that is not a standard Javascript complied file. I don't want to use Require JS or any other external module loading system.
I want to use external .ts files has modules import them into a 'build' file and have that file compile down to a standard self contained javascript file without the need to include require js or anything else.
Hope that clears it up a little more..
Thanks.
I believe you can use --outfile to get everything into one file. The trick is to remove import and export statements and use /// <reference path="path/to/file.ts" /> on the first lines to declare the dependency / ordering relationships. Since it is the import/export statements that produce the calls to CommonJS or Require, omitting them prevents their generation.
Ref: https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html
For two output files, you'll probably need two tsconfig.json. (I might be incorrect here.)
If you don't bother with .d.ts files you can simply use just --module=commonjs(it is and will be supported) to compile each file into .js and then browserify to concat all modules together.
I'm using CLI scripts instead of grunt but it should be obvious what it does:
$ ./node_modules/.bin/tsc --module commonjs main.ts
$ ./node_modules/.bin/browserify main.js -o bundle.js
Then you can run it like any other JavaScript from browser or CLI using node.
There's also --outFile option for tsc but this is limited only to amd and systemjs modules so I think it's easier to stick to commonjs and browserify.
browserify is an amazing tool. You can generate UMD module with --standalone parameter that works also as global or exclude any external dependencies.
I highly recommend you to read the Official Handbook: https://github.com/substack/browserify-handbook

Browserify, minifyify, conditional compilation

TL;DR
minifyify (the Browserify plugin) makes use of uglify-js but appears to be unable to handle Conditional compilation:
compression works
uglifyjs alone works for conditional compilation
minifyify provides additional compilation optimization but I have been unable to use conditional compilation with it
I'm using Browserify with the babelify transformer and the minifyify plugin. Here is the cmd, broken down in readable parts:
browserify
src/scripts/app/index.js
-o
build/prod/public/assets/js/appBundle.min.js
-t
[ babelify --presets [ es2015 ] ]
-p
[ minifyify --no-map --uglify [ --compress [ --drop_console --dead_code --conditionals --unused --if_return ] --mangle --screw-ie8 --define [ DEBUG=falseĀ ] ] ]
I've gotten every setting/option to work. However, I am unable to get conditional compilation to work.
Minifyify uses uglifyjs' minify method. The fact I'm passing by minifyify shouldn't really change anything.
Building directly through uglifyjs works
uglifyjs input.js --compress --dead_code --define DEBUG=false -o output.js
But then I lose the additional compressions/optimizations provided by minifyify.
I'm also open to another build process. My needs are resumed in the settings of the current process:
CommonJS required modules
transpiling of ES6 to ES5
advanced minification/compression
It turns out that the culprit was, more or less, uglifyjs. The property name for global definition in the task is different between CMD and Programmatic API.
cmd line: --define VARNAME=VALUE
programmatic: compress: {global_defs: { varname: value } }
That being said, it also seems that minifyify or browserify isn't passing the cmd-line options properly for global definitions - we're still investigating this
programmatic solution
By using the Browserify & minifyify programmatic API, the build task works. Below is the same task as the one in OP, but it works:
"use strict";
var browserify = require("browserify"),
fs = require("fs");
browserify("src/scripts/app/index.js")
.transform("babelify", {presets: ["es2015"], plugins: ["transform-object-assign"]})
.plugin("minifyify", {map: false, uglify: {
compress: {
drop_console: true,
dead_code: true,
conditionals: true,
unused: true,
if_return: true,
global_defs: {
DEBUG: false
}
},
mangle: true,
"screw-ie8": true
}})
.bundle()
.pipe(fs.createWriteStream("build/prod/public/assets/js/appBundle.js"));
update in uglifyjs docs
I've proposed a modification to the current uglifyjs docs, providing an example using the programmatic API as above, so as to avoid this issue for others in the future.

Requirejs and Grunt: mainConfigFile vs grunt task options

I find there to be a lot of confusion/lack a specific way of handling building with require in grunt. I'm just confused what configuration should go directly in Grunt task options:
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
requirejs: {
compile: {
options: {
baseUrl: './js',
mainConfigFile: 'config.js',
optimize: 'none',
include: ['./main'],
out: 'optimized.js'
}
}
}
});
And then in config file:
({
appDir: './',
baseUrl: './js',
dir: './dist',
optimize: 'none',
optimizeCss: 'standard',
removeCombined: true,
paths: {
jquery: './js/jQuery/jquery',
}
})
Obviosuly there seems to be some redundancy but that is mostly what I've found. Can someone explain why or if I'm mistaken? Can I place all config in one or the other? I'm only planning on working off of the optimized single file with almond.
Also do I only state the initial single point of entry to build the dependency chain from ie my main.js file and any require calls in there or can I state a wildcard list of files that calls modules:
include: ['./variousFiles/*.js']
Any and all clarifications of how to best utilize require with Grunt will be appreciated. Thank you!
When you use RequireJS' r.js optimizer there are two configurations to speak of:
The runtime configuration which is what is described in the RequireJS documentation. This is where you tell RequireJS where to find modules at runtime.
The build configuration, which is what is described with r.js' documentation. This tells r.js how to build bundles from your modules.
The mainConfigFile option is for the build configuration, it tells r.js where to find the runtime configuration you plan to use when you run the bundles it will create. This is to prevent having to duplicate shim and paths options from the runtime configuration to the build configuration.
In your description, it looks like you are making mainConfigFile point to a build configuration. This is useless.

Working project structure that uses grunt.js to combine JavaScript files using RequireJS?

I have some projects that use RequireJS to load individual JavaScript modules in the browser, but I haven't optimized them yet. In both development and production, the app makes a separate request for each JavaScript file, and now I would like to fix that using Grunt.
I have tried to put together a simple project structure to no avail, so I'm wondering if someone can provide a working example for me. My goals are the following:
In development mode, everything works in the browser by issuing a separate request for each required module. No grunt tasks or concatenation are required in development mode.
When I'm ready, I can run a grunt task to optimize (combine) all of the JavaScript files using r.js and test that out locally. Once I'm convinced the optimized application runs correctly, I can deploy it.
Here's a sample structure for the sake of this conversation:
grunt-requirejs-example/
grunt.js
main.js (application entry point)
index.html (references main.js)
lib/ (stuff that main.js depends on)
a.js
b.js
requirejs/
require.js
text.js
build/ (optimized app goes here)
node_modules/ (necessary grunt tasks live here)
Specifically, I'm looking for a working project structure that I can start from. My main questions are:
If this project structure is flawed, what do you recommend?
What exactly needs to be in my grunt.js file, especially to get the r.js optimizer working?
If all of this isn't worth the work and there's a way to use the grunt watch task to automatically build everything in development mode every time I save a file, then I'm all ears. I want to avoid anything that slows down the loop from making a change to seeing it in the browser.
I use the grunt-contrib-requirejs task to build project based on require.js. Install it inside your project directory with:
npm install grunt-contrib-requirejs --save-dev
BTW: --save-dev will add the package to your development dependencies in your package.json. If you're not using a package.json in your project, ignore it.
Load the task in your grunt file with:
grunt.loadNpmTasks('grunt-contrib-requirejs');
And add the configuration to your grunt.initConfig
requirejs: {
production: {
options: {
baseUrl: "path/to/base",
mainConfigFile: "path/to/config.js",
out: "path/to/optimized.js"
}
}
}
Now you're able to build your require.js stuff into a single file that will be minimized with uglifyjs by running grunt requirejs
You can bundle a set of different tasks into some sort of main task, by adding this to your grunt file
grunt.registerTask('default', ['lint', 'requirejs']);
With this, you can simply type grunt and grunt will automatically run the default task with the two 'subtasks': lint and requirejs.
If you need a special production task: define it like the above
grunt.registerTask('production', ['lint', 'requirejs', 'less', 'copy']);
and run it with
grunt production
If you need different behaviors for 'production' and 'development' inside i.e. the requirejs task, you can use so called targets. In the configuration example above it's already defined as production. You can add another target if you need (BTW, you can define a global config for all targets by adding a options object on the same level)
requirejs: {
// global config
options: {
baseUrl: "path/to/base",
mainConfigFile: "path/to/config.js"
},
production: {
// overwrites the default config above
options: {
out: "path/to/production.js"
}
},
development: {
// overwrites the default config above
options: {
out: "path/to/development.js",
optimize: none // no minification
}
}
}
Now you can run them both at the same time with grunt requirejs or individually with grunt requirejs:production, or you define them in the different tasks with:
grunt.registerTask('production', ['lint', 'requirejs:production']);
grunt.registerTask('development', ['lint', 'requirejs:development']);
Now to answer your questions:
I would definitely use a subfolder in your project. In my case I use a 'src' folder for development that is build into a 'htdocs' folder for production. The project layout I prefere is:
project/
src/
js/
libs/
jquery.js
...
appname/
a.js
b.js
...
main.js // require.js starter
index.html
...
build/
... //some tmp folder for the build process
htdocs/
... // production build
node_modules/
...
.gitignore
grunt.js
package.json
see above
You can do so, but I wouldn't recommend to add requirejs to the watch task, it's a resource hungry task and it will slow down your machine noticeable.
Last but not least: Be very cautious when playing around with r.js. Especially when you want to optimize the whole project with r.js by adding a modules directive to your config. R.js will delete the output directory without asking. If it happens that it is accidentally configured to be your system root, r.js will erase your HDD. Be warned, I erased my whole htdocs folder permanently some time ago while setting up my grunt task... Always add keepBuildDir:true to your options when playing around with the r.js config.

require js on build file combine

I use require js in my single page app and I am wondering if it is possible to combine all modules, vendor libraries (jQuery, Underscore, Backbone) in one single file, since this would speed up my app.
app
css
html
homepage
main.html
login.html
signup.html
js
build.js
libs
jquery.js
backbone.js
underscore.js
require.js
modules
collections
models
views
main
main.js
app.js
router.js
app-build
My build configuration file currently is:
({
appDir: '../',
baseUrl: 'js/modules',
mainConfigFile: 'modules/main/main.js',
dir: '../../app-build',
optimize: 'uglify',
uglify: {
toplevel: true,
ascii_only: true,
beautify: true,
max_line_length: 1000
},
preserveLicenseComments: true,
logLevel: 0
})
I'm certainly not an expert in this area, but I did manage to get this working for one of my projects, admittedly mainly by trial and error.
I found that I needed to make either/both of appdir and baseUrl be a directory which contained both my code and the library code - in your case, this would either be ./app/js or ./app (depending on whether you also wanted your templates combined).
I also supplied paths to the various library functions.
Having got it working I really ought to go back and see what was actually necessary, and what was cargo-cult coding. For now I have more pressing code to be writing...
For your interest I include my options: note I am optimizing from node.js via rjs.optimize(...).
var options = {
baseUrl: "./site/app",
appdir: "./site/app",
name: "js/main",
out: "main-built.js",
paths: {
jQuery: 'js/libs/jquery/jquery',
Underscore: 'js/libs/underscore/underscore',
Backbone: 'js/libs/backbone/backbone',
Handlebars: 'js/libs/handlebars/handlebars',
templates: 'templates'
}
};
I'm not expecting this to be the correct answer, but hopefully it will lead you somewhere useful.

Categories