UNPKG

11.2 kBJavaScriptView Raw
1'use strict';
2/**
3 * @本地widget预览和发布至外端机器
4 */
5
6const path = require('path');
7const fs = require('fs');
8const readline = require('readline');
9const jdfUtils = require('jdf-utils');
10const $ = jdfUtils.base;
11const f = jdfUtils.file;
12const glob = require('glob');
13const logger = require('jdf-log');
14const jdf = require('./jdf');
15const VFS = require('./VFS/VirtualFileSystem');
16const openner = require("./server/openurl");
17const bs = require('./server/browserSyncServer');
18//exports
19const widget = module.exports;
20/**
21 * @widget path check
22 */
23widget.pathCheck = function(name) {
24 if (typeof name === 'undefined') {
25 return true;
26 }
27
28 if (!f.exists(name)) {
29 logger.error('widget path is not exists');
30 return true;
31 }
32
33 return false;
34}
35
36/**
37 * @本地预览页面templete
38 * @todo: 放在server上控制
39 */
40widget.templete = function(str, title) {
41 if (typeof str == 'undefined' || !str) {
42 str = '';
43 }
44
45 let css = `<link rel="stylesheet" href="//misc.360buyimg.com/jdf/1.0.0/unit/ui-base/1.0.0/ui-base.css" />`;
46
47 let js = `<script src="//misc.360buyimg.com/jdf/lib/jquery-1.6.4.js"></script>`;
48
49 return `<!DOCTYPE html>
50 <html>
51 <head>
52 <meta charset="utf-8" />
53 <title>${title}</title>
54 <link rel="icon" href="//jdf.jd.com/favicon.ico" mce_href="//jdf.jd.com/favicon.ico" type="image/x-icon">
55 ${css}
56 ${js}
57 </head>
58 <body>
59 ${str}
60 </body>
61 </html>`;
62}
63
64/**
65 * @本地预览widget
66 * @example jdf widget --preview widget/header
67 */
68widget.preview = function(name) {
69 let previewPage = 'previewWidget.html';
70 let widgets = [];
71 if (name === true) {
72 widgets = fs.readdirSync(path.resolve(VFS.originDir, jdf.config.widgetDir));
73 }
74 else {
75 widgets = name.split(',');
76 }
77
78 let widgettpl = '';
79 widgets.forEach(widgetName => {
80 widgettpl += `
81<h1 style="padding: 10px;">Widget: ${widgetName}</h1>
82{%widget name="${widgetName}" %}`;
83 });
84
85 let previewContent = this.templete(widgettpl, 'Preview Widgets');
86 let previewPath = path.resolve(VFS.originDir, jdf.config.htmlDir, previewPage);
87
88 VFS.addFile(previewPath, previewContent);
89
90 jdf.build({}, function (port) {
91 openner.open(`http://localhost:${port}/${jdf.config.htmlDir}/${previewPage}`);
92 });
93}
94
95/**
96 * @下载widget到当前项目文件夹
97 * @example jdf widget -install widget/name
98 * @time 2014-3-14 14:50:29
99 */
100widget.install = function(name, force) {
101 force = force || false;
102 const ftp = createFtp();
103
104 function getWidgetVersion(widgetName) {
105 logger.verbose(`get widget version ${widgetName}`);
106 const target = path.resolve(process.cwd(), 'widget', widgetName);
107 return ftp.list($.pathJoin(jdf.config.widgetServerDir, 'widget', widgetName))
108 .then(data => {
109 let version = '';
110 data.sort().forEach(item => version = item.name);
111
112 if (!parseInt(version, 10)) {
113 version = '';
114 }
115
116 if (f.exists(target) && !force) {
117 const err = new Error(`widget ${name} exists in current project`);
118 err.pin = 66997304;
119 throw err;
120 } else {
121 return version;
122 }
123 })
124 .catch(error => {
125 if(error.hasOwnProperty('pin')) {
126 throw error;
127 }
128 else {
129 throw new Error(`widget ${name} doesn't exists on remote server`);
130 }
131 })
132 }
133
134 /**
135 * 先遍历对应name下面文件夹,看是否有版本号
136 * 有版本号,就下载最后一个版本号里面的文件,没有版本号的,就直接下载widgetName下面的所有文件
137 * 如果发现这个widget还有依赖的其他widget,则都下载
138 * todo 需要解决相同依赖的去冗余,现在会把所有依赖都下载,如果一个模块被依赖了多次,那么就会有多次下载
139 * @param widgetName
140 * @returns {*|Function|any|Promise.<TResult>|Promise}
141 */
142 function downloadWidget(widgetName) {
143 return getWidgetVersion(widgetName)
144 .then(version => {
145 logger.verbose(`download widget content ${widgetName}`);
146 const source = $.pathJoin(jdf.config.widgetServerDir, 'widget', widgetName, version);
147 const target = path.resolve(process.cwd(), 'widget', widgetName);
148 return ftp.list(source)
149 .then(files => {
150 f.mkdir(target);
151 return files.filter(item => item.type === 'file')
152 .reduce((p, n) => {
153 const sourcePut = $.pathJoin(source, n.name);
154 const targetPut = path.join(target, n.name);
155 return p.then(() => {
156 return ftp.get(sourcePut, targetPut);
157 })
158 }, Promise.resolve())
159 .then(function() {
160 return downDependencies(widgetName);
161 })
162 .then(function() {
163 return widgetName + '/' + version;
164 });
165 });
166 });
167
168 }
169
170 /**
171 * 当一个widget安装完毕后,查看是否有依赖,有的话就下载依赖
172 * @param name
173 * @returns {Promise.<T>}
174 */
175 function downDependencies(name) {
176 const target = path.resolve(process.cwd(), 'widget', name);
177 let chain = Promise.resolve();
178 f.readJSON(path.resolve(target, 'component.json'), function(data) {
179 logger.verbose(`download widget dependencies ${name}, %j`, data.dependencies);
180 for(let item in data.dependencies) {
181 chain = chain.then(function(){return downloadWidget(item);})
182 }
183 });
184 return chain;
185 }
186 downloadWidget(name)
187 .then(function(v) {
188 ftp.client.end();
189 logger.info(`widget [${v}] install done`);
190 })
191 .catch(error => {
192 ftp.client.end();
193 logger.error(error);
194 });
195}
196
197/**
198 * @发布widget至server
199 * @time 2014-3-14 14:50:29
200 * @example jdf widget -publish name
201 * @todo 增加name验证和版本控制
202 */
203
204widget.publish = function(name, force) {
205 if (widget.pathCheck(name)) {
206 return;
207 }
208
209 const publishDir = $.pathJoin(jdf.config.widgetServerDir);
210 const widgetHomeDir = name;
211 force = force || false;
212
213
214 const ftp = createFtp();
215 const vtarget = $.pathJoin(publishDir, name);
216 const files = glob.sync('**', {
217 cwd: widgetHomeDir
218 });
219
220 ftp.mkdir($.pathJoin(publishDir, name))
221 .then(function() {
222 return ftp.mkdir(vtarget);
223 })
224 .then(function() {
225 return ftp
226 .connect()
227 .then(function() {
228 return files.reduce((p, n) => {
229 return p.then(function() {
230 const source = path.resolve(widgetHomeDir, n);
231 const target = $.pathJoin(vtarget, n);
232 const stat = fs.statSync(source);
233 if(stat.isFile()) {
234 return ftp.put(source, target);
235 }
236 else {
237 return ftp.mkdir(target);
238 }
239 })
240 }, Promise.resolve())
241 })
242 })
243 .then(function() {
244 ftp.client.end();
245 logger.info(`widget [${name}] publish success`);
246 })
247 .catch(error => {
248 logger.error(error);
249 });
250}
251
252/**
253 * @取得所有widget的列表
254 * @time 2014-6-23 11:04:00
255 */
256widget.list = function() {
257 const publishDir = $.pathJoin(jdf.config.widgetServerDir, 'widget');
258 const ftp = createFtp();
259
260 logger.profile('fetch remote widget');
261 ftp.list(publishDir)
262 .then(data => {
263 if (data) {
264 ftp.client.end();
265 data.forEach(function(item) {
266 console.log(item.name);
267 });
268
269 if(data.length === 0) {
270 logger.info('there are no widgets in remote');
271 }
272
273 logger.profile('fetch remote widget');
274 }
275 })
276 .catch(error => {
277 logger.error(error);
278 logger.profile('fetch remote widget');
279 })
280}
281
282/**
283 * @widget自动生成目录
284 * @time 2014-6-23 11:04:00
285 */
286widget.create = function(name, isSmarty) {
287 const widgetDir = 'widget/' + name;
288 if (f.exists(widgetDir)) {
289 logger.warn(`widget [${name}] is exist`);
290 return;
291 }
292 console.log(`if you want to create it, type 'y', else 'n'`);
293
294 process.stdout.isTTY = false;
295 process.stderr.isTTY = false;
296
297 var rl = readline.createInterface({
298 input: process.stdin,
299 output: process.stdout
300 });
301
302 var start = 0;
303 var answers = [];
304 var questions = ['vm', 'js', 'scss', 'json'];
305 if (isSmarty) {
306 questions[0] = 'smarty';
307 }
308
309 function next(q) {
310 if (start == questions.length) {
311 rl.close();
312 promptComplete(answers);
313 }
314 else {
315 rl.question(q + ': (y)', function(answer) {
316 if (answer === '') {
317 answer = 'y';
318 }
319 if (answer != 'y' && answer != 'n') {
320 console.log("answer choice 'y' or 'n'");
321 next(questions[start]);
322 }
323 else {
324 answers.push(answer);
325 next(questions[++start]);
326 }
327 });
328 }
329 }
330
331 function promptComplete(ans) {
332 //给compoent.json写入默认的内容
333 var componentDefault = {name: name, version: '1.0.0', dependencies: {}};
334 var createFileObj = {
335 'component.json' : JSON.stringify(componentDefault, null, '\t')
336 };
337 ans.forEach(function(item, index) {
338 if (item === 'y') {
339 createFileObj[name + '.' + questions[index]] = ''
340 }
341 });
342
343 f.mkdir(widgetDir);
344 for(var p in createFileObj) {
345 f.write(widgetDir + '/' + p, createFileObj[p]);
346 }
347
348 process.stdout.isTTY = true;
349 process.stderr.isTTY = true;
350 logger.info(`widget [${name}] create done`);
351 process.exit(0);
352 }
353
354 next(questions[start]);
355}
356
357function createFtp() {
358 return require('jdf-upload/src/baseUploader.js').create('ftp', {
359 host: jdf.config.host || jdf.config.upload.host,
360 user: jdf.config.user || jdf.config.upload.user,
361 password: jdf.config.password || jdf.config.upload.password,
362 });
363}