@spicato-inc/create-fortenv 0.0.0-stage → 0.1.0

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.
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env node
2
+ import * as p from '@clack/prompts';
3
+ import { spawn } from 'node:child_process';
4
+ import { access, cp, mkdir, readFile, writeFile } from 'node:fs/promises';
5
+ import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+
8
+ const TEMPLATES = [
9
+ {
10
+ value: 'mpa',
11
+ label: 'mpa',
12
+ hint: '静的サイト(Astro)',
13
+ },
14
+ {
15
+ value: 'spa',
16
+ label: 'spa',
17
+ hint: '静的サイト(Astro + Swup)',
18
+ },
19
+ {
20
+ value: 'wp-mpa',
21
+ label: 'wp-mpa',
22
+ hint: 'WordPress',
23
+ },
24
+ {
25
+ value: 'wp-spa',
26
+ label: 'wp-spa',
27
+ hint: 'WordPress + Swup',
28
+ },
29
+ ];
30
+
31
+ const TEMPLATE_PACKAGES = {
32
+ mpa: '@spicato-inc/fortenv-mpa',
33
+ spa: '@spicato-inc/fortenv-spa',
34
+ 'wp-mpa': '@spicato-inc/fortenv-wp-mpa',
35
+ 'wp-spa': '@spicato-inc/fortenv-wp-spa',
36
+ };
37
+
38
+ const SKIP = new Set([
39
+ 'node_modules',
40
+ 'dist',
41
+ '.git',
42
+ '.astro',
43
+ 'package-lock.json',
44
+ '.DS_Store',
45
+ ]);
46
+
47
+ function parseArgs(argv) {
48
+ const args = { install: true };
49
+ for (let i = 0; i < argv.length; i += 1) {
50
+ const token = argv[i];
51
+ if (token === '--help' || token === '-h') {
52
+ args.help = true;
53
+ } else if (token === '--no-install') {
54
+ args.install = false;
55
+ } else if (token === '--type' || token === '--dir' || token === '--name') {
56
+ const value = argv[i + 1];
57
+ if (!value) {
58
+ throw new Error(`${token} には値が必要です`);
59
+ }
60
+ args[token.slice(2)] = value;
61
+ i += 1;
62
+ } else {
63
+ throw new Error(`不明な引数です: ${token}`);
64
+ }
65
+ }
66
+ return args;
67
+ }
68
+
69
+ function printHelp() {
70
+ console.log(`使い方
71
+ create-fortenv
72
+ create-fortenv --type <mpa|spa|wp-mpa|wp-spa> --dir <directory> --name <name>
73
+
74
+ オプション
75
+ --no-install コピー後の npm install を省略する
76
+ `);
77
+ }
78
+
79
+ function cancel() {
80
+ p.cancel('中止しました');
81
+ process.exit(0);
82
+ }
83
+
84
+ function templateDir(type) {
85
+ const packageName = TEMPLATE_PACKAGES[type];
86
+ const resolved = import.meta.resolve(`${packageName}/package.json`);
87
+ return path.dirname(fileURLToPath(resolved));
88
+ }
89
+
90
+ async function exists(target) {
91
+ try {
92
+ await access(target);
93
+ return true;
94
+ } catch {
95
+ return false;
96
+ }
97
+ }
98
+
99
+ async function assertTemplate(type) {
100
+ const dir = templateDir(type);
101
+ if (!(await exists(path.join(dir, 'package.json')))) {
102
+ throw new Error(`雛形が見つかりません: ${dir}`);
103
+ }
104
+ return dir;
105
+ }
106
+
107
+ async function promptValues(args) {
108
+ p.intro('fortenv');
109
+
110
+ let type = args.type;
111
+ if (!type) {
112
+ type = await p.select({
113
+ message: '開発環境を選んでください',
114
+ options: TEMPLATES,
115
+ });
116
+ if (p.isCancel(type)) cancel();
117
+ }
118
+ if (!TEMPLATES.some((item) => item.value === type)) {
119
+ throw new Error(`対応していない種類です: ${type}`);
120
+ }
121
+
122
+ let dir = args.dir;
123
+ if (!dir) {
124
+ dir = await p.text({
125
+ message: '作成先ディレクトリ',
126
+ placeholder: './my-site',
127
+ validate: (value) => (value.trim() ? undefined : 'ディレクトリを入力してください'),
128
+ });
129
+ if (p.isCancel(dir)) cancel();
130
+ }
131
+
132
+ const destination = path.resolve(process.cwd(), dir.trim());
133
+ let name = args.name;
134
+ if (!name) {
135
+ name = await p.text({
136
+ message: 'package.json の name',
137
+ initialValue: path.basename(destination),
138
+ validate: (value) => (value.trim() ? undefined : 'name を入力してください'),
139
+ });
140
+ if (p.isCancel(name)) cancel();
141
+ }
142
+
143
+ const scripted = Boolean(args.type && args.dir && args.name);
144
+ if (!scripted) {
145
+ const confirmed = await p.confirm({
146
+ message: `${type} を ${destination} に作成しますか?`,
147
+ });
148
+ if (p.isCancel(confirmed) || !confirmed) cancel();
149
+ }
150
+
151
+ return { type, destination, name: name.trim(), install: args.install };
152
+ }
153
+
154
+ async function copyTemplate(source, destination) {
155
+ await cp(source, destination, {
156
+ recursive: true,
157
+ filter: (entry) => !SKIP.has(path.basename(entry)),
158
+ });
159
+ }
160
+
161
+ async function rewritePackage(destination, name) {
162
+ const file = path.join(destination, 'package.json');
163
+ const pkg = JSON.parse(await readFile(file, 'utf8'));
164
+ pkg.name = name;
165
+ delete pkg.private;
166
+ await writeFile(file, `${JSON.stringify(pkg, null, '\t')}\n`);
167
+ }
168
+
169
+ function install(destination) {
170
+ return new Promise((resolve, reject) => {
171
+ const child = spawn('npm', ['install'], {
172
+ cwd: destination,
173
+ stdio: 'inherit',
174
+ });
175
+ child.on('error', reject);
176
+ child.on('exit', (code) => {
177
+ if (code === 0) resolve();
178
+ else reject(new Error(`npm install が終了コード ${code} で失敗しました`));
179
+ });
180
+ });
181
+ }
182
+
183
+ async function main() {
184
+ const args = parseArgs(process.argv.slice(2));
185
+ if (args.help) {
186
+ printHelp();
187
+ return;
188
+ }
189
+
190
+ const { type, destination, name, install: shouldInstall } = await promptValues(args);
191
+ const source = await assertTemplate(type);
192
+ if (await exists(destination)) {
193
+ throw new Error(`作成先が既にあります: ${destination}`);
194
+ }
195
+ await mkdir(path.dirname(destination), { recursive: true });
196
+
197
+ const spinner = p.spinner();
198
+ spinner.start('雛形をコピーしています');
199
+ await copyTemplate(source, destination);
200
+ await rewritePackage(destination, name);
201
+ spinner.stop('雛形をコピーしました');
202
+
203
+ if (shouldInstall) {
204
+ spinner.start('npm install を実行しています');
205
+ await install(destination);
206
+ spinner.stop('依存関係をインストールしました');
207
+ }
208
+
209
+ const relative = path.relative(process.cwd(), destination);
210
+ const shown = !relative || relative.startsWith('..') ? destination : relative;
211
+ p.outro(`作成しました。\n cd ${shown}\n npm run dev`);
212
+ }
213
+
214
+ main().catch((error) => {
215
+ p.log.error(error.message);
216
+ process.exit(1);
217
+ });
package/package.json CHANGED
@@ -1,6 +1,24 @@
1
1
  {
2
- "name": "@spicato-inc/create-fortenv",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
2
+ "name": "@spicato-inc/create-fortenv",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "bin": {
6
+ "create-fortenv": "./bin/create-fortenv.js"
7
+ },
8
+ "files": [
9
+ "bin"
10
+ ],
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
14
+ "dependencies": {
15
+ "@clack/prompts": "^1.8.1",
16
+ "@spicato-inc/fortenv-mpa": "5.1.1",
17
+ "@spicato-inc/fortenv-spa": "5.0.3",
18
+ "@spicato-inc/fortenv-wp-mpa": "4.1.0",
19
+ "@spicato-inc/fortenv-wp-spa": "5.0.1"
20
+ },
21
+ "engines": {
22
+ "node": ">=22.18.0"
23
+ }
24
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.