Separating/combining gulp task sources using yargs and gulp-if - javascript

Lets say I want to include all js files in a source folder concatenated in a specific order (using gulp-concat). However, I want to only include certain files if there is a production flag.
For example, in production I need these files:
modals.js
utilities.js
analytics.js
signup.js
cookies.js
However, for local and staging, I don't want these two:
analytics.js
cookies.js
Is it possible using yargs and perhaps gulp-if to specify this? I know that I could have two lists of sources, for instance:
if (argv.production) {
return gulp.src([
'modals.js',
'utilities.js',
'analytics.js',
'signup.js',
'cookies.js'
])
} else if (argv.staging) {
return gulp.src([
'modals.js',
'utilities.js',
'signup.js',
])
}
However, this would mean I'm not adhering to DRY principles, and just feels silly and prone to error. Can I somehow stitch these together as a single source that only includes things like analytics and cookies if the task has a production flag?
In an ideal world, I could inject gulp if statements inside the gulp.src array that would only include the production-only files if the correct flag were passed.
Some out-loud thinking:
- Should I just create two files here? One a base that includes everything necessary across environments, and then a production specific one, then combine those? Seems unnecessarily complex, but could solve the problem.

If your goal is to be a bit more DRY, you can give this a shot, since your set of staging files is a subset of the production files:
var base_list = ['modals.js', 'utilities.js', 'signup.js'];
if (argv.staging) {
return gulp.src(base_list);
} else if (argv.production) {
var prod_list = base_list.concat(['analytics.js', 'cookies.js']);
return gulp.src(prod_list);
}
Or more simplified (as long as only production needs the other files, and all other environments get the subset):
var file_list = ['modals.js', 'utilities.js', 'signup.js'];
if (argv.production) {
file_list = file_list.concat(['analytics.js', 'cookies.js']);
}
return gulp.src(file_list);
There might be some other gulp-specific best practices or tools that could come into play here, but some simple JavaScript might do the trick.

Related

Gulp - add contents of one file to end of multiple files

