@seip/blue-bird 0.4.5 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/.env_example +26 -25
  2. package/AGENTS.md +199 -199
  3. package/README.md +79 -79
  4. package/backend/index.js +13 -13
  5. package/backend/routes/frontend.js +41 -41
  6. package/backend/routes/seo.js +39 -39
  7. package/core/app.js +328 -325
  8. package/core/auth.js +114 -114
  9. package/core/cache.js +44 -44
  10. package/core/cli/component.js +42 -42
  11. package/core/cli/init.js +119 -118
  12. package/core/cli/react.js +435 -435
  13. package/core/cli/route.js +42 -42
  14. package/core/config.js +48 -47
  15. package/core/debug.js +248 -248
  16. package/core/logger.js +100 -100
  17. package/core/middleware.js +27 -27
  18. package/core/router.js +333 -333
  19. package/core/seo.js +95 -100
  20. package/core/template.js +472 -462
  21. package/core/upload.js +76 -76
  22. package/core/validate.js +380 -380
  23. package/frontend/index.html +26 -26
  24. package/frontend/landing.html +69 -69
  25. package/frontend/resources/css/tailwind.css +17 -17
  26. package/frontend/resources/js/App.jsx +70 -70
  27. package/frontend/resources/js/Main.jsx +18 -18
  28. package/frontend/resources/js/blue-bird/components/Button.jsx +67 -67
  29. package/frontend/resources/js/blue-bird/components/Card.jsx +18 -18
  30. package/frontend/resources/js/blue-bird/components/DataTable.jsx +126 -126
  31. package/frontend/resources/js/blue-bird/components/Input.jsx +21 -21
  32. package/frontend/resources/js/blue-bird/components/Label.jsx +12 -12
  33. package/frontend/resources/js/blue-bird/components/LanguageButton.jsx +23 -23
  34. package/frontend/resources/js/blue-bird/components/Link.jsx +15 -15
  35. package/frontend/resources/js/blue-bird/components/Modal.jsx +27 -27
  36. package/frontend/resources/js/blue-bird/components/Skeleton.jsx +44 -44
  37. package/frontend/resources/js/blue-bird/components/Translate.jsx +12 -12
  38. package/frontend/resources/js/blue-bird/components/Typography.jsx +69 -69
  39. package/frontend/resources/js/blue-bird/contexts/LanguageContext.jsx +41 -41
  40. package/frontend/resources/js/blue-bird/contexts/SPAContext.jsx +239 -237
  41. package/frontend/resources/js/blue-bird/contexts/SnackbarContext.jsx +38 -38
  42. package/frontend/resources/js/blue-bird/contexts/ThemeContext.jsx +49 -49
  43. package/frontend/resources/js/blue-bird/locales/en.json +47 -47
  44. package/frontend/resources/js/blue-bird/locales/es.json +47 -47
  45. package/frontend/resources/js/components/Header.jsx +55 -55
  46. package/frontend/resources/js/pages/About.jsx +31 -31
  47. package/frontend/resources/js/pages/Home.jsx +82 -82
  48. package/package.json +57 -57
  49. package/vite.config.js +22 -22
  50. package/frontend/public/robots.txt +0 -0
  51. package/frontend/public/sitemap.xml +0 -0
