@devrune/cli 1.0.4 → 1.0.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.
- package/README.md +17 -1
- package/dist/check-file-name-style-guide.js +2 -1
- package/dist/generate-feature.js +17 -8
- package/dist/index.js +42 -6
- package/dist/util.js +19 -0
- package/package.json +3 -2
- package/scripts/check-file-name-style-guide.ts +2 -1
- package/scripts/generate-feature.ts +19 -8
- package/scripts/index.ts +59 -14
- package/scripts/util.ts +15 -0
package/README.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
1
|
# DevRune CLI
|
|
2
2
|
|
|
3
|
-
Use this
|
|
3
|
+
Use this CLI to check and manage structure on Frontend NextJS projects
|
|
4
|
+
|
|
5
|
+
## How to install
|
|
6
|
+
|
|
7
|
+
```npm install --save-dev @devrune/cli```
|
|
8
|
+
|
|
9
|
+
## How to use
|
|
10
|
+
|
|
11
|
+
```npx @devrune/cli```
|
|
12
|
+
|
|
13
|
+
then choose what prompt suggests
|
|
14
|
+
|
|
15
|
+
## Available Commands
|
|
16
|
+
|Command|Description|
|
|
17
|
+
|-------|-----------|
|
|
18
|
+
|Check Style Guide 🖌| Check project folders according style guide |
|
|
19
|
+
|Create new feature | Creates basic folder structure in ```src/[feature or shared]/[name passed in prompt]``` folder|
|
|
@@ -5,7 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const fs_1 = __importDefault(require("fs"));
|
|
7
7
|
const path_1 = __importDefault(require("path"));
|
|
8
|
-
const
|
|
8
|
+
const util_1 = require("./util");
|
|
9
|
+
const SRC_DIR = (0, util_1.isRootFolder)();
|
|
9
10
|
const RESERVED_NEXT_FILES = new Set([
|
|
10
11
|
"page.tsx",
|
|
11
12
|
"layout.tsx",
|
package/dist/generate-feature.js
CHANGED
|
@@ -9,6 +9,7 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
9
9
|
const constants_1 = require("./constants");
|
|
10
10
|
const select_1 = __importDefault(require("@inquirer/select"));
|
|
11
11
|
const prompts_1 = require("@inquirer/prompts");
|
|
12
|
+
const util_1 = require("./util");
|
|
12
13
|
function toPascalCase(str) {
|
|
13
14
|
return str
|
|
14
15
|
.split("-")
|
|
@@ -22,7 +23,15 @@ function toCamelCase(str) {
|
|
|
22
23
|
function ensureDir(dirPath) {
|
|
23
24
|
node_fs_1.default.mkdirSync(dirPath, { recursive: true });
|
|
24
25
|
}
|
|
26
|
+
function createFile(filePath, content) {
|
|
27
|
+
if (node_fs_1.default.existsSync(filePath)) {
|
|
28
|
+
console.warn(`⚠️ File ${filePath} already exists. Skipping creation.`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
node_fs_1.default.writeFileSync(filePath, content);
|
|
32
|
+
}
|
|
25
33
|
async function main() {
|
|
34
|
+
const srcFolder = (0, util_1.isRootFolder)();
|
|
26
35
|
const type = await (0, select_1.default)({
|
|
27
36
|
message: 'What do you want to create?',
|
|
28
37
|
choices: [{ name: 'Feature', value: 'feature', description: 'Create feature if it should be private and nothing should be imported outside' }, { name: 'Shared', value: 'shared', description: 'Create shared feature that can be imported anywhere' }],
|
|
@@ -31,21 +40,21 @@ async function main() {
|
|
|
31
40
|
message: `Enter ${type} name:`,
|
|
32
41
|
validate: (input) => input ? true : 'Name cannot be empty',
|
|
33
42
|
});
|
|
34
|
-
const ROOT_DIR_PATH = type === 'feature' ? node_path_1.default.join(
|
|
43
|
+
const ROOT_DIR_PATH = type === 'feature' ? node_path_1.default.join(srcFolder, constants_1.FEATURE_DIR) : node_path_1.default.join(srcFolder, constants_1.SHARED_DIR);
|
|
35
44
|
ensureDir(node_path_1.default.join(ROOT_DIR_PATH, name));
|
|
36
|
-
|
|
45
|
+
createFile(node_path_1.default.join(ROOT_DIR_PATH, name, `${name}.class.ts`), `// export class ${toPascalCase(name)} = {}`);
|
|
37
46
|
ensureDir(node_path_1.default.join(ROOT_DIR_PATH, name, constants_1.API_DIR));
|
|
38
|
-
|
|
47
|
+
createFile(node_path_1.default.join(ROOT_DIR_PATH, name, constants_1.API_DIR, `${name}.api.ts`), `// export const ${toCamelCase(`${name}Api`)} = {}`);
|
|
39
48
|
ensureDir(node_path_1.default.join(ROOT_DIR_PATH, name, constants_1.UI_DIR));
|
|
40
|
-
|
|
49
|
+
createFile(node_path_1.default.join(ROOT_DIR_PATH, name, constants_1.UI_DIR, `${name}.ui.tsx`), `import type { ${toPascalCase(name)}Props } from '../types/${name}.type';\n\nexport const ${toPascalCase(name)} = ({}: ${toPascalCase(name)}Props) => { \n return null;\n};`);
|
|
41
50
|
ensureDir(node_path_1.default.join(ROOT_DIR_PATH, name, constants_1.HOOKS_DIR));
|
|
42
|
-
|
|
51
|
+
createFile(node_path_1.default.join(ROOT_DIR_PATH, name, constants_1.HOOKS_DIR, `use-${name}.hook.ts`), `// export const ${toCamelCase(`use${toPascalCase(name)}`)} = () => {}`);
|
|
43
52
|
ensureDir(node_path_1.default.join(ROOT_DIR_PATH, name, constants_1.UTILS_DIR));
|
|
44
|
-
|
|
53
|
+
createFile(node_path_1.default.join(ROOT_DIR_PATH, name, constants_1.UTILS_DIR, `${name}.util.ts`), `// utils for ${name} feature`);
|
|
45
54
|
ensureDir(node_path_1.default.join(ROOT_DIR_PATH, name, constants_1.TYPES_DIR));
|
|
46
|
-
|
|
55
|
+
createFile(node_path_1.default.join(ROOT_DIR_PATH, name, constants_1.TYPES_DIR, `${name}.type.ts`), `// export type ${toPascalCase(name)}ModalProps = {};\nexport type ${toPascalCase(name)}Props = {};`);
|
|
47
56
|
ensureDir(node_path_1.default.join(ROOT_DIR_PATH, name, constants_1.MODALS_DIR));
|
|
48
|
-
|
|
57
|
+
createFile(node_path_1.default.join(ROOT_DIR_PATH, name, constants_1.MODALS_DIR, `${name}.modal.tsx`), `// import type { ${toPascalCase(name)}ModalProps } from '../types/${name}.type';\n\n// export const ${toPascalCase(name)}Modal = ({}: ${toPascalCase(name)}ModalProps) => {\n// return null;\n// };`);
|
|
49
58
|
}
|
|
50
59
|
main()
|
|
51
60
|
.catch((e) => {
|
package/dist/index.js
CHANGED
|
@@ -5,16 +5,52 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
const select_1 = __importDefault(require("@inquirer/select"));
|
|
8
|
+
const util_1 = require("./util");
|
|
8
9
|
console.log("Welcome to DevRune CLI! Use this tool to generate features and check file name style guide.");
|
|
9
|
-
(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
async function run() {
|
|
11
|
+
const args = process.argv.slice(2);
|
|
12
|
+
if (args.includes('-h') || args.includes('--help')) {
|
|
13
|
+
console.log(`
|
|
14
|
+
DevRune CLI
|
|
15
|
+
|
|
16
|
+
❗️❗️❗️Important to call command only from folder where src is located, otherwise it won't work
|
|
17
|
+
|
|
18
|
+
Usage:
|
|
19
|
+
@devrune/cli Interactive mode
|
|
20
|
+
@devrune/cli --check Check folders style guide
|
|
21
|
+
@devrune/cli --feature Create new feature
|
|
22
|
+
@devrune/cli -h Help
|
|
23
|
+
`);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
(0, util_1.isRootFolder)();
|
|
27
|
+
if (args.includes('--check')) {
|
|
28
|
+
return require('./check-file-name-style-guide');
|
|
29
|
+
}
|
|
30
|
+
if (args.includes('--feature')) {
|
|
31
|
+
return require('./generate-feature');
|
|
32
|
+
}
|
|
33
|
+
// 👉 fallback to interactive mode
|
|
34
|
+
const answer = await (0, select_1.default)({
|
|
35
|
+
message: 'What do you want to create?',
|
|
36
|
+
choices: [
|
|
37
|
+
{
|
|
38
|
+
name: 'Check Style Guide 🎨',
|
|
39
|
+
value: 'check',
|
|
40
|
+
description: 'Check project folders according style guide',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: 'Create new feature 📂',
|
|
44
|
+
value: 'feature',
|
|
45
|
+
description: 'Create new feature',
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
});
|
|
14
49
|
if (answer === 'check') {
|
|
15
50
|
require('./check-file-name-style-guide');
|
|
16
51
|
}
|
|
17
52
|
if (answer === 'feature') {
|
|
18
53
|
require('./generate-feature');
|
|
19
54
|
}
|
|
20
|
-
}
|
|
55
|
+
}
|
|
56
|
+
run();
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isRootFolder = isRootFolder;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const constants_1 = require("./constants");
|
|
10
|
+
function isRootFolder() {
|
|
11
|
+
const currentDir = process.cwd();
|
|
12
|
+
const rootFolder = node_path_1.default.join(currentDir, constants_1.ROOT_DIR);
|
|
13
|
+
const folderExits = node_fs_1.default.existsSync(rootFolder);
|
|
14
|
+
if (!folderExits) {
|
|
15
|
+
console.error(`❌ Error: This command should be run from the root of the project where the ${constants_1.ROOT_DIR} folder is located.`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
return rootFolder;
|
|
19
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devrune/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"bin": {
|
|
5
5
|
"devrune": "./dist/index.js"
|
|
6
6
|
},
|
|
7
7
|
"scripts": {
|
|
8
8
|
"generate-feature": "ts-node ./scripts/generate-feature.ts",
|
|
9
|
-
"check-file-name": "ts-node ./scripts/check-file-name-style-guide.ts"
|
|
9
|
+
"check-file-name": "ts-node ./scripts/check-file-name-style-guide.ts",
|
|
10
|
+
"publish:lib": "npx tsc && npm publish --access public"
|
|
10
11
|
},
|
|
11
12
|
"devDependencies": {
|
|
12
13
|
"@types/node": "^25.3.0",
|
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { API_DIR, HOOKS_DIR, MODALS_DIR, FEATURE_DIR, ROOT_DIR, SHARED_DIR, TYPES_DIR, UI_DIR, UTILS_DIR } from "./constants";
|
|
5
5
|
import select from "@inquirer/select";
|
|
6
6
|
import {input} from '@inquirer/prompts';
|
|
7
|
+
import { isRootFolder } from "./util";
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
function toPascalCase(str: string) {
|
|
@@ -24,7 +25,17 @@ function ensureDir(dirPath: string) {
|
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
|
|
28
|
+
function createFile(filePath: string, content: string) {
|
|
29
|
+
if (fs.existsSync(filePath)) {
|
|
30
|
+
console.warn(`⚠️ File ${filePath} already exists. Skipping creation.`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
fs.writeFileSync(filePath, content);
|
|
35
|
+
}
|
|
36
|
+
|
|
27
37
|
async function main() {
|
|
38
|
+
const srcFolder = isRootFolder();
|
|
28
39
|
const type = await select(
|
|
29
40
|
{
|
|
30
41
|
message: 'What do you want to create?',
|
|
@@ -40,21 +51,21 @@ async function main() {
|
|
|
40
51
|
},
|
|
41
52
|
);
|
|
42
53
|
|
|
43
|
-
const ROOT_DIR_PATH = type === 'feature' ? path.join(
|
|
54
|
+
const ROOT_DIR_PATH = type === 'feature' ? path.join(srcFolder, FEATURE_DIR) : path.join(srcFolder, SHARED_DIR);
|
|
44
55
|
ensureDir(path.join(ROOT_DIR_PATH, name));
|
|
45
|
-
|
|
56
|
+
createFile(path.join(ROOT_DIR_PATH, name, `${name}.class.ts`), `// export class ${toPascalCase(name)} = {}`);
|
|
46
57
|
ensureDir(path.join(ROOT_DIR_PATH, name, API_DIR));
|
|
47
|
-
|
|
58
|
+
createFile(path.join(ROOT_DIR_PATH, name, API_DIR, `${name}.api.ts`), `// export const ${toCamelCase(`${name}Api`)} = {}`);
|
|
48
59
|
ensureDir(path.join(ROOT_DIR_PATH, name, UI_DIR));
|
|
49
|
-
|
|
60
|
+
createFile(path.join(ROOT_DIR_PATH, name, UI_DIR, `${name}.ui.tsx`), `import type { ${toPascalCase(name)}Props } from '../types/${name}.type';\n\nexport const ${toPascalCase(name)} = ({}: ${toPascalCase(name)}Props) => { \n return null;\n};`);
|
|
50
61
|
ensureDir(path.join(ROOT_DIR_PATH, name, HOOKS_DIR));
|
|
51
|
-
|
|
62
|
+
createFile(path.join(ROOT_DIR_PATH, name, HOOKS_DIR, `use-${name}.hook.ts`), `// export const ${toCamelCase(`use${toPascalCase(name)}`)} = () => {}`);
|
|
52
63
|
ensureDir(path.join(ROOT_DIR_PATH, name, UTILS_DIR));
|
|
53
|
-
|
|
64
|
+
createFile(path.join(ROOT_DIR_PATH, name, UTILS_DIR, `${name}.util.ts`), `// utils for ${name} feature`);
|
|
54
65
|
ensureDir(path.join(ROOT_DIR_PATH, name, TYPES_DIR));
|
|
55
|
-
|
|
66
|
+
createFile(path.join(ROOT_DIR_PATH, name, TYPES_DIR, `${name}.type.ts`), `// export type ${toPascalCase(name)}ModalProps = {};\nexport type ${toPascalCase(name)}Props = {};`);
|
|
56
67
|
ensureDir(path.join(ROOT_DIR_PATH, name, MODALS_DIR));
|
|
57
|
-
|
|
68
|
+
createFile(path.join(ROOT_DIR_PATH, name, MODALS_DIR, `${name}.modal.tsx`), `// import type { ${toPascalCase(name)}ModalProps } from '../types/${name}.type';\n\n// export const ${toPascalCase(name)}Modal = ({}: ${toPascalCase(name)}ModalProps) => {\n// return null;\n// };`);
|
|
58
69
|
|
|
59
70
|
}
|
|
60
71
|
|
package/scripts/index.ts
CHANGED
|
@@ -1,20 +1,65 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import select from "@inquirer/select";
|
|
3
|
+
import { isRootFolder } from "./util";
|
|
4
|
+
|
|
5
|
+
|
|
3
6
|
|
|
4
7
|
console.log("Welcome to DevRune CLI! Use this tool to generate features and check file name style guide.");
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
).
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async function run() {
|
|
11
|
+
const args = process.argv.slice(2);
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
if (args.includes('-h') || args.includes('--help')) {
|
|
15
|
+
console.log(`
|
|
16
|
+
DevRune CLI
|
|
17
|
+
|
|
18
|
+
❗️❗️❗️Important to call command only from folder where src is located, otherwise it won't work
|
|
19
|
+
|
|
20
|
+
Usage:
|
|
21
|
+
@devrune/cli Interactive mode
|
|
22
|
+
@devrune/cli --check Check folders style guide
|
|
23
|
+
@devrune/cli --feature Create new feature
|
|
24
|
+
@devrune/cli -h Help
|
|
25
|
+
`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
isRootFolder();
|
|
30
|
+
|
|
31
|
+
if (args.includes('--check')) {
|
|
32
|
+
return require('./check-file-name-style-guide');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (args.includes('--feature')) {
|
|
36
|
+
return require('./generate-feature');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// 👉 fallback to interactive mode
|
|
40
|
+
const answer = await select({
|
|
41
|
+
message: 'What do you want to create?',
|
|
42
|
+
choices: [
|
|
43
|
+
{
|
|
44
|
+
name: 'Check Style Guide 🎨',
|
|
45
|
+
value: 'check',
|
|
46
|
+
description: 'Check project folders according style guide',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: 'Create new feature 📂',
|
|
50
|
+
value: 'feature',
|
|
51
|
+
description: 'Create new feature',
|
|
52
|
+
},
|
|
53
|
+
],
|
|
19
54
|
});
|
|
20
55
|
|
|
56
|
+
if (answer === 'check') {
|
|
57
|
+
require('./check-file-name-style-guide');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (answer === 'feature') {
|
|
61
|
+
require('./generate-feature');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
run();
|
package/scripts/util.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ROOT_DIR } from "./constants";
|
|
4
|
+
|
|
5
|
+
export function isRootFolder() {
|
|
6
|
+
const currentDir = process.cwd();
|
|
7
|
+
const rootFolder = path.join(currentDir, ROOT_DIR);
|
|
8
|
+
const folderExits = fs.existsSync(rootFolder);
|
|
9
|
+
if (!folderExits) {
|
|
10
|
+
console.error(`❌ Error: This command should be run from the root of the project where the ${ROOT_DIR} folder is located.`);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return rootFolder;
|
|
15
|
+
}
|