-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathindex.js
328 lines (313 loc) · 9.76 KB
/
index.js
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
/*
* alias module combo pulgin for gulp
* Author : [email protected]
* Date : 2015-06-03
*/
var through = require('through2')
var gutil = require('gulp-util')
var fs = require('fs')
var path = require('path')
var PLUGIN_NAME = 'gulp-alias-combo'
var requireReg = /require\s*\(\s*(["'])(.+?)\1\s*\)/g
var requirejsReg = /require(js)?\s*\(\s*\[(.+?)\]/g
var commentReg = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg
var jsFileReg = /^.+\.js$/
var DepStore = require('./libs/dep_store')
/*
* 提取模块中的依赖
* param { String } content 文件内容
* param { Object } options 配置参数
* param { String } filePath 文件路径
* param { Object } depStore 依赖存储对象
* return 提取的依赖存到 options 里
*/
function analyseDeps(content, filePath, options, depStore){
var relativePath = '', parsedDep = null, deps = null
deps = getDeps(content, options.exclude)
if(deps.length > 0){
deps.forEach(function(dep){
if(dep){
if(options.alias && options.alias[dep]){
depStore.addAlias(dep, mergePath(dep, options))
analyseDeps(
readModule(dep, depStore.getAlias(dep)),
depStore.getAlias(dep),
options,
depStore
)
}else{
if(options.supportRelative && !depStore.hasAlias(dep)){
relativePath = getRelativePath(filePath, dep, options)
parsedDep = parseDep(relativePath, options.baseUrl, options.moduleIdPrefix)
depStore.addAlias(parsedDep, relativePath)
if(!depStore.hasRelative(dep)){
depStore.addRelative(dep, parsedDep)
analyseDeps(
readModule(parsedDep, relativePath),
relativePath,
options,
depStore
)
}
}
}
}
})
}
}
/*
* 使用正则提取依赖
* param { String } content 内容
* return { Array } 提取的依赖
*/
function getDeps(content, exclude){
var deps = [], moduleId, moduleIds, result = []
var requires = content.match(requireReg)
if(requires){
requires.forEach(function(dep){
moduleId = dep.substring(dep.indexOf('(') + 1, dep.lastIndexOf(')')).trim()
moduleId = moduleId.substring(1, moduleId.length-1)
deps.push(moduleId)
})
}
requires = content.match(requirejsReg)
if(requires){
requires.forEach(function(dep){
try {
moduleIds = eval(dep.substring(dep.indexOf('['), dep.lastIndexOf(']') + 1))
if(moduleIds && moduleIds.length > 0){
deps = deps.concat(moduleIds)
}
}catch(e){}
})
}
if(exclude && (exclude instanceof Array)){
for(var i=0,l=deps.length; i<l; i++){
if(deps[i] && !inArray(exclude, deps[i])){
result.push(deps[i])
}
}
}else{
result = deps
}
return result
}
/*
* 生成相对baseUrl的模块ID
*/
function parseDep(filePath, baseUrl, moduleIdPrefix){
filePath = filePath.replace(/\\/g, '/')
baseUrl = baseUrl.replace(/\\/g, '/')
var dep = filePath.replace(baseUrl, '')
dep = dep.substring(0, dep.length - 3)
if (moduleIdPrefix) {
dep = moduleIdPrefix + dep
}
return dep
}
/*
* 读取依赖的文件内容
* param { String } moduleId 模块ID
* param { String } filePath 文件路径
* return { String } 模块内容
*/
function readModule(moduleId, filePath){
var content = ''
try{
if(filePath && fs.existsSync(filePath)){
content = fs.readFileSync(filePath).toString()
}else{
content = ''
}
return content
}catch(e){
gutil.log(gutil.colors.red(e))
return content
}
}
/*
* 获取模块ID对应的文件地址
* param { String } moduleId 模块ID
* param { Object } options 配置参数
* return { String } 文件地址
*/
function mergePath(moduleId, options){
return path.join(options.baseUrl, options.alias[moduleId])
}
/*
* 根据相对路径获取绝对路径
*/
function getRelativePath(filePath, dep, options){
var reg = /^\.{1,2}[^\.]+/, url = ''
if(reg.test(dep)){
url = path.resolve(filePath, '../', dep)
}else{
if(options.paths){
for(var key in options.paths){
if(dep.indexOf(key) === 0){
dep = dep.replace(key, options.paths[key])
}
}
}
url = path.resolve(options.baseUrl, dep)
}
if(!jsFileReg.test(url)){
url += '.js'
}
return url
}
/*
* 根据模块路径获取模块的ID
* param { String } filePath 文件路径
* param { Object } options 配置参数
* return { String } 模块ID
*/
function getModuleId(filePath, options){
var moduleId = null
for(var key in options.alias){
if(path.normalize(mergePath(key, options)) == path.normalize(filePath)){
moduleId = key
break
}
}
// //如果在alias中未找到文件路径对应的别名,那么取相对于baseUrl的路径作为ID
// if(moduleId == null){
// moduleId = parseDep(filePath, options.baseUrl)
// }
return moduleId
}
/*
* 给模块添加ID和转换模块ID为相对baseUrl的ID
* param { String } moduleId 模块ID
* param { String } filePath 模块文件路径
* param { Object } relativeDep 文件的所有依赖
* param { Object } options 配置对象
* return { Buffer } 文件
*/
function tranform(moduleId, filePath, relativeDep, options){
var content = ''
var parseDefineReg = /define\s*\(/
if (options.parseAllDefine) {
parseDefineReg = /define\s*\(/g
}
if(filePath && fs.existsSync(filePath)){
content = fs.readFileSync(filePath).toString()
if(moduleId){
content = content.replace(parseDefineReg, 'define("' + moduleId + '", ')
}
if(relativeDep){
for(var key in relativeDep){
if(key != relativeDep[key]){
content = content.replace(
new RegExp('require\\s*\\(\\s*[\'"]{1}'+key+'[\'"]{1}\\s*\\)', 'g'),
'require("' + relativeDep[key] + '")'
)
}
}
}
}
return new Buffer(content)
}
/*
* 合并依赖的模块到入口文件中
* param { Object } depStore 依赖存储对象
* param { String } filePath 入口文件路径
* param { String } moduleId 入口文件对应的模块ID
* param { Object } options 配置对象
* return { Buffer } 合并后的Buffer
*/
function concatDeps(depStore, filePath, moduleId, options){
var buffers = [], deps = null
deps = depStore.getAlias()
for(var key in deps){
if(fs.existsSync(deps[key])){
buffers.push(
tranform(key, deps[key], depStore.getRelative(), options),
new Buffer('\n')
)
}else{
depStore.addError(key, deps[key])
}
}
buffers.push(tranform(moduleId, filePath, depStore.getRelative(), options))
return Buffer.concat(buffers)
}
/*
* 判断数组中是否含有某个元素
* param { Array } array 数组
* param { String,Int } e 要判断的元素
* return { Boolean } 是否含有此元素
*/
function inArray(array, e){
for(i=0; i<array.length && array[i]!=e; i++);
return !(i==array.length)
}
/*
* 文件类型处理,如果没有js后缀,会自动添加 .js
*/
function parseFileType(alias){
for(var key in alias){
if(!jsFileReg.test(alias[key])){
alias[key] += '.js'
}
}
}
/*
* 打印构建日志
* param { filePath } filePath 构建的文件
* param { Object } depStore 依赖的模块
*/
function buildLog(filePath, depStore){
var deps = depStore.getAlias()
var error = depStore.getError()
gutil.log(gutil.colors.green('build ' + filePath + ':'))
for(var key in deps){
if(deps[key]){
console.log(' ' + key + ': [' + deps[key] + ']')
}else{
console.log(gutil.colors.red(' ' + key + ': [Not Found]'))
}
}
if(error){
for(var key in error){
console.log(gutil.colors.red(' ' + key + ': [' + error[key] + '] Not Found'))
}
}
}
/*
* 插件入口函数
* param { Object } options 配置参数,必须参数
*/
function combo(options){
options.supportRelative = options.supportRelative || false
options.parseAllDefine = options.parseAllDefine || false
if(!options){
gutil.log(gutil.colors.red(PLUGIN_NAME, 'The options param is required'))
}
if(!options.supportRelative && !options.alias){
gutil.log(gutil.colors.red(PLUGIN_NAME, 'The option alias is required when supportRelative is false'))
}
if(!options.baseUrl){
gutil.log(gutil.colors.red(PLUGIN_NAME, 'The option baseUrl is required'))
}
if(options.alias){
parseFileType(options.alias)
}else{
options.alias = {}
}
return through.obj(function(file, enc, callback){
if(file.isBuffer()){
var moduleId = getModuleId(file.path, options)
var depStore = new DepStore()
analyseDeps(file.contents.toString(), file.path, options, depStore)
file.contents = concatDeps(depStore, file.path, moduleId, options)
buildLog(file.path, depStore)
callback(null, file)
depStore.destroy()
}
else{
callback(null, file)
}
})
}
module.exports = combo