create-bubbles 0.1.24 → 0.1.25

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/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ import e from"node:fs";import t from"node:path";import{fileURLToPath as n}from"node:url";import*as r from"@clack/prompts";import i from"cross-spawn";import a from"gradient-string";import o from"mri";const s={vue:a([`#42B883`,`#42B883`]),react:a([`#087EA4`,`#087EA4`]),taro:a([`#0000C2`,`#fff`]),nextjs:a([`#000`,`#fff`]),others:a([`#8B5CF6`,`#A855F7`])},c=o(process.argv.slice(2),{alias:{h:`help`,t:`template`},boolean:[`help`,`overwrite`],string:[`template`]}),l=process.cwd(),u=`\
2
+ Usage: create-bubbles [OPTION]... [DIRECTORY]
3
+
4
+ Create a new Bubbles project in JavaScript or TypeScript.
5
+ With no arguments, start the CLI in interactive mode.
6
+
7
+ Options:
8
+ -t, --template NAME use a specific template
9
+
10
+ Available templates:
11
+ ${s.vue(`vue-vp-eslint vue`)}
12
+ ${s.vue(`vp-vue-eslint-vapor vue`)}
13
+ ${s.react(`react-rsbuild-biome react`)}
14
+ ${s.react(`vp-react react`)}
15
+ ${s.react(`vp-react-shadcn react`)}
16
+ ${s.react(`vp-monorepo-react-nestjs react`)}
17
+ ${s.taro(`taro-vue-eslint taro`)}
18
+ ${s.nextjs(`nextjs-vinext-eslint nextjs`)}`,d=e=>e.trim().replace(/\/+$/g,``),f=e=>{if(!e)return;let t=e.split(` `)[0].split(`/`);return{name:t[0],version:t[1]}},p=t=>{let n=e.readdirSync(t);return n.length===0||n.length===1&&n[0]===`.git`},m=async n=>{await Promise.all(e.readdirSync(n).filter(e=>e!==`.git`).map(r=>e.promises.rm(t.resolve(n,r),{recursive:!0,force:!0})))},h=e=>/^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(e),g=e=>e.trim().toLowerCase().replace(/\s+/g,`-`).replace(/^[._]/,``).replace(/[^a-z\d\-~]+g/,`-`),_=[{name:`vue`,display:`Vue`,color:s.vue,variants:[{name:`vue-vp-eslint`,display:`vp + eslint`,color:s.vue},{name:`vp-vue-eslint-vapor`,display:`vp + eslint + vapor`,color:s.vue}]},{name:`react`,display:`React`,color:s.react,variants:[{name:`react-rsbuild-biome`,display:`rsbuild-biome`,color:s.react},{name:`vp-react`,display:`vp-react`,color:s.react},{name:`vp-react-shadcn`,display:`vp-react-shadcn`,color:s.react},{name:`vp-monorepo-react-nestjs`,display:`vp-monorepo-react-nestjs`,color:s.react}]},{name:`taro`,display:`Taro`,color:s.taro,variants:[{name:`taro-vue-eslint`,display:`taro-vue + eslint`,color:s.taro},{name:`taro-react-oxc`,display:`taro-react + oxc`,color:s.taro}]},{name:`nextjs`,display:`NextJS`,color:s.nextjs,variants:[{name:`nextjs-vinext-eslint`,display:`nextjs + vinext + eslint`,color:s.nextjs}]},{name:`others`,display:`Others`,color:s.others,variants:[{name:`create-eletron-vite`,display:`Electron ↗`,color:s.others,customCommand:`pnpm create electron-vite@latest TARGET_DIR`}]}],v=_.map(e=>e.variants.map(e=>`${e.name}`)).reduce((e,t)=>e.concat(t),[]),y=(e,t)=>{console.log(`💦customCommand`,e,t);let n=t?t.name:`npm`,r=n===`yarn`&&t?.version.startsWith(`1.`);return e.replace(/^npm create (?:-- )?/,()=>n===`bun`?`bun x create-`:n===`pnpm`?`pnpm create `:e.startsWith(`npm create -- `)?`${n} create -- `:`${n} create `).replace(`@latest`,()=>r?``:`@latest`).replace(/^npm exec/,()=>n===`pnpm`?`pnpm dlx`:n===`yarn`&&!r?`yarn dlx`:n===`bun`?`bun x`:`npm exec`)},b={_gitignore:`.gitignore`},x=(n,r)=>{e.mkdirSync(r,{recursive:!0});for(let i of e.readdirSync(n)){let e=t.resolve(n,i),a=t.resolve(r,i);S(e,a)}},S=(t,n)=>{e.statSync(t).isDirectory()?x(t,n):e.copyFileSync(t,n)},C=(t,n)=>{let r=e.readFileSync(t,`utf-8`);e.writeFileSync(t,n(r),`utf-8`)},w=(e,n)=>{C(t.resolve(e,`package.json`),e=>e.replace(/"@vitejs\/plugin-react": ".+?"/,`"@vitejs/plugin-react-swc": "^4.0.1"`)),C(t.resolve(e,`vite.config.${n?`ts`:`js`}`),e=>e.replace(`@vitejs/plugin-react`,`@vitejs/plugin-react-swc`))};(async()=>{console.log(c);let o=c._[0]?d(c._[0]):void 0,x=c.template,C=c.overwrite,T=`bubbles-project`;if(c.help){console.log(u);return}let E=f(process.env.npm_config_user_agent),D=()=>r.cancel(`Operation cancelled`),O=o;if(!O){let e=await r.text({message:`Project name`,defaultValue:T,placeholder:T,validate:e=>(e||[]).length===0||d(e||``).length>0?void 0:`Invalid project name`});if(r.isCancel(e))return D();O=d(e)}if(e.existsSync(O)&&!p(O)){let e=C?`yes`:await r.select({message:O===`.`?`Current directory`:`Target directory "${O}" is not empty. Please choose how to proceed:`,options:[{label:`${s.vue(`Cancel operation`)}`,value:`no`},{label:`Remove existing files and continue`,value:`yes`},{label:`Ignore files and continue`,value:`ignore`}]});if(r.isCancel(e))return D();switch(e){case`yes`:m(O);break;case`no`:D();return}}let k=t.basename(t.resolve(O));if(!h(k)){let e=await r.text({message:`Package name is invalid. please input again:`,defaultValue:g(k),placeholder:g(k),validate(e){if(!h(e))return`Invalid package.json name`}});if(r.isCancel(e))return D();k=e}let A=x,j=!1;if(x&&!v.includes(x)&&(A=void 0,j=!0),!A){let e=await r.select({message:j?`"${x}" isn't a valia template. please choose from below:`:`Select a framework`,options:_.map(e=>{let t=e.color;return{label:t(e.display),value:e}})});if(r.isCancel(e))return D();console.log(`💦pkgInfo`,E);let t=await r.select({message:`Select a variant:`,options:e.variants.map(e=>{let t=e.color,n=e.customCommand?y(e.customCommand,E).replace(/ TARGET_DIR$/,``):void 0;return{label:t(e.display||e.name),value:e.name,hint:n}})});if(r.isCancel(t))return D();A=t}let M=t.join(l,O);e.mkdirSync(M,{recursive:!0});let N=!1;A.includes(`-swc`)&&(N=!0,A=A.replace(`-swc`,``));let P=E?E.name:`npm`,{customCommand:F}=_.flatMap(e=>e.variants).find(e=>e.name===A)??{};if(F){let[e,...t]=y(F,E).split(` `),n=t.map(e=>e.replace(`TARGET_DIR`,()=>O));console.log(`💦replacedArgs`,n);let{status:r}=i.sync(e,n,{stdio:`inherit`});process.exit(r??0)}r.log.step(`scaffolding project in ${M}...`);let I=t.resolve(n(import.meta.url),`../..`,`template-${A}`),L=(n,r)=>{let i=t.join(M,b[n]??n);r?e.writeFileSync(i,r):S(t.join(I,n),i)},R=e.readdirSync(I);for(let e of R)e!==`package.json`&&L(e);let z=JSON.parse(e.readFileSync(t.join(I,`package.json`),`utf-8`));z.name=k,L(`package.json`,`${JSON.stringify(z,null,2)}\n`),N&&w(M,A.endsWith(`-ts`));let B=``,V=t.relative(l,M);switch(console.log(`💦cwd`,l),console.log(`💦root`,M),console.log(`💦cdProjectName`,V),B+=`Done. Now run:
19
+ `,M!==l&&(B+=`\n cd ${V.includes(` `)?`"${V}"`:V} `),P){case`yarn`:B+=`
20
+ yarn`,B+=`
21
+ yarn dev`;break;default:B+=a([`pink`,`white`])(`\n ${P} install`),B+=a([`pink`,`white`])(`\n ${P} run dev`);break}r.outro(B)})().catch(e=>{console.error(`💦`,e)});export{};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-bubbles",
3
- "version": "0.1.24",
3
+ "version": "0.1.25",
4
4
  "homepage": "https://github.com/435012357/bubblesjs/tree/master/packages/create-bubbles#readme",