I'm fairly new to Gulp so, hopefully this is a simple question. My project comprises many files (more than shown in the example below), and the magic of Gulp lets me combine, minify, babel, etc. All good - I've used Grunt for years so I understand the basic concepts, but this project requires Gulp for ((reasons)), so here I am.
The project results in several output scripts. Each one is, basically, the same core application compiled with different configuration options. The config is substantial, so not appropriate to pass in at instantiation. Anyway, all good there, just some background.
The problem I'm having is the project is dependent on one vendor script. I don't want to run that script though all the processing, rather I need to prepend (preferably) or append it to each output script after all processing has happened on the app codebase.
Here's what I've tried to do, but it's failing. I also tried the commented out stuff (without the extra task in the series) but the files aren't output to dist until the entire routine is complete (which makes sense, but you know... ever hopeful). Note: I 'anonymized' the script a bit, so hopefully I didn't put any weird syntax errors in... my build script runs fine, I just can't figure out how to get the vendor script prepended without a bunch of ugly hardcoded tasks for each instance (there's several instances, so I'd rather not do it that way).
Thanks in advance!
function instance1(cb) {
return gulp.src([
'src/app/data/data_en-us.js',
'src/app/data/data_pt-br.js',
'src/app/data/data.js',
'src/app/instances/_global/global_config.js',
'src/app/instances/_global/report_config.js',
'src/app/instances/1/config.js',
'src/app/instances/1/report_config.js',
'src/app/core/calculator.js',
'src/app/core/report.js',
'src/app/instances/_global/global.js',
'src/app/instances/1/instance.js',
])
.pipe(sourcemaps.init())
.pipe(babel({
presets: ['#babel/env']
}))
.pipe(concat('app.js'))
.pipe(sourcemaps.write('maps'))
.pipe(gulp.dest('dist'))
.pipe(minify({preserveComments:'some'}))
.pipe(gulp.dest('dist'))
//.pipe(src(['src/vendor/script.js','dist/app.js']))
//.pipe(concat('dist/app.js'))
//.pipe(src(['src/vendor/script.js','dist/app-min.js']))
//.pipe(concat('dist/app-min.js'))
//.pipe(gulp.dest('dist'));
cb();
}
function appendVendorScript(cb) {
return gulp.src(['src/vendor/script.js','dist/*.js'])
.pipe(concat())
.pipe(gulp.dest('dist'));
cb();
}
exports.build = series(
cleanup,
parallel(
series(instance1,appendVendorScript)
//more items here for each instance
)
);
I found a solution that also lets me minimize redundancy. Turns our, the sourcemap plugin was responsible, so I just removed it. Talking with some other colleagues, I learned that they too had issues with that plugin. I guess it's just poorly done, or old, or otherwise not well maintained. Such is life in open source :)
Here's what I came up with. Works well, but when I run build to do them all at once, it somehow breaks the code. Works perfectly when I build them one at a time... so far, haven't been able to explain that one, but one problem at a time!
const coreFilesList = [
//list of codebase files here
];
const vendorFilesList = [
//list of vendor files here
];
const outputFileData = {
prefix: 'app',
seperator: '_',
ext: '.js'
}
function instance1(cb) {
const token = 'instance1';
process(coreFilesList,token);
cb();
}
function instance2(cb) {
const token = 'instance2';
process(coreFilesList,token);
cb();
}
function process(srclist,token){
let outputFileName = outputFileData.prefix + outputFileData.seperator + token + outputFileData.ext;
for (let i = 0; i < srclist.length; i++){
if(srclist[i].match(/_TOKEN_/)){
srclist[i] = srclist[i].replace(/_TOKEN_/g,token);
}
}
return src(srclist)
.pipe(concat(outputFileName))
.pipe(babel({
presets: ['#babel/env']
}))
.pipe(src(vendorFilesList))
.pipe(concat(outputFileName))
.pipe(minify({preserveComments:'some'}))
.pipe(dest('dist'));
}
exports.instance1 = series(cleanup, instance1);
exports.instance2 = series(cleanup, instance2);
//more here
exports.build = series(
cleanup,
parallel(
instance1,
instance2
//more here
)
);

Dojo build doesn't include dojo/dom, dom/when, dojo/dom-class and about 100 other modules

