Concatenating and minifying RequireJS with Grunt - coffeescript

I have a project written in CoffeeScript that uses AngularJS. My vendor dependancies are installed using Bower and my file structure is like this:
- assets
- js
- app
- model
- *.coffee
- factory
- *.coffee
...
- app.coffee
- config.coffee
- routes.cofeee
- vendor
- angular
- lodash
...
- dist
What I'm trying to do is the following:
I'm trying to work out how I can use RequireJS's r.js to optimise my app files so that I essentially get a concatenated file all ordered nice (so vendor dependancies, my config and routes, and they my app files).
Integrate this into my Grunt file.
I've tried using the r.js optimiser but maybe I've being too silly as all it seems to do is copy my app files (minus the vendor dependancies) into the dist folder; it does, however, manage to optimise the coffee generated js files.
Has anyone got any experience with this?

I figured it out: r.js works by reading your mainConfigFile and any modules you name within your configuration, the important note here is that r.js only looks at the first require/define within your named modules and goes off to seek them; so, for example, I had one named module called app:
require ['config'], (cfg) ->
require ['angular'], (A) ->
A.module cfg.ngApp, []
require ['routes'], () ->
require [
'factory/a-factory',
'service/a-service',
'controller/a-controller'
], () ->
A.bootstrap document, [cfg.ngApp]
The problem here was that r.js never got past the first require statement and thus the concatenation wasn't working. When I changed this to, say (my app.coffee):
require ['config'], (cfg) ->
require ['angular'], (A) ->
A.module cfg.ngApp, []
require ['bootstrap'], (bootstrap) ->
bootstrap()
And my bootstrap.coffee:
define [
'config',
'angular',
'routes',
'factory/a-factory',
'service/a-service',
'controller/a-controller'
], (cfg, A, routes) ->
class Bootstrap
constructor: () ->
routes()
A.bootstrap document, [cfg.ngApp]
This meant that I only needed to define angular and bootstrap in my r.js configuration as includes and then r.js would do the rest, like so:
baseUrl: 'assets/js/app',
mainConfigFile: 'assets/js/app/config.js',
name: 'app',
include: [
'../vendor/requirejs/require',
'bootstrap'
],
out: 'assets/js/dist/app.js'
And now it all works fine! ~~It's a shame that I have to tell r.js to include requirejs though, maybe I've done something silly there?~~
Blimey, I'm such a dingus!
So in my HTML I was loading my concatenated script as:
<script src="assets/js/dist/app.js"></script>
When really it should be loaded like this:
<script src="assets/js/vendor/requirejs/require.js" data-main="assets/js/dist/app"></script>
D'oh!

From r.js doc
https://github.com/jrburke/r.js/blob/master/build/example.build.js#L322
Nested dependencies can be bundled in requireJS > v1.0.3
//Finds require() dependencies inside a require() or define call. By default
//this value is false, because those resources should be considered dynamic/runtime
//calls. However, for some optimization scenarios, it is desirable to
//include them in the build.
//Introduced in 1.0.3. Previous versions incorrectly found the nested calls
//by default.
findNestedDependencies: false,

Related

unable to specify path to javascript file in Karma

