-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathgulp-html-partial.js
More file actions
162 lines (138 loc) · 4.46 KB
/
gulp-html-partial.js
File metadata and controls
162 lines (138 loc) · 4.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
const fs = require('fs');
const partition = require('lodash.partition');
const html = require('html');
const gutil = require('gulp-util');
module.exports = (function () {
"use strict";
/**
* @type {String}
*/
const pluginName = 'gulp-html-partial';
/**
* Default settings
*
* @enum {String}
*/
const options = {
tagName: 'partial',
basePath: '',
variablePrefix: '@@'
};
/**
* Matches <tagName></tagName> and <tagName />
*
* @param {String} html - stringified file content
* @returns {Array.<String>}
*/
function getTags(html) {
const closed = html.match(new RegExp(`<${options.tagName}(.*)/${options.tagName}>`, 'g')) || [];
const selfClosed = html.match(new RegExp(`<${options.tagName}(.*?)\/>`, 'g')) || [];
return [].concat(closed, selfClosed);
}
/**
* Extracts attributes from template tags as an array of objects
*
* @example of output
* [
* {
* key: 'src',
* value: 'partial.html'
* },
* {
* key: 'title',
* value: 'Some title'
* }
* ]
*
* @param {String} tag - tag to replace
* @returns {Array.<Object>}
*/
function getAttributes(tag) {
let running = true;
const attributes = [];
const regexp = /(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']?/g;
while (running) {
const match = regexp.exec(tag);
if (match) {
attributes.push({
key: match[1],
value: match[2]
})
} else {
running = false;
}
}
return attributes;
}
/**
* Gets file using node.js' file system based on src attribute
*
* @param {Array.<Object>} attributes - tag
* @returns {String}
*/
function getPartial(attributes) {
const splitAttr = partition(attributes, (attribute) => attribute.key === 'src');
const sourcePath = splitAttr[0][0] && splitAttr[0][0].value;
let file;
if (sourcePath && fs.existsSync(options.basePath + sourcePath)) {
file = injectHTML(fs.readFileSync(options.basePath + sourcePath))
} else if (!sourcePath) {
gutil.log(`${pluginName}:`, new gutil.PluginError(pluginName, gutil.colors.red(`Some partial does not have 'src' attribute`)).message);
} else {
gutil.log(`${pluginName}:`, new gutil.PluginError(pluginName, gutil.colors.red(`File ${options.basePath + sourcePath} does not exist.`)).message);
}
return replaceAttributes(file, splitAttr[1]);
}
/**
* Replaces partial content with given attributes
*
* @param {Object|undefined} file - through2's file object
* @param {Array.<Object>} attributes - tag
* @returns {String}
*/
function replaceAttributes(file, attributes) {
return (attributes || []).reduce((html, attrObj) =>
html.replace(options.variablePrefix + attrObj.key, attrObj.value), file && file.toString() || '');
}
/**
* @param {String} html - HTML content of modified file
* @returns {String}
*/
function getHTML(html) {
const tags = getTags(html);
const partials = tags.map((tag) => getPartial(getAttributes(tag)));
return tags.reduce((output, tag, index) =>
output.replace(tag, partials[index]), html);
}
/**
* @param {Object} file - through2's or nodejs' file object
* @returns {Object}
*/
function injectHTML(file) {
if (file.contents) {
file.contents = new Buffer(html.prettyPrint(getHTML(file.contents.toString())));
} else {
file = new Buffer(html.prettyPrint(getHTML(file.toString())))
}
return file;
}
/**
* @param {Object} config - config object
* @returns {Buffer}
*/
function transform(config) {
Object.assign(options, config);
return require('through2').obj(function (file, enc, callback) {
if (file.isStream()) {
this.emit('error', new gutil.PluginError(pluginName, 'Streams are not supported'));
return callback(null, file);
}
if (file.isBuffer()) {
file = injectHTML(file);
}
this.push(file);
return callback()
});
}
return transform;
})();