I have a problem with my build in Dojo. It does build, and most of all widgets seems to be included in dojo.js after the build.
But when I test the built project it still loads about 100 files on demand.
I think the common denominator for the files that doesn't get build, is that they don't use return declare(
But instead returns functions or objects.
I attach a print-screen of some of the modules that doesn't get bundled in the build.
Dump from Firebug NET-console
The question is, is there some way of bundling these files into dojo.js, and avoid the 100+ extra requests?
Dojo builds are a pain in my neck. There are several different ways to configure them.
Generally, if you're trying to build everything (including Dojo) into one Javascript source file make sure your layer has "customBase" and "boot" set to "true".
build.profile.js
var profile = (function() {
return {
layers: {
"my/layer": {
customBase: true,
boot: true
}
}
}
}();
That should catch all of the Dojo source files. Otherwise, if something somehow slips that's what the "include" option is for. It's an explicit list of modules that get built into the layer.
build.profile.js
var profile = (function() {
return {
layers: {
"my/layer": {
include: [ "dojo/dojo", "dojo/date", ... ]
}
}
}
}();

Different settings for debug/local ("grunt serve") vs. dist/build ("grunt")?

I want to define some application settings, but I want to provide different values depending on whether I'm running in 'debug' mode (e.g. grunt serve), or whether the final compiled app is running (e.g. the output of grunt). That is, something like:
angular.module('myApp').factory('AppSettings', function() {
if (DebugMode()) { // ??
return { apiPort: 12345 };
} else {
return { apiPort: 8008 };
}
});
How can I accomplish this?
The way I handle it in my apps:
move all your config data for one environment to a file: config.js, config.json,... whatever your app finds easy to read.
now modify your config file to turn it into a template using grunt config values, and generate the file with grunt-template as part of your build - for example: app.constant('myAppConfig', {bananaHammocks: <%= banana.hammocks %>});
finally, add grunt-stage to switch grunt config values depending on environment: create your different config/secret/(env).json files, update your template (app.constant('myAppConfig', {bananaHammocks: <%= stg.banana.hammocks %>});), and then grunt stage:local:build or grunt stage:prod:build
I find this the good balance between complexity and features (separation between environments, runtime code not concerned with building options,...)

Exclude folders from builds in Brocfile

Is there a way to exclude a folder from a build in a Brocfile (or any other place).
The use case is packaging, where I have an app made of sub-apps within pods. eg.
/app/modules/components
/app/modules/app1
/app/modules/app2
/app/modules/app3
I'd like to build them all when environment is set to 'development' or only eg. 'app1' when environment is 'app1'. Any suggestions?
I have tried different combinations of broccoli-file-remover, broccoli-funnel and broccoli-merge-trees to no avail.
var removeFile = require('broccoli-file-remover');
module.exports = removeFile(app.toTree(), {
paths: ['app/modules/pod1/', 'app/modules/pod2/']
});
Ah, so after actually thinking about this clearly, everything is actually working exactly as expected in my previous example.
I clearly wasn't paying enough attention. app.toTree() is far too late to perform this operation, as everything has already been built and concated.
Luckily, ember-cli does enable addons to modify the appropriate trees at various life cycle milestones.
See: https://github.com/ember-cli/ember-cli/blob/master/ADDON_HOOKS.md for more details on which hooks are currently available.
The hook that should do the trick is Addon.prototype.postprocessTree. Now we have two choices, we can build a standalone addon, via ember addon or we can create a light-weight in-repo addon via ember g in-repo-addon. Typically for these types of situations, I prefer in-repo-addons as they don't require a second project, but otherwise they are the same.
ember g in-repo-addon remove
we need to install broccoli-stew via npm install --save broccoli-stew
include it var stew = require('broccoli-stew');
add hook postprocessTree to the add-on
when the postprocessTree is for the type we care about, use broccoli-stew to remove the directories we no longer care care.
The resulting pull request: https://github.com/WooDzu/ember-exclude-pod/pull/1
Note: I noticed template wasn't one of the types available in postprocess, so I added it: https://github.com/ember-cli/ember-cli/pull/4263 (should be part of the next ember-cli release)
Note: we really do want an additional hook
Addon.prototype.preprocessTree, as to ignore the files before we
even build them. I have opened a related issue:
https://github.com/ember-cli/ember-cli/issues/4262
output of the above steps
var stew = require('broccoli-stew');
module.exports = {
name: 'remove',
isDevelopingAddon: function() {
return true;
},
postprocessTree: function(type, tree){
if (type === 'js' || type === 'template') {
return stew.rm(tree, '*/modules/pod{1,2}/**/*');
} else {
return tree;
}
}
};
I am pretty confident broccoli-stew's rm will handle this correctly.
https://github.com/stefanpenner/broccoli-stew/blob/master/lib/rm.js#L4-L40 there are even tests that test a very similar scenario: https://github.com/stefanpenner/broccoli-stew/blob/master/tests/rm-test.js#L48-L57
var stew = require('broccoli-stew');
module.exports = stew.rm(app.tree(), 'app/modules/{pod1,pod2}');
If this doesn't work, feel free to open an issue on broccoli-stew. Be sure to provide a running example though
This is really late, but I created a Broccoli plugin to do just this. It's available at https://www.npmjs.com/package/broccoli-rm.
(The trick is to detect whether an excluded path is a folder, and then use a glob match to make sure that none of the children of the folder get symlinked during copying.)
var rm = require('broccoli-rm');
var input = app.toTree();
module.exports = output = rm([input], {
paths: ['app/modules/pod1', 'app/modules/pod2']
});

Minify Scripts/CSS in production mode with node.js

I have a web app that runs in node. All the (client) Javascript/CSS files are not minified at the moment to make it easier to debug.
When I am going into production, I would like to minify these scripts. It would be nice to have something like:
node app.js -production
How do I serve the minified version of my scripts without changing the script tags in my html files? There should be something like: if I am in production, use these 2 minified(combined) scripts, else use all my unminified scripts..
Is this possible? Maybe I am thinking too complicated?
You might be interested in Piler. It's a Node.js module that delivers all the JavaScript (and CSS) files you specify as usual when in debug mode, but concatenated and minified when in production mode.
As a special feature, you can force CSS updates via Socket.io in real-time to appear in your browser (called "CSS Live Updated" in Piler), which is quite awesome :-).
The trick is that inside your template you only have placeholders for the script and link elements, and Piler renders these elements at runtime - as single elements in debug mode, and as a dynamically generated single element in production mode.
This way you can forget about creating concatenated and minified versions of your assets manually or using a build tool, it's just there at runtime, but you always have the separated, full versions when developing and debugging.
you could use 2 separate locations for your static files
Here's some express code:
if (process.env.MODE === "production") {
app.use(express['static'](__dirname + '/min'));
} else {
app.use(express['static'](__dirname + '/normal'));
}
and start node with
MODE=production node app.js
Furthermore, if you don't want to duplicate all your files, you could take advantage of the fact that express static router stops at the first file, and do something like this instead:
if (process.env.MODE === "production") {
app.use(express['static'](__dirname + '/min')); // if minized version exists, serves it
}
app.use(express['static'](__dirname + '/normal')); // fallback to regular files
Using the same name for minimized or not is going to cause problem with browser caching, though.
I want to share my final solution with you guys.
I use JSHTML for Express (enter link description here)
In my main node file I use a special route:
app.get('/**:type(html)', function (req, res, next) {
var renderingUrl = req.url.substring(1, req.url.lastIndexOf("."));
//TODO: Find a better solution
try{
var assetUrl = req.url.substring(req.url.lastIndexOf("/") + 1, req.url.lastIndexOf("."));
var assets = config.getResourceBundle(assetUrl);
assets.production = config.getEnviroment() === "production";
res.locals(assets);
res.render(renderingUrl);
}catch(e){
res.redirect("/");
}
});
As you can see, I get my assets from config.getResourceBundle. This is a simply function:
exports.getResourceBundle = function(identifier){
switch(enviroment){
case "development":
return devConfig.getResourceBundle(identifier);
case "production":
return prodConfig.getResourceBundle(identifier);
default:
return devConfig.getResourceBundle(identifier);
}
}
And finally an example for an asset file collection is here:
exports.getResourceBundle = function (identifier) {
return resourceBundle[identifier];
};
resourceBundle = {
index:{
cssFiles:[
"resources/dev/css/login.css",
"resources/dev/css/logonDlg.css",
"resources/dev/css/footer.css"
],
jsFiles:[
"resources/dev/js/lib/jquery/jquery.183.js",
"resources/dev/js/utilities.js",
"resources/dev/js/lib/crypto.3.1.2.js"
]
},
register:{
cssFiles:[
"resources/dev/css/login.css",
"resources/dev/css/modalDialog.css",
"resources/dev/css/footer.css"
],
jsFiles:[
"resources/dev/js/lib/jquery/jquery.183.js",
"resources/dev/js/utilities.js",
"resources/dev/js/lib/crypto.3.1.2.js",
"resources/dev/js/lib/jquery.simplemodal.js",
"resources/dev/js/xfiles.register.js"
]
}
(...)
I have 2 folders. dev / prod. grunt will copy the minified files into prod/.. and deletes the files from dev/...
And if the NODE_ENV variable is set to production, I will ship the minified versions of my scripts/css.
I think this is the most elegant solution at the moment.
There are build tool plugins for you, may help you gracefully solve this problem:
For Gulp:
https://www.npmjs.org/package/gulp-useref/
For Grunt:
https://github.com/pajtai/grunt-useref
Another Node.js module which could be relevant is connect-cachify.
It doesn't seem to do the actual minification for you, but it does let you serve the minified version in production, or all the original scripts in development, without changing the templates (thanks to cachify_js and cachify_css).
Seems it's not as feature-rich as Piler, but probably a bit simpler, and should meet all the requirements mentioned in the question.

Categories

Resources