My JS file (which I need to test) is /JasmineTest/src/mySource.js. It has myObj object
myObj={
setA:function(value){
a=value;
},
getA:function(){
return a;
},
};
My Jasmine spec file is /JasmineTest/spec/mySpec.js. It tests myObj
describe("Jasmine sample suite",function(){
it("tracks that spy was called",function(){
expect(myObj.getA).toHaveBeenCalled();
});
In karma, I have specified the spec file location as
files: [
'spec/*.js'
],
when I start Karma in /JasmineTest, the test gives error
Chrome 60.0.3112 (Windows 10 0.0.0) Jasmine sample suite tracks that spy was called FAILED
ReferenceError: myObj is not defined
at UserContext.<anonymous> (spec/mySpec.js:4:9)
I tried exporting myObj module.exports = myObj; and importing it in spec file using require('../src/mySource.js') but I got error require is not defined
How do I make myObj visible in the specs file?
Karma doesn't know how to do module requiring unless you configure it specially to do bundling. In general I would expect to use the same sort of bundling in Karma as you do for your web app, so Webpack or Browserify or similar.
Another way is to list mySource.js in under the "files" field in karma.conf.js, which will just execute it and put myObj in as a global, but that doesn't scale very well.

Error : You need to include some adapter that implements __karma__.start method

I'm trying to run unit tests using karma and i'm getting the error You need to include some adapter that implements __karma__.start method!. I tried running with grunt and karma start commands. I did googling and all the solutions didn't work out. Not sure what i'm doing wrong. I included the right adapter which comes with karma-jasmine, which has the __karma__.start method, under plugins in karma.conf.js file. Here's my configuration file :-
module.exports = function(config){
config.set({
// root path location that will be used to resolve all relative paths in files and exclude sections
basePath : '../',
files : [
'bower_components/angular/angular.js',
'bower_components/angular-mocks/angular-mocks.js',
'node_modules/requirejs/require.js',
'node_modules/karma-jasmine/lib/adapter.js',
'app.js',
'mainapp/mainapp.js',
'mainapp/notes/notes.js',
'mainapp/notes/partial/create/create.js',
'mainapp/notes/partial/create/create-spec.js'
],
// files to exclude
exclude: [
'bower_components/angular/angular/*.min.js'
],
// karma has its own autoWatch feature but Grunt watch can also do this
autoWatch : false,
// testing framework, be sure to install the correct karma plugin
frameworks: ['jasmine', 'browserify', 'requirejs'],
// browsers to test against, be sure to install the correct browser launcher plugins
browsers : ['PhantomJS'],
// map of preprocessors that is used mostly for plugins
preprocessors: {
'mainapp/notes/partial/create/create-spec.js' : ['browserify']
},
reporters: ['progress'],
// list of karma plugins
plugins : [
'karma-teamcity-reporter',
'karma-chrome-launcher',
'karma-phantomjs-launcher',
'karma-babel-preprocessor',
'karma-requirejs',
'karma-jasmine',
'karma-browserify'
],
singleRun: true
})}
Using the requirejs framework turns off the automatic calling of __karma__.start. You need to create a file that a) configures RequireJS and b) calls __karma__.start to kick of the tests. Here's an example. It scans through the files that Karma is serving to find the files that contains tests. This is based on a naming convention that any file that ends with spec.js or test.js is a test file. It converts the file names to module names. It then configures RequireJS. One thing it does is pass all the test modules as deps so that all test modules are loaded right away. It sets __karma__.start as callback so that when all modules passed in deps are loaded, the tests start.
var allTestFiles = [];
var TEST_REGEXP = /(spec|test)\.js$/i;
Object.keys(window.__karma__.files).forEach(function(file) {
if (TEST_REGEXP.test(file)) {
// Normalize paths to RequireJS module names.
allTestFiles.push(file);
}
});
require.config({
baseUrl: '/base',
paths: {
'chai': 'node_modules/chai/chai',
'chai-jquery': 'node_modules/chai-jquery/chai-jquery',
'jquery': 'node_modules/jquery/dist/jquery.min',
'underscore': '//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min',
'sn/sn-underscore': 'static/scripts/sn/sn-underscore',
'vendor/jquery-ui': '//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min'
},
deps: allTestFiles,
callback: window.__karma__.start
});
You need to include this file in your files parameter in your karma.conf.js file. Since you use the requirejs framework, you just need to put it first in the list. For instance, if you call the file test-main.js (as suggested in Karma's documentation):
files: [
'test-main.js',
...
]
If you load RequireJS yourself by listing it in files, you need to put test-main.js after RequireJS.

Importing external module with ES6 syntax and absolute path

So I have a project that looks something like this:
app/
bin/
lib/
src/
main/
submodule.ts
utilities/
common.ts
main.ts
tsconfig.json
gulpfile.js
and app/src/main/submodule.ts needs to import app/src/utilities/common.ts. I am trying to use the ES6 syntax for this. Thus I expect something like this in submodule.ts:
import {common} from '/utilities/common';
Where the root / is app/src/ since that is where tsconfig is found. Yes, app/src/utilities/common.ts does export a module named common.
The problem is that I get "cannot find module '/utilities/common'" errors. I have tried a variety of things:
utilities/common
/src/utilities/common
/app/src/utilities/common
None of these work. A relative path of ../utilities/common does work, but relative paths for common modules is a maintenance nightmare.
It may be worth noting that I just updated from TS 1.5 to 1.6: using utilities/common had worked in 1.5. I cannot find any mention of a breaking change along these lines in the 1.6 notes, though.
I mention the gulpfile.ts and other folders because ultimately I want Gulp to get the TS files from src and put the compiled JS files in bin. I am reasonably confident that I have correctly configured Gulp for this, using gulp-typescript, but for completion's sake, here are my tsconfig.json and gulpfile.js.
tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"noEmitOnError": true
},
"filesGlob": [
"./**/*.ts",
"!./typings/**/*.ts"
]
}
gulpfile.js
var gulp = require('gulp');
var ts = require('gulp-typescript');
var less = require('gulp-less');
var sourcemaps = require('gulp-sourcemaps');
var merge = require('merge2');
var path = require('path');
var tsProject = ts.createProject('src/tsconfig.json', { noExternalResolve: true });
gulp.task('html', function () {
gulp.src([
'src/**/*.html',
])
.pipe(gulp.dest('bin'));
});
gulp.task('typescript', function () {
tsProject.src()
.pipe(sourcemaps.init())
.pipe(ts(tsProject))
.js
.pipe(sourcemaps.write())
.pipe(gulp.dest('bin'));
});
gulp.task('less', function () {
gulp.src([
'src/**/*.less',
])
.pipe(sourcemaps.init())
.pipe(less())
.pipe(sourcemaps.write())
.pipe(gulp.dest('bin'))
});
gulp.task('default', ['html', 'typescript', 'less']);
Finally solved this. Per What's New, 1.6 changed module resolution to behave like Node's. I have not yet investigated Node's module resolution to determine if a fix is possible using that behavior, but I have found a workaround:
The old behavior can be triggered by specifying "moduleResolution": "classic" in tsconfig.json.
Module resolution is performed relative to the current file if the path starts with ./ or ../.
Here is a quick example using:
/
/src/
/src/thing.ts
/main/
/main/submodule.ts
/utilities/
/utilities/common.ts
So the correct statement to import common.ts into submodule.ts would be:
import {common} from '../utilities/common';
You can also use the following root-path (note that there is no leading / or any .s here):
import {common} from 'src/utilities/common';
This works for me in Visual Studio code, with the parent folder of src opened as the working folder. In my case I am targeting ES5 with UMD modules.
You can also resolve a module if it can be found by traversing up from the current location (this is a feature of NodeJS). So you can import thing.ts into submodule.ts using:
import {something} from 'thing';
The resolver will check the current folder, then the parent, then the parent's parent... and so on.
Absolute vs Relative Paths
When it comes to links on web pages, I'm in agreement with you that absolute paths are easier to maintain, especially where resources are shared at multiple levels.
When it comes to modules, I'm not sure I see the same maintenance problem as the paths here are "relative to the file that the import statement appears in" not relative to the web page. I wonder if this may be the application of a very sensible rule in the wrong place.

