UNPKG

7.89 kBJavaScriptView Raw
1'use strict';
2
3const path = require('path');
4const fs = require('fs');
5const url = require('url');
6const _ = require('lodash');
7const express = require('express');
8const bodyParser = require('body-parser');
9const Watchpack = require('watchpack');
10const prettier = require('prettier');
11const rekitCore = require('rekit-core');
12const fetchProjectData = require('./api/fetchProjectData');
13const getFileContent = require('./api/getFileContent');
14const saveFile = require('./api/saveFile');
15const runBuild = require('./api/runBuild');
16const runTest = require('./api/runTest');
17
18const utils = rekitCore.utils;
19
20// rekitCore.utils.setProjectRoot('/Users/i305656/workspace/rekit');
21rekitCore.plugin.loadPlugins(rekitCore);
22
23let lastProjectData = null;
24module.exports = function() { // eslint-disable-line
25 let io = null;
26
27 const bgProcesses = {};
28
29 const wp = new Watchpack({
30 // options:
31 aggregateTimeout: 1000,
32 // fire "aggregated" event when after a change for 1000ms no additonal change occured
33 // aggregated defaults to undefined, which doesn't fire an "aggregated" event
34
35 poll: false,
36 // poll: true - use polling with the default interval
37 // poll: 10000 - use polling with an interval of 10s
38 // poll defaults to undefined, which prefer native watching methods
39 // Note: enable polling when watching on a network path
40
41 ignored: /node_modules/,
42 // anymatch-compatible definition of files/paths to be ignored
43 // see https://github.com/paulmillr/chokidar#path-filtering
44 });
45
46 // Watchpack.prototype.watch(string[] files, string[] directories, [number startTime])
47 // Watch src files change only
48 wp.watch([], [path.join(rekitCore.utils.getProjectRoot(), 'src'), path.join(rekitCore.utils.getProjectRoot(), 'coverage')], Date.now() - 10);
49 // starts watching these files and directories
50 // calling this again will override the files and directories
51
52 wp.on('aggregated', (changes) => {
53 // changes: an array of all changed files
54 rekitCore.vio.reset();
55 // const newProjectData = fetchProjectData();
56 // if (_.isEqual(newProjectData, lastProjectData)) return;
57 // lastProjectData = newProjectData;
58 if (io) io.emit('fileChanged', changes);
59 });
60
61 const rootPath = '/rekit';
62
63 function execCmd(req, res) {
64 try {
65 const args = req.body;
66 rekitCore.handleCommand(args);
67 const logs = rekitCore.vio.flush();
68 rekitCore.vio.reset();
69 res.write(JSON.stringify({
70 args,
71 logs,
72 }));
73 res.end();
74 } catch (e) {
75 res.statusCode = 500;
76 res.write(e.toString());
77 res.end();
78 }
79 }
80
81 function setupSocketIo(server) {
82 io = require('socket.io')(server);
83
84 io.on('connection', (client) => {
85 client.on('disconnect', () => {
86 console.log('socket disconnected');
87 });
88 });
89 }
90
91 function reply403(res) {
92 res.statusCode = 403;
93 res.write('Forbidden: Rekit portal is running on readonly mode.');
94 res.end();
95 }
96
97 function rekitMiddleware(server, app, args) {
98 args = args || {};
99 setupSocketIo(server);
100 const prjRoot = rekitCore.utils.getProjectRoot();
101 app.use('/coverage', express.static(utils.joinPath(prjRoot, 'coverage'), { fallthrough: false }));
102 app.use(bodyParser.json());
103 app.use(bodyParser.urlencoded({ extended: true }));
104
105 return (req, res, next) => {
106 const urlObject = url.parse(req.originalUrl);
107 const p = urlObject.pathname.replace(rootPath, '');
108
109 try {
110 switch (p) {
111 case '/api/project-data': {
112 lastProjectData = fetchProjectData();
113 res.write(JSON.stringify(Object.assign({
114 bgProcesses,
115 }, lastProjectData)));
116 res.end();
117 break;
118 }
119 case '/api/format-code': {
120 const content = req.body.content;
121 const ext = req.body.ext;
122 const options = {
123 singleQuote: true,
124 trailingComma: 'es5',
125 printWidth: 120,
126 };
127 if (ext === 'less' || ext === 'scss') options.parser = ext;
128 try {
129 res.write(JSON.stringify({ content: prettier.format(content, options) }));
130 } catch (err) {
131 res.write(JSON.stringify({ content, error: err }));
132 }
133 res.end();
134 break;
135 }
136 case '/api/save-file': {
137 if (args.readonly) { reply403(res); break; }
138 const absPath = utils.joinPath(prjRoot, req.body.file);
139 if (!_.startsWith(absPath, prjRoot)) {
140 // prevent ../.. in req.query.file
141 res.statusCode = 403;
142 res.write('Forbidden: not allowed to access file out of the project.');
143 res.end();
144 break;
145 }
146
147 if (!fs.existsSync(absPath)) {
148 res.statusCode = 404;
149 res.write(JSON.stringify({ error: 'Not found.' }));
150 res.end();
151 } else {
152 try {
153 saveFile(absPath, req.body.content);
154 res.write(JSON.stringify({ success: true }));
155 } catch (err) {
156 res.write(JSON.stringify({ error: err }));
157 }
158 res.end();
159 }
160 break;
161 }
162 case '/api/file-content': {
163 const absPath = utils.joinPath(prjRoot, req.query.file);
164 if (!_.startsWith(absPath, prjRoot)) {
165 // prevent ../.. in req.query.file
166 res.statusCode = 403;
167 res.write('Forbidden: not allowed to access file out of the project.');
168 res.end();
169 break;
170 }
171
172 if (!fs.existsSync(absPath)) {
173 res.statusCode = 404;
174 res.write(JSON.stringify({ error: 'Not found.' }));
175 res.end();
176 } else {
177 res.write(JSON.stringify({ content: getFileContent(absPath) }));
178 res.end();
179 }
180 break;
181 }
182 case '/api/exec-cmd':
183 if (args.readonly) { reply403(res); break; }
184 execCmd(req, res);
185 break;
186 case '/api/run-build':
187 if (args.readonly) { reply403(res); break; }
188 if (bgProcesses.runningBuild) {
189 res.statusCode = 500;
190 res.write(JSON.stringify({ error: 'Build process is running...' }));
191 res.end();
192 } else {
193 bgProcesses.runningBuild = true;
194 runBuild(io).then(() => {
195 bgProcesses.runningBuild = false;
196 }).catch(() => {
197 bgProcesses.runningBuild = false;
198 });
199 res.write('{}');
200 res.end();
201 }
202 break;
203 case '/api/run-test':
204 if (args.readonly) { reply403(res); break; }
205 if (bgProcesses.runningTest) {
206 res.statusCode = 500;
207 res.write(JSON.stringify({ error: 'Test process is running...' }));
208 res.end();
209 } else {
210 bgProcesses.runningTest = true;
211 runTest(io, req.body.testFile || '').then(() => {
212 bgProcesses.runningTest = false;
213 }).catch(() => {
214 bgProcesses.runningTest = false;
215 });
216 res.write('{}');
217 res.end();
218 }
219 break;
220 default: {
221 if (/^\/api\//.test(p)) {
222 res.statusCode = 404;
223 res.write(JSON.stringify({ error: `API not found: ${p}` }));
224 res.end();
225 } else {
226 next();
227 }
228 break;
229 }
230 }
231 } catch (e) {
232 res.statusCode = 500;
233 res.write(e.toString());
234 res.end();
235 }
236 };
237 }
238 return rekitMiddleware;
239};