create-du-app 0.1.0 → 0.1.1

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 CHANGED
@@ -1,28 +1,64 @@
1
- # @company-starter/cli (`create-app`)
1
+ # create-du-app
2
2
 
3
- CLI generator: hỏi tên project + chọn template (Mobile / Frontend / Backend) → sinh ra
4
- một monorepo sản phẩm độc lập.
3
+ > The `create-app` CLI scaffolds a standalone product monorepo from company templates.
5
4
 
6
- ## Chạy
5
+ Prompts for a project name and a selection of stacks (Mobile / Frontend / Backend), then copies
6
+ the matching templates, renames manifests, substitutes the project name, and emits a ready-to-run
7
+ pnpm workspace.
8
+
9
+ ## Installation
7
10
 
8
11
  ```bash
9
12
  cd packages/cli
10
13
  pnpm install
11
- pnpm link --global # đăng lệnh global `create-app`
12
- create-app # hoặc: node src/index.js
14
+ pnpm link --global # registers the global `create-app` command
15
+ ```
16
+
17
+ Or run without linking:
18
+
19
+ ```bash
20
+ node src/index.js
13
21
  ```
14
22
 
15
- ## Source
23
+ ## Usage
24
+
25
+ **Interactive** (requires a real terminal):
26
+
27
+ ```bash
28
+ create-app
29
+ ```
30
+
31
+ **Non-interactive** (CI or any terminal):
32
+
33
+ ```bash
34
+ create-app --name my-shop --mobile expo --fe nextjs --be nestjs
35
+ ```
36
+
37
+ ### Options
38
+
39
+ | Flag | Description |
40
+ |----------------------|------------------------------------------|
41
+ | `-n`, `--name <name>`| Project name |
42
+ | `--mobile <id>` | `rn` · `expo` · `flutter` |
43
+ | `--fe <id>` | `nextjs` · `reactjs` |
44
+ | `--be <id>` | `nodejs` · `nestjs` · `php` |
45
+ | `-h`, `--help` | Print help |
46
+ | `-v`, `--version` | Print version |
47
+
48
+ ## How it works
49
+
50
+ | File | Responsibility |
51
+ |---------------|----------------|
52
+ | `index.js` | Entry point. Parses flags, decides interactive vs. non-interactive, runs the generator. |
53
+ | `registry.js` | **Data declaration** of groups, technologies, `templatePath`, and `lang`. The only file to edit when adding or removing a stack. |
54
+ | `prompts.js` | Interactive prompts (`@clack/prompts`): project name → multi-select groups → select one technology per group. |
55
+ | `generate.js` | Copies templates, renames `_package.json` → `package.json`, generates the root `package.json` + `pnpm-workspace.yaml`, conditionally creates `packages/shared`, and substitutes `{{PROJECT_NAME}}`. |
16
56
 
17
- | File | Vai trò |
18
- |---------------|---------|
19
- | `index.js` | entry point, parse args (`--help`, `--version`), gọi prompt rồi generate |
20
- | `registry.js` | **khai báo dạng data**: các nhóm + tech + `templatePath` + `lang`. Thêm/bớt template chỉ sửa file này |
21
- | `prompts.js` | logic hỏi tương tác (`@clack/prompts`): tên project → multiselect nhóm → select tech mỗi nhóm |
22
- | `generate.js` | copy template, rename `_package.json`→`package.json`, sinh `package.json` + `pnpm-workspace.yaml` gốc động, tạo `packages/shared` có điều kiện, thay `{{PROJECT_NAME}}` |
57
+ ## Template conventions
23
58
 
24
- ## Quy ước template
59
+ - A manifest named `_package.json` is renamed to `package.json` on generate. Likewise
60
+ `_gitignore` → `.gitignore` and `_npmrc` → `.npmrc`.
61
+ - Any text file containing `{{PROJECT_NAME}}` has it replaced with the real project name.
62
+ - `packages/shared` is created only when **≥ 2** selections have `lang === 'js'`.
25
63
 
26
- - File đặt tên `_package.json` → generate thành `package.json`. Tương tự `_gitignore` `.gitignore`, `_npmrc` `.npmrc`.
27
- - Mọi file text chứa `{{PROJECT_NAME}}` sẽ được thay bằng tên project thật.
28
- - `packages/shared` chỉ được tạo khi có **>= 2** lựa chọn `lang === 'js'`.
64
+ See **[CONTRIBUTING.md](../../CONTRIBUTING.md)** for the full workflow of adding real source.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-du-app",
3
- "version": "0.1.0",
4
- "description": "CLI generator: chọn template (Mobile/FE/BE) sinh ra monorepo sản phẩm",
3
+ "version": "0.1.1",
4
+ "description": "CLI generator: pick templates (Mobile/FE/BE) and scaffold a product monorepo",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "create-app": "src/index.js"
package/src/generate.js CHANGED
@@ -1,7 +1,7 @@
1
- // generate.js — copy template + rename + sinh package.json động + replace placeholder.
1
+ // generate.js — copy templates + rename + generate dynamic package.json + replace placeholders.
2
2
  //
3
- // Đầu vào: plan từ prompts.js → { projectName, selections: [{group, option}] }
4
- // `repoRoot` (root của company-starter, nơi chứa templates/).
3
+ // Input: the plan from prompts.js → { projectName, selections: [{group, option}] }
4
+ // and `repoRoot` (the company-starter root that contains templates/).
5
5
 
