Grunt & requirejs optimizer for a multi app project - javascript

I'm having issues getting Grunt to perform requirejs optimization on a project with the following structure:
static/js
|── apps
|── app.js
|── dash.js
|── news.js
... (many more 'app' files)
|── build
|── collections
|── libs
|── models
|── util
|── views
Each of static/js/apps/*.js should be compiled to static/js/build/*.js containing the relevant dependencies (eg. views/view1, libs/query etc).
This is currently being performed by a basic bash script:
JS_ROOT="static/js"
for f in ${JS_ROOT}/apps/*
do
FILE=$(basename -s .js ${f})
pushd .
cd ${JS_ROOT} && r.js -o baseUrl=. name=libs/require-min.js include=apps/${FILE} out=build/${FILE}.js
popd
done
I'm attempting to move to a Grunt-based optimization, with the following in Grunt.js:
requirejs: {
compile: {
options: {
appDir: 'static/js/',
baseUrl: './apps/',
dir: 'static/js/build/',
modules: [
{
name: 'app',
}
]
}
}
}
Running generates the following error:
>> Tracing dependencies for: app
>> Error: ENOENT, no such file or directory
>> 'static/js/build/apps/libs/jquery.js'
>> In module tree:
>> app
I can clearly see what the problem is, but am failing to figure out how to indicate that the dependencies in each static/js/apps/*.js file are in static/js/ not static/js/build
In addition to this, I'm assuming that the modules block containing name: 'app' should be outputting the compiled file static/js/build/app.js from the contents of static/js/apps/app.js.
Without creating an additional module block for each file in static/js/apps, how can I compile each of the files into their relevant static/js/build/*.js file?
Update 1
So the following in my Gruntfile compiles static/js/apps/app.js successfully into static/js/build/app.js:
requirejs: {
compile: {
options: {
baseUrl: 'static/js/',
include: './apps/app.js',
out: 'static/js/build/app.js',
}
}
}
The next step being to compile static/js/apps/*.js into static/js/build/*.js without having to define each individually...
Update 2
Modifying the above to:
requirejs: {
compile: {
options: {
baseUrl: '../',
include: './apps/<%= appFile %>',
out: 'static/js/build/<%= appFile %>',
}
}
}
And creating the task:
grunt.registerTask('buildrjs', function() {
var dir='static/js/apps/';
grunt.file.setBase(dir);
var files = grunt.file.expand(['*.js']);
files.forEach(function(filename) {
grunt.log.write('Compiling '+filename+'\n');
grunt.config.set('appFile', filename);
grunt.task.run('requirejs:compile');
});
});
Almost gets me to the solution. The tasks runs through each file in static/js/apps/ and passes the filename into grunt.config.set('appFile', filename);. The output of the task outputs Compiling app.js Compiling news.js... etc, however afterwards the actual requirejs:compile tasks runs over & over on the last file in the static/js/apps/ directory, rather than each individual file. An async issue?

Solved, by passing multiple sets of options to the requirejs task, thanks to this article for the final pointers I needed:
module.exports = function(grunt) {
var files = grunt.file.expand('static/js/apps/*.js');
var requirejsOptions = {};
files.forEach(function(file) {
var filename = file.split('/').pop();
requirejsOptions[filename] = {
options: {
baseUrl: 'static/js/',
include: './apps/'+filename,
out: 'static/js/build/'+filename
}
};
});
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
requirejs: requirejsOptions,
});
};
Then the ['requirejs'] task can be run as normal and will output the appropriate compiled .js file as per each of the options: {} blocks that were specified in requirejsOptions, eg:
grunt.registerTask('default', ['requirejs']);

You need to change baseUrl to static/js from apps in requirejs build config. As currently baseUrl is pointing to apps directory, require is trying to search dependency files in apps directory. If you change the baseUrl to static/js it will find those dependency files.
requirejs: {
compile: {
options: {
appDir: 'static/js/',
baseUrl: 'static/js',
dir: 'static/js/build/',
modules: [
{
name: 'app',
}
]
}
}
}

Related

webpack babel how to copy transpiled file if i don't use import/require

I have a file in my project called test.js
I don't import/require it anywhere which means my webpack won't call babel-loader for that js file.
Question: what I want is to move test.js into /dist folder, but as a compiled/transpiled. What's the best practice for it?
What I tried: I tried to use a copy-webpack-plugin and use its transform parameters before copying the file, but I can't seem to find the good babel package.
{
from: 'test.js',
to: '/dist/test.js',
transform(content, path) {
//what do I write here?
},
}
The simplest approach I could think about is to use several entry points like this:
{
entry: {
a: "./your-main-stuff",
b: "./your-single-file",
},
output: {
path: path.join(__dirname, "dist"),
filename: "[name].js"
}
}
which will create your a.js main bundle and b.js file in __dirname/dist folder both transpiled provided you used corresponding loader(s).
And from copy-webpack-plugin docs section:
webpack-copy-plugin is not designed to copy files generated from the
build process; rather, it is to copy files that already exist in the
source tree, as part of the build process.
so it seems to be difficult (if possible) making it move transpiled files.
Update. If you want to output files into different folders w/o changing your src folder, additonal tools needed. For your case (just 1 file) I would write a simple script and add it into package.json script section combined with webpack call like:
"scripts": {
"dev": "webpack && babel path-to-script.js --out-file path-to-script-compiled.js"
}
Just like in the previous answer, initially I went with the "scripts" entry in package.json that runs babel. But for a number of reasons I wanted to use webpack 5 to do the job. So after failing with webpack-copy-plugin and a good amount of digging around I came to this solution:
let config = {
entry: [
glob.sync(srcDir + '/**/*.js') // get all .js files from the source dir
],
output : {
filename : '[name].rem.js', // webpack wants to bundle - it can bundle here ;)
path: outDir
},
resolve: {
alias: {
'app': appDir
}
},
plugins: [
new RemoveEmptyScriptsPlugin({extensions: ['js'], scriptExtensions: /\.rem\.js/}) // for all .js source files that get bundled remove the bundle .rem.js file
],
module: {
rules:[{
test: /\.jsx?$/,
type: 'asset/resource', // get webpack to take it out instead of bundling
generator: {
filename: ({filename}) => filename // return full file name so directory structure is preserved
},
use: {
loader: 'babel-loader',
options: {
targets: { node: 16 },
presets: [
['#babel/preset-env', { modules: 'commonjs' /* transpile import/export */}],
]
}
}
}]
}
};
// Since the code does not go through the full pipeline and imports are not getting resolved, aliases will remain in the code.
// To resolve them it takes to hack the aliases object into the babel config
config.module.rules[0].use.options.plugins.push(['babel-plugin-webpack-alias-7', {config: {resolve: {alias: config.resolve.alias}}}];
And it does a good job but beware that it takes to use the patched versions of the two plugins (unless the patches have been merged already)!

grunt-email-workflow Grunt tasks?

I'm using Lee Munroe's grunt-email-workflow to build out a set of email templates but I am not able to find where are the tasks configured.
When I run "grunt" from the terminal I can see that few tasks are executed:
-clean
-sass
-assemble pages
-juice
etc.
But the Gruntfile.js does not contain anything a part from:
module.exports = function(grunt) {
require('load-grunt-config')(grunt, {
// Pass data to tasks
data: {
// Re-usable filesystem path variables
paths: {
src: 'src',
src_img: 'src/img',
dist: 'dist',
dist_img: 'dist/img',
preview: 'preview'
},
// secrets.json is ignored in git because it contains sensitive data
// See the README for configuration settings
secrets: grunt.file.readJSON('secrets.json')
}
});
};
Am I missing something?
load-grunt-config is auto-loading the required grunt modules located in the package.json of the project. This is where clean (grunt-contrib-clean) sass (grunt-sass) etc are coming from.
This File determines which tasks are run by grunt

Problems generating bundle with RequireJS

I have a Typescript project:
myproject
|
+-src (folder)
| |
| +-main.ts
| +-stringHandler.ts
| +-disposable.ts
+-out (folder)
| |
| +-...
+-Gruntfile.js
In my Grunt configuration I have a 2-step task which compiles all .ts files in myproject/src/ and generates corresponding .js files into myproject/out/. So after the first step of the task is complete, I have the following:
myproject
|
+-out (folder)
|
+-main.js
+-stringHandler.js
+-disposable.js
Bundling
The second step of the task is generating bundle file myproject.js. I am using RequireJS for this purpose.
I have installed grunt-contrib-requirejs. The Gruntfile.js file handling the bundling task is as follows (showing only relevant parts in the file):
module.exports = function(grunt) {
var config = {
pkg: grunt.file.readJSON('package.json'),
requirejs: {
compile: {
options: {
baseUrl: "out",
bundles: {
'myproject': ['main', 'stringHandler', 'disposable']
},
out: 'out/myproject.js'
}
}
}
};
grunt.initConfig(config);
grunt.loadNpmTasks('grunt-contrib-requirejs');
grunt.registerTask('default', ['compile', 'requirejs']);
};
When Grunt reaches requirejs, after successfully compiling the project, I get the following error:
Running "requirejs:compile" (requirejs) task { [Error: Error: Missing
either a "name", "include" or "modules" option
at Function.build.createConfig (C:\Users\myuser\Documents\myproject\node_modules\grunt-contrib-requirejs\node_modules\requirejs\bin\r.js:29567:19)
] originalError: [Error: Missing either a "name", "include" or
"modules" option] }
I can understand there are missing parameters, but when I use name I get other errors. I guess there must be something wrong at a more generic level. What is the correct configuration format? Thanks
This assumes main.ts is your application's entry point and that it contains a require.config section with your application dependencies (libraries and shims).
First, move the require.config section out of main.ts and into its own file, config.ts. Leave the application bootstrap code in main.ts.
Then determine where you want this optimized application code deployed. Let's assume it is to a directory named build, which is parallel to your src and out folders.
Update you Gruntfile to reflect this configuration:
requirejs: {
compile: {
options: {
baseUrl: "out",
mainConfigFile: "out/config.js",
modules: [
{ name: "main" }
],
dir: "build",
optimize: "none" // skip compression while debugging
}
}
}
You can read more about each of these config options at http://requirejs.org/ but here's the basic rundown:
baseUrl: Where the source JS code lives.
mainConfigFile: Points to the config object mentioned above. It tells the plugin where the dependencies live. This obviates the need to specify and manually update the list of dependencies in two places.
modules: Is an array of application bootstraps. In this case a list of one, main.js.
dir: Where the optimized application will be generated. Note that your dependencies will also be copied here.
optimize: I left this off so you can easily debug the resulting app under ./build. Remove it when you're happy and the plugin will optimize (compress and munge) your build files.

grunt-contrib-requirejs - need a little advice

I have a project with the following structure:
project
|──css
|──img
|──js
|──app
|──collections
|──models
|──views
|──vendor
|──jquery
|──backbone
|──underscore
|──require
app.js
index.html
Gruntfile.js
package.json
I'm trying to use grunt-contrib-requirejs to build the project into a www folder but I'm not having a lot of luck. The www folder is pretty straight forward - it should look like this:
www
|──css
|──img
|──js
|──optimized.js
index.html
Where optimized.js is the build from the require. It should include everything from the app folder, vendor folder, and app.js
right now my Gruntfile.coffee looks like this:
requirejs:
compile:
options:
mainConfigFile: 'project/js/config.js'
baseUrl: 'project/js'
name: 'app'
include: ['config']
out: 'www/js/optimized.js'
optimize: 'none'
and my config.js file looks like this:
requirejs.config({
baseUrl: 'js',
paths: {
app: 'app',
models: 'app/models',
collections: 'app/collections',
views: 'app/views'
}
});
When I run the grunt task it doesn't give me an error - but the output doesn't include everything from the project/js folder?
Any help would be greatly appreciated!
Thanks!
I dont think the accepted answer is of use. I know its an old question but I ran into the same problems so here is my solution, not for the thread starter but for people in the same situation.
The main problem you probably have is that your require-files are setup in the wrong way. The reason why the grunt job doesnt include all files is most likely because the files aren't properly required or defined. It has nothing todo with the grunt job.
It's not enough to have a config file (app.js in your case), you also need to require the files. For example you can do that in a main.js file:
require(["global","highcharts-chartdata"], function () {});
Please have a look at my setup that works. This is the folder structure:
Project
|
├───dist
| ├───css
| | bundle.css
| ├───fonts
| ├───images
| └───js
| bundle.js
| index.html
|
└───src
├───js
│ └───vendor
│ ├───bootstrap
│ ├───highcharts
│ ├───jquery
│ └───require
| require.js
| charts.js
| global.js
| main.js
| require.config.js
└───less
├───bootstrap
│ └───mixins
├───bootstrap_overrides
└───font-awesome
gruntfile.js
This is my gruntfile:
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
jshint: {
files: ['src/js/*.js'],
options: {
globals: {
$: false,
console: true,
module: true,
document: true
}
}
},
less: {
compile: {
options: {
paths: ['src/less'],
plugins: [
new (require('less-plugin-autoprefix'))({browsers: ["last 2 versions"]}),
new (require('less-plugin-clean-css'))
]
},
files: {
'dist/css/bundle.css': 'src/less/style.less'
}
}
},
requirejs: {
compile: {
options: {
baseUrl: "src/js",
mainConfigFile: 'src/js/require.config.js',
paths: {
requireLib: "vendor/require/require"
},
include: "requireLib",
name: "require.config",
out: "dist/js/bundle.js"
}
}
},
watch: {
files: ['src/js/*.js','src/less/**/*.less'],
tasks: ['jshint','less','requirejs']
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-requirejs');
// Default task(s).
grunt.registerTask('default', ['jshint','less','requirejs']);
};
This is the require.config-file:
requirejs.config({
baseUrl: 'js',
paths: {
"jquery": "vendor/jquery/jquery",
"jquery-ui": "vendor/jquery/jquery-ui",
"bootstrap-collapse": "vendor/bootstrap/collapse",
"bootstrap-transition": "vendor/bootstrap/transition",
"highcharts": "vendor/highcharts/highcharts-4.1.4.min",
"highcharts-chartdata": "charts"
},
shim: {
"bootstrap": ["jquery"],
"jquery-ui": ["jquery"],
"bootstrap-collapse": ["jquery"],
"bootstrap-transition": ["jquery"],
"highcharts" : ["jquery"],
"highcharts-chartdata" : ["highcharts"]
}
});
// Load the main app module to start the app
requirejs(["main"]);
Please observe that the line above requirejs(["main"]); is actually "loading" the code in my main.js-file (below) which in its turn is loading other files:
require(["global","highcharts-chartdata"], function () {});
Without those, grunt-contrib-requirejs wont know what to include.
In our case we want to output a single js-file (optimized.js) to be included in your start page (index.html). For this to work I want point out that we need to add the require.js-script as well, without it the console would just say require is not defined.
To do that you just add the following rows in your gruntfile:
paths: {
requireLib: "vendor/require/require"
},
include: "requireLib",
You can read about it here: http://requirejs.org/docs/optimization.html#onejs
Try putting in appDir and dir properties in the grunt-contrib-requirejs config.
For more details on the proprties, please refer to example.build.js in https://github.com/jrburke/r.js/blob/master/build/example.build.js
Try it with the option preserveLicenseComments: false. The license comments in bower.js start with '//' and somehow requirejs skips the whole file then

How can I customize this build script with Node?

I have a unique directory structure that I need help making a build script for.
Here is the link (slightly different) or directory structure:
client
/extensions
/sandbox
/widgets
/form
/collections
/models
/views
/styles
custom.css
/controllers
main.coffee
server
/views
/layouts
/errors
app.coffee
config.coffee
Couple things I need:
Compile coffeescript with a watch task into a server-dist +
client-dist
Copy over all other files into their nested folders, preferably with a watch task also
Problems:
If I just compile coffeescript it just copies over the .coffee files
to .js into their nested directories but that leaves behind .css /
imgs / etc loaded with require.js. I need a way to bring them as well
into the -dist directories
Main.coffee in the /client folder is a require.config and can be used with requirejs grunt build tool to optimize things.
Anyways the easiest solution is what I am looking for.
I ended up using grunt - with the following tasks:
clean: Clears the server / client build directories
watch: Monitors .coffee files and both build directories
copy: Copies over client / server files to build directories ignoring .coffee files which are managed by the coffee task
coffee: Compiles .coffee files to .js moving them to the build directories
Here is the grunt file in its current iteration:
grunt.initConfig({
clean: {
build: ['client-dist', 'server-dist'],
release: []
},
watch: {
coffee: {
files: ['client/**/*.coffee', 'server/**/*.coffee'],
tasks: 'coffee reload'
},
reload: {
files: ['client/**/*.!(coffee)', 'server/**/*.!(coffee)'],
tasks: 'copy reload'
}
},
copy: {
client: {
files: {
"client-dist/": "client/**/*.!(coffee)"
},
options: {
basePath: "client"
}
},
server: {
files: {
"server-dist/": "server/**/*.!(coffee)"
},
options: {
basePath: "server"
}
}
},
coffee: {
compile: {
files: {
'server-dist/*.js': 'server/**/*.coffee',
'client-dist/*.js': 'client/**/*.coffee'
}
}
}
});
grunt.loadNpmTasks('grunt-contrib');
grunt.loadNpmTasks('grunt-reload');
grunt.registerTask('default', '');
grunt.registerTask('build', 'clean:build copy coffee watch');

Categories