create-bubbles 0.1.17 → 0.1.19
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/LICENSE +21 -0
- package/dist/index.js +406 -11
- package/index.js +2 -2
- package/package.json +48 -49
- package/template-vp-monorepo-react-nestjs/.vscode/extensions.json +3 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-present, bubblesplant.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,36 @@
|
|
|
1
|
-
import
|
|
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
|
+
|
|
9
|
+
//#region src/index.ts
|
|
10
|
+
const colorMap = {
|
|
11
|
+
vue: gradient(["#42B883", "#42B883"]),
|
|
12
|
+
react: gradient(["#087EA4", "#087EA4"]),
|
|
13
|
+
taro: gradient(["#0000C2", "#fff"]),
|
|
14
|
+
nextjs: gradient(["#000", "#fff"]),
|
|
15
|
+
others: gradient(["#8B5CF6", "#A855F7"])
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* process.argv 是 node index.js 前两个参数
|
|
19
|
+
* 第一个参数是 node 可执行文件的路径
|
|
20
|
+
* 第二个参数是当前执行的脚本文件的路径
|
|
21
|
+
* 虽然最终使用的是 pnpm create bubbles 但最后都会指向bin 执行 node index.js
|
|
22
|
+
*/
|
|
23
|
+
const argv = mri(process.argv.slice(2), {
|
|
24
|
+
alias: {
|
|
25
|
+
h: "help",
|
|
26
|
+
t: "template"
|
|
27
|
+
},
|
|
28
|
+
boolean: ["help", "overwrite"],
|
|
29
|
+
string: ["template"]
|
|
30
|
+
});
|
|
31
|
+
/** 执行命令的绝对路径 代指执行命令的地方 */
|
|
32
|
+
const cwd = process.cwd();
|
|
33
|
+
const helpMessage = `\
|
|
2
34
|
Usage: create-bubbles [OPTION]... [DIRECTORY]
|
|
3
35
|
|
|
4
36
|
Create a new Bubbles project in JavaScript or TypeScript.
|
|
@@ -8,13 +40,376 @@ Options:
|
|
|
8
40
|
-t, --template NAME use a specific template
|
|
9
41
|
|
|
10
42
|
Available templates:
|
|
11
|
-
${
|
|
12
|
-
${
|
|
13
|
-
${
|
|
14
|
-
${
|
|
15
|
-
${
|
|
16
|
-
${
|
|
17
|
-
${
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
43
|
+
${colorMap.vue("vue-vite-eslint 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-vite-eslint",
|
|
109
|
+
display: "vite + eslint",
|
|
110
|
+
color: colorMap.vue
|
|
111
|
+
}]
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: "react",
|
|
115
|
+
display: "React",
|
|
116
|
+
color: colorMap.react,
|
|
117
|
+
variants: [
|
|
118
|
+
{
|
|
119
|
+
name: "react-rsbuild-biome",
|
|
120
|
+
display: "rsbuild-biome",
|
|
121
|
+
color: colorMap.react
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: "vp-react",
|
|
125
|
+
display: "vp-react",
|
|
126
|
+
color: colorMap.react
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: "vp-react-shadcn",
|
|
130
|
+
display: "vp-react-shadcn",
|
|
131
|
+
color: colorMap.react
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: "vp-monorepo-react-nestjs",
|
|
135
|
+
display: "vp-monorepo-react-nestjs",
|
|
136
|
+
color: colorMap.react
|
|
137
|
+
}
|
|
138
|
+
]
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: "taro",
|
|
142
|
+
display: "Taro",
|
|
143
|
+
color: colorMap.taro,
|
|
144
|
+
variants: [{
|
|
145
|
+
name: "taro-vue-eslint",
|
|
146
|
+
display: "taro-vue + eslint",
|
|
147
|
+
color: colorMap.taro
|
|
148
|
+
}, {
|
|
149
|
+
name: "taro-react-oxc",
|
|
150
|
+
display: "taro-react + oxc",
|
|
151
|
+
color: colorMap.taro
|
|
152
|
+
}]
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: "nextjs",
|
|
156
|
+
display: "NextJS",
|
|
157
|
+
color: colorMap.nextjs,
|
|
158
|
+
variants: [{
|
|
159
|
+
name: "nextjs-vinext-eslint",
|
|
160
|
+
display: "nextjs + vinext + eslint",
|
|
161
|
+
color: colorMap.nextjs
|
|
162
|
+
}]
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: "others",
|
|
166
|
+
display: "Others",
|
|
167
|
+
color: colorMap.others,
|
|
168
|
+
variants: [{
|
|
169
|
+
name: "create-eletron-vite",
|
|
170
|
+
display: "Electron ↗",
|
|
171
|
+
color: colorMap.others,
|
|
172
|
+
customCommand: "pnpm create electron-vite@latest TARGET_DIR"
|
|
173
|
+
}]
|
|
174
|
+
}
|
|
175
|
+
];
|
|
176
|
+
const TEMPLATES = FRAMEWORKS.map((f) => f.variants.map((v) => `${v.name}`)).reduce((a, b) => a.concat(b), []);
|
|
177
|
+
/**
|
|
178
|
+
*
|
|
179
|
+
* @param customCommand
|
|
180
|
+
* @param pkgInfo { name: 'pnpm', version: '10.0.0' }
|
|
181
|
+
* @returns
|
|
182
|
+
*/
|
|
183
|
+
const getFullCustomCommand = (customCommand, pkgInfo) => {
|
|
184
|
+
console.log("💦customCommand", customCommand, pkgInfo);
|
|
185
|
+
const pkgManager = pkgInfo ? pkgInfo.name : "npm";
|
|
186
|
+
const isYarn1 = pkgManager === "yarn" && pkgInfo?.version.startsWith("1.");
|
|
187
|
+
return customCommand.replace(/^npm create (?:-- )?/, () => {
|
|
188
|
+
if (pkgManager === "bun") return "bun x create-";
|
|
189
|
+
if (pkgManager === "pnpm") return "pnpm create ";
|
|
190
|
+
/** 这里的 -- 时参数分隔符 前面的给 npm create 后面的 给 create-vite 这个脚手架 */
|
|
191
|
+
return customCommand.startsWith("npm create -- ") ? `${pkgManager} create -- ` : `${pkgManager} create `;
|
|
192
|
+
}).replace("@latest", () => isYarn1 ? "" : "@latest").replace(/^npm exec/, () => {
|
|
193
|
+
if (pkgManager === "pnpm") return "pnpm dlx";
|
|
194
|
+
if (pkgManager === "yarn" && !isYarn1) return "yarn dlx";
|
|
195
|
+
if (pkgManager === "bun") return "bun x";
|
|
196
|
+
return "npm exec";
|
|
197
|
+
});
|
|
198
|
+
};
|
|
199
|
+
/**
|
|
200
|
+
* 历史遗留问题 npmjs 会把.gitignore 忽略掉,于是vite官方把.g
|
|
201
|
+
*/
|
|
202
|
+
const renameFiles = { _gitignore: ".gitignore" };
|
|
203
|
+
const copyDir = (srcDir, destDir) => {
|
|
204
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
205
|
+
for (const file of fs.readdirSync(srcDir)) copy(path.resolve(srcDir, file), path.resolve(destDir, file));
|
|
206
|
+
};
|
|
207
|
+
const copy = (src, dest) => {
|
|
208
|
+
if (fs.statSync(src).isDirectory()) copyDir(src, dest);
|
|
209
|
+
else fs.copyFileSync(src, dest);
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* 修改文件内容
|
|
213
|
+
* @param file 文件路径
|
|
214
|
+
* @param callback 修改内容的回调 参数是当前内容
|
|
215
|
+
*/
|
|
216
|
+
const editFile = (file, callback) => {
|
|
217
|
+
const content = fs.readFileSync(file, "utf-8");
|
|
218
|
+
fs.writeFileSync(file, callback(content), "utf-8");
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* 对 react swc 的处理 就是将plugin-react 换成 plugin-react-swc
|
|
222
|
+
* @param src 源目录
|
|
223
|
+
* @param dest 目标目录
|
|
224
|
+
*/
|
|
225
|
+
const setupReactSwc = (root, isTs) => {
|
|
226
|
+
const reactSwcPluginVersion = "4.0.1";
|
|
227
|
+
editFile(path.resolve(root, "package.json"), (content) => {
|
|
228
|
+
return content.replace(/"@vitejs\/plugin-react": ".+?"/, `"@vitejs/plugin-react-swc": "^${reactSwcPluginVersion}"`);
|
|
229
|
+
});
|
|
230
|
+
editFile(path.resolve(root, `vite.config.${isTs ? "ts" : "js"}`), (content) => content.replace("@vitejs/plugin-react", "@vitejs/plugin-react-swc"));
|
|
231
|
+
};
|
|
232
|
+
const init = async () => {
|
|
233
|
+
console.log(argv);
|
|
234
|
+
/**
|
|
235
|
+
* 取的是那些没有 --key value的参数 这里代指模板
|
|
236
|
+
* bun index.js template-vue -t vue -s 12321 template-react
|
|
237
|
+
* {
|
|
238
|
+
* _: [ "template-vue", "template-react" ],
|
|
239
|
+
* t: "vue",
|
|
240
|
+
* s: 12321,
|
|
241
|
+
* template: "vue",
|
|
242
|
+
* }
|
|
243
|
+
*/
|
|
244
|
+
const argTargetDir = argv._[0] ? formatTargetDir(argv._[0]) : void 0;
|
|
245
|
+
const argTemplate = argv.template;
|
|
246
|
+
const argOverwrite = argv.overwrite;
|
|
247
|
+
const defaultTargetDir = "bubbles-project";
|
|
248
|
+
if (argv.help) {
|
|
249
|
+
console.log(helpMessage);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* 获取用户执行命令的包管理工具的详细信息 包含 name 和 版本
|
|
254
|
+
*
|
|
255
|
+
* - bun x node packages/create-bubbles-tsdown/index.js
|
|
256
|
+
* - pnpm exec node packages/create-bubbles-tsdown/index.js
|
|
257
|
+
* - 或 npm exec node packages/create-bubbles-tsdown/index.js
|
|
258
|
+
*/
|
|
259
|
+
const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent);
|
|
260
|
+
const cancel = () => prompts.cancel("Operation cancelled");
|
|
261
|
+
/** 项目名 */
|
|
262
|
+
let targetDir = argTargetDir;
|
|
263
|
+
if (!targetDir) {
|
|
264
|
+
const projectName = await prompts.text({
|
|
265
|
+
message: "Project name",
|
|
266
|
+
defaultValue: defaultTargetDir,
|
|
267
|
+
placeholder: defaultTargetDir,
|
|
268
|
+
validate: (value) => {
|
|
269
|
+
return (value || []).length === 0 || formatTargetDir(value || "").length > 0 ? void 0 : "Invalid project name";
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
if (prompts.isCancel(projectName)) return cancel();
|
|
273
|
+
targetDir = formatTargetDir(projectName);
|
|
274
|
+
}
|
|
275
|
+
if (fs.existsSync(targetDir) && !isEmpty(targetDir)) {
|
|
276
|
+
const overwrite = argOverwrite ? "yes" : await prompts.select({
|
|
277
|
+
message: targetDir === "." ? "Current directory" : `Target directory "${targetDir}" is not empty. Please choose how to proceed:`,
|
|
278
|
+
options: [
|
|
279
|
+
{
|
|
280
|
+
label: `${colorMap.vue("Cancel operation")}`,
|
|
281
|
+
value: "no"
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
label: "Remove existing files and continue",
|
|
285
|
+
value: "yes"
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
label: "Ignore files and continue",
|
|
289
|
+
value: "ignore"
|
|
290
|
+
}
|
|
291
|
+
]
|
|
292
|
+
});
|
|
293
|
+
if (prompts.isCancel(overwrite)) return cancel();
|
|
294
|
+
switch (overwrite) {
|
|
295
|
+
case "yes":
|
|
296
|
+
emptyDir(targetDir);
|
|
297
|
+
break;
|
|
298
|
+
case "no":
|
|
299
|
+
cancel();
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
/** 提取绝对路径最后的path 与 targetDir 不同 因为 targetDir 可以输入 xxx/xxx */
|
|
304
|
+
let packageName = path.basename(path.resolve(targetDir));
|
|
305
|
+
if (!isValidPackageName(packageName)) {
|
|
306
|
+
const packageNameResult = await prompts.text({
|
|
307
|
+
message: "Package name is invalid. please input again:",
|
|
308
|
+
defaultValue: toValidPackageName(packageName),
|
|
309
|
+
placeholder: toValidPackageName(packageName),
|
|
310
|
+
validate(dir) {
|
|
311
|
+
if (!isValidPackageName(dir)) return "Invalid package.json name";
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
if (prompts.isCancel(packageNameResult)) return cancel();
|
|
315
|
+
packageName = packageNameResult;
|
|
316
|
+
}
|
|
317
|
+
let template = argTemplate;
|
|
318
|
+
let hasInvalidArgTemplate = false;
|
|
319
|
+
/** 输入的-t 模板不能存在 */
|
|
320
|
+
if (argTemplate && !TEMPLATES.includes(argTemplate)) {
|
|
321
|
+
template = void 0;
|
|
322
|
+
hasInvalidArgTemplate = true;
|
|
323
|
+
}
|
|
324
|
+
if (!template) {
|
|
325
|
+
const framework = await prompts.select({
|
|
326
|
+
message: hasInvalidArgTemplate ? `"${argTemplate}" isn't a valia template. please choose from below:` : "Select a framework",
|
|
327
|
+
options: FRAMEWORKS.map((framework) => {
|
|
328
|
+
const frameworkColor = framework.color;
|
|
329
|
+
return {
|
|
330
|
+
label: frameworkColor(framework.display),
|
|
331
|
+
value: framework
|
|
332
|
+
};
|
|
333
|
+
})
|
|
334
|
+
});
|
|
335
|
+
if (prompts.isCancel(framework)) return cancel();
|
|
336
|
+
console.log("💦pkgInfo", pkgInfo);
|
|
337
|
+
const variant = await prompts.select({
|
|
338
|
+
message: "Select a variant:",
|
|
339
|
+
options: framework.variants.map((variant) => {
|
|
340
|
+
const variantColor = variant.color;
|
|
341
|
+
const command = variant.customCommand ? getFullCustomCommand(variant.customCommand, pkgInfo).replace(/ TARGET_DIR$/, "") : void 0;
|
|
342
|
+
return {
|
|
343
|
+
label: variantColor(variant.display || variant.name),
|
|
344
|
+
value: variant.name,
|
|
345
|
+
hint: command
|
|
346
|
+
};
|
|
347
|
+
})
|
|
348
|
+
});
|
|
349
|
+
if (prompts.isCancel(variant)) return cancel();
|
|
350
|
+
template = variant;
|
|
351
|
+
}
|
|
352
|
+
/** 合起来就是 项目文件夹的 绝对路径 */
|
|
353
|
+
const root = path.join(cwd, targetDir);
|
|
354
|
+
fs.mkdirSync(root, { recursive: true });
|
|
355
|
+
let isReactSwc = false;
|
|
356
|
+
if (template.includes("-swc")) {
|
|
357
|
+
isReactSwc = true;
|
|
358
|
+
template = template.replace("-swc", "");
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* 用户输入命令的包管理工具
|
|
362
|
+
*/
|
|
363
|
+
const pkgManager = pkgInfo ? pkgInfo.name : "npm";
|
|
364
|
+
const { customCommand } = FRAMEWORKS.flatMap((f) => f.variants).find((v) => v.name === template) ?? {};
|
|
365
|
+
if (customCommand) {
|
|
366
|
+
const [command, ...args] = getFullCustomCommand(customCommand, pkgInfo).split(" ");
|
|
367
|
+
const replacedArgs = args.map((arg) => arg.replace("TARGET_DIR", () => targetDir));
|
|
368
|
+
console.log("💦replacedArgs", replacedArgs);
|
|
369
|
+
/** 这里的 inherit 表示继承父进程的输入输出 */
|
|
370
|
+
const { status } = spawn.sync(command, replacedArgs, { stdio: "inherit" });
|
|
371
|
+
process.exit(status ?? 0);
|
|
372
|
+
}
|
|
373
|
+
prompts.log.step(`scaffolding project in ${root}...`);
|
|
374
|
+
/** 选则的模板地址 */
|
|
375
|
+
const templateDir = path.resolve(fileURLToPath(import.meta.url), "../..", `template-${template}`);
|
|
376
|
+
const write = (file, content) => {
|
|
377
|
+
const targetPath = path.join(root, renameFiles[file] ?? file);
|
|
378
|
+
if (content) fs.writeFileSync(targetPath, content);
|
|
379
|
+
else copy(path.join(templateDir, file), targetPath);
|
|
380
|
+
};
|
|
381
|
+
const files = fs.readdirSync(templateDir);
|
|
382
|
+
for (const file of files) if (file !== "package.json") write(file);
|
|
383
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(templateDir, "package.json"), "utf-8"));
|
|
384
|
+
pkg.name = packageName;
|
|
385
|
+
/** null => replacer 主要是用于序列化的时候 做一些操作 数组 每一个属性都会执行该函数 如果是数组就是只有这些属性会留下
|
|
386
|
+
* 2 代表 缩进级别
|
|
387
|
+
*/
|
|
388
|
+
write("package.json", `${JSON.stringify(pkg, null, 2)}\n`);
|
|
389
|
+
if (isReactSwc) setupReactSwc(root, template.endsWith("-ts"));
|
|
390
|
+
let doneMessage = "";
|
|
391
|
+
/** 克隆文件夹之后 目录的 的相对路径 其实就是 root 和 cwd 的差值 */
|
|
392
|
+
const cdProjectName = path.relative(cwd, root);
|
|
393
|
+
console.log("💦cwd", cwd);
|
|
394
|
+
console.log("💦root", root);
|
|
395
|
+
console.log("💦cdProjectName", cdProjectName);
|
|
396
|
+
doneMessage += `Done. Now run:\n`;
|
|
397
|
+
if (root !== cwd) doneMessage += `\n cd ${cdProjectName.includes(" ") ? `"${cdProjectName}"` : cdProjectName} `;
|
|
398
|
+
switch (pkgManager) {
|
|
399
|
+
case "yarn":
|
|
400
|
+
doneMessage += `\n yarn`;
|
|
401
|
+
doneMessage += `\n yarn dev`;
|
|
402
|
+
break;
|
|
403
|
+
default:
|
|
404
|
+
doneMessage += gradient(["pink", "white"])(`\n ${pkgManager} install`);
|
|
405
|
+
doneMessage += gradient(["pink", "white"])(`\n ${pkgManager} run dev`);
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
prompts.outro(doneMessage);
|
|
409
|
+
};
|
|
410
|
+
init().catch((e) => {
|
|
411
|
+
console.error("💦", e);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
//#endregion
|
|
415
|
+
export { };
|
package/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
3
|
import './dist/index.js'
|
package/package.json
CHANGED
|
@@ -1,49 +1,48 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "create-bubbles",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"author": "bubbles plant",
|
|
6
|
-
"bin": {
|
|
7
|
-
"create-bubbles": "index.js",
|
|
8
|
-
"cbp": "index.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"index.js",
|
|
12
|
-
"template-*",
|
|
13
|
-
"dist"
|
|
14
|
-
],
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
},
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "create-bubbles",
|
|
3
|
+
"version": "0.1.19",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"author": "bubbles plant",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-bubbles": "index.js",
|
|
8
|
+
"cbp": "index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"index.js",
|
|
12
|
+
"template-*",
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": "^20.19.0 || >=22.12.0"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/435012357/bubblesjs.git",
|
|
21
|
+
"directory": "packages/create-bubbles"
|
|
22
|
+
},
|
|
23
|
+
"bugs": {
|
|
24
|
+
"url": "https://github.com/435012357/bubblesjs/issues"
|
|
25
|
+
},
|
|
26
|
+
"homepage": "https://github.com/435012357/bubblesjs/tree/master/packages/create-bubbles#readme",
|
|
27
|
+
"funding": "https://github.com/435012357/bubblesjs",
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^24.3.0",
|
|
30
|
+
"tsdown": "^0.22.2"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@clack/prompts": "^1.5.1",
|
|
34
|
+
"cross-spawn": "^7.0.6",
|
|
35
|
+
"gradient-string": "^3.0.0",
|
|
36
|
+
"mri": "^1.2.0",
|
|
37
|
+
"picocolors": "^1.1.1"
|
|
38
|
+
},
|
|
39
|
+
"publishConfig": {
|
|
40
|
+
"access": "public",
|
|
41
|
+
"registry": "https://registry.npmjs.org/"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"dev": "tsdown --watch",
|
|
45
|
+
"build": "tsdown",
|
|
46
|
+
"typecheck": "tsc"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
{
|
|
2
|
-
"recommendations": ["VoidZero.vite-plus-extension-pack"]
|
|
3
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"recommendations": ["VoidZero.vite-plus-extension-pack"]
|
|
3
|
+
}
|