Skip to content
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
* ability to embed ng-include
* update javadoc with comparison with gulp-angular-templatecache

2.3.1 / 2017-09-01
==================
* Add support for embedding style templates. Support includes CSS (default) and LESS. You can use the styleType configuration option to enable the "less" processor.
{sourceType: 'ts', styleType: 'less', styleOptions: {compress: true}}

2.3.0 / 2016-08-08
==================
* Keep attribute quotes by default (add quotes if they missed in source code). Removing quotes caused serious of issues with bindings. You can get old behaviour by specifying config property minimize: {quotes: false}
Expand Down
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ src
+-hello-world
|-hello-world-component.ts
+-hello-world-template.html
+-hello-world-template.css
```

`hello-world-component.ts`:
Expand All @@ -93,11 +94,13 @@ class Component extends Directive {
controller: Controller;
controllerAs: string = "vm";
templateUrl: string = "angular2-template.html";
styleUrls: string[] = ["hello-world-template.css"]
}
// or
@View({
...
templateUrl: 'angular2-template.html'
templateUrl: 'angular2-template.html',
styleUrls: ["hello-world-template.css"]
})
```

Expand All @@ -109,6 +112,14 @@ class Component extends Directive {
</task-cmp>
```

`angular2-template.css`:

```css
.my-style {
padding-right: 4px;
}
```

`gulpfile.js`:

```javascript
Expand All @@ -129,11 +140,13 @@ class Component extends Directive {
restrict: string = "E";
controller: Controller;
controllerAs: string = "vm";
styles: string[] = ['.my-style{padding-right:4px}']
template:string='<task-cmp [model]="task" (complete)="onCmpl(task)">{{index}}</task-cmp>';
}
// or
@View({
...
styles:['.my-style{padding-right:4px}'],
template:'<task-cmp [model]="task" (complete)="onCmpl(task)">{{index}}</task-cmp>'
})
```
Expand All @@ -149,6 +162,14 @@ Type: `String`. Default value: 'js'. Available values:
- 'js' both for Angular 1.x syntax `templateUrl: 'path'` and Angular 2.x syntax `@View({templateUrl: 'path'})`
- 'ts' additionally support Angular 2.x TypeScript syntax `class Component {templateUrl: string = 'path'}`

#### options.styleType
Type: `String`. Default value: 'css'. Available values:
- 'css' Use a CSS style processor for style URL templates.
- 'less' Use a LESS style processor for style URL templates.

#### options.styleOptions
Type: `Object`. Options passed on to the style processor. For example `styleOptions: {compress: true}` will enabled the compression option on the style processor and embedded the compressed styles.

#### options.basePath
Type: `String`. By default plugin use path specified in 'templateUrl' as a relative path to corresponding '.js' file (file with 'templateUrl'). This option allow to specify another basePath to search templates as 'basePath'+'templateUrl'

Expand Down
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var PluginError = gutil.PluginError;
var ProcessorEngine = require('./lib/ProcessorEngine');
var Angular1Processor = require('./lib/Angular1Processor');
var Angular2TypeScriptTemplateProcessor = require('./lib/Angular2TypeScriptProcessor');
var Angular2TypeScriptStyleTemplateProcessor = require('./lib/Angular2TypeScriptStylesProcessor');
var utils = require('./lib/utils');

const PLUGIN_NAME = 'gulp-angular-embed-template';
Expand All @@ -16,7 +17,7 @@ module.exports = function (options) {
delete options.sourceType;
switch (sourceType) {
case 'ts':
options.processors = [new Angular1Processor(), new Angular2TypeScriptTemplateProcessor()];
options.processors = [new Angular1Processor(), new Angular2TypeScriptTemplateProcessor(), new Angular2TypeScriptStyleTemplateProcessor()];
break;
case 'js':
default:
Expand Down
39 changes: 22 additions & 17 deletions lib/Angular1Processor.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,6 @@ var RegexpProcessor = require('./RegexpProcessor');
const TEMPLATE_BEGIN = Buffer('template:\'');
const TEMPLATE_END = Buffer('\'');

function escapeSingleQuotes(string) {
const ESCAPING = {
'\'': '\\\'',
'\\': '\\\\',
'\n': '\\n',
'\r': '\\r',
'\u2028': '\\u2028',
'\u2029': '\\u2029'
};
return string.replace(/['\\\n\r\u2028\u2029]/g, function (character) {
return ESCAPING[character];
});
}

var Angular1Processor = extend(RegexpProcessor, {
init : function(config) {
this._super.init(config);
Expand All @@ -35,8 +21,12 @@ var Angular1Processor = extend(RegexpProcessor, {
}
this.minimizer = new Minimize(this.config.minimize);
if (!this.config.minimize.parser) {
var htmlOptions = this.config.minimize.dom || {lowerCaseAttributeNames:false};
if (htmlOptions.lowerCaseAttributeNames === undefined) {
htmlOptions.lowerCaseAttributeNames = false;
}
this.minimizer.htmlparser = new html.Parser(
new html.DomHandler(this.minimizer.emits('read')), this.config.minimize.dom || {lowerCaseAttributeNames:false}
new html.DomHandler(this.minimizer.emits('read')), htmlOptions
);
}

Expand Down Expand Up @@ -82,6 +72,7 @@ var Angular1Processor = extend(RegexpProcessor, {
}
}

var _this = this;
var embedTemplate = this.embedTemplate.bind(this);
var minimizer = this.minimizer;
fs.readFile(templatePath, {encoding: this.config.templateEncoding}, function(err, templateContent) {
Expand All @@ -96,7 +87,7 @@ var Angular1Processor = extend(RegexpProcessor, {
return;
}

var templateBuffer = Buffer(escapeSingleQuotes(minifiedContent));
var templateBuffer = Buffer(_this.escapeSingleQuotes(minifiedContent));
cb(embedTemplate(match, templateBuffer));
});
});
Expand All @@ -108,7 +99,21 @@ var Angular1Processor = extend(RegexpProcessor, {
length: match[0].length,
replace: [TEMPLATE_BEGIN, templateBuffer, TEMPLATE_END]
}
}
},

escapeSingleQuotes: function(string) {
const ESCAPING = {
'\'': '\\\'',
'\\': '\\\\',
'\n': '\\n',
'\r': '\\r',
'\u2028': '\\u2028',
'\u2029': '\\u2029'
};
return string.replace(/['\\\n\r\u2028\u2029]/g, function (character) {
return ESCAPING[character];
});
}
});

module.exports = Angular1Processor;
140 changes: 140 additions & 0 deletions lib/Angular2TypeScriptStylesProcessor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
var fs = require('fs');
var pathModule = require('path');
var cssProcessor = require('clean-css');
var lessProcessor = require('less');

var extend = require('./utils').extend;
var Angular1Processor = require('./Angular1Processor');

//const TEMPLATE_BEGIN = Buffer('styles:string[]=[');
const TEMPLATE_BEGIN = Buffer('styles:[');
const TEMPLATE_END = Buffer(']');

var Angular2TypeScriptStylesProcessor = extend(Angular1Processor, {
init : function(config) {
this._super.init(config);

if (!this.config.styleOptions) {
this.config.styleOptions = {};
}

var styleType = this.config.styleType;
var styleOptions = this.config.styleOptions;
switch (styleType) {
case 'less':
this.minimizer = {
template: function(path) {
return path.replace(/\.css$/, ".less");
},
process: function(path, source, cb) {
var processorOptions = Object.assign({}, styleOptions);
processorOptions["filename"] = path;
lessProcessor.render(source, processorOptions, function(err, minified) {
if (err) {
cb(minified == null ? err : minified.errors, null);
return;
}
cb(null, minified.css);
});
}
};
break;
case 'css':
default:
this.minimizer = {
template: function(path) {
return path;
},
process: function(path, source, cb) {
new cssProcessor(styleOptions).minify(source, function(err, minified) {
if (err) {
cb(minified.errors, null);
return;
}
cb(null, minified.styles);
});
}
};
}
},
/**
* @override
*/
getPattern : function() {
// for typescript: 'styleUrls: string[] = ["template.css"]'
//return '[\'"]?styleUrls[\'"]?[\\s]*:[\\s]*string\[][\\s]*=[\\s]*(\[[^](.[^]*?)\])';
return '[\'"]?styleUrls[\'"]?[\\s]*:[\\s]*(\[[^](.[^]*?)\])';
},

/**
* Find next "styleUrls:", and try to replace url with content if template available, less then maximum size.
* This is recursive function: it call itself until one of two condition happens:
* - error happened (error emitted in pipe and stop recursive calls)
* - no 'styleUrls' left (call 'fileCallback' and stop recursive calls)
*
* @param {Object} fileContext source file content
* @param {Object} match Regexp.exec result
* @param {Function} cb to call after match replaced
* @param {Function} onErr error handler
*/
replaceMatch : function(fileContext, match, cb, onErr) {
var urls = JSON.parse(match[1].replace(/'/g, '"'));
var relativeTemplatePath = match[1];
var templatePath = pathModule.join(fileContext.path, relativeTemplatePath);
var warnNext = function(msg) {
this.logger.warn(msg);
cb();
}.bind(this);
var onError = this.config.skipErrors ? warnNext : onErr;

var embedTemplate = this.embedTemplate.bind(this);
var minimizer = this.minimizer;

var _this = this;
var templateBuffers = [];
var numFiles = urls.length;
urls.map(function (relativeTemplatePath) {
var templatePath = pathModule.join(fileContext.path, minimizer.template(relativeTemplatePath));
_this.logger.debug('template path: %s', templatePath);

if (_this.config.maxSize) {
var fileStat = fs.statSync(templatePath);
if (fileStat && fileStat.size > _this.config.maxSize) {
warnNext('template file "' + templatePath + '" exceeds configured max size "' + _this.config.maxSize + '" actual size is "' + fileStat.size + '"');
return;
}
}

fs.readFile(templatePath, {encoding: _this.config.templateEncoding}, function(err, templateContent) {
if (err) {
onError('Can\'t read template file: "' + templatePath + '". Error details: ' + err);
return;
}
minimizer.process(templatePath, templateContent, function (err, minifiedContent) {
if (err) {
onError('Error while minifying angular style template "' + templatePath + '". Error from "style minimizer" plugin: ' + err);
return;
}
var beginTmpl = templateBuffers.length == 0 ? '\'' : ',\n\'';
var num = templateBuffers.push(new Buffer(beginTmpl + _this.escapeSingleQuotes(minifiedContent) + '\''));
if (num == numFiles) {
cb(embedTemplate(match, Buffer.concat(templateBuffers)));
}
});
});
});
},

/**
* @override
*/
embedTemplate : function(match, templateBuffer) {
return {
start : match.index,
length: match[0].length,
replace: [TEMPLATE_BEGIN, templateBuffer, TEMPLATE_END]
}
}
});

module.exports = Angular2TypeScriptStylesProcessor;
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gulp-angular-embed-templates",
"version": "2.3.0",
"version": "2.3.1",
"description": "gulp plugin to include the contents of angular templates inside directive's code",
"main": "index.js",
"scripts": {
Expand Down Expand Up @@ -30,7 +30,9 @@
"minimize": "^2.0.0",
"through2": "^2.0.1",
"htmlparser2": "~3.9.1",
"object-assign": "4.1.0"
"object-assign": "4.1.0",
"clean-css": "^4.1.7",
"less": "^2.7.2"
},
"devDependencies": {
"mocha": "^3.0.2",
Expand Down
5 changes: 5 additions & 0 deletions test/cases/angular2-less/directive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@Component({
selector: "my-component",
styleUrls: ["template.css"],
directives: [ROUTER_DIRECTIVES]
})
5 changes: 5 additions & 0 deletions test/cases/angular2-less/embedded.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@Component({
selector: "my-component",
styles:['.my-style{padding-right:4px}.my-style:hover{border:1px}'],
directives: [ROUTER_DIRECTIVES]
})
6 changes: 6 additions & 0 deletions test/cases/angular2-less/template.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.my-style {
padding-right: 4px;
&:hover {
border: 1px;
}
}
5 changes: 5 additions & 0 deletions test/cases/angular2-styleUrls/directive.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@Component({
selector: "my-component",
styleUrls: ["template.css"],
directives: [ROUTER_DIRECTIVES]
})
5 changes: 5 additions & 0 deletions test/cases/angular2-styleUrls/embedded.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@Component({
selector: "my-component",
styles:['.my-style{padding-right:4px}'],
directives: [ROUTER_DIRECTIVES]
})
3 changes: 3 additions & 0 deletions test/cases/angular2-styleUrls/template.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.my-style {
padding-right: 4px;
}
10 changes: 9 additions & 1 deletion test/mocha.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,5 +217,13 @@ describe('gulp-angular-embed-templates', function () {

it('should allow to remove attribute quotes', function (done) {
testEmbed('attr-quotes-remove', done, {minimize:{quotes: false}});
})
})

it('should embed styleUrls: path in Angular2.x just fine', function(done) {
testEmbed('angular2-styleUrls', done, {sourceType: 'ts', debug:true});
});

it('should embed styleUrls with less: path in Angular2.x just fine', function(done) {
testEmbed('angular2-less', done, {sourceType: 'ts', styleType: 'less', styleOptions: {compress: true}});
});
});