1 | 'use strict';
|
2 |
|
3 | var concat = require('concat-stream'),
|
4 | es = require('event-stream'),
|
5 | gutil = require('gulp-util'),
|
6 | path = require('path'),
|
7 | fs = require('fs');
|
8 |
|
9 | module.exports = function(options) {
|
10 | var prefix, basepath, filters, context;
|
11 |
|
12 | if (typeof options === 'object') {
|
13 | basepath = options.basepath || '@file';
|
14 | prefix = options.prefix || '@@';
|
15 | context = options.context || {};
|
16 | filters = options.filters;
|
17 | } else {
|
18 | prefix = options || '@@';
|
19 | basepath = '@file';
|
20 | context = {};
|
21 | }
|
22 |
|
23 | var includeRegExp = new RegExp(prefix + 'include\\s*\\([^)]*["\'](.*?)["\'](,\\s*({[\\s\\S]*?})){0,1}\\s*\\)+');
|
24 |
|
25 | function fileInclude(file) {
|
26 | var self = this;
|
27 |
|
28 | if (file.isNull()) {
|
29 | self.emit('data', file);
|
30 | } else if (file.isStream()) {
|
31 | file.contents.pipe(concat(function(data) {
|
32 | var text = String(data);
|
33 | text = stripCommentedIncludes(text);
|
34 | text = parseConditionalIncludes(text);
|
35 |
|
36 | try {
|
37 | self.emit('data', include(file, text));
|
38 | } catch (e) {
|
39 | self.emit('error', new gutil.PluginError('gulp-file-include', e.message));
|
40 | }
|
41 | }));
|
42 | } else if (file.isBuffer()) {
|
43 | try {
|
44 | var text = String(file.contents);
|
45 | text = stripCommentedIncludes(text);
|
46 | text = parseConditionalIncludes(text);
|
47 |
|
48 | self.emit('data', include(file, text));
|
49 | } catch (e) {
|
50 | self.emit('error', new gutil.PluginError('gulp-file-include', e.message));
|
51 | }
|
52 | }
|
53 | }
|
54 |
|
55 | return es.through(fileInclude);
|
56 |
|
57 | |
58 |
|
59 |
|
60 | function stripCommentedIncludes(content) {
|
61 |
|
62 | var regex = new RegExp('<\!--(.*)' + prefix + 'include([\\s\\S]*?)-->', 'g');
|
63 | return content.replace(regex, '');
|
64 | }
|
65 |
|
66 | function parseConditionalIncludes(content) {
|
67 |
|
68 | var regexp = new RegExp(prefix + 'if.*\\{[^{}]*\\}\\s*'),
|
69 | matches = regexp.exec(content),
|
70 | included = false;
|
71 |
|
72 | context.content = content;
|
73 |
|
74 | while (matches) {
|
75 | var match = matches[0],
|
76 | includeExps = /\{([^{}]*)\}/.exec(match)[1];
|
77 |
|
78 |
|
79 | var exp = /if(.*)\{/.exec(match)[1];
|
80 | included = new Function('var context = this; return ' + exp + ';').call(context);
|
81 |
|
82 |
|
83 | if (included) {
|
84 | content = content.replace(match, includeExps);
|
85 | } else {
|
86 | content = content.replace(match, '');
|
87 | }
|
88 |
|
89 | matches = regexp.exec(content);
|
90 | }
|
91 |
|
92 | return content;
|
93 | }
|
94 |
|
95 | function include(file, text) {
|
96 | var matches = includeRegExp.exec(text);
|
97 |
|
98 | switch (basepath) {
|
99 | case '@file':
|
100 | basepath = path.dirname(file.path);
|
101 | break;
|
102 | case '@root':
|
103 | basepath = process.cwd();
|
104 | break;
|
105 | default:
|
106 | break;
|
107 | }
|
108 |
|
109 | basepath = path.resolve(process.cwd(), basepath);
|
110 |
|
111 |
|
112 | var currentFilename = path.resolve(file.base, file.path);
|
113 |
|
114 | while (matches) {
|
115 | var match = matches[0];
|
116 | var includePath = path.resolve(basepath, matches[1]);
|
117 |
|
118 | if (currentFilename.toLowerCase() === includePath.toLowerCase()) {
|
119 | throw new Error('recursion detected in file: ' + currentFilename);
|
120 | }
|
121 |
|
122 | var includeContent = fs.readFileSync(includePath);
|
123 |
|
124 |
|
125 | includeContent = includeContent.toString('utf-8').replace(/\uFEFF/, '');
|
126 |
|
127 |
|
128 | includeContent = includeContent.replace(/\$/gi, '$$$$');
|
129 |
|
130 |
|
131 | if (typeof filters === 'object') {
|
132 | includeContent = applyFilters(includeContent, match);
|
133 | }
|
134 |
|
135 | text = text.replace(match, includeContent);
|
136 |
|
137 | if (matches[3]) {
|
138 |
|
139 | var data = JSON.parse(matches[3]);
|
140 | for (var k in data) {
|
141 | text = text.replace(new RegExp(prefix + k, 'g'), data[k]);
|
142 | }
|
143 | }
|
144 |
|
145 | matches = includeRegExp.exec(text);
|
146 | }
|
147 |
|
148 | file.contents = new Buffer(text);
|
149 | return file;
|
150 | }
|
151 |
|
152 | function applyFilters(includeContent, match) {
|
153 | if (match.match(/\)+$/)[0].length === 1) {
|
154 |
|
155 | return includeContent;
|
156 | }
|
157 |
|
158 |
|
159 | var filterlist = match.split('(').slice(1, -1);
|
160 | filterlist = filterlist.map(function(str) {
|
161 | return filters[str.trim()];
|
162 | });
|
163 |
|
164 |
|
165 | var filter = filterlist.reduce(compose);
|
166 |
|
167 |
|
168 | return filter(String(includeContent));
|
169 | }
|
170 | };
|
171 |
|
172 | function compose(f, g) {
|
173 | return function(x) {
|
174 | return f(g(x));
|
175 | };
|
176 | }
|