@doyuli/kits-core 0.0.1-beta.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.
@@ -0,0 +1,33 @@
1
+ //#region src/utils/command.d.ts
2
+ declare function getPackageManager(): "pnpm" | "yarn" | "bun" | "npm";
3
+ declare function getPackageCommand(packageManager: string, scriptName: string, args?: string): string;
4
+ declare function getOutroMessage(root: string, cwd: string): string;
5
+ //#endregion
6
+ //#region src/utils/directoryTraverse.d.ts
7
+ declare function preOrderDirectoryTraverse(dir: string, dirCallback: (dir: string) => void, fileCallback: (file: string) => void): void;
8
+ declare const dotGitDirectoryState: {
9
+ hasDotGitDirectory: boolean;
10
+ };
11
+ declare function postOrderDirectoryTraverse(dir: string, dirCallback: (dir: string) => void, fileCallback: (file: string) => void): void;
12
+ declare function canSkipEmptying(dir: string): boolean;
13
+ declare function emptyDir(dir: string): void;
14
+ //#endregion
15
+ //#region src/utils/prompts.d.ts
16
+ declare function unwrapPrompt<T>(maybeCancelPromise: Promise<T | symbol>): Promise<T>;
17
+ //#endregion
18
+ //#region src/utils/renderTemplate.d.ts
19
+ /**
20
+ * Renders a template folder/file to the file system,
21
+ * by recursively copying all files under the `src` directory,
22
+ * with the following exception:
23
+ * - `_filename` should be renamed to `.filename`
24
+ * - Fields in `package.json` should be recursively merged
25
+ * @param {string} src source filename to copy
26
+ * @param {string} dest destination filename of the copy operation
27
+ */
28
+ declare function renderTemplate(src: string, dest: string, callbacks: any[]): void;
29
+ //#endregion
30
+ //#region src/utils/render.d.ts
31
+ declare function renderFile(root: string, fileName: string, content: string): void;
32
+ //#endregion
33
+ export { canSkipEmptying, dotGitDirectoryState, emptyDir, getOutroMessage, getPackageCommand, getPackageManager, postOrderDirectoryTraverse, preOrderDirectoryTraverse, renderFile, renderTemplate, unwrapPrompt };
package/dist/index.js ADDED
@@ -0,0 +1,198 @@
1
+ import * as path from "node:path";
2
+ import { relative, resolve } from "node:path";
3
+ import process from "node:process";
4
+ import pico from "picocolors";
5
+ import * as fs from "node:fs";
6
+ import { writeFileSync } from "node:fs";
7
+ import { cancel, isCancel } from "@clack/prompts";
8
+ import { pathToFileURL } from "node:url";
9
+
10
+ //#region src/utils/command.ts
11
+ function getPackageManager() {
12
+ const userAgent = process.env.npm_config_user_agent ?? "";
13
+ return /pnpm/.test(userAgent) ? "pnpm" : /yarn/.test(userAgent) ? "yarn" : /bun/.test(userAgent) ? "bun" : "npm";
14
+ }
15
+ function getPackageCommand(packageManager, scriptName, args) {
16
+ if (scriptName === "install") return packageManager === "yarn" ? "yarn" : `${packageManager} install`;
17
+ if (scriptName === "build") return packageManager === "npm" || packageManager === "bun" ? `${packageManager} run build` : `${packageManager} build`;
18
+ if (args) return packageManager === "npm" ? `npm run ${scriptName} -- ${args}` : `${packageManager} ${scriptName} ${args}`;
19
+ else return packageManager === "npm" ? `npm run ${scriptName}` : `${packageManager} ${scriptName}`;
20
+ }
21
+ function getOutroMessage(root, cwd) {
22
+ const packageManager = getPackageManager();
23
+ let message = `项目初始化完成,可执行以下命令:\n\n`;
24
+ if (root !== cwd) {
25
+ const cdProjectName = relative(cwd, root);
26
+ message += ` ${pico.bold(pico.green(`cd ${cdProjectName.includes(" ") ? `"${cdProjectName}"` : cdProjectName}`))}\n`;
27
+ }
28
+ message += ` ${pico.bold(pico.green(getPackageCommand(packageManager, "install")))}\n`;
29
+ message += ` ${pico.bold(pico.green(getPackageCommand(packageManager, "lint:fix")))}\n`;
30
+ message += ` ${pico.bold(pico.green(getPackageCommand(packageManager, "dev")))}\n`;
31
+ return message;
32
+ }
33
+
34
+ //#endregion
35
+ //#region src/utils/directoryTraverse.ts
36
+ function preOrderDirectoryTraverse(dir, dirCallback, fileCallback) {
37
+ for (const filename of fs.readdirSync(dir)) {
38
+ if (filename === ".git") continue;
39
+ const fullpath = path.resolve(dir, filename);
40
+ if (fs.lstatSync(fullpath).isDirectory()) {
41
+ dirCallback(fullpath);
42
+ if (fs.existsSync(fullpath)) preOrderDirectoryTraverse(fullpath, dirCallback, fileCallback);
43
+ continue;
44
+ }
45
+ fileCallback(fullpath);
46
+ }
47
+ }
48
+ const dotGitDirectoryState = { hasDotGitDirectory: false };
49
+ function postOrderDirectoryTraverse(dir, dirCallback, fileCallback) {
50
+ for (const filename of fs.readdirSync(dir)) {
51
+ if (filename === ".git") {
52
+ dotGitDirectoryState.hasDotGitDirectory = true;
53
+ continue;
54
+ }
55
+ const fullpath = path.resolve(dir, filename);
56
+ if (fs.lstatSync(fullpath).isDirectory()) {
57
+ postOrderDirectoryTraverse(fullpath, dirCallback, fileCallback);
58
+ dirCallback(fullpath);
59
+ continue;
60
+ }
61
+ fileCallback(fullpath);
62
+ }
63
+ }
64
+ function canSkipEmptying(dir) {
65
+ if (!fs.existsSync(dir)) return true;
66
+ const files = fs.readdirSync(dir);
67
+ if (files.length === 0) return true;
68
+ if (files.length === 1 && files[0] === ".git") {
69
+ dotGitDirectoryState.hasDotGitDirectory = true;
70
+ return true;
71
+ }
72
+ return false;
73
+ }
74
+ function emptyDir(dir) {
75
+ if (!fs.existsSync(dir)) return;
76
+ postOrderDirectoryTraverse(dir, (dir$1) => fs.rmdirSync(dir$1), (file) => fs.unlinkSync(file));
77
+ }
78
+
79
+ //#endregion
80
+ //#region src/utils/prompts.ts
81
+ async function unwrapPrompt(maybeCancelPromise) {
82
+ const result = await maybeCancelPromise;
83
+ if (isCancel(result)) {
84
+ cancel(`${pico.red("✖")} 操作取消`);
85
+ process.exit(0);
86
+ }
87
+ return result;
88
+ }
89
+
90
+ //#endregion
91
+ //#region src/utils/deepMerge.ts
92
+ const isObject = (val) => val && typeof val === "object";
93
+ const mergeArrayWithDedupe = (a, b) => Array.from(new Set([...a, ...b]));
94
+ /**
95
+ * Recursively merge the content of the new object to the existing one
96
+ * @param {object} target the existing object
97
+ * @param {object} obj the new object
98
+ */
99
+ function deepMerge(target, obj) {
100
+ for (const key of Object.keys(obj)) {
101
+ const oldVal = target[key];
102
+ const newVal = obj[key];
103
+ if (Array.isArray(oldVal) && Array.isArray(newVal)) target[key] = mergeArrayWithDedupe(oldVal, newVal);
104
+ else if (isObject(oldVal) && isObject(newVal)) target[key] = deepMerge(oldVal, newVal);
105
+ else target[key] = newVal;
106
+ }
107
+ return target;
108
+ }
109
+ var deepMerge_default = deepMerge;
110
+
111
+ //#endregion
112
+ //#region src/utils/sortDependencies.ts
113
+ function sortDependencies(packageJson) {
114
+ const sorted = {};
115
+ const depTypes = [
116
+ "dependencies",
117
+ "devDependencies",
118
+ "peerDependencies",
119
+ "optionalDependencies"
120
+ ];
121
+ for (const depType of depTypes) if (packageJson[depType]) {
122
+ sorted[depType] = {};
123
+ Object.keys(packageJson[depType]).sort().forEach((name) => {
124
+ sorted[depType][name] = packageJson[depType][name];
125
+ });
126
+ }
127
+ return {
128
+ ...packageJson,
129
+ ...sorted
130
+ };
131
+ }
132
+
133
+ //#endregion
134
+ //#region src/utils/renderTemplate.ts
135
+ /**
136
+ * Renders a template folder/file to the file system,
137
+ * by recursively copying all files under the `src` directory,
138
+ * with the following exception:
139
+ * - `_filename` should be renamed to `.filename`
140
+ * - Fields in `package.json` should be recursively merged
141
+ * @param {string} src source filename to copy
142
+ * @param {string} dest destination filename of the copy operation
143
+ */
144
+ function renderTemplate(src, dest, callbacks) {
145
+ if (fs.statSync(src).isDirectory()) {
146
+ if (path.basename(src) === "node_modules") return;
147
+ fs.mkdirSync(dest, { recursive: true });
148
+ for (const file of fs.readdirSync(src)) renderTemplate(path.resolve(src, file), path.resolve(dest, file), callbacks);
149
+ return;
150
+ }
151
+ const filename = path.basename(src);
152
+ if (filename === "package.json" && fs.existsSync(dest)) {
153
+ const existing = JSON.parse(fs.readFileSync(dest, "utf8"));
154
+ const newPackage = JSON.parse(fs.readFileSync(src, "utf8"));
155
+ const pkg = sortDependencies(deepMerge_default(existing, newPackage));
156
+ fs.writeFileSync(dest, `${JSON.stringify(pkg, null, 2)}\n`);
157
+ return;
158
+ }
159
+ if (filename === "extensions.json" && fs.existsSync(dest)) {
160
+ const existing = JSON.parse(fs.readFileSync(dest, "utf8"));
161
+ const newExtensions = JSON.parse(fs.readFileSync(src, "utf8"));
162
+ const extensions = deepMerge_default(existing, newExtensions);
163
+ fs.writeFileSync(dest, `${JSON.stringify(extensions, null, 2)}\n`);
164
+ return;
165
+ }
166
+ if (filename === "settings.json" && fs.existsSync(dest)) {
167
+ const existing = JSON.parse(fs.readFileSync(dest, "utf8"));
168
+ const newSettings = JSON.parse(fs.readFileSync(src, "utf8"));
169
+ const settings = deepMerge_default(existing, newSettings);
170
+ fs.writeFileSync(dest, `${JSON.stringify(settings, null, 2)}\n`);
171
+ return;
172
+ }
173
+ if (filename.startsWith("_")) dest = path.resolve(path.dirname(dest), filename.replace(/^_/, "."));
174
+ if (filename === "_gitignore" && fs.existsSync(dest)) {
175
+ const existing = fs.readFileSync(dest, "utf8");
176
+ const newGitignore = fs.readFileSync(src, "utf8");
177
+ fs.writeFileSync(dest, `${existing}\n${newGitignore}`);
178
+ return;
179
+ }
180
+ if (filename.endsWith(".data.mjs")) {
181
+ dest = dest.replace(/\.data\.mjs$/, "");
182
+ callbacks.push(async (dataStore) => {
183
+ const getData = (await import(pathToFileURL(src).toString())).default;
184
+ dataStore[dest] = await getData({ oldData: dataStore[dest] || {} });
185
+ });
186
+ return;
187
+ }
188
+ fs.copyFileSync(src, dest);
189
+ }
190
+
191
+ //#endregion
192
+ //#region src/utils/render.ts
193
+ function renderFile(root, fileName, content) {
194
+ writeFileSync(resolve(root, fileName), content, "utf-8");
195
+ }
196
+
197
+ //#endregion
198
+ export { canSkipEmptying, dotGitDirectoryState, emptyDir, getOutroMessage, getPackageCommand, getPackageManager, postOrderDirectoryTraverse, preOrderDirectoryTraverse, renderFile, renderTemplate, unwrapPrompt };
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@doyuli/kits-core",
3
+ "type": "module",
4
+ "version": "0.0.1-beta.1",
5
+ "description": "@doyuli/kits-core",
6
+ "sideEffects": false,
7
+ "exports": {
8
+ ".": "./dist/index.js",
9
+ "./package.json": "./package.json"
10
+ },
11
+ "main": "./dist/index.js",
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "engines": {
18
+ "node": "^20.19.0 || >=22.12.0"
19
+ },
20
+ "dependencies": {
21
+ "@clack/prompts": "^0.11.0",
22
+ "picocolors": "^1.1.1"
23
+ },
24
+ "scripts": {
25
+ "dev": "tsdown --watch",
26
+ "build": "tsdown"
27
+ }
28
+ }