prefix assets for production with static path

for assets such as /assets/image.png, that I call in stylesheets, javascript etc...
I need to prefix or prepend a path to server.
so that /assets/image.png becomes /static/ember/memory/dist/assets/image.png for production.
where it will be served as ie: http://domain.com/static/ember/memory/dist/assets/image.png
i need to pass and use a STATIC_PATH variable when compiled/built for production that will be prefixed, or compile it so that it does it automatically.
I have checked ember-cli docs for assets compilation:
var app = new EmberApp({
fingerprint: {
prepend: '/static/ember/memory/dist/'
}
});
however, this doesn't work as where the assets are being called the path doesn't change in production, unless it actually modified the path where it's called.
for example in styles.css if I call /assets/image.png somewhere, I need to prefix this with STATIC_PATH + /assets/image.png when in production.
the STATIC_PATH will look something like:
/static/ember/memory/dist/
I can add this manually for production in development, but then cannot test in development.
thanks for any response.
You can pass a prepend option in the ember-cli-build file, you will have to exclude the assets that don't need the path:
var app = new EmberApp({
fingerprint: {
exclude: ['excludedAssets/'],
prepend: '/static/ember/memory/dist/'
}
});
For more information check out asset compilation

Uglifying for require.js with shim doesn't work on play2 with CoffeeScript

