Skip to content
This repository has been archived by the owner on Mar 26, 2018. It is now read-only.

feat(build): add Jade support #420

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@ language: node_js
node_js:
- '0.10'
- '0.8'
before_install:
- currentfolder=${PWD##*/}
- if [ "$currentfolder" != 'generator-angular' ]; then cd .. && eval "mv $currentfolder generator-angular" && cd generator-angular; fi

146 changes: 130 additions & 16 deletions app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,21 @@ var Generator = module.exports = function Generator(args, options) {
this.env.options.coffee = this.options.coffee;
}

if (typeof this.env.options.jade === 'undefined') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is working right now. I tried:
yo angular --jade
and then
yo angular:view miview
which created a jade file:
create app/views/miview.jade
and
yo angular:view miview2 --jade=false
with created an html file:
create app/views/miview2.html

Anyway, I'm going to add a description for the Jade option, so it is correctly displayed when typing yo angular -help

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if that works then something else is broken, such as the auto create jade files if one already exists.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain a little bit more? When jade files exist, yo is informing that they are identical or that there is a conflict and asking how to solve it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yo angular:view new --jade should create a new new.jade file
yo angular:view another should create another.jade file (automatically creating the jade file since there are already jade files in the project)
yo angular:view last --jade=false should create last.html since jade false is specified, even though are already jade files

if you dont pass in the jade option then (lines)[https://github.com/gonzaloruizdevilla/generator-angularexpress/blob/72e994c9d4e63dea0f4e6a7d5d78e06724601e3f/app/index.js#L53-L54] should automatically create jade files. passing in false should also do that since this.env.options.jade is undefined

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is working that way. I have tried this:

$ yo angular
....creating proyect
$ yo angular:view myview1
   create app/views/myview1.html
$ yo angular:view myview2 --jade
   create app/views/myview2.jade
$ yo angular:view myview3
   create app/views/myview3.jade
$ yo angular:view myview4 --jade=false
   create app/views/myview4.html

this.option('jade', {
desc: 'Generate Jade templates instead of HTML'
});

// attempt to detect if user is using jade or not
// if cml arg provided, use that; else look for the existence of cs
if (!this.options.jade &&
this.expandFiles(path.join(this.appPath, '/**/*.jade'), {}).length > 0) {
this.options.jade = true;
}

this.env.options.jade = this.options.jade;
}

if (typeof this.env.options.minsafe === 'undefined') {
this.option('minsafe', {
desc: 'Generate AngularJS minification safe code'
Expand Down Expand Up @@ -174,11 +189,72 @@ Generator.prototype.askForModules = function askForModules() {
};

Generator.prototype.readIndex = function readIndex() {
this.indexFile = this.engine(this.read('../../templates/common/index.html'), this);
var extension = this.env.options.jade ? "jade" : "html";
this.indexFile = this.engine(this.read('../../templates/common/index.' + extension), this);
};

function spacePrefix(jade, block){
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like all these functions should go in the utils file

var prefix;
jade.split('\n').forEach( function (line) { if( line.indexOf(block)> -1 ) {
prefix = line.split("/")[0];
}});
return prefix;
}

function generateJadeBlock(blockType, optimizedPath, filesBlock, searchPath, prefix) {
var blockStart, blockEnd;
var blockSearchPath = '';

if (searchPath !== undefined) {
if (util.isArray(searchPath)) {
searchPath = '{' + searchPath.join(',') + '}';
}
blockSearchPath = '(' + searchPath + ')';
}

blockStart = '\n' + prefix + '// build:' + blockType + blockSearchPath + ' ' + optimizedPath + ' \n';
blockEnd = prefix + '// endbuild\n' + prefix;
return blockStart + filesBlock + blockEnd;
}

function appendJade(jade, tag, blocks){
var mark = "//- build:" + tag,
position = jade.indexOf(mark);
return [jade.slice(0, position), blocks, jade.slice(position)].join('');
}

function appendFilesToJade(jadeOrOptions, fileType, optimizedPath, sourceFileList, attrs, searchPath) {
var blocks, updatedContent, prefix, jade, files = '';
jade = jadeOrOptions;
if (typeof jadeOrOptions === 'object') {
jade = jadeOrOptions.html;
fileType = jadeOrOptions.fileType;
optimizedPath = jadeOrOptions.optimizedPath;
sourceFileList = jadeOrOptions.sourceFileList;
attrs = jadeOrOptions.attrs;
searchPath = jadeOrOptions.searchPath;
}
if (fileType === 'js') {
prefix = spacePrefix(jade, "build:body");
sourceFileList.forEach(function (el) {
files += prefix + 'script(' + (attrs||'') + 'src="' + el + '")\n';
});
blocks = generateJadeBlock('js', optimizedPath, files, searchPath, prefix);
updatedContent = appendJade(jade, 'body', blocks);
} else if (fileType === 'css') {
prefix = spacePrefix(jade, "build:head");
sourceFileList.forEach(function (el) {
files += prefix + 'link(' + (attrs||'') + 'rel="stylesheet", href="' + el + '")\n';
});
blocks = generateJadeBlock('css', optimizedPath, files, searchPath, prefix);
updatedContent = appendJade(jade, 'head', blocks);
}
return updatedContent;
}

// Waiting a more flexible solution for #138
Generator.prototype.bootstrapFiles = function bootstrapFiles() {
var filesToAppend;
var sass = this.compassBootstrap;
var files = [];
var source = 'styles/' + ( sass ? 's' : '' ) + 'css/';
Expand All @@ -197,24 +273,36 @@ Generator.prototype.bootstrapFiles = function bootstrapFiles() {
this.copy(source + file, 'app/styles/' + file);
}.bind(this));

this.indexFile = this.appendFiles({
filesToAppend = {
html: this.indexFile,
fileType: 'css',
optimizedPath: 'styles/main.css',
sourceFileList: files.map(function (file) {
return 'styles/' + file.replace('.scss', '.css');
}),
searchPath: '.tmp'
});
};

if (this.env.options.jade) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var appendFiles = this.env.options.jade ? appendFilesToJade : this.appendFiles;
appendFiles(filesToAppend)

Would something like that work better so you dont have this block repeated so many times?

this.indexFile = appendFilesToJade(filesToAppend);
} else {
this.indexFile = this.appendFiles(filesToAppend);
}
};


function appendScriptsJade(jade, optimizedPath, sourceFileList, attrs) {
return appendFilesToJade(jade, 'js', optimizedPath, sourceFileList, attrs);
}

Generator.prototype.bootstrapJS = function bootstrapJS() {
var list;
if (!this.bootstrap) {
return; // Skip if disabled.
}

// Wire Twitter Bootstrap plugins
this.indexFile = this.appendScripts(this.indexFile, 'scripts/plugins.js', [
list = [
'bower_components/sass-bootstrap/js/affix.js',
'bower_components/sass-bootstrap/js/alert.js',
'bower_components/sass-bootstrap/js/button.js',
Expand All @@ -226,8 +314,15 @@ Generator.prototype.bootstrapJS = function bootstrapJS() {
'bower_components/sass-bootstrap/js/scrollspy.js',
'bower_components/sass-bootstrap/js/tab.js',
'bower_components/sass-bootstrap/js/tooltip.js',
'bower_components/sass-bootstrap/js/transition.js',
]);
'bower_components/sass-bootstrap/js/transition.js'
];

if (this.env.options.jade) {
this.indexFile = appendScriptsJade(this.indexFile, 'scripts/plugins.js', list);
} else {
this.indexFile = this.appendScripts(this.indexFile, 'scripts/plugins.js', list);
}

};

Generator.prototype.extraModules = function extraModules() {
Expand All @@ -245,27 +340,46 @@ Generator.prototype.extraModules = function extraModules() {
}

if (modules.length) {
this.indexFile = this.appendScripts(this.indexFile, 'scripts/modules.js',
modules);
if (this.env.options.jade) {
this.indexFile = appendScriptsJade(this.indexFile, 'scripts/plugins.js', modules);
} else {
this.indexFile = this.appendScripts(this.indexFile, 'scripts/plugins.js', modules);
}
}
};

Generator.prototype.appJs = function appJs() {
this.indexFile = this.appendFiles({
html: this.indexFile,
fileType: 'js',
optimizedPath: 'scripts/scripts.js',
sourceFileList: ['scripts/app.js', 'scripts/controllers/main.js'],
searchPath: ['.tmp', 'app']
});
if (this.env.options.jade) {
this.indexFile = appendFilesToJade({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config block should be a variable and passed into the functions so its not duplicated (at least it looks duplicated to me)

html: this.indexFile,
fileType: 'js',
optimizedPath: 'scripts/scripts.js',
sourceFileList: ['scripts/app.js'],
searchPath: ['.tmp', 'app']
});
} else {
this.indexFile = this.appendFiles({
html: this.indexFile,
fileType: 'js',
optimizedPath: 'scripts/scripts.js',
sourceFileList: ['scripts/app.js', 'scripts/controllers/main.js'],
searchPath: ['.tmp', 'app']
});
}
};

Generator.prototype.createIndexHtml = function createIndexHtml() {
this.write(path.join(this.appPath, 'index.html'), this.indexFile);
var indexFile = 'index.' + (this.env.options.jade ? 'jade' : 'html');
this.write(path.join(this.appPath, indexFile), this.indexFile);
};

Generator.prototype.packageFiles = function () {
this.template('../../templates/common/_bower.json', 'bower.json');
this.template('../../templates/common/_package.json', 'package.json');
this.template('../../templates/common/Gruntfile.js', 'Gruntfile.js');
};

Generator.prototype.addMainView = function addMainView() {
var mainFile = 'main.' + (this.env.options.jade ? 'jade' : 'html');
this.copy('../../templates/common/' + mainFile, 'app/views/' + mainFile);
};
12 changes: 12 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,18 @@ angular.module('myMod').config(function ($provide) {
## Options
In general, these options can be applied to any generator, though they only affect generators that produce scripts.

### Jade
For generators that output html, the `--jade` option will output Jade instead of html files.

For example:
```bash
yo angular:view user --jade

Produces `app/scripts/views/user.jade`:
```jade
p This is the user view.
```

### CoffeeScript
For generators that output scripts, the `--coffee` option will output CoffeeScript instead of JavaScript.

Expand Down
44 changes: 41 additions & 3 deletions script-base.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,20 @@ var Generator = module.exports = function Generator() {
this.env.options.coffee = this.options.coffee;
}

this.env.options.jade = this.options.jade;
if (typeof this.env.options.jade === 'undefined') {
this.option('jade');

// attempt to detect if user is using jade or not
// if cml arg provided, use that; else look for the existence of cs
if (!this.options.jade &&
this.expandFiles(path.join(this.env.options.appPath, '/**/*.jade'), {}).length > 0) {
this.options.jade = true;
}

this.env.options.jade = this.options.jade;
}

if (typeof this.env.options.minsafe === 'undefined') {
this.option('minsafe');
this.env.options.minsafe = this.options.minsafe;
Expand Down Expand Up @@ -89,9 +103,25 @@ Generator.prototype.htmlTemplate = function (src, dest) {
]);
};

Generator.prototype.addScriptToIndex = function (script) {
function addScriptToIndexJade(script, env) {
try {
var appPath = this.env.options.appPath;
var appPath = env.options.appPath;
var fullPath = path.join(appPath, 'index.jade');
angularUtils.rewriteFile({
file: fullPath,
needle: '// endbuild',
splicable: [
'script(src="scripts/' + script + '.js")'
]
});
} catch (e) {
console.log('\nUnable to find '.yellow + fullPath + '. Reference to '.yellow + script + '.js ' + 'not added.\n'.yellow);
}
}

function addScriptToIndexHtml(script, env) {
try {
var appPath = env.options.appPath;
var fullPath = path.join(appPath, 'index.html');
angularUtils.rewriteFile({
file: fullPath,
Expand All @@ -103,6 +133,14 @@ Generator.prototype.addScriptToIndex = function (script) {
} catch (e) {
console.log('\nUnable to find '.yellow + fullPath + '. Reference to '.yellow + script + '.js ' + 'not added.\n'.yellow);
}
}

Generator.prototype.addScriptToIndex = function (script) {
if (this.env.options.jade) {
addScriptToIndexJade(script, this.env);
} else {
addScriptToIndexHtml(script, this.env);
}
};

Generator.prototype.generateSourceAndTest = function (appTemplate, testTemplate, targetDirectory, skipAdd) {
Expand All @@ -111,4 +149,4 @@ Generator.prototype.generateSourceAndTest = function (appTemplate, testTemplate,
if (!skipAdd) {
this.addScriptToIndex(path.join(targetDirectory, this.name));
}
};
};
2 changes: 1 addition & 1 deletion templates/coffeescript-min/spec/service.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
describe 'Service: <%= classedName %>', () ->

# load the service's module
beforeEach module '<%= scriptAppName %>App'
beforeEach module '<%= scriptAppName %>'

# instantiate service
<%= classedName %> = {}
Expand Down
2 changes: 1 addition & 1 deletion templates/coffeescript/spec/service.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
describe 'Service: <%= classedName %>', () ->

# load the service's module
beforeEach module '<%= scriptAppName %>App'
beforeEach module '<%= scriptAppName %>'

# instantiate service
<%= classedName %> = {}
Expand Down
29 changes: 29 additions & 0 deletions templates/common/Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ module.exports = function (grunt) {
files: ['<%%= yeoman.app %>/styles/{,*/}*.{scss,sass}'],
tasks: ['compass:server', 'autoprefixer']
},<% } %>
jade: {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

need conditionals for all these. I know there arent really a lot currently in the file for other things but im hoping that will change soon

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it. In my first try, there was a question when running yo angular and the answer was used for the conditionals inside the gruntfile template. However, now it's using an argument for jade templates, so this would mean that the first time this argument is used the Gruntfile must be modified directly, somehow duplicating the conditional logic inside the template.

The same happens with coffescript. If conditionals are used for the coffescript tasks, do you have any ideas of how to modify the gruntfile in a clean way when in a later moment the user decides to use coffescript?

files: ['<%%= yeoman.app %>/**/*.jade'],
tasks: ['jade']
},
styles: {
files: ['<%%= yeoman.app %>/styles/{,*/}*.css'],
tasks: ['copy:styles', 'autoprefixer']
Expand Down Expand Up @@ -174,6 +178,28 @@ module.exports = function (grunt) {
}
}
},
jade: {
index: {
files: {
'<%%= yeoman.app %>/': ['<%%= yeoman.app %>/index.jade']
},
options: {
basePath: '<%%= yeoman.app %>/',
client: false,
pretty: true
}
},
views: {
files: {
'<%%= yeoman.app %>/views/': ['<%%= yeoman.app %>/views/**/*.jade']
},
options: {
basePath: '<%%= yeoman.app %>/views',
client: false,
pretty: true
}
}
},
useminPrepare: {
html: '<%%= yeoman.app %>/index.html',
options: {
Expand Down Expand Up @@ -274,16 +300,19 @@ module.exports = function (grunt) {
},
concurrent: {
server: [
'jade',
'coffee:dist',<% if (compassBootstrap) { %>
'compass:server',<% } %>
'copy:styles'
],
test: [
'jade',
'coffee',<% if (compassBootstrap) { %>
'compass',<% } %>
'copy:styles'
],
dist: [
'jade',
'coffee',<% if (compassBootstrap) { %>
'compass:dist',<% } %>
'copy:styles',
Expand Down
1 change: 1 addition & 0 deletions templates/common/_package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"grunt-google-cdn": "~0.2.0",
"grunt-ngmin": "~0.0.2",
"time-grunt": "~0.1.0",
"grunt-jade": "~0.4.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why grunt-jade instead of grunt-contrib-jade?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been using grunt-jade for a long time and it has worked fine. I'm going to check grunt-contrib-jade and verify that a few issues I had a long ago with grunt-jade are not present.

"jshint-stylish": "~0.1.3"
},
"engines": {
Expand Down
Loading