6
6
  import path from 'node:path';
7
7
  import fse from 'fs-extra';
@@ -12,15 +12,15 @@ import {
12
12
 
13
13
  const PLACEHOLDER = /\{\{PROJECT_NAME\}\}/g;
14
14
 
15
- // File trong template được đặt tên "an toàn" để npm/tooling repo gốc không quét nhầm.
16
- // Khi generate ra sản phẩm thật thì rename lại tên chuẩn.
15
+ // Files inside a template are given "safe" names so the root repo's npm/tooling does not
16
+ // pick them up by mistake. When generating the real product, they are renamed back.
17
17
  const RENAME_MAP = {
18
18
  '_package.json': 'package.json',
19
19
  '_gitignore': '.gitignore',
20
20
  '_npmrc': '.npmrc',
21
21
  };
22
22
 
23
- // Phần mở rộng được coi là "text" → sẽ thay {{PROJECT_NAME}}.
23
+ // Extensions treated as "text" → {{PROJECT_NAME}} gets replaced.
24
24
  const TEXT_EXT = new Set([
25
25
  '.json', '.js', '.jsx', '.ts', '.tsx', '.md', '.txt', '.yml', '.yaml',
26
26
  '.env', '.html', '.css', '.scss', '.gitignore', '.npmrc', '.dart', '.php',
@@ -36,11 +36,11 @@ function renameBasename(name) {
36
36
  return RENAME_MAP[name] ?? name;
37
37
  }
38
38
 
39
- // Copy 1 template folder → đích, vừa rename file vừa replace {{PROJECT_NAME}}.
39
+ // Copy one template folder → destination, renaming files and replacing {{PROJECT_NAME}}.
40
40
  async function copyTemplate(srcDir, destDir, projectName) {
41
- // Template rỗng (chưa bỏ source thật vào) vẫn phải chạy được.
41
+ // An empty template (real source not dropped in yet) must still work.
42
42
  if (!(await fse.pathExists(srcDir))) {
43
- throw new Error(`Không tìm thấy template: ${srcDir}`);
43
+ throw new Error(`Template not found: ${srcDir}`);
44
44
  }
45
45
 
46
46
  await fse.ensureDir(destDir);
@@ -66,7 +66,7 @@ async function copyTemplate(srcDir, destDir, projectName) {
66
66
  }
67
67
  }
68
68
 
69
- // Sinh package.json gốc của project động (pnpm: KHÔNG có field "workspaces").
69
+ // Build the project's root package.json dynamically (pnpm: NO "workspaces" field).
70
70
  function buildRootPackageJson(projectName) {
71
71
  return {
72
72
  name: projectName,
@@ -77,8 +77,8 @@ function buildRootPackageJson(projectName) {
77
77
  };
78
78
  }
79
79
 
80
- // Sinh nội dung pnpm-workspace.yaml.
81
- // packages: chỉ các apps/* thực sự tạo + luôn packages/*.
80
+ // Build the contents of pnpm-workspace.yaml.
81
+ // packages: only the apps/* actually created + always packages/*.
82
82
  function buildPnpmWorkspaceYaml(createdAppDirs) {
83
83
  const entries = [...createdAppDirs, 'packages/*'];
84
84
  const lines = entries.map((p) => ` - '${p}'`);
@@ -91,12 +91,12 @@ export async function generate(plan, repoRoot, cwd = process.cwd()) {
91
91
  const logs = [];
92
92
 
93
93
  if (await fse.pathExists(projectRoot)) {
94
- throw new Error(`Thư mục "${projectName}" đã tồn tại tại ${projectRoot}.`);
94
+ throw new Error(`Directory "${projectName}" already exists at ${projectRoot}.`);
95
95
  }
96
96
 
97
97
  await fse.ensureDir(projectRoot);
98
98
 
99
- // 1) Copy từng app đã chọn.
99
+ // 1) Copy each selected app.
100
100
  const createdAppDirs = [];
101
101
  for (const { group, option } of selections) {
102
102
  const srcDir = path.resolve(repoRoot, option.templatePath);
@@ -106,18 +106,18 @@ export async function generate(plan, repoRoot, cwd = process.cwd()) {
106
106
  logs.push(`✓ ${group.label} (${option.label}) → ${group.outputDir}`);
107
107
  }
108
108
 
109
- // 2) packages/shared ĐIỀU KIỆN: >= 2 lựa chọn lang === 'js'.
109
+ // 2) packages/shared is CONDITIONAL: >= 2 selections with lang === 'js'.
110
110
  const jsCount = selections.filter((s) => s.option.lang === 'js').length;
111
111
  if (jsCount >= 2) {
112
112
  const srcDir = path.resolve(repoRoot, SHARED_TEMPLATE_PATH);
113
113
  const destDir = path.resolve(projectRoot, SHARED_OUTPUT_DIR);
114
114
  await copyTemplate(srcDir, destDir, projectName);
115
- logs.push(`✓ shared → ${SHARED_OUTPUT_DIR} (${jsCount} app JS/TS)`);
115
+ logs.push(`✓ shared → ${SHARED_OUTPUT_DIR} (${jsCount} JS/TS apps)`);
116
116
  } else {
117
- logs.push(`• Bỏ qua shared không đủ app JS/TS (cần >= 2, hiện ${jsCount}).`);
117
+ logs.push(`• Skipping shared: not enough JS/TS apps (need >= 2, have ${jsCount}).`);
118
118
  }
119
119
 
120
- // 3) Sinh package.json gốc + pnpm-workspace.yaml động.
120
+ // 3) Generate the root package.json + pnpm-workspace.yaml dynamically.
121
121
  const rootPkg = buildRootPackageJson(projectName);
122
122
  await fse.writeJson(path.join(projectRoot, 'package.json'), rootPkg, { spaces: 2 });
123
123
  logs.push('✓ package.json');
package/src/index.js CHANGED
@@ -1,19 +1,19 @@
1
1
  #!/usr/bin/env node
2
- // index.js — entry point. Parse args + chạy prompt (TTY) hoặc generate trực tiếp (flags).
2
+ // index.js — entry point. Parse args + run prompts (TTY) or generate directly (flags).
3
3
  //
4
- // Cách chạy:
5
- // create-app chạy tương tác (cần terminal thật)
6
- // create-app --name my-shop --be nestjs non-interactive (chạy mọi nơi)
4
+ // Usage:
5
+ // create-app interactive (requires a real terminal)
6
+ // create-app --name my-shop --be nestjs non-interactive (runs anywhere)
7
7
  // node src/index.js
8
8
  //
9
9
  // Flags:
10
- // -h, --help in trợ giúp
11
- // -v, --version in version
12
- // -n, --name <name> tên project (non-interactive)
10
+ // -h, --help print help
11
+ // -v, --version print version
12
+ // -n, --name <name> project name (non-interactive)
13
13
  // --mobile <id> rn | expo | flutter
14
14
  // --fe <id> nextjs | reactjs
15
15
  // --be <id> nodejs | nestjs | php
16
- // -y, --yes không hỏi, generate luôn (cần --name + >=1 nhóm)
16
+ // -y, --yes generate without prompting (needs --name + >=1 group)
17
17
 
18
18
  import path from 'node:path';
19
19
  import { fileURLToPath } from 'node:url';
@@ -24,11 +24,11 @@ import { GROUPS, findOption } from './registry.js';
24
24
 
25
25
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
26
26
 
27
- // Tìm thư mục gốc chứa templates/. Hỗ trợ 2 kịch bản:
28
- // 1) Chạy từ repo (clone): templates ../../../templates (root company-starter)
29
- // 2) Cài qua npm (đã publish): templates được đóng gói NGAY TRONG package CLI,
30
- // packages/cli/templates → ../templates
31
- // Chọn nơi nào thực sự tồn tại folder templates/.
27
+ // Resolve the root folder that contains templates/. Supports 2 scenarios:
28
+ // 1) Running from the repo (clone): templates at ../../../templates (company-starter root)
29
+ // 2) Installed via npm (published): templates are bundled INSIDE the CLI package,
30
+ // at packages/cli/templates → ../templates
31
+ // Pick whichever location actually has a templates/ folder.
32
32
  function resolveTemplatesRoot() {
33
33
  const candidates = [
34
34
  path.resolve(__dirname, '..'), // npm publish: cli/templates
@@ -37,12 +37,12 @@ function resolveTemplatesRoot() {
37
37
  for (const root of candidates) {
38
38
  if (fse.pathExistsSync(path.join(root, 'templates'))) return root;
39
39
  }
40
- // fallback: repo root (để thông báo lỗi của generate.js đủ rõ)
40
+ // fallback: repo root (so generate.js can emit a clear error)
41
41
  return path.resolve(__dirname, '..', '..', '..');
42
42
  }
43
43
  const REPO_ROOT = resolveTemplatesRoot();
44
44
 
45
- // map flag → groupId (để gợi ý lỗi cho gọn)
45
+ // Map flag → groupId (for concise error hints).
46
46
  const GROUP_FLAGS = { mobile: 'mobile', fe: 'fe', be: 'be' };
47
47
 
48
48
  async function readVersion() {
@@ -56,30 +56,30 @@ async function readVersion() {
56
56
 
57
57
  function printHelp() {
58
58
  console.log(`
59
- create-app — sinh monorepo sản phẩm từ template công ty
59
+ create-app — scaffold a product monorepo from company templates
60
60
 
61
- Cách dùng:
62
- create-app chạy tương tác (cần terminal thật)
63
- create-app --name my-shop --be nestjs non-interactive (chạy mọi nơi)
64
- node src/index.js tương đương (khi chưa link)
61
+ Usage:
62
+ create-app interactive (requires a real terminal)
63
+ create-app --name my-shop --be nestjs non-interactive (runs anywhere)
64
+ node src/index.js equivalent (when not linked)
65
65
 
66
- Tuỳ chọn:
67
- -h, --help in trợ giúp này
68
- -v, --version in version
69
- -n, --name <name> tên project
66
+ Options:
67
+ -h, --help print this help
68
+ -v, --version print version
69
+ -n, --name <name> project name
70
70
  --mobile <id> rn | expo | flutter
71
71
  --fe <id> nextjs | reactjs
72
72
  --be <id> nodejs | nestjs | php
73
- -y, --yes generate luôn không hỏi (cần --name + >=1 nhóm)
73
+ -y, --yes generate without prompting (needs --name + >=1 group)
74
74
 
75
- Ví dụ:
75
+ Example:
76
76
  create-app --name my-shop --mobile expo --fe nextjs --be nestjs
77
77
 
78
- Thêm/bớt template: sửa packages/cli/src/registry.js
78
+ Add/remove templates: edit packages/cli/src/registry.js
79
79
  `);
80
80
  }
81
81
 
82
- // Parse argv kiểu "--key value" / "-k value" + cờ boolean.
82
+ // Parse argv as "--key value" / "-k value" + boolean flags.
83
83
  function parseArgs(argv) {
84
84
  const out = { _flags: new Set() };
85
85
  for (let i = 0; i < argv.length; i++) {
@@ -93,25 +93,25 @@ function parseArgs(argv) {
93
93
  case '--fe': out.fe = argv[++i]; break;
94
94
  case '--be': out.be = argv[++i]; break;
95
95
  default:
96
- throw new Error(`Tham số không hợp lệ: ${a}. Xem: create-app --help`);
96
+ throw new Error(`Invalid argument: ${a}. See: create-app --help`);
97
97
  }
98
98
  }
99
99
  return out;
100
100
  }
101
101
 
102
- // bất kỳ flag chọn template nào không đi đường non-interactive.
102
+ // Any template-selection flag presentgo the non-interactive route.
103
103
  function hasSelectionFlags(args) {
104
104
  return Boolean(args.name || args.mobile || args.fe || args.be || args._flags.has('yes'));
105
105
  }
106
106
 
107
- // Dựng plan từ flags, validate từng id theo registry.
107
+ // Build a plan from flags, validating each id against the registry.
108
108
  function planFromFlags(args) {
109
109
  const name = (args.name ?? '').trim();
110
110
  if (!name) {
111
- throw new Error('Thiếu --name <tên-project>.');
111
+ throw new Error('Missing --name <project-name>.');
112
112
  }
113
113
  if (!/^[a-z0-9][a-z0-9-_]*$/i.test(name)) {
114
- throw new Error('Tên project chỉ gồm chữ/số/-/_ bắt đầu bằng chữ/số.');
114
+ throw new Error('Project name may only contain letters/numbers/-/_ and must start with a letter/number.');
115
115
  }
116
116
 
117
117
  const selections = [];
@@ -121,13 +121,13 @@ function planFromFlags(args) {
121
121
  const found = findOption(groupId, optionId);
122
122
  if (!found) {
123
123
  const valid = GROUPS.find((g) => g.id === groupId).options.map((o) => o.id).join(', ');
124
- throw new Error(`--${flag} "${optionId}" không hợp lệ. Hợp lệ: ${valid}.`);
124
+ throw new Error(`--${flag} "${optionId}" is invalid. Valid: ${valid}.`);
125
125
  }
126
126
  selections.push(found);
127
127
  }
128
128
 
129
129
  if (selections.length === 0) {
130
- throw new Error('Cần ít nhất 1 nhóm: --mobile / --fe / --be.');
130
+ throw new Error('At least one group is required: --mobile / --fe / --be.');
131
131
  }
132
132
  return { projectName: name, selections };
133
133
  }
@@ -144,17 +144,17 @@ async function main() {
144
144
  return;
145
145
  }
146
146
 
147
- // Quyết định luồng:
148
- // - flag chọn template → non-interactive
149
- // - không flag NHƯNG stdin không phải TTY → báo lỗi ràng thay treo
150
- // - còn lại tương tác
147
+ // Decide the flow:
148
+ // - selection flags present → non-interactive
149
+ // - no flags BUT stdin is not a TTY → fail with a clear error instead of hanging
150
+ // - otherwiseinteractive
151
151
  let plan;
152
152
  if (hasSelectionFlags(args)) {
153
153
  plan = planFromFlags(args);
154
154
  } else if (!process.stdin.isTTY) {
155
155
  throw new Error(
156
- 'Terminal hiện tại không hỗ trợ chế độ tương tác (stdin không phải TTY).\n' +
157
- ' → Chạy trong Terminal.app/iTerm, HOẶC dùng flags:\n' +
156
+ 'This terminal does not support interactive mode (stdin is not a TTY).\n' +
157
+ ' → Run it in Terminal.app/iTerm, OR use flags:\n' +
158
158
  ' create-app --name my-shop --mobile expo --fe nextjs --be nestjs',
159
159
  );
160
160
  } else {
@@ -168,13 +168,13 @@ async function main() {
168
168
  console.log('');
169
169
 
170
170
  const rel = path.relative(process.cwd(), projectRoot) || projectRoot;
171
- const msg = `Xong! Tiếp theo:\n\n cd ${rel} && pnpm install\n`;
172
- // outro() (clack) chỉ đẹp trong TTY; non-interactive thì in thường.
171
+ const msg = `Done! Next:\n\n cd ${rel} && pnpm install\n`;
172
+ // outro() (clack) only looks good in a TTY; print plainly in non-interactive mode.
173
173
  if (process.stdout.isTTY && !hasSelectionFlags(args)) outro(msg);
174
174
  else console.log(msg);
175
175
  }
176
176
 
177
177
  main().catch((err) => {
178
- console.error('\n✗ Lỗi:', err.message);
178
+ console.error('\n✗ Error:', err.message);
179
179
  process.exit(1);
180
180
  });
package/src/prompts.js CHANGED
@@ -1,8 +1,8 @@
1
- // prompts.js — logic hỏi tương tác.
1
+ // prompts.js — interactive prompt logic.
2
2
  //
3
- // Trả về một "plan" thuần data để generate.js thực thi:
3
+ // Returns a pure-data "plan" for generate.js to execute:
4
4
  // { projectName, selections: [{ group, option }, ...] }
5
- // Không làm I/O ra đĩa đây chỉ hỏi và validate.
5
+ // No disk I/O happens here this only asks and validates.
6
6
 
7
7
  import {
8
8
  intro,
@@ -17,47 +17,47 @@ import { GROUPS, findOption } from './registry.js';
17
17
 
18
18
  function ensureNotCancelled(value) {
19
19
  if (isCancel(value)) {
20
- cancel('Đã huỷ. Không có gì được tạo.');
20
+ cancel('Cancelled. Nothing was created.');
21
21
  process.exit(0);
22
22
  }
23
23
  return value;
24
24
  }
25
25
 
26
26
  export async function runPrompts() {
27
- intro('create-app — sinh monorepo sản phẩm từ template công ty');
27
+ intro('create-app — scaffold a product monorepo from company templates');
28
28
 
29
- // 1) Tên project
29
+ // 1) Project name
30
30
  const projectName = ensureNotCancelled(
31
31
  await text({
32
- message: 'Tên project?',
32
+ message: 'Project name?',
33
33
  placeholder: 'my-shop',
34
34
  validate(value) {
35
35
  const v = (value ?? '').trim();
36
- if (!v) return 'Tên project không được để trống.';
36
+ if (!v) return 'Project name must not be empty.';
37
37
  if (!/^[a-z0-9][a-z0-9-_]*$/i.test(v)) {
38
- return 'Chỉ dùng chữ, số, "-" hoặc "_" bắt đầu bằng chữ/số.';
38
+ return 'Use only letters, numbers, "-" or "_", and start with a letter/number.';
39
39
  }
40
40
  return undefined;
41
41
  },
42
42
  }),
43
43
  );
44
44
 
45
- // 2) Multi-select các nhóm cần (chọn được nhiều)
45
+ // 2) Multi-select the groups to include (you can pick several)
46
46
  const groupIds = ensureNotCancelled(
47
47
  await multiselect({
48
- message: 'Chọn các nhóm cần (Space để chọn, Enter để xác nhận):',
48
+ message: 'Select the groups you need (Space to select, Enter to confirm):',
49
49
  options: GROUPS.map((g) => ({ value: g.id, label: g.label })),
50
50
  required: true,
51
51
  }),
52
52
  );
53
53
 
54
- // 3) Với mỗi nhóm đã chọn → single-select 1 công nghệ
54
+ // 3) For each selected group → single-select one technology
55
55
  const selections = [];
56
56
  for (const groupId of groupIds) {
57
57
  const group = GROUPS.find((g) => g.id === groupId);
58
58
  const optionId = ensureNotCancelled(
59
59
  await select({
60
- message: `${group.label}: chọn 1 công nghệ`,
60
+ message: `${group.label}: pick one technology`,
61
61
  options: group.options.map((o) => ({ value: o.id, label: o.label })),
62
62
  }),
63
63
  );
package/src/registry.js CHANGED
@@ -1,15 +1,15 @@
1
- // registry.js — KHAI BÁO các nhóm + tech + đường dẫn template.
1
+ // registry.js — declares all groups + technologies + template paths.
2
2
  //
3
- // Đây nguồn dữ liệu duy nhất để thêm/bớt template. Muốn thêm một công nghệ mới
4
- // chỉ cần thêm một object vào `options` của nhóm tương ứng (hoặc thêm hẳn một nhóm).
5
- // KHÔNG cần đụng tới logic prompts.js / generate.js.
3
+ // This is the single source of truth for adding/removing templates. To add a new
4
+ // technology, just add one object to the relevant group's `options` (or add a whole
5
+ // new group). You do NOT need to touch the logic in prompts.js / generate.js.
6
6
  //
7
- // Mỗi option:
8
- // id : định danh ngắn (dùng nội bộ)
9
- // label : nhãn hiển thị khi hỏi
10
- // templatePath : đường dẫn template, TÍNH TỪ ROOT của repo company-starter này
7
+ // Each option:
8
+ // id : short identifier (used internally)
9
+ // label : display label shown when prompting
10
+ // templatePath : path to the template, RELATIVE TO the company-starter repo root
11
11
  // lang : 'js' | 'dart' | 'php' ...
12
- // Chỉ option lang === 'js' mới đủ điều kiện dùng packages/shared.
12
+ // Only options with lang === 'js' are eligible to use packages/shared.
13
13
 
14
14
  export const GROUPS = [
15
15
  {
@@ -43,11 +43,11 @@ export const GROUPS = [
43
43
  },
44
44
  ];
45
45
 
46
- // path tới folder template dùng chung (chỉ copy khi >= 2 lựa chọn lang === 'js')
46
+ // Path to the shared template folder (only copied when >= 2 selections have lang === 'js').
47
47
  export const SHARED_TEMPLATE_PATH = 'templates/shared';
48
48
  export const SHARED_OUTPUT_DIR = 'packages/shared';
49
49
 
50
- // Tra cứu nhanh 1 option theo (groupId, optionId).
50
+ // Quickly look up a single option by (groupId, optionId).
51
51
  export function findOption(groupId, optionId) {
52
52
  const group = GROUPS.find((g) => g.id === groupId);
53
53
  if (!group) return null;
@@ -1,11 +1,11 @@
1
1
  # Placeholder: NestJS (be/nestjs)
2
2
 
3
- Đây **placeholder** cho template **NestJS**.
3
+ This is a **placeholder** for the **NestJS** template.
4
4
 
5
- Bỏ source chuẩn của NestJS vào đây. Lưu ý quy ước:
5
+ Drop the standard NestJS source here. Conventions:
6
6
 
7
- - Đặt `package.json` của template thành **`_package.json`** (CLI sẽ tự rename lại khi generate).
8
- - Đặt `.gitignore` thành **`_gitignore`**, `.npmrc` thành **`_npmrc`** nếu có.
9
- - Bất kỳ chỗ nào cần tên project, ghi `{{PROJECT_NAME}}` — CLI sẽ thay bằng tên thật.
7
+ - Name the template's `package.json` as **`_package.json`** (the CLI renames it on generate).
8
+ - Name `.gitignore` as **`_gitignore`**, `.npmrc` as **`_npmrc`** if present.
9
+ - Wherever you need the project name, write `{{PROJECT_NAME}}` — the CLI replaces it with the real name.
10
10
 
11
- `lang: js` → app này được tính vào điều kiện tạo `packages/shared`.
11
+ `lang: js` → this app counts toward the condition for creating `packages/shared`.
@@ -2,5 +2,5 @@
2
2
  "name": "{{PROJECT_NAME}}-backend",
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
- "description": "Placeholder NestJS backend cho {{PROJECT_NAME}}"
5
+ "description": "Placeholder NestJS backend for {{PROJECT_NAME}}"
6
6
  }
@@ -1,11 +1,11 @@
1
1
  # Placeholder: Node.js (be/nodejs)
2
2
 
3
- Đây **placeholder** cho template **Node.js**.
3
+ This is a **placeholder** for the **Node.js** template.
4
4
 
5
- Bỏ source chuẩn của Node.js backend vào đây. Lưu ý quy ước:
5
+ Drop the standard Node.js backend source here. Conventions:
6
6
 
7
- - Đặt `package.json` của template thành **`_package.json`** (CLI sẽ tự rename lại khi generate).
8
- - Đặt `.gitignore` thành **`_gitignore`**, `.npmrc` thành **`_npmrc`** nếu có.
9
- - Bất kỳ chỗ nào cần tên project, ghi `{{PROJECT_NAME}}` — CLI sẽ thay bằng tên thật.
7
+ - Name the template's `package.json` as **`_package.json`** (the CLI renames it on generate).
8
+ - Name `.gitignore` as **`_gitignore`**, `.npmrc` as **`_npmrc`** if present.
9
+ - Wherever you need the project name, write `{{PROJECT_NAME}}` — the CLI replaces it with the real name.
10
10
 
11
- `lang: js` → app này được tính vào điều kiện tạo `packages/shared`.
11
+ `lang: js` → this app counts toward the condition for creating `packages/shared`.
@@ -2,5 +2,5 @@
2
2
  "name": "{{PROJECT_NAME}}-backend",
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
- "description": "Placeholder Node.js backend cho {{PROJECT_NAME}}"
5
+ "description": "Placeholder Node.js backend for {{PROJECT_NAME}}"
6
6
  }
@@ -1,15 +1,15 @@
1
1
  # Placeholder: PHP (be/php)
2
2
 
3
- Đây **placeholder** cho template **PHP**.
3
+ This is a **placeholder** for the **PHP** template.
4
4
 
5
- Bỏ source chuẩn của PHP backend vào đây.
5
+ Drop the standard PHP backend source here.
6
6
 
7
- > **Lưu ý riêng cho PHP (`lang: php`):**
8
- > - **Không** `_package.json` (PHP dùng `composer.json`, không phải npm).
9
- > - **Không** nằm trong npm workspaces của project.
10
- > - **Không** dùng `packages/shared` (shared chỉ dành cho hệ JS/TS).
7
+ > **Notes specific to PHP (`lang: php`):**
8
+ > - **No** `_package.json` (PHP uses `composer.json`, not npm).
9
+ > - **Not** part of the project's npm/pnpm workspaces.
10
+ > - **Does not** use `packages/shared` (shared is JS/TS only).
11
11
  >
12
- > Khi generate, CLI vẫn copy nguyên folder này vào `apps/backend` thay `{{PROJECT_NAME}}`
13
- > trong các file text (vd `composer.json`, `README.md`).
12
+ > On generate, the CLI still copies this whole folder into `apps/backend` and replaces
13
+ > `{{PROJECT_NAME}}` in text files (e.g. `composer.json`, `README.md`).
14
14
 
15
- Bạn thể đặt `{{PROJECT_NAME}}` trong `composer.json` để CLI tự điền tên project.
15
+ You can put `{{PROJECT_NAME}}` in `composer.json` and the CLI will fill in the project name.
@@ -1,11 +1,11 @@
1
1
  # Placeholder: Next.js (fe/nextjs)
2
2
 
3
- Đây **placeholder** cho template **Next.js**.
3
+ This is a **placeholder** for the **Next.js** template.
4
4
 
5
- Bỏ source chuẩn của Next.js vào đây. Lưu ý quy ước:
5
+ Drop the standard Next.js source here. Conventions:
6
6
 
7
- - Đặt `package.json` của template thành **`_package.json`** (CLI sẽ tự rename lại khi generate).
8
- - Đặt `.gitignore` thành **`_gitignore`**, `.npmrc` thành **`_npmrc`** nếu có.
9
- - Bất kỳ chỗ nào cần tên project, ghi `{{PROJECT_NAME}}` — CLI sẽ thay bằng tên thật.
7
+ - Name the template's `package.json` as **`_package.json`** (the CLI renames it on generate).
8
+ - Name `.gitignore` as **`_gitignore`**, `.npmrc` as **`_npmrc`** if present.
9
+ - Wherever you need the project name, write `{{PROJECT_NAME}}` — the CLI replaces it with the real name.
10
10
 
11
- `lang: js` → app này được tính vào điều kiện tạo `packages/shared`.
11
+ `lang: js` → this app counts toward the condition for creating `packages/shared`.
@@ -2,5 +2,5 @@
2
2
  "name": "{{PROJECT_NAME}}-web",
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
- "description": "Placeholder Next.js app cho {{PROJECT_NAME}}"
5
+ "description": "Placeholder Next.js app for {{PROJECT_NAME}}"
6
6
  }
@@ -1,11 +1,11 @@
1
1
  # Placeholder: ReactJS (fe/reactjs)
2
2
 
3
- Đây **placeholder** cho template **ReactJS**.
3
+ This is a **placeholder** for the **ReactJS** template.
4
4
 
5
- Bỏ source chuẩn của ReactJS vào đây. Lưu ý quy ước:
5
+ Drop the standard ReactJS source here. Conventions:
6
6
 
7
- - Đặt `package.json` của template thành **`_package.json`** (CLI sẽ tự rename lại khi generate).
8
- - Đặt `.gitignore` thành **`_gitignore`**, `.npmrc` thành **`_npmrc`** nếu có.
9
- - Bất kỳ chỗ nào cần tên project, ghi `{{PROJECT_NAME}}` — CLI sẽ thay bằng tên thật.
7
+ - Name the template's `package.json` as **`_package.json`** (the CLI renames it on generate).
8
+ - Name `.gitignore` as **`_gitignore`**, `.npmrc` as **`_npmrc`** if present.
9
+ - Wherever you need the project name, write `{{PROJECT_NAME}}` — the CLI replaces it with the real name.
10
10
 
11
- `lang: js` → app này được tính vào điều kiện tạo `packages/shared`.
11
+ `lang: js` → this app counts toward the condition for creating `packages/shared`.
@@ -2,5 +2,5 @@
2
2
  "name": "{{PROJECT_NAME}}-web",
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
- "description": "Placeholder ReactJS app cho {{PROJECT_NAME}}"
5
+ "description": "Placeholder ReactJS app for {{PROJECT_NAME}}"
6
6
  }
@@ -1,11 +1,11 @@
1
1
  # Placeholder: Expo (mobile/expo)
2
2
 
3
- Đây **placeholder** cho template **Expo**.
3
+ This is a **placeholder** for the **Expo** template.
4
4
 
5
- Bỏ source chuẩn của Expo vào đây. Lưu ý quy ước:
5
+ Drop the standard Expo source here. Conventions:
6
6
 
7
- - Đặt `package.json` của template thành **`_package.json`** (CLI sẽ tự rename lại khi generate).
8
- - Đặt `.gitignore` thành **`_gitignore`**, `.npmrc` thành **`_npmrc`** nếu có.
9
- - Bất kỳ chỗ nào cần tên project, ghi `{{PROJECT_NAME}}` — CLI sẽ thay bằng tên thật.
7
+ - Name the template's `package.json` as **`_package.json`** (the CLI renames it on generate).
8
+ - Name `.gitignore` as **`_gitignore`**, `.npmrc` as **`_npmrc`** if present.
9
+ - Wherever you need the project name, write `{{PROJECT_NAME}}` — the CLI replaces it with the real name.
10
10
 
11
- `lang: js` → app này được tính vào điều kiện tạo `packages/shared`.
11
+ `lang: js` → this app counts toward the condition for creating `packages/shared`.
@@ -2,5 +2,5 @@
2
2
  "name": "{{PROJECT_NAME}}-mobile",
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
- "description": "Placeholder Expo app cho {{PROJECT_NAME}}"
5
+ "description": "Placeholder Expo app for {{PROJECT_NAME}}"
6
6
  }
@@ -1,15 +1,15 @@
1
1
  # Placeholder: Flutter (mobile/flutter)
2
2
 
3
- Đây **placeholder** cho template **Flutter**.
3
+ This is a **placeholder** for the **Flutter** template.
4
4
 
5
- Bỏ source chuẩn của Flutter (Dart) vào đây.
5
+ Drop the standard Flutter (Dart) source here.
6
6
 
7
- > **Lưu ý riêng cho Flutter (`lang: dart`):**
8
- > - **Không** `_package.json` (Flutter dùng `pubspec.yaml`, không phải npm).
9
- > - **Không** nằm trong npm workspaces của project.
10
- > - **Không** dùng `packages/shared` (shared chỉ dành cho hệ JS/TS).
7
+ > **Notes specific to Flutter (`lang: dart`):**
8
+ > - **No** `_package.json` (Flutter uses `pubspec.yaml`, not npm).
9
+ > - **Not** part of the project's npm/pnpm workspaces.
10
+ > - **Does not** use `packages/shared` (shared is JS/TS only).
11
11
  >
12
- > Khi generate, CLI vẫn copy nguyên folder này vào `apps/mobile` thay `{{PROJECT_NAME}}`
13
- > trong các file text (vd `pubspec.yaml`, `README.md`).
12
+ > On generate, the CLI still copies this whole folder into `apps/mobile` and replaces
13
+ > `{{PROJECT_NAME}}` in text files (e.g. `pubspec.yaml`, `README.md`).
14
14
 
15
- Bạn thể đặt `{{PROJECT_NAME}}` trong `pubspec.yaml` để CLI tự điền tên project.
15
+ You can put `{{PROJECT_NAME}}` in `pubspec.yaml` and the CLI will fill in the project name.
@@ -1,11 +1,11 @@
1
1
  # Placeholder: React Native (mobile/rn)
2
2
 
3
- Đây **placeholder** cho template **React Native**.
3
+ This is a **placeholder** for the **React Native** template.
4
4
 
5
- Bỏ source chuẩn của React Native vào đây. Lưu ý quy ước:
5
+ Drop the standard React Native source here. Conventions:
6
6
 
7
- - Đặt `package.json` của template thành **`_package.json`** (CLI sẽ tự rename lại khi generate).
8
- - Đặt `.gitignore` thành **`_gitignore`**, `.npmrc` thành **`_npmrc`** nếu có.
9
- - Bất kỳ chỗ nào cần tên project, ghi `{{PROJECT_NAME}}` — CLI sẽ thay bằng tên thật.
7
+ - Name the template's `package.json` as **`_package.json`** (the CLI renames it on generate).
8
+ - Name `.gitignore` as **`_gitignore`**, `.npmrc` as **`_npmrc`** if present.
9
+ - Wherever you need the project name, write `{{PROJECT_NAME}}` — the CLI replaces it with the real name.
10
10
 
11
- `lang: js` → app này được tính vào điều kiện tạo `packages/shared`.
11
+ `lang: js` → this app counts toward the condition for creating `packages/shared`.
@@ -2,5 +2,5 @@
2
2
  "name": "{{PROJECT_NAME}}-mobile",
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
- "description": "Placeholder React Native app cho {{PROJECT_NAME}}"
5
+ "description": "Placeholder React Native app for {{PROJECT_NAME}}"
6
6
  }
@@ -1,11 +1,11 @@
1
1
  # Placeholder: shared (packages/shared)
2
2
 
3
- Đây **placeholder** cho package dùng chung của hệ **JS/TS**: types + api client.
3
+ This is a **placeholder** for the shared **JS/TS** package: shared types + api client.
4
4
 
5
- Bỏ source chuẩn (types, api client...) dùng chung cho các app JS/TS vào đây. Lưu ý quy ước:
5
+ Drop the standard shared source (types, api client, ...) for the JS/TS apps here. Conventions:
6
6
 
7
- - Đặt `package.json` của template thành **`_package.json`** (CLI sẽ tự rename lại khi generate).
8
- - Bất kỳ chỗ nào cần tên project, ghi `{{PROJECT_NAME}}` — CLI sẽ thay bằng tên thật.
7
+ - Name the template's `package.json` as **`_package.json`** (the CLI renames it on generate).
8
+ - Wherever you need the project name, write `{{PROJECT_NAME}}` — the CLI replaces it with the real name.
9
9
 
10
- > Package này **chỉ được tạo** khi project **>= 2** lựa chọn `lang === 'js'`.
11
- > Nếu ít hơn, CLI sẽ bỏ qua in thông báo.
10
+ > This package is **created only** when the project has **>= 2** selections with `lang === 'js'`.
11
+ > Otherwise the CLI skips it and prints a notice.
@@ -2,7 +2,7 @@
2
2
  "name": "@{{PROJECT_NAME}}/shared",
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
- "description": "Types + api client dùng chung cho {{PROJECT_NAME}}",
5
+ "description": "Shared types + api client for {{PROJECT_NAME}}",
6
6
  "main": "index.js",
7
7
  "type": "module"
8
8
  }
@@ -1,4 +1,4 @@
1
- // Package dùng chung cho {{PROJECT_NAME}}.
2
- // Placeholder — bỏ types + api client thật vào đây.
1
+ // Shared package for {{PROJECT_NAME}}.
2
+ // Placeholder — drop the real shared types + api client here.
3
3
 
4
4
  export const PROJECT_NAME = '{{PROJECT_NAME}}';