1 | #!/usr/bin/env node
|
2 |
|
3 | const fs = require("fs");
|
4 | const path = require("path");
|
5 |
|
6 | const argv = process.argv;
|
7 | const action = argv[2];
|
8 | const name = argv.length === 4? argv[3] : argv[4];
|
9 | const repo = argv.length === 4? undefined : argv[4];
|
10 |
|
11 | switch (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 |
|
20 | function 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 |
|
55 | function createIndex() {
|
56 | if(fs.existsSync(path.resolve("index.js"))) {
|
57 | return;
|
58 | }
|
59 |
|
60 | const projectIndex =
|
61 | `const Server = require("@dominion-framework/dominion");
|
62 |
|
63 | Server.addComponent(require("@dominion-framework/dominion/components/cors"));
|
64 | Server.addComponent(require("@dominion-framework/dominion/components/logging"));
|
65 |
|
66 | Server.addComponent(require("./components/hello"));
|
67 |
|
68 | Server.start();
|
69 |
|
70 | Server.openApiToFile();
|
71 | `;
|
72 | fs.writeFileSync(path.resolve("index.js"), projectIndex, 'utf8');
|
73 | }
|
74 |
|
75 | function 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 |
|
87 | function createConfig() {
|
88 | if(fs.existsSync(path.resolve("config"))) {
|
89 | return;
|
90 | }
|
91 |
|
92 | const indexConfig =
|
93 | `const config = require("./config.dev");
|
94 |
|
95 | module.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 |
|
131 | function 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 |
|
148 | function createControllerDeclaration(componentName, componentPath) {
|
149 | const controllerDeclaration =
|
150 | `const Factories = require("@dominion-framework/dominion/core/factories");
|
151 |
|
152 | const ${componentName}Factory = Factories("${componentName}");
|
153 |
|
154 |
|
155 | module.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 |
|
217 | function 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 |
|
222 | const ${componentName}Factory = Factories("${componentName}");
|
223 |
|
224 |
|
225 | module.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 |
|
278 | function createFactoryDeclaration(componentName, componentPath) {
|
279 | const factoriesDeclaration =
|
280 | `const Property = require("@dominion-framework/dominion/core/property");
|
281 |
|
282 |
|
283 | module.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 |
|
310 | function createFactoryDeclarationWithRepository(componentName, componentPath) {
|
311 | const factoriesDeclaration =
|
312 | `const Property = require("@dominion-framework/dominion/core/property");
|
313 | const ${componentName}Repository = require("./repository");
|
314 |
|
315 |
|
316 | module.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 |
|
346 | module.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 |
|
355 | function 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 | }
|