I'm trying to combine both JS and JSX files (to use with React.js) in a single, versioned all.js file.
My current Gulpfile:
var elixir = require('laravel-elixir');
elixir.config.sourcemaps = false;
elixir(function(mix) {
mix
.less([
'app.less'
], null, 'resources/assets/less')
.babel([
'libs/**', //contains react.js among other libs (jquery, bootstrap)
'app.js', //contains normal jquery
'quiz/quiz.jsx'
], null, 'resources/assets/js')
//.babel([
//'quiz/quiz.jsx'
//])
.version(["css/app.css", "js/all.js"]);
});
Output:
[18:38:28] Starting 'default'...
[18:38:28] Starting 'less'...
Fetching Less Source Files...
- resources/assets/less/app.less
Saving To...
- public/css/app.css
[18:38:30] Finished 'default' after 1.91 s
[18:38:34] gulp-notify: [Laravel Elixir] Less Compiled!
[18:38:34] Finished 'less' after 6 s
[18:38:34] Starting 'babel'...
Fetching Babel Source Files...
- resources/assets/js/libs/**/**/*
- resources/assets/js/app.js
- resources/assets/js/quiz/quiz.jsx
Saving To...
- public/js/all.js <---- STUCK HERE
It's been stuck there for a while and I'm not sure how to debug this.
Changing the first babel() with scripts() works, but of course won't compile the JSX file (and won't complain about it).
Calling babel after scripts (the 3 commented out lines) will correctly compile the JSX file, but without everything else (it will simply compile the JSX and save it to all.js).
How can I fix this?
Related
I have a task:
gulp.task('compile_scss, function() {
return gulp.src('/admin_app/scss/*.scss')
.pipe(sass())
.pipe(dest('/admin_app/css/'))
});
When I am adding new empty ".scss" file to '/admin_app/scss/' and running task from above, empty ".scss" files is copied to destination folder. If file is not empty everything is ok: a valid css file( with ".css" extension) is compiled and no ".scss" files are copied. The problem is when I add new ".scss" file to "/admin_app/scss/" directory, a "watch" task is triggered, and because file is empty, it is copied to destination directory. As a result, a lot of unneeded garbage is dest folder. Why this happens and how can I get rid of it?
UPDATED
My "watch" and "default" tasks:
gulp.task('watch', ['compile_scss'], function() {
apps.forEach(function(appName) {
gulp.watch('/admin_app/scss/*.scss', ['compile_scss']);
});
});
gulp.task('default', ['watch']);
One way to solve this problem would be to simply filter the empty files.
Try something like this:
var filter = require('gulp-filter'),
gulp.task('compile_scss, function() {
return gulp.src('/admin_app/scss/*.scss')
.pipe(filter(function(a){ return a.stat && a.stat.size }))
.pipe(sass())
.pipe(dest('/admin_app/css/'))
});
There's also a plugin specifically for this purpose. You can use it like this:
var clip = require('gulp-clip-empty-files'),
gulp.task('compile_scss, function() {
return gulp.src('/admin_app/scss/*.scss')
.pipe(clip())
.pipe(sass())
.pipe(dest('/admin_app/css/'))
});
In addition: there seem to have been several reports of problems in gulp-sass and underlying libraries when compiling empty files. There is a Github issue for gulp-sass, reporting this should be solved in the 2.x versions of the plugin. If you're already running 2.x, the problem you are facing might be an issue introduced by solving the original problem.
If you add empty scss files in your sass folder, prefix them with underscore: _empty.scss.
See "Partials" here: http://sass-lang.com/guide#topic-4
You can create partial Sass files that contain little snippets of CSS
that you can include in other Sass files. This is a great way to
modularize your CSS and help keep things easier to maintain. A partial
is simply a Sass file named with a leading underscore. You might name
it something like _partial.scss. The underscore lets Sass know that
the file is only a partial file and that it should not be generated
into a CSS file. Sass partials are used with the #import directive.
I have got a theme from themeforest which has a plugins/ directory with 20-30 directories inside that with js/ and css/ folders.
So I need to have a folder outside of the default sails js/ and styles/ called plugins/, which will then be included in the grunt build.
I tried to copy the usual jsFilesToInject and replicate it all with this:
var plugInJSFilesToInject = [
// All of the rest of your client-side js files
// will be injected here in no particular order.
'plugins/**/*.js'
];
Then specifing it in the sails-linker:devJs like so:
devJs: {
options: {
startTag: '<!--SCRIPTS-->',
endTag: '<!--SCRIPTS END-->',
fileTmpl: '<script src="%s"></script>',
appRoot: '.tmp/public'
},
files: {
'.tmp/public/**/*.html': require('../pipeline').jsFilesToInject,
'views/**/*.html': require('../pipeline').jsFilesToInject,
'views/**/*.ejs': require('../pipeline').jsFilesToInject,
'.tmp/public/**/*.html': require('../pipeline').plugInJSFilesToInject,
'views/**/*.html': require('../pipeline').plugInJSFilesToInject,
'views/**/*.ejs': require('../pipeline').plugInJSFilesToInject
}
},
And recieved this error when running sails lift:
------------------------------------------------------------------------
Aborted due to warnings.
Running "sails-linker:devJs" (sails-linker) task
Warning: Cannot read property 'indexOf' of undefined
This also needs to be done for a css/ directory, which is why I definitely need to have it outside of the js/ or styles directory.
I ended up just placing the plugins/ in the same jsFilesToInject array, because there is no reason why it can't go in there:
var plugInJSFilesToInject = [
// All of the rest of your client-side js files
// will be injected here in no particular order.
'js/**/*.js',
// Plugins go here
'plugins/**/*.js'
];
we're using gulp and run-sequence in order to build our application having the following sequence in place:
gulp.task('build', function(cb) {
runSequence(
'lint',
'clean',
'compile-templates',
'copy-dependencies',
'compile-styles',
'revall',
'cdnize',
'overlays',
cb
);
});
Here is what the single tasks do and use (pasting the specific code would be quite long so I'll try to give the picture briefly describing the tasks):
'compile-templates': using jade (~1.1.5) + gulp-minify-html (^0.1.3), it picks up all our jade files and compiles them to html files
'copy-dependencies': using gulp-spa (0.2.2) + gulp-uglify (^0.2.1) + gulp-concat (^2.1.7) + gulp-ngmin (^0.1.2), it aggregate all our javascript files and angular templates building and minifying our vendor.js and app.js files together with the small/index.html and large/index.html files.
'compile-styles': using gulp-compass (~1.1.3) + gulp-minify-css (~0.3.1), it compiles our scss files into our desktop.css and mobile.css files
'revall': using gulp-rev-all (~0.3.0), takes care of revving all the prev generated files
'cdnize': using gulp-cdnizer (^0.2.6), it substitutes paths for our js and css files updating them with our CDN urls inside the small/index.html and large/index.html files
'overlays': using gulp-replace (^0.3.0), it will find the previously generated and revved css files and fill up their actual name inside our small/index.html and large/index.html files replacing some other placeholders as well.
here is our 'overlays' task code:
gulp.task('overlays', function() {
var styles = fs.readdirSync('./dist/styles');
var desktopCss = '';
var mobileCss = '';
for (var index in styles) {
var style = styles[index];
if (style.indexOf('desktop' + 'rev') > -1) {
desktopCss = style.replace('.css', '');
}
if (style.indexOf('mobile' + 'rev') > -1) {
mobileCss = style.replace('.css', '');
}
}
return gulp.src('./dist/large/index.html')
.pipe(plugins.replace('DIST_CSS', desktopCss))
.pipe(plugins.if(!isDev, gulp.dest('./dist/large')))
.pipe(gulp.src('./dist/small/index.html'))
.pipe(plugins.replace('DIST_CSS', mobileCss))
.pipe(plugins.if(!isDev, gulp.dest('./dist/small')));
});
It's a lot of stuff to do but nothing really complicated.
Everything usually runs nicely BUT for some reason, some times, we're experiencing an odd problem we cannot understand:
The 2 index.html files ends up without the needed final substitution (those happening in the 'overlays' task)
It seems almost like some of the prev tasks in the run-sequence block are actually still running when the next one is run (namely 'copy-dependencies' or 'cdnize')
We triple checked all the tasks and all of them are returning the gulp pipeline as expected by run-sequence.
From the gulp output it doesn't look like anything is running in parallel but we still have those placeholders instead of our values.
I guess we're missing something about gulp or the plugins we're using but we cannot really understand what and why.
I am having trouble building a layer for my web app. I have Node installed and I can use the packages directive to compress all my files but I cannot get a layer to build despite several days of trying every possible path and directive combination I can think of.
Here is a profile I thought should work:
var profile = {
// point basePath to ~/dev
basePath: "/Users/ferg/Dropbox/webdev/x-wing_squadron_builder/www/js/",
// point releaseDir to ~/dev/myapp-deploy
releaseDir: "./",
action:"release",
optimize:"shrinksafe",
stripConsole: "normal",
async: 1,
layers: {
"squad_builder_deploy/squad_builder_all": {
include: [
"squad_builder/SquadList.js",
"squad_builder/SquadPane.js" // there are actually many more files, this is just for testing...
]
}
},
resourceTags: {
amd: function(filename, mid) {
return /\.js$/.test(filename);
}
}
}
My directory structure is:
www
- js
- dojo_toolkit
- dojo
- dijit
- dojox
- squad_builder (my app)
- squad_builder_deploy (where I want to generate my layer)
Running this in the terminal:
buildscripts ferg$ ./build.sh load=build profile=../../../squad_builder/squad_builder -r
Gives me:
processing profile resource /Users/ferg/Dropbox/webdev/x-wing_squadron_builder/www/js/squad_builder/squad_builder.profile.js
discovering resources...
starting reading resources...
starting processing raw resource content...
starting tokenizing resource...
starting processing resource tokens...
starting parsing resource...
starting processing resource AST...
starting executing global optimizations...
starting writing resources...
error(303) Missing include module for layer. missing: squad_builder/SquadList.js; layer: squad_builder_deploy/squad_builder_all
error(303) Missing include module for layer. missing: squad_builder/SquadPane.js; layer: squad_builder_deploy/squad_builder_all
starting cleaning up...
waiting for the optimizer runner to finish...
starting reporting...
Report written to /Users/ferg/Dropbox/webdev/x-wing_squadron_builder/www/js/build-report.txt
Process finished normally.
errors: 2
warnings: 0
build time: 1.734 seconds
What am I doing wrong?
Module IDs are not file names. You should not have extensions on SquadList or SquadPane. You also should not be generating layers that aren’t files that already exist within your application. A layer is just an existing module file that contains many additional modules for efficiency.
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.