UNPKG

12.3 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3const fs = require("fs");
4const path = require("path");
5
6const argv = process.argv;
7const action = argv[2];
8const name = argv.length === 4? argv[3] : argv[4];
9const repo = argv.length === 4? undefined : argv[4];
10
11switch (action) {
12 case "create":
13 createComponent(name, repo);
14 break;
15 default:
16 console.error(`There is no '${action}' command. Have you misspell?`);
17}
18
19
20function createComponent(componentName, repo) {
21 const componentPath = `components/${componentName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}`;
22 componentName = componentName.split("/").map(s => s[0].toUpperCase() + s.substring(1)).join("");
23
24 if(fs.existsSync(componentPath)) {
25 console.error(`Folder '${path.resolve(componentPath)}' already exists.`);
26 return 1;
27 }
28
29 fs.mkdirSync(componentPath, { recursive: true });
30
31 if(componentName === "Hello") {
32 createIndex();
33 createConfig();
34 addStartScriptToPackageJson();
35 }
36
37 createComponentDeclaration(componentName, componentPath);
38
39 if(repo) {
40 createControllerDeclarationWithRepository(componentName, componentPath);
41 } else {
42 createControllerDeclaration(componentName, componentPath);
43 }
44
45 if(repo) {
46 createFactoryDeclarationWithRepository(componentName, componentPath);
47 } else {
48 createFactoryDeclaration(componentName, componentPath);
49 }
50 addComponentToProject(componentName, componentPath);
51
52 console.log(`Component '${componentName}' created in ${path.resolve(componentPath)}.`);
53}
54
55function createIndex() {
56 if(fs.existsSync(path.resolve("index.js"))) {
57 return;
58 }
59
60 const projectIndex =
61 `const Server = require("@dominion-framework/dominion");
62
63Server.addComponent(require("@dominion-framework/dominion/components/cors"));
64Server.addComponent(require("@dominion-framework/dominion/components/logging"));
65
66Server.addComponent(require("./components/hello"));
67
68Server.start();
69
70Server.openApiToFile();
71`;
72 fs.writeFileSync(path.resolve("index.js"), projectIndex, 'utf8');
73}
74
75function addStartScriptToPackageJson() {
76 if(!fs.existsSync(path.resolve("package.json"))) {
77 return;
78 }
79 const packageJson =JSON.parse(fs.readFileSync(path.resolve("package.json")).toString());
80 if(!packageJson.scripts) {
81 packageJson.scripts = {};
82 }
83 packageJson.scripts.start = "node index.js";
84 fs.writeFileSync(path.resolve("package.json"), JSON.stringify(packageJson, null, 2), 'utf8');
85}
86
87function createConfig() {
88 if(fs.existsSync(path.resolve("config"))) {
89 return;
90 }
91
92 const indexConfig =
93 `const config = require("./config.dev");
94
95module.exports = config;
96`;
97
98 const projectConfig =
99 `module.exports = {
100 server: {
101 protocol: "http",
102 host: "localhost",
103 port: 7042,
104 // No slash in the end
105 url: "http://localhost:7042"
106 },
107
108 router: {
109 // e.g. api/v2/
110 urlPrefix: "",
111 // e.g. "[a-f\\d]{8}-[a-f\\d]{4}-4[a-f\\d]{3}-[89ab][a-f\\d]{3}-[a-f\\d]{12}"
112 primaryKeyPattern: "\\\\d+"
113 },
114
115 cors: {
116 // e.g. * | ["example.com"] | () => {} (synchronous callback function with Message context returning array of allowed origins)
117 origin: ["http://localhost:7042", "https://editor.swagger.io"],
118 methods: ["OPTIONS", "GET", "POST", "PUT", "DELETE"],
119 headers: ["Content-Type", "Set-Cookies", "Authorization"],
120 credentials: false,
121 maxAge: 5 /* seconds */
122 }
123};
124`;
125 fs.mkdirSync(path.resolve("config"));
126 fs.writeFileSync(path.resolve("config/config.dev.js"), projectConfig, 'utf8');
127 fs.writeFileSync(path.resolve("config/index.js"), indexConfig, 'utf8');
128
129}
130
131function createComponentDeclaration(componentName, componentPath) {
132 const componentDeclaration =
133 `module.exports = {
134 factories: [
135 __dirname + '/factory',
136 ],
137 controllers: [
138 __dirname + '/controller',
139 ],
140 requestInterceptors: [],
141 responseInterceptors: [],
142 bootstrap: []
143};
144`;
145 fs.writeFileSync(path.resolve(componentPath, "index.js"), componentDeclaration, 'utf8');
146}
147
148function createControllerDeclaration(componentName, componentPath) {
149 const controllerDeclaration =
150 `const Factories = require("@dominion-framework/dominion/core/factories");
151
152const ${componentName}Factory = Factories("${componentName}");
153
154
155module.exports = {
156
157 factory: ${componentName}Factory,
158
159 GET: [
160 //${componentName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}?offset=0&limit=10
161 function (offset = 0, limit = 10) {
162 // @summary: ${componentName === "Hello"? "Demo endpoint with optional arguments" : ""}
163 // @description: ${componentName === "Hello"? `Open http://localhost:7042/${componentName.toLowerCase()}?offset=0&limit=10 to see results` : ""}
164
165 ${componentName === "Hello"?
166 "return HelloFactory.new({message: `Welcome to Dominion! Nice to meet you! [${offset}, ${limit}]` });"
167 :
168 "return ;"
169 }
170 }${componentName === "Hello" ?
171 `,
172
173 //${componentName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}/42
174 function (${componentName[0].toLowerCase() + componentName.substring(1)}Id) {
175 // @summary: '@summary' and '@description' annotations will be used for generating OpenApi docs
176 // @description: Open http://localhost:7042/${componentName.toLowerCase()}/42 to see results
177
178 return ${componentName}Factory.new({
179 id: +${componentName.toLowerCase()}Id,
180 email: "my.name@example.com"
181 });
182 }`
183 :
184 ""}
185 ],
186
187 POST: [
188 //${componentName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}
189 function (${componentName === "Hello" ? "id" : ""}) {
190 ${componentName === "Hello"? "// @path: custom\\/url\\/(\\d+)" : ""}
191
192 return ;
193 }
194 ],
195
196 PUT: [
197 //${componentName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}/42
198 function (${componentName[0].toLowerCase() + componentName.substring(1)}Id) {
199
200 return ;
201 }
202 ],
203
204 DELETE: [
205 //${componentName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}/42
206 function (${componentName[0].toLowerCase() + componentName.substring(1)}Id) {
207
208 return ;
209 }
210 ]
211
212};
213`;
214 fs.writeFileSync(path.resolve(componentPath, "controller.js"), controllerDeclaration, 'utf8');
215}
216
217function createControllerDeclarationWithRepository(componentName, componentPath) {
218 const componentVarName = `${componentName[0].toLowerCase() + componentName.substring(1)}`;
219 const controllerDeclaration =
220 `const Factories = require("@dominion-framework/dominion/core/factories");
221
222const ${componentName}Factory = Factories("${componentName}");
223
224
225module.exports = {
226
227 factory: ${componentName}Factory,
228
229 GET: [
230 //${componentName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}?offset=0&limit=10&order=+id
231 function (offset = 0, limit = 10, order = "+id") {
232 return ${componentName}Factory.find({}, offset, limit, order);
233 },
234
235 //${componentName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}/42
236 function (${componentVarName}Id) {
237 return ${componentName}Factory.get({ id: ${componentVarName}Id });
238 }
239 ],
240
241 POST: [
242 //${componentName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}
243 function () {
244 return ${componentName}Factory.new(this.request.body)
245 .then(${componentVarName} => ${componentVarName}.save())
246 .then(${componentVarName} => ${componentName}Factory.get({ id: ${componentVarName}.id }));
247 }
248 ],
249
250 PUT: [
251 //${componentName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}/42
252 function (${componentVarName}Id) {
253 return ${componentName}Factory.get({ id: ${componentVarName}Id })
254 .then(${componentVarName} => ${componentVarName}.populate(this.request.body))
255 .then(${componentVarName} => ${componentVarName}.save());
256 }
257 ],
258
259 DELETE: [
260 //${componentName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}/42
261 function (${componentVarName}Id) {
262 return ${componentName}Factory.get({ id: ${componentVarName}Id })
263 .then(${componentVarName} => Promise.all([${componentVarName},${componentVarName}.remove()]))
264 .then(([${componentVarName}, result]) => {
265 if (result.affectedRows) {
266 this.response.status = this.response.STATUSES._204_NoContent;
267 }
268 return ${componentVarName};
269 });
270 }
271 ]
272
273};
274`;
275 fs.writeFileSync(path.resolve(componentPath, "controller.js"), controllerDeclaration, 'utf8');
276}
277
278function createFactoryDeclaration(componentName, componentPath) {
279 const factoriesDeclaration =
280 `const Property = require("@dominion-framework/dominion/core/property");
281
282
283module.exports = {
284
285 name: "${componentName}",
286
287 properties: {
288 id: Property.id(),
289 message: Property.string().required(),
290 guid: Property.string().example("123e4567-e89b-12d3-a456-426655440000"),
291 email: Property.string().example("my.name@example.com"),
292 state: Property.enum(["open", "close"]),
293 parentId: Property.model("${componentName}").private(),
294 creationTime: Property.date().private(),
295 modificationTime: Property.date().private()
296 },
297
298 factory: {
299
300 },
301
302 instance: {
303
304 }
305};
306`;
307 fs.writeFileSync(path.resolve(componentPath, "factory.js"), factoriesDeclaration, 'utf8');
308}
309
310function createFactoryDeclarationWithRepository(componentName, componentPath) {
311 const factoriesDeclaration =
312 `const Property = require("@dominion-framework/dominion/core/property");
313const ${componentName}Repository = require("./repository");
314
315
316module.exports = {
317
318 name: "${componentName}",
319
320 repository: ${componentName}Repository,
321
322 properties: {
323 id: Property.id(),
324 message: Property.string().required(),
325 guid: Property.string().example("123e4567-e89b-12d3-a456-426655440000"),
326 email: Property.string().example("my.name@example.com"),
327 state: Property.enum(["open", "close"]),
328 parentId: Property.model("${componentName}").private(),
329 creationTime: Property.date().private(),
330 modificationTime: Property.date().private()
331 },
332
333 factory: {
334
335 },
336
337 instance: {
338
339 }
340};
341`;
342 const repositoryDeclaration =
343 `const Repositories = require("@dominion-framework/repository-mysql");
344
345
346module.exports = Repositories.create('${componentName.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase()}', {
347
348});
349`;
350
351 fs.writeFileSync(path.resolve(componentPath, "factory.js"), factoriesDeclaration, 'utf8');
352 fs.writeFileSync(path.resolve(componentPath, "repository.js"), repositoryDeclaration, 'utf8');
353}
354
355function addComponentToProject(componentName, componentPath) {
356 if(!fs.existsSync(path.resolve("index.js"))) {
357 console.error("File `index.js` in project's root doesn't exist. Can't include created component.");
358 return;
359 }
360
361 const indexFile = fs.readFileSync(path.resolve("index.js")).toString().split("\n");
362 const position = indexFile.reverse().findIndex(line => line.indexOf("Server.addComponent") > -1);
363 indexFile.splice(position, 0, `Server.addComponent(require("./${componentPath}"));`);
364 fs.writeFileSync(path.resolve("index.js"), indexFile.reverse().join("\n"));
365}