I am using bower for asset management. The Bootstrap bower repo is coming with a Gruntfile.js.
Is there a way to exclude this from the linker?
I have tried:
var jsFilesToInject = [
'!**Gruntfile.js',
// ...
]
But it's not working - am I putting this string in the wrong spot?
Generated HTML and Error
File Structure
P.S. I followed this guide to get here: StackOverFlow Question and ran bower install bootstrap and bower install angular.
A working configuration for me has been the following :
put the bower_components folder one level up, directly in the assets folder.
put all your app related files in the linker folder
| .bowerrc
| assets/
|-- bower_components/
|---- bootstrap/
|------ dist/
|-------- bootstrap.js
|---- angular/
|-- linker/
|---- js/
|------ sails.io.js
|------ socket.io.js
|---- styles/
|---- templates/
In your Gruntfile.js have a jsFileToInject setup like so
var jsFilesToInject = [
// Below, as a demonstration, you'll see the built-in dependencies
// linked in the proper order order
// Bring in the socket.io client
'linker/js/socket.io.js',
// then beef it up with some convenience logic for talking to Sails.js
'linker/js/sails.io.js',
// jQuery and plugins
'bower_components/jquery/jquery.js',
// Bootstrap
'bower_components/bootstrap/dist/bootstrap.js',
// Angular
'bower_components/angular/angular.js',
// App file that needs to load first
'linker/js/app.js',
// All of the rest of your app scripts imported here
'linker/**/*.js'
];
And in the .bowerrc at the root of your project, you put
{
"directory": "assets/bower_components"
}
This is a bug in the current sails frontend generator. I have submitted a PR, but existing applications will have to be fixed manually, since the generator is only executed once.
The issue is in the end of the current pipeline.js file, in the section that actually exports the settings. At the moment it goes like this:
// pipeline.js
//... Defined rules ...
module.exports.cssFilesToInject = cssFilesToInject.map(function(path) {
return '.tmp/public/' + path;
});
module.exports.jsFilesToInject = jsFilesToInject.map(function(path) {
return '.tmp/public/' + path;
});
module.exports.templateFilesToInject = templateFilesToInject.map(function(path) {
return 'assets/' + path;
});
As you can see it prepends the relative path to the tmp folder or the assets folder for templates. This results in the rule
!js/foo.js in .tmp/public/!js/foo.js, which will probably not match anything.
The solution is to replace the block above in pipeline.js with the following:
module.exports.cssFilesToInject = cssFilesToInject.map(function(path) {
var tmpPath = '.tmp/public/';
if (path.substring(0,1) == '!')
return '!' + tmpPath + path.substring(1);
return tmpPath + path;
});
module.exports.jsFilesToInject = jsFilesToInject.map(function(path) {
var tmpPath = '.tmp/public/';
if (path.substring(0,1) == '!')
return '!' + tmpPath + path.substring(1);
return tmpPath + path;
});
module.exports.templateFilesToInject = templateFilesToInject.map(function(path) {
var tmpPath = 'assets/';
if (path.substring(0,1) == '!')
return '!' + tmpPath + path.substring(1);
return tmpPath + path;
});
This will check if the given rule starts with an ! and prepend it correctly, so !js/foo.js will result in !.tmp/public/js/foo.js that will correctly match.
The exclusions have to be set AFTER the result set was created. This is according to grunt's documentation. So for your case it would be:
var jsFilesToInject = [
//...
'!**Gruntfile.js',
]
Related
Given the following dir structure:
<project>
|- src
|- gen
|- target
We have a Gulp build chain in place which performs the entire front-end build for us. Intermediate results are put in the gen directory and the final results are put in the target directory.
During development, we want to monitor the target directory and synchronize changes to a separate directory that contains our Grails based application. We use the following snippet for this:
'use strict';
var debug = require('gulp-debug');
var config = require('../config');
var gulp = require('gulp');
var watch = require('gulp-watch');
module.exports = {
//command: 'prod',
//desc: 'Builds "production" version',
run: function(){
gulp.task('watch', ['server'], function() {
// Copy files from the 'target' directory to the framework directory while developing
var source = config.dist.root,
destination = config.fw.root;
gulp.src(source + '/**/*', {base: source})
.pipe(debug({title: 'unicorn:'}))
.pipe(watch(source + '/**/*', {base: source}))
.pipe(debug({title: 'minotaur:'}))
.pipe(gulp.dest(destination))
.pipe(debug({title: 'centaur:'}));
});
}
};
When I update a source file, the build chain fires and puts the updated results in the target directory. But the updates are NOT synced to separate Grails directory. When I check the logging, I see this:
[14:29:42] Rebundle...
[14:29:42] minotaur: target\web-app\portal\js\appLoader.js
[14:29:43] minotaur: target\web-app\portal\js\appLoader.js
It seems that the file IS regenerated in the target directory and the regeneration IS picked up by the gulp-watch package. But the file is NOT written by the gulp.dest() function?!
What's might be going on here?
After some trial and error, it seems that you cannot use watch() in the middle of your pipeline. Instead, you should use it as the head of your pipeline (instead of gulp.src()). Splitting the single pipeline into two separate pipelines solved the issue.
So this (removed debug() statements for brevity):
gulp.src(source + '/**/*', {base: source})
.pipe(watch(source + '/**/*', {base: source}))
.pipe(gulp.dest(destination));
Becomes this:
gulp.src(source + '/**/*', {base: source})
.pipe(gulp.dest(destination));
watch(source + '/**/*', {base: source})
.pipe(gulp.dest(destination));
My project structure is the following:
MyApp
- hooks
- platforms
- android
- ios
- www
- js / css / templates..
- lib (including all bower components)
Right now, the www/lib directory is taking up 21,8 Mb. (I have a large set of bower components added to my project.)
When building each project, the entire www folder is copied to the platform/android (for instance) folder for build, including of course www/lib.
This leads to a very big build, as lots of files included into bower
components are useless for production.
Manually managing all bower dependencies is clearly not an option. So how do you guys manage to clean your project platform directory for build?
I was thinking about creating a hook for that but before writing lines of code in a language that i do not know (nodeJS), I was hoping for your return and advises.
According to Cordova workflow you can add a hook script that removes unnecessary files.
A detailed example of a cleanup script can be found here: https://www.thepolyglotdeveloper.com/2015/01/hooks-apache-cordova-mobile-applications/
But to give a quick step by step summary:
Add to the after_prepare hook folder (/hooks/after_prepare) a script (01_junk_cleanup.js - 01 to be run first, the rest whatever you want) and in the file specify the files and folders you want to delete. For example, here is how you can delete a test folder and relevant files just change to you lib directory and to the files there. Note that this example is a bit different from the example in the link i gave earlier so you might want to take a look there as well.
01_junk_cleanup.js:
#!/usr/bin/env node
var fs = require('fs');
var path = require('path');
var foldersToProcess = [
"js",
"css"
];
var foldersToDelete = [
"test"
];
var filesToDelete = [
"karmaOnBrowser.conf.js",
"karmaOnEmulators.conf.js",
"SpecRunner.html"
];
var iosPlatformsDir = "platforms/ios/www/";
var androidPlatformsDir = "platforms/android/assets/www/";
filesToDelete.forEach(function(file) {
var filePathIOS = iosPlatformsDir + file;
var filePathAndroid = androidPlatformsDir + file;
if(fs.existsSync(filePathIOS)){
fs.unlinkSync(filePathIOS);
};
if(fs.existsSync(filePathAndroid)){
fs.unlinkSync(filePathAndroid);
};
});
foldersToProcess.forEach(function(folder) {
processFiles(iosPlatformsDir + folder);
processFiles(androidPlatformsDir + folder);
});
foldersToDelete.forEach(function(folder) {
deleteFolderRecursive(iosPlatformsDir + folder);
deleteFolderRecursive(androidPlatformsDir + folder);
});
function deleteFolderRecursive(path){
if( fs.existsSync(path) ) {
fs.readdirSync(path).forEach(function(file,index){
var curPath = path + "/" + file;
if(fs.lstatSync(curPath).isDirectory()) { // recurse
deleteFolderRecursive(curPath);
} else { // delete file
fs.unlinkSync(curPath);
}
});
fs.rmdirSync(path);
}
}
function processFiles(dir) {
fs.readdir(dir, function(err, list) {
if(err) {
console.log('processFiles err: ' + err);
return;
}
list.forEach(function(file) {
file = dir + '/' + file;
fs.stat(file, function(err, stat) {
if(!stat.isDirectory()) {
switch(path.basename(file)) {
case ".DS_Store":
fs.unlink(file, function(error) {
console.log("Removed file " + file);
});
break;
case "Thumbs.db":
fs.unlink(file, function(error) {
console.log("Removed file " + file);
});
break;
default:
console.log("Skipping file " + file);
break;
}
}
});
});
});
}
Aside to above, A bit more obvious but I feel worth mentioning anyhow, After having the www/lib bloat as well I always try to keep the folder lean and add only libraries required for deployment, the other dev. dependencies such as jasmine I either hold in the 'node_modules' folder or 'bower_components' as I only install today through them.
Hope this helps,
Good luck
I think the best approach would be to do this:
Move the bower_components folder and your index.html file to the project root, outside the /www folder
Install gulp and gulp-usemin
Wrap all of the .js files and .css files from bower components in usemin <build:js> and <build:css> sections
Configure a task in your gulpfile to concatenate all those files into a lib.js and a lib.css file. Make sure that those two files as well as the rewritten index.html are output to the /www folder
Execute the gulp task before your next build, and each time you add a new bower component.
This will keep your /www folder tidy and only containing the files you need in your cordova build.
With Bower you need to use npm preen to remove unnecessary files
See my example using Gulp with Ionic Framework: https://github.com/jdnichollsc/Ionic-Starter-Template
Basically you can set your bower.json file to indicate the path which files you need, for example:
"preen": {
//... More libraries
"ionic-datepicker": [
"dist/*.js" //You only need these files
//Other files and folders will be deleted to reduce the size of your app
],
"ion-floating-menu": [
"dist/*" //Keep all the files (.html, .css, .js, etc) of the directory.
]
}
Regards, Nicholls
This is an improvement over this answer. I've applied it to my own project.
Move the bower_components folder to the project root, outside the www folder.
Rename index.html to _index.html. We will later make sure that Gulp automatically generates index.html.
Install gulp and gulp-useref.
Edit your _index.html so that it looks something like this:
<!-- build:js dist/js/vendor.js -->
<script src="../bower_components/ionic/release/js/ionic.bundle.min.js"></script>
<script src="../bower_components/ngstorage/ngStorage.min.js"></script>
<script src="../bower_components/ngCordova/dist/ng-cordova.min.js"></script>
<!-- endbuild -->
Configure your gulp watch task to build new index.html file in the www folder with the concatenated files.
var entrypoint = './www/_index.html';
gulp.task('watch', function() {
gulp.watch(entrypoint, ['concat-js-and-css']);
});
gulp.task('concat-js-and-css', function () {
return gulp.src(entrypoint)
.pipe(useref())
.pipe(rename(function (path) {
// rename _index.html to index.html
if (path.basename == '_index' && path.extname == '.html') {
path.basename = "index";
}
}))
.pipe(gulp.dest('./www'))
});
gulp.task('build', ['concat-js-and-css']);
When that task runs, your index.html file will contain just this:
<script src="dist/js/vendor.js"></script>
Edit your ionic.project file so that it looks like the following. This will make sure that gulp watch is run before ionic serve.
{
"watchPatterns": [
"www/_index.html",
],
"gulpStartupTasks": [
"watch"
]
}
This is my first day doing node, I'm having some problems trying to bundle some js files.
MyFolder
|-- app (folder)
| |-- Collections (contains: movies.js)
| |-- Models (contains: movie.js)
| |-- node_modules
|-- main.js
|-- node_modules (folder)
|-- static (folder)
This is the content of js files I want to compress into static/bundle.js.
// app/models/movie.js
var Backbone = require("backbone");
var Movie = Backbone.Model.extend({
defaults: {
title: "default",
year: 0,
description: "empty",
selected: false
}
});
module.exports = Movie;
// app/collections/movies.js
var Backbone = require("backbone");
var Movie = require('models/movie');
var Movies = Backbone.Collection.extend({
model: Movie
});
module.exports = Movies;
When I run browserify -r ./app/main:app > static/bundle.js the file bundle.js is created with the scripts from app/main.js. It works as expected.
But when I run browserify -r ./app/collections/movies.js:movies \ -r ./app/models/movie.js:movie > static/bundle.js, it creates an empty bundle.js and shows this:
Error: Cannot find module '/Users/MyFolder/app/models/movie.js:movie' from '/Users/MyFolder'
My folder app/node_modules is sync with ln -sf ../models . and ln -sf ../collections .
Question 1: Any hint what I'm doing wrong?
Question 2: If static/bundle.js exists. Does running browserify again overwrites the file or not? On my local tests it doesn't overwrite, so am I supposed to delete this file each time for update?
Might consider adding ./ to your path:
var Movie = require('./models/movie');
see: How to use browserify to bundle a backbone app?
For people coming from search engines:
It might be that you are using a mac and you have not used proper case while requiring the file.
This is equivalent in mac:
require('./someFile');
require('./somefile');
But not in centOs for example.
If you use your debugger and step into the require call you'll find yourself inside some minified code (typically).
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require== ...
Go to your console and inspect t[o][1]
This will show you a list of the correct paths for your modules.
Object {
'<module name>' : <id>
...
}
If this becomes too confusing temporarily un-minify the first line in your compiled bundle.js file (I use alt-cmd-l in PhpStorm) and try again.
I had the same error. In my case, it's because Browserify does not detect the file if it's not directly specified in the require.
Example:
Change:
var f = 'filename'
var handler = require('./' + f + '.js'); --> this will generate an error
To:
var handler = require('./filename.js');
I was having the same error recently. I am running Browserify using Gulp 4.
The problem was that I was reorganizing my project folder architecture and I did not empty the destination (dist) folder for my bundled .js files. After I emptied that folder (in my case it was called assets) and run Browserfy it was working fine again.
In my case, I replaced
const router = require(path.join(__dirname, 'router'))
to
const router = require('./router')
for it to work. It seems Browserify does not support local file access through node 'path' module
Basically I'm looking for a Gulp plugin to turn a directory like this:
/app
- htmlfile1.html
- htmlfile2.html
- htmlfile3.html
- /css
-cssmain.css
-/js
-global.js
And turn that into this:
/dist
-/htmlfile1
- htmlfile1.html
- /css
-cssmain.css
-/js
-global.js
- /htmlfile2
- htmlfile2.html
- /css
-cssmain.css
-/js
-global.js
- /htmlfile3
- htmlfile3.html
- /css
-cssmain.css
-/js
-global.js
Any thoughts on how to do accomplish a build system like this?
The code allows common files to be added to every page distribution as well as unique dependencies defined as an array in the pages object.
The following Gulp file relies on gulp-foreach, parse-filepath, and event-stream: npm install gulp gulp-foreach parse-filepath event-stream --save-dev
gulpfile.js:
// Command:
// npm install gulp gulp-foreach parse-filepath event-stream --save-dev
// Include gulp
var gulp = require('gulp');
var foreach = require('gulp-foreach'); // https://www.npmjs.org/package/gulp-foreach
var parsePath = require('parse-filepath'); // https://www.npmjs.org/package/parse-filepath
var es = require('event-stream'); // https://www.npmjs.org/package/event-stream
// The pages that each make a distribution
// Unique dependencies are defined as an array value for each page.
var pages = {
'./app/htmlfile1.html': [
'./app/images/1.png',
'./app/images/1-another.png',
],
'./app/htmlfile2.html': [],
'./app/htmlfile3.html': []
};
// Files added to each page distribution
var common = [
'./app/css/cssmain.css',
'./app/js/global.js',
];
function makeDistributionStream(page)
{
var gulpStream = gulp.src(page)
.pipe(foreach(function(stream, file) {
var pathParts = parsePath(file.path);
// Assemble the distribution path
var destinationPath = './dist/' + pathParts.name + '/';
// Pipe the html into the distribution folder
stream.pipe(gulp.dest(destinationPath));
// Move all of the unique and common files into the distibution
var uniqueDependencies = pages[page];
// Merge the common files to the unique ones
var distFiles = uniqueDependencies.concat(common);
gulp.src(distFiles, {base: './app/'})
.pipe(gulp.dest(destinationPath));
}));
return gulpStream;
}
// Assemble the distribution directories for each page
gulp.task('make-distributions', function() {
var mergedStream = null;
for(var page in pages)
{
var stream = makeDistributionStream(page);
// Merge the streams, if there is already one
if(mergedStream)
{
mergedStream = es.merge(mergedStream, stream);
}
// Otherwise, just make it this one
else
{
mergedStream = stream;
}
}
return mergedStream;
});
// Rerun the task when a file changes
gulp.task('watch', function() {
// If the html pages change, re-make the distributions
gulp.watch(Object.keys(pages), ['make-distributions']);
});
// Default Task
gulp.task('default', ['make-distributions', 'watch']);
I'm writing a CMS on Node.js with Express Framework. On my CMS I have several modules for users, pages, etc.
I want that each module will have his files on separate folder, including the view files.
Anyone know how can I achieve that?
I'm using swig as my template engine but I can replace it to something else if it will helps.
Last Update
The multiple view folders feature is supported by the framework since Express 4.10
Just pass an array of locations to the views property, like so.
app.set('views', [__dirname + '/viewsFolder1', __dirname + '/viewsFolder2']);
Express 2.0
As far as I know express doesn't support multiple view paths or namespaces at the moment (like the static middleware do)
But you can modify the lookup logic yourself so that it works the way you want, for example:
function enableMultipleViewFolders(express) {
// proxy function to the default view lookup
var lookupProxy = express.view.lookup;
express.view.lookup = function (view, options) {
if (options.root instanceof Array) {
// clones the options object
var opts = {};
for (var key in options) opts[key] = options[key];
// loops through the paths and tries to match the view
var matchedView = null,
roots = opts.root;
for (var i=0; i<roots.length; i++) {
opts.root = roots[i];
matchedView = lookupProxy.call(this, view, opts);
if (matchedView.exists) break;
}
return matchedView;
}
return lookupProxy.call(express.view, view, options)
};
}
You will enable the new logic by calling the function above and passing express as a parameter, and then you will be able to specify an array of views to the configuration:
var express = require('express');
enableMultipleViewFolders(express);
app.set('views', [__dirname + '/viewsFolder1', __dirname + '/viewsFolder2']);
Or, if you prefer, you can patch the framework directly (updating the view.js file inside it)
This should work in Express 2.x, not sure if it will with the new version (3.x)
UPDATE
Unluckily the above solution won't work in Express 3.x since express.view would be undefined
Another possible solution will be to proxy the response.render function and set the views folder config until it gets a match:
var renderProxy = express.response.render;
express.render = function(){
app.set('views', 'path/to/custom/views');
try {
return renderProxy.apply(this, arguments);
}
catch (e) {}
app.set('views', 'path/to/default/views');
return renderProxy.apply(this, arguments);
};
I've not tested it, it feels very hacky to me anyway, unluckily this feature has been pushed back again:
https://github.com/visionmedia/express/pull/1186
UPDATE 2
This feature has been added in Express 4.10, since the following pull request has been merged:
https://github.com/strongloop/express/pull/2320
In addition to #user85461 answer, the require view part did not work for me.
What i did: removed the path stuff and moved it all to a module i could require,
patch.ViewEnableMultiFolders.js (Works with current express):
function ViewEnableMultiFolders(app) {
// Monkey-patch express to accept multiple paths for looking up views.
// this path may change depending on your setup.
var lookup_proxy = app.get('view').prototype.lookup;
app.get('view').prototype.lookup = function(viewName) {
var context, match;
if (this.root instanceof Array) {
for (var i = 0; i < this.root.length; i++) {
context = {root: this.root[i]};
match = lookup_proxy.call(context, viewName);
if (match) {
return match;
}
}
return null;
}
return lookup_proxy.call(this, viewName);
};
}
module.exports.ViewEnableMultiFolders = ViewEnableMultiFolders;
and used:
var Patch = require('patch.ViewEnableMultiFolders.js');
Patch.ViewEnableMultiFolders(app);
app.set('views', ['./htdocs/views', '/htdocs/tpls']);
Here's a solution for Express 3.x. It monkey-patches express 3.x's "View" object to do the same lookup trick as #ShadowCloud's solution above. Unfortunately, the path lookup for the View object is less clean, since 3.x doesn't expose it to express -- so you have to dig into the bowels of node_modules.
function enable_multiple_view_folders() {
// Monkey-patch express to accept multiple paths for looking up views.
// this path may change depending on your setup.
var View = require("./node_modules/express/lib/view"),
lookup_proxy = View.prototype.lookup;
View.prototype.lookup = function(viewName) {
var context, match;
if (this.root instanceof Array) {
for (var i = 0; i < this.root.length; i++) {
context = {root: this.root[i]};
match = lookup_proxy.call(context, viewName);
if (match) {
return match;
}
}
return null;
}
return lookup_proxy.call(this, viewName);
};
}
enable_multiple_view_folders();
You can however, put all the view files inside the 'view' folder, but separate each module's view into it's own folders inside the 'view' folder. So, the structure is something like this :
views
--moduleA
--moduleB
----submoduleB1
----submoduleB2
--moduleC
Set the view files like usual :
app.set('views', './views');
And when render for each module, include the module's name :
res.render('moduleA/index', ...);
or even submodule's name :
res.render('moduleB/submoduleB1/index', ...);
This solution is also works in express before version 4.x,
Install glob npm install glob
If you have a views directory that looks something like:
views
├── 404.ejs
├── home.ejs
├── includes
│ ├── header.ejs
│ └── footer.ejs
├── post
│ ├── create.ejs
│ └── edit.ejs
└── profile.ejs
You can use this glob function to return an array of subdirectories in the views directory (add the path.substring to remove the trailing /)
let viewPaths = glob.sync('views/**/').map(path => {
return path.substring(0, path.length - 1)
})
console.log(viewPaths)
>> ['views', 'views/post', 'views/includes']
So now you can set
app.set('views', viewPaths)
and now you can use
res.render('404')
res.render('home')
res.render('post/edit')
res.render('post/create')