file structure
assets/js/
- build/
- plugin/
jquery.min.js
- src/
index.js
config.js
builds.js
require.js
assets/js/src/index.js
requirejs(['jquery']);
assets/js/config.js
requirejs.config({
baseUrl: './',
paths: {
jquery: 'plugin/jquery.min'
}
})
If I want to use r.js to optimize the file, just execute r.js -o config.js name=src/index out=build/index.js, the r.js will compile a file into build/index.js with optimization and dependency, but there will be many files need to compile in the future, so I create a builds.js
assets/js/builds.js
({
appDir: 'src',
dir: 'build',
mainConfigFile: 'config.js',
modules: [
{name: 'index'}
]
})
If I run r.js -o builds.js, I will got wrong path message.
Error: Error: ENOENT: no such file or directory, open 'D:\www\r\build\plugin\jquery.min.js'
I need to go back to config.js, and edit the path relative to src.
requirejs.config({
baseUrl: './',
paths: {
jquery: '../plugin/jquery.min'
}
})
It will work, but is it possible to write one config file for both purpose?
Specify paths again in the build file, relative to /src.
builds.js
({
appDir: 'src',
baseUrl: './',
dir: 'build',
modules: [
{name: 'index'}
],
paths: {
jquery: '../plugin/jquery.min'
}
})
The paths in the build file work differently than in the config file.
The appDir option of /src specifies that all your scripts are located in /src, however your config.js and folder structure have the /plugins outside of /src.
When the jquery path is resolved, the paths in the config.js file are used because the mainConfigFile option is used.
All module paths would need to be redefined in your build file in addition to your config file. This is because all module paths are resolved relative to the baseUrl, which is in relation to the appDir in the build file – this is the confusing bit.
Reference the following r.js example build file. https://github.com/jrburke/r.js/blob/master/build/example.build.js
The official doc on the RequireJS optimizer contains a helpful section about path resolution.
http://requirejs.org/docs/optimization.html#basics
Relative path resolution rules:
In general, if it is a path, it is relative to the build.js file used to hold the build options, or if just using command line arguments, relative to the current working directory. Example of properties that are file paths: appDir, dir, mainConfigFile, out, wrap.startFile, wrap.endFile.
For baseUrl, it is relative to appDir. If no appDir, then baseUrl is relative to the build.js file, or if just using command line arguments, the current working directory.
For paths and packages, they are relative to baseUrl, just as they are for require.js.
Related
I have in my Scripts folder a "Lib" folder and an "App" folder.
The Lib folder contains 3rd part library JavaScript files. Some of these are minified without the original sources, others we have the original sources for.
The App folder contains all of our own JavaScript files, all of which are not minified.
I'm new to Grunt but I have a gruntfile which does the following:
Uglifies all the JS files in the Lib folder and produces minified versions with sourcemaps.
Uglifies all the JS files in the App folder and produces minified versions with sourcemaps.
Obvious problem: some of the files in the Lib folder are minified, so minifying them again/generating source maps is a bad idea and can fail for various reasons.
My solution: I run Uglify only on .js files in the Lib folder into lib-unmin.min.js. I then concat all the already minified files into a lib-min.min.js file, then I concat both those files together to get lib.min.js.
The new problem
What if I can't concat the already minified scripts to the end of the other minififed scripts without it breaking?
I have a dependency issue like this:
scripts/lib/a.js (required for b to run)
scripts/lib/b.min.js (required for c to run)
scripts/lib/c.js (required for the App scripts to run)
If I have an array of these file paths in my gruntfile, in that order, what's the easiest way of uglifying/concating all the files into a single minified JS file in that order, making sure we don't attempt to minify the minified file?
What do other developers do in similar situations?
Thanks!
I like to concat the files then uglify all of them together. This way uglify makes sure there aren't duplicate variable values overriding each other when it compresses the variable names.
You can bundle as many files as you want in the concats. Make different concat groups to uglify together and maintain the 'first, second, third, ...' order like this:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
app: {
options: {
sourceMap: true,
sourceMapName: 'build/maps/map.map'
},
files: {
'build/app.min.js': ['build/js/app-first-unmin.js', 'build/js/app-second-min.js', 'build/js/app-third-unmin.js']
}
}
},
concat: {
options: {
separator: ';'
},
firstUnminified: {
src: [
'lib/underscore.js'
],
dest: 'build/js/app-first-unmin.js'
},
secondMinified: {
src: [
'lib/moment.min.js'
],
dest: 'build/js/app-second-min.js'
},
thirdUnminified: {
src: [
'lib/favico.js'
],
dest: 'build/js/app-third-unmin.js'
}
},
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('default', ['concat:firstUnminified', 'concat:secondMinified', 'concat:thirdUnminified','uglify']);
};
I'm using coffeescript to do some work. The coffeescript is compiled to js with grunt and served with a simple nodejs express app.
My folder structure does follow the common one with a assets folder for things to be compiled (coffeescript, stylus) and public folder with the compiled stuff (js, css):
/
-- assets /
-- coffee /
-- lib /
-- util.coffee
-- main.coffee
-- styl
-- public /
-- css
-- js /
-- lib /
-- util.js
-- main.js
My coffee setup in grunt is:
coffee:
client:
options:
sourceMap: true
#sourceRoot: '??'
files: [
expand: true
cwd: 'assets/coffee/'
src: ['**/*.coffee']
dest: 'public/js/'
ext: '.js'
]
To serve files from the assets dir I added this to be a static directory in my express app:
app.use express.static(process.cwd() + '/assets')
Chrome correctly recognizes that the are source maps but the location to the coffee files are wrong. For example a url looks like http://localhost:3000/assets/coffee/main.coffee. Of course this results in a 404 because assets is the root for all coffee files and is served by my express app.
So I need to adjust the sourceRoot variable.
If I set sourceRoot to sourceRoot: '/assets/', Chrome generates links to http://localhost:3000/assets/main.coffee.
If I set it to sourceRoot: '/coffee/' the link is http://localhost:3000/coffee/main.coffee. This works for files in assets/coffee/. Files in a subdirectory of assets/coffee/ like assets/coffee/lib/ aren't found (the generated link is http://localhost:3000/coffee/util.coffee)
Setting the sourceRoot option seems to remove the folder structure?!
Long question short: What's the correct setting for sourceRoot? How can I preserve the folder structure?
I filed this issue as a possible bug report: https://github.com/jashkenas/coffee-script/issues/3075
This appears to actually be a bug in the CoffeeScript Grunt task.
See: https://github.com/gruntjs/grunt-contrib-coffee/blob/master/tasks/coffee.js#L87
options = _.extend({
generatedFile: path.basename(paths.dest),
sourceRoot: mapOptions.sourceRoot,
sourceFiles: mapOptions.sourceFiles
}, options);
Here, if the options object has a "sourceRoot" element, it will override the generated sourceRoot which is created by this function:
https://github.com/gruntjs/grunt-contrib-coffee/blob/master/tasks/coffee.js#L130
var createOptionsForFile = function (file, paths) {
return {
code: grunt.file.read(file),
sourceFiles: [path.basename(file)],
sourceRoot: appendTrailingSlash(path.relative(paths.destDir, path.dirname(file)))
};
};
Which uses the relative path from the destination directory to where the source file is (which would probably work if you mapped things like /js to /public/js instead, but for you there will be an extra ../ in the path).
For your case, it might work if you modified the code so that you replaced the options = _.extend({... code with something like:
var newRoot = undefined
if (options.sourceRoot) newRoot = appendTrailingSlash(path.join(options.sourceRoot, path.dirname(file)));
options = _.extend({
generatedFile: path.basename(paths.dest),
sourceRoot: mapOptions.sourceRoot,
sourceFiles: mapOptions.sourceFiles
}, options);
if (newRoot) options.sourceRoot = newRoot;
I think that will work because file should be relative to your cwd setting.
If that change to the Grunt task works, it would be worth making a cleaner version and submitting a pull request, because I think if your .coffee files are in a directory tree, that should be reflected in your sourcemaps' sourceRoot property.
Let's say you have the following directory structure, and the following files:
root
|-- require-jquery.js
+-- folder
|-- index.html
|-- main.js
+-- AnotherModule.js
In RequireJS, when you reference a module starting with a ".", RequireJS looks in the same folder that your current module is in, even if that's a subdirectory. However, if you change baseUrl just before calling define(), RequireJS will map dependencies to the new baseUrl.
You can fix this by setting baseUrl in index.html and changing data-main to a path relative to baseUrl:
folder/index.html:
<script>
var require = {
baseUrl : "../"
};
</script>
<script data-main="folder/main" src="../require-jquery.js"></script>
folder/main.js:
define(
[ "jquery", './AnotherModule' ],
function($, AnotherModule) {});
This only works for define(), though:
folder/main.js:
require(
[ "jquery", './AnotherModule' ],
function($, AnotherModule) {});
if I try it with require(), RequireJS will look for AnotherModule.js in root, not folder. Why is this, and in particular, why the design difference between define() and require()?
This is because define specifies a module name and require does not.
Define can be called like this:
define('folder/main',
[ "jquery", './AnotherModule' ],
function($, AnotherModule) {});
The first parameter is the module name - an explicit path to the module. Define() always implicitly specifies a path, and in general using an explicit path is not recommended. Require does not take a name parameter.
When you include a relative dependency in define() (like './AnotherModule'), it's found relative to the module name. In this case, ./AnotherModule would resolve to folder/AnotherModule.
In a call to require(), there is no module name. Relative dependencies are resolved to the root.
I have this build.js file:
({
appDir: './',
baseUrl: './js',
dir: './dist',
modules: [
{
name: 'main'
}
]
})
And in my index.html I have:
<script data-main="js/main" src="js/lib/require/require.js"></script>
When I optimize the application using:
node r.js -o build.js
All the app directory is copies into dist directory. This method is really convenient since I don't need to do any changes and I can deploy the project immediately after the optimization.
The problem is that my code uses require.js which cause overhead. How can I integrate Almond? I still with the app directory will be copied into dist directory but I don't want to edit my index.html after each optimization call.
Is there a way to integrate almond?
I've got some paths configured in require-config.js as follows:
var require = {
baseUrl: '/javascript',
paths: {
'jquery': 'jquery/jquery-1.8.1.min'
// etc. -- several paths to vendor files here
},
}
I am trying to get the optimization working for deployment. The docs say I should have a build.js that looks something like this:
({
baseUrl: 'javascript',
paths: {
'jquery': 'jquery/jquery-1.8.1.min'
},
name: 'main',
out: 'main-build.js'
})
Is there a way to have the optimizer read my config file instead of (or in addition to) build.js? I don't want to have to manually keep the paths configured the same in both files if they change.
I tried to just run node r.js -o path/to/require-config.js, but it threw an error, "malformed: SyntaxError: Unexpected token var"
Edit: for clarification, my require-config.js file is the config only, not my main module. I did this so I could use the same configuration but load a different main module when unit testing.
You'll need to adjust the way your config options are defined. Taken from the RequireJS documentation:
In version 1.0.5+ of the optimizer, the mainConfigFile option can be used to specify the location of the runtime config. If specified with the path to your main JS file, the first requirejs({}), requirejs.config({}), require({}), or require.config({}) found in that file will be parsed out and used as part of the configuration options passed to the optimizer:
So basically you can point your r.js build file to your config options that will also be shared with the browser.
You will need to make use of the mainConfigFile option
For other's reference:
https://github.com/jrburke/r.js/blob/master/build/example.build.js
The build settings (no need to repeat your config.js lib inclusions here):
baseUrl: 'app',
name: 'assets/js/lib/almond', // or require
// Read config and then also build it into the app
mainConfigFile: 'app/config.js',
include: ['config'],
// Needed for almond (and does no harm for require)
wrap: true,