5
5
  "bugs": {
6
6
  "url": "https://github.com/435012357/bubblesjs/issues"
@@ -36,7 +36,6 @@
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "^24.3.0",
39
- "tsdown": "^0.22.14",
40
39
  "vite": "npm:@voidzero-dev/vite-plus-core@0.2.4",
41
40
  "vite-plus": "0.2.4"
42
41
  },
package/dist/index.mjs DELETED
@@ -1,422 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
- import * as prompts from "@clack/prompts";
5
- import spawn from "cross-spawn";
6
- import gradient from "gradient-string";
7
- import mri from "mri";
8
- //#region src/index.ts
9
- const colorMap = {
10
- vue: gradient(["#42B883", "#42B883"]),
11
- react: gradient(["#087EA4", "#087EA4"]),
12
- taro: gradient(["#0000C2", "#fff"]),
13
- nextjs: gradient(["#000", "#fff"]),
14
- others: gradient(["#8B5CF6", "#A855F7"])
15
- };
16
- /**
17
- * process.argv 是 node index.js 前两个参数
18
- * 第一个参数是 node 可执行文件的路径
19
- * 第二个参数是当前执行的脚本文件的路径
20
- * 虽然最终使用的是 pnpm create bubbles 但最后都会指向bin 执行 node index.js
21
- */
22
- const argv = mri(process.argv.slice(2), {
23
- alias: {
24
- h: "help",
25
- t: "template"
26
- },
27
- boolean: ["help", "overwrite"],
28
- string: ["template"]
29
- });
30
- /** 执行命令的绝对路径 代指执行命令的地方 */
31
- const cwd = process.cwd();
32
- const helpMessage = `\
33
- Usage: create-bubbles [OPTION]... [DIRECTORY]
34
-
35
- Create a new Bubbles project in JavaScript or TypeScript.
36
- With no arguments, start the CLI in interactive mode.
37
-
38
- Options:
39
- -t, --template NAME use a specific template
40
-
41
- Available templates:
42
- ${colorMap.vue("vue-vp-eslint vue")}
43
- ${colorMap.vue("vp-vue-eslint-vapor vue")}
44
- ${colorMap.react("react-rsbuild-biome react")}
45
- ${colorMap.react("vp-react react")}
46
- ${colorMap.react("vp-react-shadcn react")}
47
- ${colorMap.react("vp-monorepo-react-nestjs react")}
48
- ${colorMap.taro("taro-vue-eslint taro")}
49
- ${colorMap.nextjs("nextjs-vinext-eslint nextjs")}`;
50
- /**
51
- * 去除空格并且替换掉末尾一个或者多个“/”
52
- * @param targetDir
53
- * @returns
54
- */
55
- const formatTargetDir = (targetDir) => {
56
- return targetDir.trim().replace(/\/+$/g, "");
57
- };
58
- const pkgFromUserAgent = (userAgent) => {
59
- if (!userAgent) return;
60
- const pkgSpecArr = userAgent.split(" ")[0].split("/");
61
- return {
62
- name: pkgSpecArr[0],
63
- version: pkgSpecArr[1]
64
- };
65
- };
66
- /**
67
- * 要么没有文件要么只有一个.git 文件夹
68
- * @param path
69
- * @returns
70
- */
71
- const isEmpty = (path) => {
72
- const files = fs.readdirSync(path);
73
- return files.length === 0 || files.length === 1 && files[0] === ".git";
74
- };
75
- const emptyDir = async (dir) => {
76
- await Promise.all(fs.readdirSync(dir).filter((file) => file !== ".git").map((file) => fs.promises.rm(path.resolve(dir, file), {
77
- recursive: true,
78
- force: true
79
- })));
80
- };
81
- /**
82
- * 验证包名是否满足以 @ - * ~ 字母 数字 开头 并且
83
- * 第一个?: 代表非捕获模式 第二个代表 可有可无
84
- * @scope/abc abc
85
- * (?:@[a-z\d\-*~][a-z\d\-*._~]*\/)? => 匹配 @scope/
86
- * [a-z\d\-~][a-z\d\-._~]* 匹配 / 后面
87
- *
88
- * @param packageName
89
- * @returns
90
- */
91
- const isValidPackageName = (packageName) => {
92
- return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(packageName);
93
- };
94
- /**
95
- * 把不合法的包名元素替换掉
96
- * @param packageName
97
- * @returns
98
- */
99
- const toValidPackageName = (packageName) => {
100
- return packageName.trim().toLowerCase().replace(/\s+/g, "-").replace(/^[._]/, "").replace(/[^a-z\d\-~]+g/, "-");
101
- };
102
- const FRAMEWORKS = [
103
- {
104
- name: "vue",
105
- display: "Vue",
106
- color: colorMap.vue,
107
- variants: [{
108
- name: "vue-vp-eslint",
109
- display: "vp + eslint",
110
- color: colorMap.vue
111
- }, {
112
- name: "vp-vue-eslint-vapor",
113
- display: "vp + eslint + vapor",
114
- color: colorMap.vue
115
- }]
116
- },
117
- {
118
- name: "react",
119
- display: "React",
120
- color: colorMap.react,
121
- variants: [
122
- {
123
- name: "react-rsbuild-biome",
124
- display: "rsbuild-biome",
125
- color: colorMap.react
126
- },
127
- {
128
- name: "vp-react",
129
- display: "vp-react",
130
- color: colorMap.react
131
- },
132
- {
133
- name: "vp-react-shadcn",
134
- display: "vp-react-shadcn",
135
- color: colorMap.react
136
- },
137
- {
138
- name: "vp-monorepo-react-nestjs",
139
- display: "vp-monorepo-react-nestjs",
140
- color: colorMap.react
141
- }
142
- ]
143
- },
144
- {
145
- name: "taro",
146
- display: "Taro",
147
- color: colorMap.taro,
148
- variants: [{
149
- name: "taro-vue-eslint",
150
- display: "taro-vue + eslint",
151
- color: colorMap.taro
152
- }, {
153
- name: "taro-react-oxc",
154
- display: "taro-react + oxc",
155
- color: colorMap.taro
156
- }]
157
- },
158
- {
159
- name: "nextjs",
160
- display: "NextJS",
161
- color: colorMap.nextjs,
162
- variants: [{
163
- name: "nextjs-vinext-eslint",
164
- display: "nextjs + vinext + eslint",
165
- color: colorMap.nextjs
166
- }]
167
- },
168
- {
169
- name: "others",
170
- display: "Others",
171
- color: colorMap.others,
172
- variants: [{
173
- name: "create-eletron-vite",
174
- display: "Electron ↗",
175
- color: colorMap.others,
176
- customCommand: "pnpm create electron-vite@latest TARGET_DIR"
177
- }]
178
- }
179
- ];
180
- const TEMPLATES = FRAMEWORKS.map((f) => f.variants.map((v) => `${v.name}`)).reduce((a, b) => a.concat(b), []);
181
- /**
182
- *
183
- * @param customCommand
184
- * @param pkgInfo { name: 'pnpm', version: '10.0.0' }
185
- * @returns
186
- */
187
- const getFullCustomCommand = (customCommand, pkgInfo) => {
188
- console.log("💦customCommand", customCommand, pkgInfo);
189
- const pkgManager = pkgInfo ? pkgInfo.name : "npm";
190
- const isYarn1 = pkgManager === "yarn" && pkgInfo?.version.startsWith("1.");
191
- return customCommand.replace(/^npm create (?:-- )?/, () => {
192
- if (pkgManager === "bun") return "bun x create-";
193
- if (pkgManager === "pnpm") return "pnpm create ";
194
- /** 这里的 -- 时参数分隔符 前面的给 npm create 后面的 给 create-vite 这个脚手架 */
195
- return customCommand.startsWith("npm create -- ") ? `${pkgManager} create -- ` : `${pkgManager} create `;
196
- }).replace("@latest", () => isYarn1 ? "" : "@latest").replace(/^npm exec/, () => {
197
- if (pkgManager === "pnpm") return "pnpm dlx";
198
- if (pkgManager === "yarn" && !isYarn1) return "yarn dlx";
199
- if (pkgManager === "bun") return "bun x";
200
- return "npm exec";
201
- });
202
- };
203
- /**
204
- * 历史遗留问题 npmjs 会把.gitignore 忽略掉,于是vite官方把.g
205
- */
206
- const renameFiles = { _gitignore: ".gitignore" };
207
- const copyDir = (srcDir, destDir) => {
208
- fs.mkdirSync(destDir, { recursive: true });
209
- for (const file of fs.readdirSync(srcDir)) {
210
- const srcFile = path.resolve(srcDir, file);
211
- const destFile = path.resolve(destDir, file);
212
- copy(srcFile, destFile);
213
- }
214
- };
215
- const copy = (src, dest) => {
216
- if (fs.statSync(src).isDirectory()) copyDir(src, dest);
217
- else fs.copyFileSync(src, dest);
218
- };
219
- /**
220
- * 修改文件内容
221
- * @param file 文件路径
222
- * @param callback 修改内容的回调 参数是当前内容
223
- */
224
- const editFile = (file, callback) => {
225
- const content = fs.readFileSync(file, "utf-8");
226
- fs.writeFileSync(file, callback(content), "utf-8");
227
- };
228
- /**
229
- * 对 react swc 的处理 就是将plugin-react 换成 plugin-react-swc
230
- * @param src 源目录
231
- * @param dest 目标目录
232
- */
233
- const setupReactSwc = (root, isTs) => {
234
- const reactSwcPluginVersion = "4.0.1";
235
- editFile(path.resolve(root, "package.json"), (content) => {
236
- return content.replace(/"@vitejs\/plugin-react": ".+?"/, `"@vitejs/plugin-react-swc": "^${reactSwcPluginVersion}"`);
237
- });
238
- editFile(path.resolve(root, `vite.config.${isTs ? "ts" : "js"}`), (content) => content.replace("@vitejs/plugin-react", "@vitejs/plugin-react-swc"));
239
- };
240
- const init = async () => {
241
- console.log(argv);
242
- /**
243
- * 取的是那些没有 --key value的参数 这里代指模板
244
- * bun index.js template-vue -t vue -s 12321 template-react
245
- * {
246
- * _: [ "template-vue", "template-react" ],
247
- * t: "vue",
248
- * s: 12321,
249
- * template: "vue",
250
- * }
251
- */
252
- const argTargetDir = argv._[0] ? formatTargetDir(argv._[0]) : void 0;
253
- const argTemplate = argv.template;
254
- const argOverwrite = argv.overwrite;
255
- const defaultTargetDir = "bubbles-project";
256
- if (argv.help) {
257
- console.log(helpMessage);
258
- return;
259
- }
260
- /**
261
- * 获取用户执行命令的包管理工具的详细信息 包含 name 和 版本
262
- *
263
- * - bun x node packages/create-bubbles-tsdown/index.js
264
- * - pnpm exec node packages/create-bubbles-tsdown/index.js
265
- * - 或 npm exec node packages/create-bubbles-tsdown/index.js
266
- */
267
- const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent);
268
- const cancel = () => prompts.cancel("Operation cancelled");
269
- /** 项目名 */
270
- let targetDir = argTargetDir;
271
- if (!targetDir) {
272
- const projectName = await prompts.text({
273
- message: "Project name",
274
- defaultValue: defaultTargetDir,
275
- placeholder: defaultTargetDir,
276
- validate: (value) => {
277
- return (value || []).length === 0 || formatTargetDir(value || "").length > 0 ? void 0 : "Invalid project name";
278
- }
279
- });
280
- if (prompts.isCancel(projectName)) return cancel();
281
- targetDir = formatTargetDir(projectName);
282
- }
283
- if (fs.existsSync(targetDir) && !isEmpty(targetDir)) {
284
- const overwrite = argOverwrite ? "yes" : await prompts.select({
285
- message: targetDir === "." ? "Current directory" : `Target directory "${targetDir}" is not empty. Please choose how to proceed:`,
286
- options: [
287
- {
288
- label: `${colorMap.vue("Cancel operation")}`,
289
- value: "no"
290
- },
291
- {
292
- label: "Remove existing files and continue",
293
- value: "yes"
294
- },
295
- {
296
- label: "Ignore files and continue",
297
- value: "ignore"
298
- }
299
- ]
300
- });
301
- if (prompts.isCancel(overwrite)) return cancel();
302
- switch (overwrite) {
303
- case "yes":
304
- emptyDir(targetDir);
305
- break;
306
- case "no":
307
- cancel();
308
- return;
309
- }
310
- }
311
- /** 提取绝对路径最后的path 与 targetDir 不同 因为 targetDir 可以输入 xxx/xxx */
312
- let packageName = path.basename(path.resolve(targetDir));
313
- if (!isValidPackageName(packageName)) {
314
- const packageNameResult = await prompts.text({
315
- message: "Package name is invalid. please input again:",
316
- defaultValue: toValidPackageName(packageName),
317
- placeholder: toValidPackageName(packageName),
318
- validate(dir) {
319
- if (!isValidPackageName(dir)) return "Invalid package.json name";
320
- }
321
- });
322
- if (prompts.isCancel(packageNameResult)) return cancel();
323
- packageName = packageNameResult;
324
- }
325
- let template = argTemplate;
326
- let hasInvalidArgTemplate = false;
327
- /** 输入的-t 模板不能存在 */
328
- if (argTemplate && !TEMPLATES.includes(argTemplate)) {
329
- template = void 0;
330
- hasInvalidArgTemplate = true;
331
- }
332
- if (!template) {
333
- const framework = await prompts.select({
334
- message: hasInvalidArgTemplate ? `"${argTemplate}" isn't a valia template. please choose from below:` : "Select a framework",
335
- options: FRAMEWORKS.map((framework) => {
336
- const frameworkColor = framework.color;
337
- return {
338
- label: frameworkColor(framework.display),
339
- value: framework
340
- };
341
- })
342
- });
343
- if (prompts.isCancel(framework)) return cancel();
344
- console.log("💦pkgInfo", pkgInfo);
345
- const variant = await prompts.select({
346
- message: "Select a variant:",
347
- options: framework.variants.map((variant) => {
348
- const variantColor = variant.color;
349
- const command = variant.customCommand ? getFullCustomCommand(variant.customCommand, pkgInfo).replace(/ TARGET_DIR$/, "") : void 0;
350
- return {
351
- label: variantColor(variant.display || variant.name),
352
- value: variant.name,
353
- hint: command
354
- };
355
- })
356
- });
357
- if (prompts.isCancel(variant)) return cancel();
358
- template = variant;
359
- }
360
- /** 合起来就是 项目文件夹的 绝对路径 */
361
- const root = path.join(cwd, targetDir);
362
- fs.mkdirSync(root, { recursive: true });
363
- let isReactSwc = false;
364
- if (template.includes("-swc")) {
365
- isReactSwc = true;
366
- template = template.replace("-swc", "");
367
- }
368
- /**
369
- * 用户输入命令的包管理工具
370
- */
371
- const pkgManager = pkgInfo ? pkgInfo.name : "npm";
372
- const { customCommand } = FRAMEWORKS.flatMap((f) => f.variants).find((v) => v.name === template) ?? {};
373
- if (customCommand) {
374
- const [command, ...args] = getFullCustomCommand(customCommand, pkgInfo).split(" ");
375
- const replacedArgs = args.map((arg) => arg.replace("TARGET_DIR", () => targetDir));
376
- console.log("💦replacedArgs", replacedArgs);
377
- /** 这里的 inherit 表示继承父进程的输入输出 */
378
- const { status } = spawn.sync(command, replacedArgs, { stdio: "inherit" });
379
- process.exit(status ?? 0);
380
- }
381
- prompts.log.step(`scaffolding project in ${root}...`);
382
- /** 选则的模板地址 */
383
- const templateDir = path.resolve(fileURLToPath(import.meta.url), "../..", `template-${template}`);
384
- const write = (file, content) => {
385
- const targetPath = path.join(root, renameFiles[file] ?? file);
386
- if (content) fs.writeFileSync(targetPath, content);
387
- else copy(path.join(templateDir, file), targetPath);
388
- };
389
- const files = fs.readdirSync(templateDir);
390
- for (const file of files) if (file !== "package.json") write(file);
391
- const pkg = JSON.parse(fs.readFileSync(path.join(templateDir, "package.json"), "utf-8"));
392
- pkg.name = packageName;
393
- /** null => replacer 主要是用于序列化的时候 做一些操作 数组 每一个属性都会执行该函数 如果是数组就是只有这些属性会留下
394
- * 2 代表 缩进级别
395
- */
396
- write("package.json", `${JSON.stringify(pkg, null, 2)}\n`);
397
- if (isReactSwc) setupReactSwc(root, template.endsWith("-ts"));
398
- let doneMessage = "";
399
- /** 克隆文件夹之后 目录的 的相对路径 其实就是 root 和 cwd 的差值 */
400
- const cdProjectName = path.relative(cwd, root);
401
- console.log("💦cwd", cwd);
402
- console.log("💦root", root);
403
- console.log("💦cdProjectName", cdProjectName);
404
- doneMessage += `Done. Now run:\n`;
405
- if (root !== cwd) doneMessage += `\n cd ${cdProjectName.includes(" ") ? `"${cdProjectName}"` : cdProjectName} `;
406
- switch (pkgManager) {
407
- case "yarn":
408
- doneMessage += `\n yarn`;
409
- doneMessage += `\n yarn dev`;
410
- break;
411
- default:
412
- doneMessage += gradient(["pink", "white"])(`\n ${pkgManager} install`);
413
- doneMessage += gradient(["pink", "white"])(`\n ${pkgManager} run dev`);
414
- break;
415
- }
416
- prompts.outro(doneMessage);
417
- };
418
- init().catch((e) => {
419
- console.error("💦", e);
420
- });
421
- //#endregion
422
- export {};