@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.
- package/dist/index.d.ts +33 -0
- package/dist/index.js +198 -0
- package/package.json +28 -0
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|