UNPKG

9.71 kBJavaScriptView Raw
1/**
2 * 这个文件是jdf的专用标签解析器
3 */
4"use strict";
5
6const path = require('path');
7const os = require('os');
8const fs = require('fs');
9const logger = require('jdf-log');
10const escapeStringRegexp = require('escape-string-regexp');
11const stripJsonComments = require('strip-json-comments');
12
13//lib自身组件
14const jdfUtils = require('jdf-utils');
15const $ = jdfUtils.base;
16const f = jdfUtils.file;
17const jdf = require('./jdf');
18const VFS = require('./VFS/VirtualFileSystem');
19const htmlAst = require('./htmlAst');
20
21let widgetParser = module.exports = {};
22
23/**
24 * 抽取并解析widget标签 parse {%widget name="widgetName" type="type1,type2" %}
25 * 解析widget标签,得到widgetInfo对象
26 * text: widget在html中的文本
27 * buildTag: widget 中需要编译的类型
28 * data: 从widgetName.json和widget标签里的data属性合并后的数据
29 * comment: widget标签里的comment属性值
30 * dirname: widget的绝对路径
31 * {
32 * text: '{%widget name="widgetName" type="widgetType" %}',
33 * name: 'widgetName',
34 * buildTag: '{tpl: false, vm: true, smarty: false, js: false, css: false}',
35 * data: '{}',
36 * comment: '',
37 * dirname: 'widget的绝对路径'
38 * }
39 * @param {html text} html html文件内容,用以抽取其中的widget
40 * @return {Array} html文件中的所有widget构建的widgetInfo对象列表
41 */
42widgetParser.parseWidget = function (html) {
43 let widgetList = [];
44 let tagList = htmlAst.findWidget(html);
45 tagList.forEach(tagText => {
46 let widgetInfo = {};
47
48 widgetInfo.text = tagText;
49
50 // 多行转成一行
51 tagText = tagText.replace(/[\r\n]/g, ' ');
52 // name {%widget name="{name}"%}
53 let result = $.reg.widget().exec(tagText);
54 widgetInfo.name = result === null ? '' : result[1];
55
56 // buildTag 允许那些文件编译进来
57 let buildTag = {
58 tpl:true,
59 vm:true,
60 smarty: true,
61 js:true,
62 css:true
63 };
64 result = $.reg.widgetType().exec(tagText);
65 let typeStr = result === null ? '' : result[1];
66 let isJM = (typeStr == 'jdj' || typeStr == 'jdm');
67 isJM && (widgetInfo.writeJMOnce = true);
68 widgetInfo.buildTag = checkbuildTag(typeStr, buildTag);
69
70 // data {%widget data="{data}"%}
71 result = $.reg.widgetData().exec(tagText);
72 widgetInfo.data = result === null ? '' : result[1];
73 widgetInfo.data = widgetInfo.data.replace(/\\'/g, "'");
74
75 // {%widget comment="{comment}"%}
76 result = $.reg.widgetComment().exec(tagText);
77 widgetInfo.comment = result === null ? '' : result[1];
78
79 // dirname widget的绝对路径
80 let originDir = VFS.originDir;
81 widgetInfo.dirname = path.join(originDir, jdf.config.widgetDir, widgetInfo.name);
82
83 widgetList.push(widgetInfo);
84 });
85
86 return widgetList;
87}
88
89widgetParser.getWidgetData = function (widgetInfo) {
90 let dataPath = path.normalize(widgetInfo.dirname + '/' + widgetInfo.name + $.is.dataSourceSuffix);
91 // widgetName.json ==> dataVfile
92 let dataVfile = VFS.queryFile(dataPath) || {}; // 性能:可以改从widgetVfiles从筛选
93 // 获取数据:widgetname.json和widget标签里的data属性
94 let widgetdata = {};
95 if (dataVfile.originPath) {
96 if (dataVfile.targetContent) {
97 widgetdata = JSON.parse(stripJsonComments(dataVfile.targetContent));
98 }
99 }
100
101 let data = {};
102 if (widgetInfo.data) {
103 data = JSON.parse(stripJsonComments(widgetInfo.data));
104 }
105 data = $.merageObj(widgetdata, data);
106
107 return data;
108}
109
110/**
111 * 解析widgetOutputName这个标签
112 * @param {[type]} html 原始html
113 * @return {[type]} 解析出来的信息
114 * { name: 'output4',
115 * text: '{%widgetOutputName="output4" type="js" %}',
116 * concatTag: { js: true, css: false } }
117 */
118widgetParser.parseOutputName = function (html) {
119 let info = {};
120
121 let nameReg = /{%widgetOutputName=["|'](.*?)["|'].*?%}/;
122 let typeReg = /{%widgetOutputName=["|'].*? type=["|'](.*?)["|'].*?%}/;
123 let tagList = htmlAst.findWidgetOutputName(html);
124
125 // 只允许一个widgetOutputName存在,忽略其他并提示
126 let tag = '';
127 if (tagList.length === 0) {
128 return info;
129 } else if (tagList.length > 1) {
130 logger.warn(`more than one widgetOutputName, just parse the first one: ${tagList[0]}`);
131 }
132 tag = tagList[0];
133
134 let result = nameReg.exec(tag);
135 let name = result[1];
136
137 info.name = name;
138 info.text = tag.replace(/^[\r\t\n]*|[\r\t\n]*$/g, '');
139
140 result = typeReg.exec(tag);
141 let typeStr = result === null ? '' : result[1];
142 let concatTag = {
143 js: true,
144 css: true
145 };
146 concatTag = checkbuildTag(typeStr, concatTag);
147 info.concatTag = concatTag;
148 return info;
149}
150
151/**
152 * 抽取某一widget允许引用那些类型
153 * @param {string} wgtType {%widget name="widget name" type="type1,type2" %} wgtType="type1,type2"
154 * @param {object} tagSet 默认允许引用类型的集合
155 * @return {object} 处理后允许引用类型的集合
156 */
157function checkbuildTag(wgtType, tagSet) {
158 var reg = /\s*,\s*/g,
159 arr = [],
160 str = wgtType,
161 noSupportType = [],
162 i, item, key;
163
164 if (!wgtType) {
165 return tagSet;
166 }
167
168 tagSet = tagSet || {};
169 arr = str.replace(reg, ',').trim().split(',');
170
171 for (key in tagSet) {
172 tagSet[key] = false;
173 }
174 for (i = 0; i < arr.length; i++) {
175 item = arr[i].toLowerCase();
176 if (item !== '' && tagSet[item] !== undefined) {
177 tagSet[item] = true;
178 } else {
179 noSupportType.push(arr[i]);
180 }
181 }
182 if (noSupportType.length > 0) {
183 logger.warn("widget type["+ noSupportType.join(',') +'] not supported.');
184 }
185
186 return tagSet;
187}
188
189/**
190 * widgetTree的节点数据结构
191 */
192widgetParser.TreeNode = function (widgetInfo, pNode) {
193 this.widgetInfo = widgetInfo
194 this.parent = pNode || null;
195 this.children = [];
196}
197
198/**
199 * 创建一个TreeNode,创建的同时检测是否存在循环嵌套
200 */
201widgetParser.createTreeNode = function (widgetInfo, pNode) {
202 // 检测是否有widget循环嵌套
203 let pointer = pNode;
204 let link = [widgetInfo.name];
205 while (pointer && pointer.parent) {
206 if (link.indexOf(pointer.widgetInfo.name) === -1) {
207 // 所有的上级都不存在该widget
208 link.push(pointer.widgetInfo.name);
209 pointer = pointer.parent;
210 }
211 else {
212 link.push(pointer.widgetInfo.name);
213 logger.error(`存在widget循环引用,引用链${link.join('->')}`);
214 logger.info(`jdf退出`);
215 process.exit(-1);
216 }
217 }
218
219 let node = new this.TreeNode(widgetInfo, pNode);
220
221 // 不去重,有可能一个sub widget里包含同一个子widget多次
222 pNode && pNode.children.push(node);
223
224 return node;
225}
226
227/**
228 * 根据根widget生成widgetTree
229 */
230widgetParser.generateWidgetTree = function (rootWidgetInfo) {
231 let rootNode = this.createTreeNode(rootWidgetInfo);
232
233 this.WidgetTreeDFS(rootNode);
234
235 return rootNode;
236}
237
238/**
239 * 深度搜索widgetTree
240 */
241widgetParser.WidgetTreeDFS = function (node) {
242 let widgetname = node.widgetInfo.name;
243 let widgetVfiles = VFS.queryDir(node.widgetInfo.dirname);
244 widgetVfiles.forEach(widgetVfile => {
245 let oBasename = path.basename(widgetVfile.originPath);
246 let dirname = path.dirname(widgetVfile.originPath);
247 let widgetNameReg = new RegExp(escapeStringRegexp(widgetname) + '\.\\w+$');
248 if (path.relative(dirname, node.widgetInfo.dirname)
249 || !widgetNameReg.test(oBasename)) {
250 // 1、相对路径不为空 2、文件名不和widget名一致
251 return;
252 }
253
254 if ($.is.vm(oBasename) || $.is.tpl(oBasename) || $.is.smarty(oBasename)) {
255 let nodeWidgetInfos = this.parseWidget(widgetVfile.targetContent);
256 nodeWidgetInfos.forEach(nodeWidgetInfo => {
257 let childnode = this.createTreeNode(nodeWidgetInfo, node);
258 // 深度遍历
259 this.WidgetTreeDFS(childnode);
260 })
261 }
262 });
263}
264
265/**
266 * 根据widgetInfo获取vmVfile
267 */
268widgetParser.findvmVfile = function (widgetInfo) {
269 let widgetVfiles = VFS.queryDir(widgetInfo.dirname);
270 for (let i = 0; i < widgetVfiles.length; i++) {
271 let widgetVfile = widgetVfiles[i];
272 let oBasename = path.basename(widgetVfile.originPath);
273 let dirname = path.dirname(widgetVfile.originPath);
274 let widgetNameReg = new RegExp(escapeStringRegexp(widgetInfo.name) + '\.\\w+$');
275 if (path.relative(dirname, widgetInfo.dirname)
276 || !widgetNameReg.test(oBasename)) {
277 // 1、相对路径不为空 2、文件名不和widget名一致
278 continue;
279 }
280
281 // 这个判断将widget type置为唯一
282 if ($.is.smarty(oBasename)) {
283 widgetInfo.buildTag.vm = false;
284 widgetInfo.buildTag.tpl= false;
285 widgetInfo.type = 'smarty';
286 return widgetVfile;
287 }
288 else if ($.is.tpl(oBasename)) {
289 widgetInfo.buildTag.vm = false;
290 widgetInfo.buildTag.smarty = false;
291 widgetInfo.type = 'tpl';
292 return widgetVfile;
293 }
294 else if ($.is.vm(oBasename)){
295 widgetInfo.buildTag.tpl = false;
296 widgetInfo.buildTag.smarty = false;
297 widgetInfo.type = 'vm';
298 return widgetVfile;
299 }
300 }
301
302 logger.verbose(`cannot find widget template ${widgetInfo.name}`);
303 return null;
304}