package/core/auth.js CHANGED
@@ -1,114 +1,114 @@
1
- import jwt from "jsonwebtoken";
2
- import crypto from "node:crypto";
3
-
4
- /**
5
- * Auth class to handle JWT generation, verification and protection with AES-256-GCM encryption.
6
- */
7
- class Auth {
8
- /**
9
- * Encrypts a payload using AES-256-GCM.
10
- * @param {Object} payload - The data to encrypt.
11
- * @param {string} secret - The secret key for encryption.
12
- * @returns {string} The encrypted string in format iv:tag:encrypted.
13
- */
14
- static encrypt(payload, secret) {
15
- const iv = crypto.randomBytes(12);
16
- const key = crypto.createHash("sha256").update(secret).digest();
17
- const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
18
- let encrypted = cipher.update(JSON.stringify(payload), "utf8", "hex");
19
- encrypted += cipher.final("hex");
20
- const tag = cipher.getAuthTag().toString("hex");
21
- return `${iv.toString("hex")}:${tag}:${encrypted}`;
22
- }
23
-
24
- /**
25
- * Decrypts a payload using AES-256-GCM.
26
- * @param {string} data - The encrypted string in format iv:tag:encrypted.
27
- * @param {string} secret - The secret key for decryption.
28
- * @returns {Object|null} The decrypted object or null if failed.
29
- */
30
- static decrypt(data, secret) {
31
- try {
32
- const [ivHex, tagHex, encryptedHex] = data.split(":");
33
- if (!ivHex || !tagHex || !encryptedHex) return null;
34
-
35
- const iv = Buffer.from(ivHex, "hex");
36
- const tag = Buffer.from(tagHex, "hex");
37
- const key = crypto.createHash("sha256").update(secret).digest();
38
- const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
39
- decipher.setAuthTag(tag);
40
-
41
- let decrypted = decipher.update(encryptedHex, "hex", "utf8");
42
- decrypted += decipher.final("utf8");
43
- return JSON.parse(decrypted);
44
- } catch (error) {
45
- return null;
46
- }
47
- }
48
-
49
- /**
50
- * Generates an encrypted JWT token.
51
- * @param {Object} payload - The data to store in the token.
52
- * @param {string} [secret=process.env.JWT_SECRET] - The secret key.
53
- * @param {string} [expiresIn="24h"] - Expiration time.
54
- * @returns {string} The generated token.
55
- */
56
- static generateToken(
57
- payload,
58
- secret = process.env.JWT_SECRET,
59
- expiresIn = "24h"
60
- ) {
61
- if (!secret)
62
- throw new Error("FATAL: JWT_SECRET environment variable is not defined.");
63
- const encrypted = this.encrypt(payload, secret);
64
- return jwt.sign({ data: encrypted }, secret, { expiresIn });
65
- }
66
-
67
- /**
68
- * Verifies and decrypts a JWT token.
69
- * @param {string} token - The token to verify.
70
- * @param {string} [secret=process.env.JWT_SECRET] - The secret key.
71
- * @returns {Object|null} The decoded and decrypted payload or null if invalid.
72
- */
73
- static verifyToken(token, secret = process.env.JWT_SECRET) {
74
- if (!secret)
75
- throw new Error("FATAL: JWT_SECRET environment variable is not defined.");
76
- try {
77
- const decoded = jwt.verify(token, secret);
78
- if (!decoded || !decoded.data) return null;
79
- return this.decrypt(decoded.data, secret);
80
- } catch (error) {
81
- return null;
82
- }
83
- }
84
-
85
- /**
86
- * Middleware to protect routes. Checks for token in Cookies or Authorization header.
87
- * @param {Object} options - Options for protection.
88
- * @param {string} [options.redirect=null] - URL to redirect if not authenticated.
89
- * @param {string} [options.key="user"] - Key to store the decoded token in the request.
90
- * @returns {Function} Express middleware.
91
- */
92
- static protect(options = { redirect: null, key: "user" }) {
93
- return (req, res, next) => {
94
- const token =
95
- req.cookies?.auth || req.headers.authorization?.split(" ")[1];
96
-
97
- if (!token) {
98
- if (options.redirect) return res.redirect(options.redirect);
99
- return res.status(401).json({ message: "Unauthorized" });
100
- }
101
-
102
- const decoded = this.verifyToken(token);
103
- if (!decoded) {
104
- if (options.redirect) return res.redirect(options.redirect);
105
- return res.status(401).json({ message: "Unauthorized" });
106
- }
107
-
108
- req[options.key || "user"] = decoded;
109
- next();
110
- };
111
- }
112
- }
113
-
114
- export default Auth;
1
+ import jwt from "jsonwebtoken";
2
+ import crypto from "node:crypto";
3
+
4
+ /**
5
+ * Auth class to handle JWT generation, verification and protection with AES-256-GCM encryption.
6
+ */
7
+ class Auth {
8
+ /**
9
+ * Encrypts a payload using AES-256-GCM.
10
+ * @param {Object} payload - The data to encrypt.
11
+ * @param {string} secret - The secret key for encryption.
12
+ * @returns {string} The encrypted string in format iv:tag:encrypted.
13
+ */
14
+ static encrypt(payload, secret) {
15
+ const iv = crypto.randomBytes(12);
16
+ const key = crypto.createHash("sha256").update(secret).digest();
17
+ const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
18
+ let encrypted = cipher.update(JSON.stringify(payload), "utf8", "hex");
19
+ encrypted += cipher.final("hex");
20
+ const tag = cipher.getAuthTag().toString("hex");
21
+ return `${iv.toString("hex")}:${tag}:${encrypted}`;
22
+ }
23
+
24
+ /**
25
+ * Decrypts a payload using AES-256-GCM.
26
+ * @param {string} data - The encrypted string in format iv:tag:encrypted.
27
+ * @param {string} secret - The secret key for decryption.
28
+ * @returns {Object|null} The decrypted object or null if failed.
29
+ */
30
+ static decrypt(data, secret) {
31
+ try {
32
+ const [ivHex, tagHex, encryptedHex] = data.split(":");
33
+ if (!ivHex || !tagHex || !encryptedHex) return null;
34
+
35
+ const iv = Buffer.from(ivHex, "hex");
36
+ const tag = Buffer.from(tagHex, "hex");
37
+ const key = crypto.createHash("sha256").update(secret).digest();
38
+ const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
39
+ decipher.setAuthTag(tag);
40
+
41
+ let decrypted = decipher.update(encryptedHex, "hex", "utf8");
42
+ decrypted += decipher.final("utf8");
43
+ return JSON.parse(decrypted);
44
+ } catch (error) {
45
+ return null;
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Generates an encrypted JWT token.
51
+ * @param {Object} payload - The data to store in the token.
52
+ * @param {string} [secret=process.env.JWT_SECRET] - The secret key.
53
+ * @param {string} [expiresIn="24h"] - Expiration time.
54
+ * @returns {string} The generated token.
55
+ */
56
+ static generateToken(
57
+ payload,
58
+ secret = process.env.JWT_SECRET,
59
+ expiresIn = "24h"
60
+ ) {
61
+ if (!secret)
62
+ throw new Error("FATAL: JWT_SECRET environment variable is not defined.");
63
+ const encrypted = this.encrypt(payload, secret);
64
+ return jwt.sign({ data: encrypted }, secret, { expiresIn });
65
+ }
66
+
67
+ /**
68
+ * Verifies and decrypts a JWT token.
69
+ * @param {string} token - The token to verify.
70
+ * @param {string} [secret=process.env.JWT_SECRET] - The secret key.
71
+ * @returns {Object|null} The decoded and decrypted payload or null if invalid.
72
+ */
73
+ static verifyToken(token, secret = process.env.JWT_SECRET) {
74
+ if (!secret)
75
+ throw new Error("FATAL: JWT_SECRET environment variable is not defined.");
76
+ try {
77
+ const decoded = jwt.verify(token, secret);
78
+ if (!decoded || !decoded.data) return null;
79
+ return this.decrypt(decoded.data, secret);
80
+ } catch (error) {
81
+ return null;
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Middleware to protect routes. Checks for token in Cookies or Authorization header.
87
+ * @param {Object} options - Options for protection.
88
+ * @param {string} [options.redirect=null] - URL to redirect if not authenticated.
89
+ * @param {string} [options.key="user"] - Key to store the decoded token in the request.
90
+ * @returns {Function} Express middleware.
91
+ */
92
+ static protect(options = { redirect: null, key: "user" }) {
93
+ return (req, res, next) => {
94
+ const token =
95
+ req.cookies?.auth || req.headers.authorization?.split(" ")[1];
96
+
97
+ if (!token) {
98
+ if (options.redirect) return res.redirect(options.redirect);
99
+ return res.status(401).json({ message: "Unauthorized" });
100
+ }
101
+
102
+ const decoded = this.verifyToken(token);
103
+ if (!decoded) {
104
+ if (options.redirect) return res.redirect(options.redirect);
105
+ return res.status(401).json({ message: "Unauthorized" });
106
+ }
107
+
108
+ req[options.key || "user"] = decoded;
109
+ next();
110
+ };
111
+ }
112
+ }
113
+
114
+ export default Auth;
package/core/cache.js CHANGED
@@ -1,45 +1,45 @@
1
- const CACHE = {};
2
-
3
- setInterval(() => {
4
- const now = Date.now();
5
- for (const key in CACHE) {
6
- if (CACHE[key].expiry <= now) {
7
- delete CACHE[key];
8
- }
9
- }
10
- }, 300000).unref();
11
- /**
12
- * Cache Middleware
13
- * @example
14
- * router.get("/stats",
15
- Cache.middleware(120),
16
- controller.stats
17
- );
18
- * */
19
- class Cache {
20
-
21
- static middleware(seconds = 60) {
22
- return (req, res, next) => {
23
-
24
- const key = req.originalUrl;
25
-
26
- if (CACHE[key] && CACHE[key].expiry > Date.now()) {
27
- return res.json(CACHE[key].data);
28
- }
29
-
30
- const originalJson = res.json.bind(res);
31
-
32
- res.json = (body) => {
33
- CACHE[key] = {
34
- data: body,
35
- expiry: Date.now() + seconds * 1000
36
- };
37
- return originalJson(body);
38
- };
39
-
40
- next();
41
- };
42
- }
43
- }
44
-
1
+ const CACHE = {};
2
+
3
+ setInterval(() => {
4
+ const now = Date.now();
5
+ for (const key in CACHE) {
6
+ if (CACHE[key].expiry <= now) {
7
+ delete CACHE[key];
8
+ }
9
+ }
10
+ }, 300000).unref();
11
+ /**
12
+ * Cache Middleware
13
+ * @example
14
+ * router.get("/stats",
15
+ Cache.middleware(120),
16
+ controller.stats
17
+ );
18
+ * */
19
+ class Cache {
20
+
21
+ static middleware(seconds = 60) {
22
+ return (req, res, next) => {
23
+
24
+ const key = req.originalUrl;
25
+
26
+ if (CACHE[key] && CACHE[key].expiry > Date.now()) {
27
+ return res.json(CACHE[key].data);
28
+ }
29
+
30
+ const originalJson = res.json.bind(res);
31
+
32
+ res.json = (body) => {
33
+ CACHE[key] = {
34
+ data: body,
35
+ expiry: Date.now() + seconds * 1000
36
+ };
37
+ return originalJson(body);
38
+ };
39
+
40
+ next();
41
+ };
42
+ }
43
+ }
44
+
45
45
  export default Cache;
@@ -1,42 +1,42 @@
1
- import path from "node:path";
2
- import fs from "node:fs";
3
- import Config from "../config.js";
4
-
5
- const __dirname = Config.dirname();
6
-
7
- class ComponentCLI {
8
- /**
9
- * Create component react
10
- * @return {void}
11
- * /
12
- */
13
-
14
- create() {
15
- const folder = path.join(process.cwd(), "frontend/resources/components");
16
- if (!fs.existsSync(folder)) {
17
- fs.mkdirSync(folder, { recursive: true });
18
- }
19
- let nameComponent =`Component-${Math.random().toString(36).substring(7)}`;
20
- const nameParam = process.argv[2];
21
- if (nameParam.length > 0 && typeof nameParam === "string") {
22
- nameComponent = nameParam;
23
- nameComponent = nameComponent.charAt(0).toUpperCase() + nameComponent.slice(1);
24
- }
25
- const filePath = path.join(folder, `${nameComponent}.jsx`);
26
- const content = `import React from 'react';
27
-
28
- export default function ${nameComponent}() {
29
- return (
30
- <div>
31
- <h1>${nameComponent} Component</h1>
32
- </div>
33
- );
34
- }
35
- `;
36
- fs.writeFileSync(filePath, content);
37
- console.log(`Component ${nameComponent} created at ${filePath}`);
38
- }
39
- }
40
-
41
- const componentCLI = new ComponentCLI();
42
- componentCLI.create();
1
+ import path from "node:path";
2
+ import fs from "node:fs";
3
+ import Config from "../config.js";
4
+
5
+ const __dirname = Config.dirname();
6
+
7
+ class ComponentCLI {
8
+ /**
9
+ * Create component react
10
+ * @return {void}
11
+ * /
12
+ */
13
+
14
+ create() {
15
+ const folder = path.join(process.cwd(), "frontend/resources/components");
16
+ if (!fs.existsSync(folder)) {
17
+ fs.mkdirSync(folder, { recursive: true });
18
+ }
19
+ let nameComponent =`Component-${Math.random().toString(36).substring(7)}`;
20
+ const nameParam = process.argv[2];
21
+ if (nameParam.length > 0 && typeof nameParam === "string") {
22
+ nameComponent = nameParam;
23
+ nameComponent = nameComponent.charAt(0).toUpperCase() + nameComponent.slice(1);
24
+ }
25
+ const filePath = path.join(folder, `${nameComponent}.jsx`);
26
+ const content = `import React from 'react';
27
+
28
+ export default function ${nameComponent}() {
29
+ return (
30
+ <div>
31
+ <h1>${nameComponent} Component</h1>
32
+ </div>
33
+ );
34
+ }
35
+ `;
36
+ fs.writeFileSync(filePath, content);
37
+ console.log(`Component ${nameComponent} created at ${filePath}`);
38
+ }
39
+ }
40
+
41
+ const componentCLI = new ComponentCLI();
42
+ componentCLI.create();
package/core/cli/init.js CHANGED
@@ -1,118 +1,119 @@
1
- #!/usr/bin/env node
2
-
3
- import fs from "node:fs";
4
- import path from "node:path";
5
- import chalk from "chalk";
6
-
7
- class ProjectInit {
8
- constructor() {
9
- this.appDir = process.cwd();
10
- this.sourceDir = path.resolve(import.meta.dirname, "../../");
11
- }
12
-
13
- async run() {
14
- console.log(chalk.cyan("Starting Blue Bird project initialization..."));
15
-
16
- const itemsToCopy = [
17
- "backend",
18
- "frontend",
19
- ".env_example"
20
- ];
21
-
22
- try {
23
- itemsToCopy.forEach(item => {
24
- const src = path.join(this.sourceDir, item);
25
- const dest = path.join(this.appDir, item);
26
-
27
- if (fs.existsSync(src)) {
28
- if (!fs.existsSync(dest)) {
29
- this.copyRecursive(src, dest);
30
- console.log(chalk.green(`✓ Copied ${item} to root.`));
31
- } else {
32
- console.log(chalk.yellow(`! ${item} already exists, skipping.`));
33
- }
34
- } else {
35
- console.warn(chalk.red(`✗ Source ${item} not found in ${this.sourceDir}`));
36
- }
37
- });
38
-
39
- const envPath = path.join(this.appDir, ".env");
40
- const envExamplePath = path.join(this.appDir, ".env_example");
41
- if (fs.existsSync(envExamplePath) && !fs.existsSync(envPath)) {
42
- fs.copyFileSync(envExamplePath, envPath);
43
- console.log(chalk.green("✓ Created .env from .env_example."));
44
- }
45
-
46
- this.updatePackageJson();
47
-
48
- console.log(chalk.blue("\nBlue Bird initialization completed!"));
49
- console.log(chalk.white("Next steps:"));
50
- console.log(chalk.bold(" npm install"));
51
- console.log(chalk.bold(" npm run react"));
52
- console.log(chalk.bold(" npm run dev"));
53
-
54
- } catch (error) {
55
- console.error(chalk.red("Error during initialization:"), error.message);
56
- }
57
- }
58
-
59
- updatePackageJson() {
60
- const pkgPath = path.join(this.appDir, "package.json");
61
- if (fs.existsSync(pkgPath)) {
62
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
63
- pkg.scripts = pkg.scripts || {};
64
-
65
- const scriptsToAdd = {
66
- "dev": "node --watch --env-file=.env backend/index.js",
67
- "start": "node --env-file=.env backend/index.js",
68
- "react": "blue-bird react",
69
- "init": "blue-bird",
70
- "route": "blue-bird route",
71
- "component": "blue-bird component",
72
- "swagger-install": "blue-bird swagger-install"
73
- };
74
-
75
- let updated = false;
76
- for (const [key, value] of Object.entries(scriptsToAdd)) {
77
- if (!pkg.scripts[key]) {
78
- pkg.scripts[key] = value;
79
- updated = true;
80
- }
81
- }
82
-
83
- if (updated) {
84
- fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
85
- console.log(chalk.green("✓ Updated package.json scripts."));
86
- }
87
- }
88
- }
89
-
90
- copyRecursive(src, dest) {
91
- const stats = fs.statSync(src);
92
- const isDirectory = stats.isDirectory();
93
-
94
- if (isDirectory) {
95
- if (!fs.existsSync(dest)) {
96
- fs.mkdirSync(dest, { recursive: true });
97
- }
98
- fs.readdirSync(src).forEach(childItemName => {
99
- this.copyRecursive(path.join(src, childItemName), path.join(dest, childItemName));
100
- });
101
- } else {
102
- fs.copyFileSync(src, dest);
103
- }
104
- }
105
- }
106
-
107
- const initializer = new ProjectInit();
108
-
109
- const args = process.argv.slice(2);
110
- const command = args[0];
111
-
112
- if (command === "react") import("./react.js");
113
- else if (command === "route") import("./route.js")
114
- else if (command === "component") import("./component.js")
115
- else if (command === "swagger-install") import("./swagger.js")
116
- else initializer.run();
117
-
118
-
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import chalk from "chalk";
6
+
7
+ class ProjectInit {
8
+ constructor() {
9
+ this.appDir = process.cwd();
10
+ this.sourceDir = path.resolve(import.meta.dirname, "../../");
11
+ }
12
+
13
+ async run() {
14
+ console.log(chalk.cyan("Starting Blue Bird project initialization..."));
15
+
16
+ const itemsToCopy = [
17
+ "backend",
18
+ "frontend",
19
+ ".env_example",
20
+ "AGENTS.md"
21
+ ];
22
+
23
+ try {
24
+ itemsToCopy.forEach(item => {
25
+ const src = path.join(this.sourceDir, item);
26
+ const dest = path.join(this.appDir, item);
27
+
28
+ if (fs.existsSync(src)) {
29
+ if (!fs.existsSync(dest)) {
30
+ this.copyRecursive(src, dest);
31
+ console.log(chalk.green(`✓ Copied ${item} to root.`));
32
+ } else {
33
+ console.log(chalk.yellow(`! ${item} already exists, skipping.`));
34
+ }
35
+ } else {
36
+ console.warn(chalk.red(`✗ Source ${item} not found in ${this.sourceDir}`));
37
+ }
38
+ });
39
+
40
+ const envPath = path.join(this.appDir, ".env");
41
+ const envExamplePath = path.join(this.appDir, ".env_example");
42
+ if (fs.existsSync(envExamplePath) && !fs.existsSync(envPath)) {
43
+ fs.copyFileSync(envExamplePath, envPath);
44
+ console.log(chalk.green("✓ Created .env from .env_example."));
45
+ }
46
+
47
+ this.updatePackageJson();
48
+
49
+ console.log(chalk.blue("\nBlue Bird initialization completed!"));
50
+ console.log(chalk.white("Next steps:"));
51
+ console.log(chalk.bold(" npm install"));
52
+ console.log(chalk.bold(" npm run react"));
53
+ console.log(chalk.bold(" npm run dev"));
54
+
55
+ } catch (error) {
56
+ console.error(chalk.red("Error during initialization:"), error.message);
57
+ }
58
+ }
59
+
60
+ updatePackageJson() {
61
+ const pkgPath = path.join(this.appDir, "package.json");
62
+ if (fs.existsSync(pkgPath)) {
63
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
64
+ pkg.scripts = pkg.scripts || {};
65
+
66
+ const scriptsToAdd = {
67
+ "dev": "node --watch --env-file=.env backend/index.js",
68
+ "start": "node --env-file=.env backend/index.js",
69
+ "react": "blue-bird react",
70
+ "init": "blue-bird",
71
+ "route": "blue-bird route",
72
+ "component": "blue-bird component",
73
+ "swagger-install": "blue-bird swagger-install"
74
+ };
75
+
76
+ let updated = false;
77
+ for (const [key, value] of Object.entries(scriptsToAdd)) {
78
+ if (!pkg.scripts[key]) {
79
+ pkg.scripts[key] = value;
80
+ updated = true;
81
+ }
82
+ }
83
+
84
+ if (updated) {
85
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
86
+ console.log(chalk.green("✓ Updated package.json scripts."));
87
+ }
88
+ }
89
+ }
90
+
91
+ copyRecursive(src, dest) {
92
+ const stats = fs.statSync(src);
93
+ const isDirectory = stats.isDirectory();
94
+
95
+ if (isDirectory) {
96
+ if (!fs.existsSync(dest)) {
97
+ fs.mkdirSync(dest, { recursive: true });
98
+ }
99
+ fs.readdirSync(src).forEach(childItemName => {
100
+ this.copyRecursive(path.join(src, childItemName), path.join(dest, childItemName));
101
+ });
102
+ } else {
103
+ fs.copyFileSync(src, dest);
104
+ }
105
+ }
106
+ }
107
+
108
+ const initializer = new ProjectInit();
109
+
110
+ const args = process.argv.slice(2);
111
+ const command = args[0];
112
+
113
+ if (command === "react") import("./react.js");
114
+ else if (command === "route") import("./route.js")
115
+ else if (command === "component") import("./component.js")
116
+ else if (command === "swagger-install") import("./swagger.js")
117
+ else initializer.run();
118
+
119
+