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 | createControllerDeclaration(componentName, componentPath);
|
39 | if(repo) {
|
40 | createFactoryDeclarationWithRepository(componentName, componentPath);
|
41 | } else {
|
42 | createFactoryDeclaration(componentName, componentPath);
|
43 | }
|
44 | addComponentToProject(componentName, componentPath);
|
45 |
|
46 | console.log(`Component '${componentName}' created in ${path.resolve(componentPath)}.`);
|
47 | }
|
48 |
|
49 | function createIndex() {
|
50 | if(fs.existsSync(path.resolve("index.js"))) {
|
51 | return;
|
52 | }
|
53 |
|
54 | const projectIndex =
|
55 | `const Server = require("@dominion-framework/dominion");
|
56 |
|
57 | Server.addComponent(require("@dominion-framework/dominion/components/cors"));
|
58 | Server.addComponent(require("@dominion-framework/dominion/components/logging"));
|
59 |
|
60 | Server.addComponent(require("./components/hello"));
|
61 |
|
62 | Server.start();
|
63 |
|
64 | Server.openApiToFile();
|
65 | `;
|
66 | fs.writeFileSync(path.resolve("index.js"), projectIndex, 'utf8');
|
67 | }
|
68 |
|
69 | function addStartScriptToPackageJson() {
|
70 | if(!fs.existsSync(path.resolve("package.json"))) {
|
71 | return;
|
72 | }
|
73 | const packageJson =JSON.parse(fs.readFileSync(path.resolve("package.json")).toString());
|
74 | if(!packageJson.scripts) {
|
75 | packageJson.scripts = {};
|
76 | }
|
77 | packageJson.scripts.start = "node index.js";
|
78 | fs.writeFileSync(path.resolve("package.json"), JSON.stringify(packageJson, null, 2), 'utf8');
|
79 | }
|
80 |
|
81 | function createConfig() {
|
82 | if(fs.existsSync(path.resolve("config"))) {
|
83 | return;
|
84 | }
|
85 |
|
86 | const indexConfig =
|
87 | `const config = require("./config.dev");
|
88 |
|
89 | module.exports = config;
|
90 | `;
|
91 |
|
92 | const projectConfig =
|
93 | `module.exports = {
|
94 | server: {
|
95 | protocol: "http",
|
96 | host: "localhost",
|
97 | port: 7042,
|
98 | // No slash in the end
|
99 | url: "http://localhost:7042"
|
100 | },
|
101 |
|
102 | router: {
|
103 | // e.g. api/v2/
|
104 | urlPrefix: "",
|
105 | // e.g. "[a-f\\d]{8}-[a-f\\d]{4}-4[a-f\\d]{3}-[89ab][a-f\\d]{3}-[a-f\\d]{12}"
|
106 | primaryKeyPattern: "\\\\d+"
|
107 | },
|
108 |
|
109 | cors: {
|
110 | // e.g. * | ["example.com"] | () => {} (synchronous callback function with Message context returning array of allowed origins)
|
111 | origin: ["http://localhost:7042", "https://editor.swagger.io"],
|
112 | methods: ["OPTIONS", "GET", "POST", "PUT", "DELETE"],
|
113 | headers: ["Content-Type", "Set-Cookies", "Authorization"],
|
114 | credentials: false,
|
115 | maxAge: 5 /* seconds */
|
116 | }
|
117 | };
|
118 | `;
|
119 | fs.mkdirSync(path.resolve("config"));
|
120 | fs.writeFileSync(path.resolve("config/config.dev.js"), projectConfig, 'utf8');
|
121 | fs.writeFileSync(path.resolve("config/index.js"), indexConfig, 'utf8');
|
122 |
|
123 | }
|
124 |
|
125 | function createComponentDeclaration(componentName, componentPath) {
|
126 | const componentDeclaration =
|
127 | `module.exports = {
|
128 | factories: [
|
129 | __dirname + '/factory',
|
130 | ],
|
131 | controllers: [
|
132 | __dirname + '/controller',
|
133 | ],
|
134 | requestInterceptors: [],
|
135 | responseInterceptors: [],
|
136 | bootstrap: []
|
137 | };
|
138 | `;
|
139 | fs.writeFileSync(path.resolve(componentPath, "index.js"), componentDeclaration, 'utf8');
|
140 | }
|
141 |
|
142 | function createControllerDeclaration(componentName, componentPath) {
|
143 | const controllerDeclaration =
|
144 | `const Factories = require("@dominion-framework/dominion/core/factories");
|
145 |
|
146 | const ${componentName}Factory = Factories("${componentName}");
|
147 |
|
148 |
|
149 | module.exports = {
|
150 |
|
151 | factory: ${componentName}Factory,
|
152 |
|
153 | GET: [
|
154 | //${componentName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}?offset=0&limit=10
|
155 | function (offset = 0, limit = 10) {
|
156 | // @summary: ${componentName === "Hello"? "Demo endpoint with optional arguments" : ""}
|
157 | // @description: ${componentName === "Hello"? `Open http://localhost:7042/${componentName.toLowerCase()}?offset=0&limit=10 to see results` : ""}
|
158 |
|
159 | ${componentName === "Hello"?
|
160 | "return HelloFactory.new({message: `Welcome to Dominion! Nice to meet you! [${offset}, ${limit}]` });"
|
161 | :
|
162 | "return ;"
|
163 | }
|
164 | }${componentName === "Hello" ?
|
165 | `,
|
166 |
|
167 | //${componentName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}/42
|
168 | function (${componentName[0].toLowerCase() + componentName.substring(1)}Id) {
|
169 | // @summary: '@summary' and '@description' annotations will be used for generating OpenApi docs
|
170 | // @description: Open http://localhost:7042/${componentName.toLowerCase()}/42 to see results
|
171 |
|
172 | return ${componentName}Factory.new({
|
173 | id: +${componentName.toLowerCase()}Id,
|
174 | email: "my.name@example.com"
|
175 | });
|
176 | }`
|
177 | :
|
178 | ""}
|
179 | ],
|
180 |
|
181 | POST: [
|
182 | //${componentName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}
|
183 | function (${componentName === "Hello" ? "id" : ""}) {
|
184 | ${componentName === "Hello"? "// @path: custom\\/url\\/(\\d+)" : ""}
|
185 |
|
186 | return ;
|
187 | }
|
188 | ],
|
189 |
|
190 | PUT: [
|
191 | //${componentName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}/42
|
192 | function (${componentName[0].toLowerCase() + componentName.substring(1)}Id) {
|
193 |
|
194 | return ;
|
195 | }
|
196 | ],
|
197 |
|
198 | DELETE: [
|
199 | //${componentName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}/42
|
200 | function (${componentName[0].toLowerCase() + componentName.substring(1)}Id) {
|
201 |
|
202 | return ;
|
203 | }
|
204 | ]
|
205 |
|
206 | };
|
207 | `;
|
208 | fs.writeFileSync(path.resolve(componentPath, "controller.js"), controllerDeclaration, 'utf8');
|
209 | }
|
210 |
|
211 | function createFactoryDeclaration(componentName, componentPath) {
|
212 | const factoriesDeclaration =
|
213 | `const Property = require("@dominion-framework/dominion/core/property");
|
214 |
|
215 |
|
216 | module.exports = {
|
217 |
|
218 | name: "${componentName}",
|
219 |
|
220 | properties: {
|
221 | id: Property.id(),
|
222 | message: Property.string().required(),
|
223 | guid: Property.string().example("123e4567-e89b-12d3-a456-426655440000"),
|
224 | email: Property.string().example("my.name@example.com"),
|
225 | state: Property.enum(["open", "close"]),
|
226 | parentId: Property.model("${componentName}").private(),
|
227 | creationTime: Property.date().private(),
|
228 | modificationTime: Property.date().private()
|
229 | },
|
230 |
|
231 | factory: {
|
232 |
|
233 | },
|
234 |
|
235 | instance: {
|
236 |
|
237 | }
|
238 | };
|
239 | `;
|
240 | fs.writeFileSync(path.resolve(componentPath, "factory.js"), factoriesDeclaration, 'utf8');
|
241 | }
|
242 |
|
243 | function createFactoryDeclarationWithRepository(componentName, componentPath) {
|
244 | const factoriesDeclaration =
|
245 | `const Property = require("@dominion-framework/dominion/core/property");
|
246 | const ${componentName}Repository = require("./repository");
|
247 |
|
248 |
|
249 | module.exports = {
|
250 |
|
251 | name: "${componentName}",
|
252 |
|
253 | repository: ${componentName}Repository,
|
254 |
|
255 | properties: {
|
256 | id: Property.id(),
|
257 | message: Property.string().required(),
|
258 | guid: Property.string().example("123e4567-e89b-12d3-a456-426655440000"),
|
259 | email: Property.string().example("my.name@example.com"),
|
260 | state: Property.enum(["open", "close"]),
|
261 | parentId: Property.model("${componentName}").private(),
|
262 | creationTime: Property.date().private(),
|
263 | modificationTime: Property.date().private()
|
264 | },
|
265 |
|
266 | factory: {
|
267 |
|
268 | },
|
269 |
|
270 | instance: {
|
271 |
|
272 | }
|
273 | };
|
274 | `;
|
275 | const repositoryDeclaration =
|
276 | `const Repositories = require("@dominion-framework/repository-mysql");
|
277 |
|
278 |
|
279 | module.exports = Repositories.create('${componentName.toLowerCase()}_table_name', {
|
280 |
|
281 | });
|
282 | `;
|
283 |
|
284 | fs.writeFileSync(path.resolve(componentPath, "factory.js"), factoriesDeclaration, 'utf8');
|
285 | fs.writeFileSync(path.resolve(componentPath, "repository.js"), repositoryDeclaration, 'utf8');
|
286 | }
|
287 |
|
288 | function addComponentToProject(componentName, componentPath) {
|
289 | if(!fs.existsSync(path.resolve("index.js"))) {
|
290 | console.error("File `index.js` in project's root doesn't exist. Can't include created component.");
|
291 | return;
|
292 | }
|
293 |
|
294 | const indexFile = fs.readFileSync(path.resolve("index.js")).toString().split("\n");
|
295 | const position = indexFile.reverse().findIndex(line => line.indexOf("Server.addComponent") > -1);
|
296 | indexFile.splice(position, 0, `Server.addComponent(require("./${componentPath}"));`);
|
297 | fs.writeFileSync(path.resolve("index.js"), indexFile.reverse().join("\n"));
|
298 | }
|