I work on Play 2.1.2 project, using Angular.js, CoffeeScript, require.js and bower to organize front-end.
With bower, I use shim in my /app/assets/javascripts/main.coffee file.
Then I deploy using play clean stage and running target/start.
The problem is: during stage phase, Play doesn't uglify resources.
In Build.scala:
val main = play.Project(appName, appVersion, appDependencies).settings(
requireJs += "main",
requireJsShim += "main.js"
)
Then after uglyfying css in stage:
Tracing dependencies for: main
Error: Load timeout for modules: angular-bootstrap,angular
http://requirejs.org/docs/errors.html#timeout
In module tree:
main
jquery
Error: Load timeout for modules: angular-bootstrap,angular
http://requirejs.org/docs/errors.html#timeout
In module tree:
main
jquery
[info] RequireJS optimization finished.
So nothing was uglified. In main.coffee:
require.config
paths:
jquery: "lib/jquery/jquery"
angular: "lib/angular/angular"
...
shim:
angular: {deps: ["jquery"], exports: "angular"}
...
define [
"angular-bootstrap"
"angular"
...
], ->
app = angular.module "app"
...
app
It works perfectly on client side, all paths are correct and so on.
requireJsShim += "main.js" also looks correct: it looks like require.js optimization takes place after compiling assets, so main.coffee or just main doesn't work.
Any ideas what are the roots of the problem? Have anyone faced it before?
I have an example application using the shim where I just answered a question very similar to yours. In a nutshell, the shim overwrites the app.build.js file.
What finally solved my problem is creating custom shim.coffee with part of require.config in it:
require.config
paths:
jquery: "lib/jquery/jquery"
angular: "lib/angular/angular"
...
Without shim part.
Then I had to explicitly define shimmed dependencies in define clauses and use requireJsShim += "shim.js" -- not the same file that I use for client-side configuration.
Then uglifying and require.js optimization begin to work!
I've encountered exactly this problem (almost; I'm not using CoffeeScript in my project), and it turns out easier to solve that I thought. To restate the issue: certain JavaScript resources—particularly those without an export setting in their shim—would produce the “Load timeout for modules” stated above. Worse, the problem appeared to be transient.
Separating the RequireJS configuration (e.g., paths, shim) from the module seemed to help, but compiling remained unreliable and it made working in development mode more complex.
I found that adding waitSeconds: 0 to the configuration object contributed to reliable builds. Why timeouts are even possible for accessing local resources during compilation is beyond me. See the RequireJS API waitSeconds documentation for details.
Here's a snippet from my RequireJS module, located in public/javascripts (your paths will likely differ).
require({
/* Fixes an unexplained bug where module loads would timeout
* at compilation. */
waitSeconds: 0,
paths: {
'angular': '../vendor/angular/angular',
'angular-animate': '../vendor/angular/angular-animate',
/* ... */
'jquery': '../vendor/jquery/jquery'
},
shim: {
'angular': {
deps: [ 'jquery' ],
exports: 'angular'
},
'angular-animate': ['angular'],
/* ... */
'jquery': {
exports: 'jQuery'
}
},
optimize: 'uglify2',
uglify2: {
warnings: false,
/* Mangling defeats Angular injection by function argument names. */
mangle: false
}
})
define(['jquery', 'angular'], function($, angular) {
/* Angular bootstrap. */
})