create-valaxy 0.14.61 → 0.15.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/bin/index.mjs +4 -0
- package/build.config.ts +3 -0
- package/dist/index.mjs +275 -0
- package/package.json +11 -4
- package/src/config.ts +45 -0
- package/src/index.ts +5 -203
- package/src/theme.ts +46 -0
- package/src/utils.ts +73 -0
- package/src/valaxy.ts +200 -0
- package/{template → template-blog}/package.json +2 -2
- package/{template → template-blog}/tsconfig.json +10 -10
- package/template-theme/README.md +17 -0
- package/index.js +0 -198
- /package/{template → template-blog}/.dockerignore +0 -0
- /package/{template → template-blog}/.editorconfig +0 -0
- /package/{template → template-blog}/.github/workflows/gh-pages.yml +0 -0
- /package/{template → template-blog}/.vscode/extensions.json +0 -0
- /package/{template → template-blog}/.vscode/settings.json +0 -0
- /package/{template → template-blog}/Dockerfile +0 -0
- /package/{template → template-blog}/README.md +0 -0
- /package/{template → template-blog}/_gitignore +0 -0
- /package/{template → template-blog}/_npmrc +0 -0
- /package/{template → template-blog}/components/README.md +0 -0
- /package/{template → template-blog}/layouts/README.md +0 -0
- /package/{template → template-blog}/locales/README.md +0 -0
- /package/{template → template-blog}/locales/en.yml +0 -0
- /package/{template → template-blog}/locales/zh-CN.yml +0 -0
- /package/{template → template-blog}/netlify.toml +0 -0
- /package/{template → template-blog}/pages/404.md +0 -0
- /package/{template → template-blog}/pages/about/index.md +0 -0
- /package/{template → template-blog}/pages/about/site.md +0 -0
- /package/{template → template-blog}/pages/archives/index.md +0 -0
- /package/{template → template-blog}/pages/categories/index.md +0 -0
- /package/{template → template-blog}/pages/links/index.md +0 -0
- /package/{template → template-blog}/pages/posts/hello-valaxy.md +0 -0
- /package/{template → template-blog}/pages/tags/index.md +0 -0
- /package/{template → template-blog}/public/_headers +0 -0
- /package/{template → template-blog}/public/favicon.svg +0 -0
- /package/{template → template-blog}/public/pwa-192x192.png +0 -0
- /package/{template → template-blog}/public/pwa-512x512.png +0 -0
- /package/{template → template-blog}/public/safari-pinned-tab.svg +0 -0
- /package/{template → template-blog}/site.config.ts +0 -0
- /package/{template → template-blog}/styles/README.md +0 -0
- /package/{template → template-blog}/styles/css-vars.scss +0 -0
- /package/{template → template-blog}/styles/index.scss +0 -0
- /package/{template → template-blog}/valaxy.config.ts +0 -0
- /package/{template → template-blog}/vercel.json +0 -0
package/bin/index.mjs
ADDED
package/build.config.ts
ADDED
package/dist/index.mjs
ADDED
@@ -0,0 +1,275 @@
|
|
1
|
+
import process from 'node:process';
|
2
|
+
import fs from 'node:fs';
|
3
|
+
import path from 'node:path';
|
4
|
+
import { fileURLToPath } from 'node:url';
|
5
|
+
import { execa } from 'execa';
|
6
|
+
import { red, dim, gray, blue, yellow, bold, green, cyan, reset } from 'kolorist';
|
7
|
+
import minimist from 'minimist';
|
8
|
+
import prompts from 'prompts';
|
9
|
+
|
10
|
+
const version = "0.15.1";
|
11
|
+
|
12
|
+
function formatTargetDir(targetDir) {
|
13
|
+
return targetDir?.trim().replace(/\/+$/g, "");
|
14
|
+
}
|
15
|
+
function isEmpty(path2) {
|
16
|
+
const files = fs.readdirSync(path2);
|
17
|
+
return files.length === 0 || files.length === 1 && files[0] === ".git";
|
18
|
+
}
|
19
|
+
function copy(src, dest) {
|
20
|
+
const stat = fs.statSync(src);
|
21
|
+
if (stat.isDirectory())
|
22
|
+
copyDir(src, dest);
|
23
|
+
else
|
24
|
+
fs.copyFileSync(src, dest);
|
25
|
+
}
|
26
|
+
function copyDir(srcDir, destDir) {
|
27
|
+
fs.mkdirSync(destDir, { recursive: true });
|
28
|
+
for (const file of fs.readdirSync(srcDir)) {
|
29
|
+
const srcFile = path.resolve(srcDir, file);
|
30
|
+
const destFile = path.resolve(destDir, file);
|
31
|
+
copy(srcFile, destFile);
|
32
|
+
}
|
33
|
+
}
|
34
|
+
function emptyDir(dir) {
|
35
|
+
console.log(`
|
36
|
+
${red(`Removing`)} ${dim(dir)}`);
|
37
|
+
if (!fs.existsSync(dir))
|
38
|
+
return;
|
39
|
+
for (const file of fs.readdirSync(dir)) {
|
40
|
+
if (file === ".git")
|
41
|
+
continue;
|
42
|
+
fs.rmSync(path.resolve(dir, file), { recursive: true, force: true });
|
43
|
+
}
|
44
|
+
}
|
45
|
+
function isValidPackageName(projectName) {
|
46
|
+
return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(
|
47
|
+
projectName
|
48
|
+
);
|
49
|
+
}
|
50
|
+
function toValidPackageName(projectName) {
|
51
|
+
return projectName.trim().toLowerCase().replace(/\s+/g, "-").replace(/^[._]/, "").replace(/[^a-z\d\-~]+/g, "-");
|
52
|
+
}
|
53
|
+
function pkgFromUserAgent(userAgent) {
|
54
|
+
if (!userAgent)
|
55
|
+
return void 0;
|
56
|
+
const pkgSpec = userAgent.split(" ")[0];
|
57
|
+
const pkgSpecArr = pkgSpec.split("/");
|
58
|
+
return {
|
59
|
+
name: pkgSpecArr[0],
|
60
|
+
version: pkgSpecArr[1]
|
61
|
+
};
|
62
|
+
}
|
63
|
+
|
64
|
+
const starterTheme = {
|
65
|
+
name: "starter",
|
66
|
+
display: `Starter`,
|
67
|
+
repo: "https://github.com/valaxyjs/valaxy-theme-starter"
|
68
|
+
};
|
69
|
+
async function initTheme(options) {
|
70
|
+
const defaultThemeName = starterTheme.name;
|
71
|
+
let themeName = options.themeName || defaultThemeName;
|
72
|
+
if (!themeName) {
|
73
|
+
const { theme } = await prompts({
|
74
|
+
type: "text",
|
75
|
+
name: "theme",
|
76
|
+
message: "Theme name: valaxy-theme-",
|
77
|
+
initial: defaultThemeName
|
78
|
+
});
|
79
|
+
themeName = theme || defaultThemeName;
|
80
|
+
}
|
81
|
+
const targetDir = `valaxy-theme-${themeName.trim()}`;
|
82
|
+
console.log(` ${dim("npx")} ${gray("degit")} ${blue(starterTheme.repo)} ${yellow(targetDir)}`);
|
83
|
+
await execa("npx", ["degit", starterTheme.repo, targetDir], { stdio: "inherit" });
|
84
|
+
console.log();
|
85
|
+
console.log(` ${bold("Check it")}:`);
|
86
|
+
console.log();
|
87
|
+
console.log(`- Change ${bold("author")} name in ${yellow("LICENSE")} & ${green("package.json")} & ${blue(".github")}`);
|
88
|
+
console.log(`- Change ${blue("valaxy.config.ts")} theme: ${yellow("starter")} to ${cyan(`${themeName}`)}`);
|
89
|
+
console.log(`- Rename ${yellow(`valaxy-theme-${themeName}`)} to ${cyan(`valaxy-theme-${themeName}`)}`);
|
90
|
+
console.log();
|
91
|
+
console.log(` ${cyan("\u2728")}`);
|
92
|
+
console.log();
|
93
|
+
return `valaxy-theme-${themeName}`;
|
94
|
+
}
|
95
|
+
|
96
|
+
const renameFiles = {
|
97
|
+
_gitignore: ".gitignore",
|
98
|
+
_npmrc: ".npmrc"
|
99
|
+
};
|
100
|
+
const TEMPLATES = [
|
101
|
+
{
|
102
|
+
name: "blog",
|
103
|
+
display: `Blog`,
|
104
|
+
desc: "For Most Users",
|
105
|
+
message: "Project name:",
|
106
|
+
initial: "valaxy-blog",
|
107
|
+
color: cyan
|
108
|
+
},
|
109
|
+
{
|
110
|
+
name: "theme",
|
111
|
+
display: `Theme`,
|
112
|
+
desc: "For Theme Developers",
|
113
|
+
message: "Theme name: valaxy-theme-",
|
114
|
+
initial: "starter",
|
115
|
+
prefix: "valaxy-theme-",
|
116
|
+
color: green,
|
117
|
+
customInit: async (options) => {
|
118
|
+
return initTheme(options).catch((e) => {
|
119
|
+
console.error(e);
|
120
|
+
});
|
121
|
+
}
|
122
|
+
},
|
123
|
+
{
|
124
|
+
name: "addon",
|
125
|
+
display: `Addon`,
|
126
|
+
desc: "For Addon Developers",
|
127
|
+
message: "Addon name: valaxy-addon-",
|
128
|
+
initial: "template",
|
129
|
+
prefix: "valaxy-addon-",
|
130
|
+
color: yellow
|
131
|
+
}
|
132
|
+
];
|
133
|
+
const TEMPLATE_CHOICES = TEMPLATES.map((template) => template.name);
|
134
|
+
|
135
|
+
const argv = minimist(process.argv.slice(2));
|
136
|
+
const cwd = process.cwd();
|
137
|
+
const defaultTargetDir = "valaxy-blog";
|
138
|
+
async function init() {
|
139
|
+
console.log();
|
140
|
+
console.log(` ${bold("\u{1F30C} Valaxy")} ${blue(`v${version}`)}`);
|
141
|
+
console.log();
|
142
|
+
const argTargetDir = formatTargetDir(argv._[0]);
|
143
|
+
const argTemplate = argv.template || argv.t;
|
144
|
+
let targetDir = argTargetDir || defaultTargetDir;
|
145
|
+
const getProjectName = () => targetDir === "." ? path.basename(path.resolve()) : targetDir;
|
146
|
+
let result;
|
147
|
+
const { template } = await prompts({
|
148
|
+
type: argTemplate && TEMPLATE_CHOICES.includes(argTemplate) ? null : "select",
|
149
|
+
name: "template",
|
150
|
+
message: typeof argTemplate === "string" && !TEMPLATE_CHOICES.includes(argTemplate) ? reset(
|
151
|
+
`"${argTemplate}" isn't a valid template. Please choose from below: `
|
152
|
+
) : reset("Select a type:"),
|
153
|
+
initial: 0,
|
154
|
+
choices: TEMPLATES.map((template2) => {
|
155
|
+
const tColor = template2.color;
|
156
|
+
return {
|
157
|
+
title: tColor(template2.display || template2.name) + dim(` - ${template2.desc}`),
|
158
|
+
value: template2
|
159
|
+
};
|
160
|
+
})
|
161
|
+
});
|
162
|
+
try {
|
163
|
+
result = await prompts([
|
164
|
+
{
|
165
|
+
type: argTargetDir ? null : "text",
|
166
|
+
name: "projectName",
|
167
|
+
message: reset(template.message),
|
168
|
+
initial: template.initial,
|
169
|
+
onState: (state) => {
|
170
|
+
targetDir = formatTargetDir(template.prefix ? template.prefix + state.value : state.value) || template.initial;
|
171
|
+
}
|
172
|
+
},
|
173
|
+
{
|
174
|
+
type: () => !fs.existsSync(targetDir) || isEmpty(targetDir) ? null : "confirm",
|
175
|
+
name: "overwrite",
|
176
|
+
message: () => `${targetDir === "." ? "Current directory" : `Target directory "${targetDir}"`} is not empty. Remove existing files and continue?`
|
177
|
+
},
|
178
|
+
{
|
179
|
+
type: (_, { overwrite: overwrite2 }) => {
|
180
|
+
if (overwrite2 === false)
|
181
|
+
throw new Error(`${red("\u2716")} Operation cancelled`);
|
182
|
+
return null;
|
183
|
+
},
|
184
|
+
name: "overwriteChecker"
|
185
|
+
},
|
186
|
+
{
|
187
|
+
type: () => isValidPackageName(getProjectName()) ? null : "text",
|
188
|
+
name: "packageName",
|
189
|
+
message: reset("Package name:"),
|
190
|
+
initial: () => toValidPackageName(getProjectName()),
|
191
|
+
validate: (dir) => isValidPackageName(dir) || "Invalid package.json name"
|
192
|
+
}
|
193
|
+
], {
|
194
|
+
onCancel: () => {
|
195
|
+
throw new Error(`${red("\u2716")} Operation cancelled`);
|
196
|
+
}
|
197
|
+
});
|
198
|
+
} catch (cancelled) {
|
199
|
+
console.log(cancelled.message);
|
200
|
+
return;
|
201
|
+
}
|
202
|
+
const { projectName, overwrite } = result;
|
203
|
+
const dirName = template.prefix ? template.prefix + projectName : projectName;
|
204
|
+
const root = path.join(cwd, dirName);
|
205
|
+
console.log(root, overwrite);
|
206
|
+
if (overwrite)
|
207
|
+
emptyDir(root);
|
208
|
+
else if (!fs.existsSync(root))
|
209
|
+
fs.mkdirSync(root, { recursive: true });
|
210
|
+
if (template.customInit) {
|
211
|
+
await template.customInit({ themeName: projectName });
|
212
|
+
} else {
|
213
|
+
const templateDir = path.resolve(fileURLToPath(new URL(".", import.meta.url)), "..", `template-${template.name}`);
|
214
|
+
const write = (filename, content) => {
|
215
|
+
const targetPath = renameFiles[filename] ? path.join(root, renameFiles[filename]) : path.join(root, filename);
|
216
|
+
if (content)
|
217
|
+
fs.writeFileSync(targetPath, content);
|
218
|
+
else
|
219
|
+
copy(path.join(templateDir, filename), targetPath);
|
220
|
+
};
|
221
|
+
const files = fs.readdirSync(templateDir);
|
222
|
+
for (const file of files.filter((f) => f !== "package.json"))
|
223
|
+
write(file);
|
224
|
+
const pkg = await import(path.join(templateDir, "package.json"));
|
225
|
+
pkg.name = projectName || getProjectName();
|
226
|
+
write("package.json", JSON.stringify(pkg, null, 2));
|
227
|
+
}
|
228
|
+
console.log(` ${dim("\u{1F4C1}")} ${dim(root)}`);
|
229
|
+
console.log();
|
230
|
+
console.log(dim(" Scaffolding project in ") + targetDir + dim(" ..."));
|
231
|
+
const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent);
|
232
|
+
const pkgManager = pkgInfo ? pkgInfo.name : "npm";
|
233
|
+
const related = path.relative(cwd, root);
|
234
|
+
console.log(green(" Done.\n"));
|
235
|
+
if (template.name === "addon")
|
236
|
+
return;
|
237
|
+
const { yes } = await prompts({
|
238
|
+
type: "confirm",
|
239
|
+
name: "yes",
|
240
|
+
initial: "Y",
|
241
|
+
message: "Install and start it now?"
|
242
|
+
});
|
243
|
+
if (yes) {
|
244
|
+
const { agent } = await prompts({
|
245
|
+
name: "agent",
|
246
|
+
type: "select",
|
247
|
+
message: "Choose the agent",
|
248
|
+
choices: ["npm", "yarn", "pnpm"].map((i) => ({ value: i, title: i }))
|
249
|
+
});
|
250
|
+
if (!agent)
|
251
|
+
return;
|
252
|
+
await execa(agent, ["install"], { stdio: "inherit", cwd: root });
|
253
|
+
await execa(agent, ["run", "dev"], { stdio: "inherit", cwd: root });
|
254
|
+
} else {
|
255
|
+
console.log(dim("\n start it later by:\n"));
|
256
|
+
if (root !== cwd)
|
257
|
+
console.log(` ${green("cd")} ${blue(related)}`);
|
258
|
+
switch (pkgManager) {
|
259
|
+
case "yarn":
|
260
|
+
console.log(` ${green("yarn")}`);
|
261
|
+
console.log(` ${green("yarn")} dev`);
|
262
|
+
break;
|
263
|
+
default:
|
264
|
+
console.log(` ${green(pkgManager)} install`);
|
265
|
+
console.log(` ${green(pkgManager)} run dev`);
|
266
|
+
break;
|
267
|
+
}
|
268
|
+
console.log();
|
269
|
+
console.log(` ${cyan("\u2728")}`);
|
270
|
+
}
|
271
|
+
}
|
272
|
+
|
273
|
+
init().catch((e) => {
|
274
|
+
console.error(e);
|
275
|
+
});
|
package/package.json
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "create-valaxy",
|
3
|
-
"
|
3
|
+
"type": "module",
|
4
|
+
"version": "0.15.1",
|
4
5
|
"description": "Create Starter Template for Valaxy",
|
5
6
|
"author": {
|
6
7
|
"email": "me@yunyoujun.cn",
|
@@ -13,17 +14,23 @@
|
|
13
14
|
"type": "git",
|
14
15
|
"url": "https://github.com/YunYouJun/valaxy"
|
15
16
|
},
|
16
|
-
"main": "index.
|
17
|
+
"main": "bin/index.mjs",
|
17
18
|
"bin": {
|
18
|
-
"create-valaxy": "index.
|
19
|
+
"create-valaxy": "bin/index.mjs"
|
19
20
|
},
|
20
21
|
"engines": {
|
21
22
|
"node": "^14.18.0 || >=16.0.0"
|
22
23
|
},
|
23
24
|
"dependencies": {
|
24
|
-
"execa": "
|
25
|
+
"execa": "8.0.1",
|
25
26
|
"kolorist": "^1.8.0",
|
26
27
|
"minimist": "^1.2.8",
|
27
28
|
"prompts": "^2.4.2"
|
29
|
+
},
|
30
|
+
"scripts": {
|
31
|
+
"build": "unbuild",
|
32
|
+
"dev": "unbuild --stub",
|
33
|
+
"start": "tsx src/index.ts",
|
34
|
+
"typecheck": "tsc --noEmit"
|
28
35
|
}
|
29
36
|
}
|
package/src/config.ts
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
import { cyan, green, yellow } from 'kolorist'
|
2
|
+
import { initTheme } from './theme'
|
3
|
+
|
4
|
+
export const renameFiles: Record<string, string> = {
|
5
|
+
_gitignore: '.gitignore',
|
6
|
+
_npmrc: '.npmrc',
|
7
|
+
}
|
8
|
+
|
9
|
+
export const TEMPLATES = [
|
10
|
+
{
|
11
|
+
name: 'blog',
|
12
|
+
display: `Blog`,
|
13
|
+
desc: 'For Most Users',
|
14
|
+
message: 'Project name:',
|
15
|
+
initial: 'valaxy-blog',
|
16
|
+
color: cyan,
|
17
|
+
},
|
18
|
+
{
|
19
|
+
name: 'theme',
|
20
|
+
display: `Theme`,
|
21
|
+
desc: 'For Theme Developers',
|
22
|
+
message: 'Theme name: valaxy-theme-',
|
23
|
+
initial: 'starter',
|
24
|
+
prefix: 'valaxy-theme-',
|
25
|
+
color: green,
|
26
|
+
customInit: async (options: {
|
27
|
+
themeName?: string
|
28
|
+
}) => {
|
29
|
+
return initTheme(options).catch((e) => {
|
30
|
+
console.error(e)
|
31
|
+
})
|
32
|
+
},
|
33
|
+
},
|
34
|
+
{
|
35
|
+
name: 'addon',
|
36
|
+
display: `Addon`,
|
37
|
+
desc: 'For Addon Developers',
|
38
|
+
message: 'Addon name: valaxy-addon-',
|
39
|
+
initial: 'template',
|
40
|
+
prefix: 'valaxy-addon-',
|
41
|
+
color: yellow,
|
42
|
+
},
|
43
|
+
]
|
44
|
+
|
45
|
+
export const TEMPLATE_CHOICES = TEMPLATES.map(template => template.name)
|
package/src/index.ts
CHANGED
@@ -1,204 +1,6 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
// /* eslint-disable no-console */
|
1
|
+
/* eslint-disable no-console */
|
2
|
+
import { init } from './valaxy'
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
// import fs from 'node:fs'
|
9
|
-
// import path from 'node:path'
|
10
|
-
// import prompts from 'prompts'
|
11
|
-
// import execa from 'execa'
|
12
|
-
// import { blue, bold, cyan, dim, gray, green, red, yellow } from 'kolorist'
|
13
|
-
// import minimist from 'minimist'
|
14
|
-
// import { version } from '../package.json'
|
15
|
-
|
16
|
-
// const argv = minimist(process.argv.slice(2))
|
17
|
-
|
18
|
-
// const cwd = process.cwd()
|
19
|
-
|
20
|
-
// const renameFiles = {
|
21
|
-
// _gitignore: '.gitignore',
|
22
|
-
// _npmrc: '.npmrc',
|
23
|
-
// }
|
24
|
-
|
25
|
-
// async function init() {
|
26
|
-
// console.log()
|
27
|
-
// console.log(` ${bold('🌌 Valaxy')} ${blue(`v${version}`)}`)
|
28
|
-
// console.log()
|
29
|
-
|
30
|
-
// let targetDir = argv._[0]
|
31
|
-
// if (!targetDir) {
|
32
|
-
// /**
|
33
|
-
// * @type {{ projectName: string }}
|
34
|
-
// */
|
35
|
-
// const { projectName } = await prompts({
|
36
|
-
// type: 'text',
|
37
|
-
// name: 'projectName',
|
38
|
-
// message: 'Project name:',
|
39
|
-
// initial: 'valaxy-blog',
|
40
|
-
// })
|
41
|
-
// targetDir = projectName.trim()
|
42
|
-
|
43
|
-
// const packageName = await getValidPackageName(targetDir)
|
44
|
-
// const root = path.join(cwd, targetDir)
|
45
|
-
|
46
|
-
// if (!fs.existsSync(root)) {
|
47
|
-
// fs.mkdirSync(root, { recursive: true })
|
48
|
-
// }
|
49
|
-
// else {
|
50
|
-
// const existing = fs.readdirSync(root)
|
51
|
-
// if (existing.length) {
|
52
|
-
// console.log(yellow(` Target directory "${targetDir}" is not empty.`))
|
53
|
-
// /**
|
54
|
-
// * @type {{ yes: boolean }}
|
55
|
-
// */
|
56
|
-
// const { yes } = await prompts({
|
57
|
-
// type: 'confirm',
|
58
|
-
// name: 'yes',
|
59
|
-
// initial: 'Y',
|
60
|
-
// message: 'Remove existing files and continue?',
|
61
|
-
// })
|
62
|
-
// if (yes)
|
63
|
-
// emptyDir(root)
|
64
|
-
|
65
|
-
// else
|
66
|
-
// return
|
67
|
-
// }
|
68
|
-
// }
|
69
|
-
|
70
|
-
// console.log(` ${dim('📁')} ${dim(root)}`)
|
71
|
-
// console.log()
|
72
|
-
// console.log(dim(' Scaffolding project in ') + targetDir + dim(' ...'))
|
73
|
-
|
74
|
-
// const templateDir = path.join(__dirname, 'template')
|
75
|
-
// const write = (file, content) => {
|
76
|
-
// const targetPath = renameFiles[file]
|
77
|
-
// ? path.join(root, renameFiles[file])
|
78
|
-
// : path.join(root, file)
|
79
|
-
// if (content)
|
80
|
-
// fs.writeFileSync(targetPath, content)
|
81
|
-
|
82
|
-
// else
|
83
|
-
// copy(path.join(templateDir, file), targetPath)
|
84
|
-
// }
|
85
|
-
|
86
|
-
// const files = fs.readdirSync(templateDir)
|
87
|
-
// for (const file of files.filter(f => f !== 'package.json'))
|
88
|
-
// write(file)
|
89
|
-
|
90
|
-
// // write pkg name & version
|
91
|
-
// // eslint-disable-next-line @typescript-eslint/no-require-imports
|
92
|
-
// const pkg = require(path.join(templateDir, 'package.json'))
|
93
|
-
// if (packageName)
|
94
|
-
// pkg.name = packageName
|
95
|
-
// pkg.version = version
|
96
|
-
|
97
|
-
// write('package.json', JSON.stringify(pkg, null, 2))
|
98
|
-
|
99
|
-
// const pkgManager = (/pnpm/.test(process.env.npm_execpath) || /pnpm/.test(process.env.npm_config_user_agent))
|
100
|
-
// ? 'pnpm'
|
101
|
-
// : /yarn/.test(process.env.npm_execpath) ? 'yarn' : 'npm'
|
102
|
-
|
103
|
-
// const related = path.relative(cwd, root)
|
104
|
-
// console.log(green(' Done.\n'))
|
105
|
-
|
106
|
-
// /**
|
107
|
-
// * @type {{ yes: boolean }}
|
108
|
-
// */
|
109
|
-
// const { yes } = await prompts({
|
110
|
-
// type: 'confirm',
|
111
|
-
// name: 'yes',
|
112
|
-
// initial: 'Y',
|
113
|
-
// message: 'Install and start it now?',
|
114
|
-
// })
|
115
|
-
|
116
|
-
// if (yes) {
|
117
|
-
// const { agent } = await prompts({
|
118
|
-
// name: 'agent',
|
119
|
-
// type: 'select',
|
120
|
-
// message: 'Choose the agent',
|
121
|
-
// choices: ['npm', 'yarn', 'pnpm'].map(i => ({ value: i, title: i })),
|
122
|
-
// })
|
123
|
-
|
124
|
-
// if (!agent)
|
125
|
-
// return
|
126
|
-
|
127
|
-
// await execa(agent, ['install'], { stdio: 'inherit', cwd: root })
|
128
|
-
// await execa(agent, ['run', 'dev'], { stdio: 'inherit', cwd: root })
|
129
|
-
// }
|
130
|
-
// else {
|
131
|
-
// console.log(dim('\n start it later by:\n'))
|
132
|
-
// if (root !== cwd)
|
133
|
-
// console.log(blue(` cd ${bold(related)}`))
|
134
|
-
|
135
|
-
// console.log(blue(` ${pkgManager === 'yarn' ? 'yarn' : `${pkgManager} install`}`))
|
136
|
-
// console.log(blue(` ${pkgManager === 'yarn' ? 'yarn dev' : `${pkgManager} run dev`}`))
|
137
|
-
// console.log()
|
138
|
-
// console.log(` ${cyan('✨')}`)
|
139
|
-
// console.log()
|
140
|
-
// }
|
141
|
-
// }
|
142
|
-
// }
|
143
|
-
|
144
|
-
// function copy(src, dest) {
|
145
|
-
// const stat = fs.statSync(src)
|
146
|
-
// if (stat.isDirectory())
|
147
|
-
// copyDir(src, dest)
|
148
|
-
|
149
|
-
// else
|
150
|
-
// fs.copyFileSync(src, dest)
|
151
|
-
// }
|
152
|
-
|
153
|
-
// function copyDir(srcDir, destDir) {
|
154
|
-
// fs.mkdirSync(destDir, { recursive: true })
|
155
|
-
// for (const file of fs.readdirSync(srcDir)) {
|
156
|
-
// const srcFile = path.resolve(srcDir, file)
|
157
|
-
// const destFile = path.resolve(destDir, file)
|
158
|
-
// copy(srcFile, destFile)
|
159
|
-
// }
|
160
|
-
// }
|
161
|
-
|
162
|
-
// async function getValidPackageName(projectName) {
|
163
|
-
// projectName = path.basename(projectName)
|
164
|
-
// const packageNameRegExp = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/
|
165
|
-
// if (packageNameRegExp.test(projectName)) {
|
166
|
-
// return projectName
|
167
|
-
// }
|
168
|
-
// else {
|
169
|
-
// const suggestedPackageName = projectName
|
170
|
-
// .trim()
|
171
|
-
// .toLowerCase()
|
172
|
-
// .replace(/\s+/g, '-')
|
173
|
-
// .replace(/^[._]/, '')
|
174
|
-
// .replace(/[^a-z0-9-~]+/g, '-')
|
175
|
-
|
176
|
-
// /**
|
177
|
-
// * @type {{ inputPackageName: string }}
|
178
|
-
// */
|
179
|
-
// const { inputPackageName } = await prompts({
|
180
|
-
// type: 'text',
|
181
|
-
// name: 'inputPackageName',
|
182
|
-
// message: 'Package name:',
|
183
|
-
// initial: suggestedPackageName,
|
184
|
-
// // validate: input =>
|
185
|
-
// // packageNameRegExp.test(input) ? true : 'Invalid package.json name',
|
186
|
-
// })
|
187
|
-
// return inputPackageName
|
188
|
-
// }
|
189
|
-
// }
|
190
|
-
|
191
|
-
// /**
|
192
|
-
// * @param {string} dir
|
193
|
-
// * @returns
|
194
|
-
// */
|
195
|
-
// function emptyDir(dir) {
|
196
|
-
// if (!fs.existsSync(dir))
|
197
|
-
// return
|
198
|
-
// console.log(red(' Remove ') + gray(dir))
|
199
|
-
// fs.rmSync(dir, { recursive: true, force: true })
|
200
|
-
// }
|
201
|
-
|
202
|
-
// init().catch((e) => {
|
203
|
-
// console.error(e)
|
204
|
-
// })
|
4
|
+
init().catch((e) => {
|
5
|
+
console.error(e)
|
6
|
+
})
|
package/src/theme.ts
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
/* eslint-disable no-console */
|
2
|
+
import prompts from 'prompts'
|
3
|
+
import { execa } from 'execa'
|
4
|
+
import { blue, bold, cyan, dim, gray, green, yellow } from 'kolorist'
|
5
|
+
|
6
|
+
const starterTheme = {
|
7
|
+
name: 'starter',
|
8
|
+
display: `Starter`,
|
9
|
+
repo: 'https://github.com/valaxyjs/valaxy-theme-starter',
|
10
|
+
}
|
11
|
+
|
12
|
+
export async function initTheme(options: {
|
13
|
+
themeName?: string
|
14
|
+
}) {
|
15
|
+
const defaultThemeName = starterTheme.name
|
16
|
+
let themeName = options.themeName || defaultThemeName
|
17
|
+
if (!themeName) {
|
18
|
+
/**
|
19
|
+
* @type {{ theme: string }}
|
20
|
+
*/
|
21
|
+
const { theme } = await prompts({
|
22
|
+
type: 'text',
|
23
|
+
name: 'theme',
|
24
|
+
message: 'Theme name: valaxy-theme-',
|
25
|
+
initial: defaultThemeName,
|
26
|
+
})
|
27
|
+
themeName = theme || defaultThemeName
|
28
|
+
}
|
29
|
+
|
30
|
+
const targetDir = `valaxy-theme-${themeName!.trim()}`
|
31
|
+
|
32
|
+
console.log(` ${dim('npx')} ${gray('degit')} ${blue(starterTheme.repo)} ${yellow(targetDir)}`)
|
33
|
+
await execa('npx', ['degit', starterTheme.repo, targetDir], { stdio: 'inherit' })
|
34
|
+
|
35
|
+
console.log()
|
36
|
+
console.log(` ${bold('Check it')}:`)
|
37
|
+
console.log()
|
38
|
+
console.log(`- Change ${bold('author')} name in ${yellow('LICENSE')} & ${green('package.json')} & ${blue('.github')}`)
|
39
|
+
console.log(`- Change ${blue('valaxy.config.ts')} theme: ${yellow('starter')} to ${cyan(`${themeName}`)}`)
|
40
|
+
console.log(`- Rename ${yellow(`valaxy-theme-${themeName}`)} to ${cyan(`valaxy-theme-${themeName}`)}`)
|
41
|
+
console.log()
|
42
|
+
console.log(` ${cyan('✨')}`)
|
43
|
+
console.log()
|
44
|
+
|
45
|
+
return `valaxy-theme-${themeName}`
|
46
|
+
}
|
package/src/utils.ts
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
import fs from 'node:fs'
|
2
|
+
import path from 'node:path'
|
3
|
+
import { dim, red } from 'kolorist'
|
4
|
+
|
5
|
+
/**
|
6
|
+
* remove trailing slash
|
7
|
+
*/
|
8
|
+
export function formatTargetDir(targetDir: string | undefined) {
|
9
|
+
return targetDir?.trim().replace(/\/+$/g, '')
|
10
|
+
}
|
11
|
+
|
12
|
+
export function isEmpty(path: string) {
|
13
|
+
const files = fs.readdirSync(path)
|
14
|
+
return files.length === 0 || (files.length === 1 && files[0] === '.git')
|
15
|
+
}
|
16
|
+
|
17
|
+
export function copy(src: string, dest: string) {
|
18
|
+
const stat = fs.statSync(src)
|
19
|
+
if (stat.isDirectory())
|
20
|
+
copyDir(src, dest)
|
21
|
+
|
22
|
+
else
|
23
|
+
fs.copyFileSync(src, dest)
|
24
|
+
}
|
25
|
+
|
26
|
+
export function copyDir(srcDir: string, destDir: string) {
|
27
|
+
fs.mkdirSync(destDir, { recursive: true })
|
28
|
+
for (const file of fs.readdirSync(srcDir)) {
|
29
|
+
const srcFile = path.resolve(srcDir, file)
|
30
|
+
const destFile = path.resolve(destDir, file)
|
31
|
+
copy(srcFile, destFile)
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
export function emptyDir(dir: string) {
|
36
|
+
// eslint-disable-next-line no-console
|
37
|
+
console.log(`\n ${red(`Removing`)} ${dim(dir)}`)
|
38
|
+
if (!fs.existsSync(dir))
|
39
|
+
return
|
40
|
+
|
41
|
+
for (const file of fs.readdirSync(dir)) {
|
42
|
+
if (file === '.git')
|
43
|
+
continue
|
44
|
+
|
45
|
+
fs.rmSync(path.resolve(dir, file), { recursive: true, force: true })
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
export function isValidPackageName(projectName: string) {
|
50
|
+
return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(
|
51
|
+
projectName,
|
52
|
+
)
|
53
|
+
}
|
54
|
+
|
55
|
+
export function toValidPackageName(projectName: string) {
|
56
|
+
return projectName
|
57
|
+
.trim()
|
58
|
+
.toLowerCase()
|
59
|
+
.replace(/\s+/g, '-')
|
60
|
+
.replace(/^[._]/, '')
|
61
|
+
.replace(/[^a-z\d\-~]+/g, '-')
|
62
|
+
}
|
63
|
+
|
64
|
+
export function pkgFromUserAgent(userAgent: string | undefined) {
|
65
|
+
if (!userAgent)
|
66
|
+
return undefined
|
67
|
+
const pkgSpec = userAgent.split(' ')[0]
|
68
|
+
const pkgSpecArr = pkgSpec.split('/')
|
69
|
+
return {
|
70
|
+
name: pkgSpecArr[0],
|
71
|
+
version: pkgSpecArr[1],
|
72
|
+
}
|
73
|
+
}
|
package/src/valaxy.ts
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
/* eslint-disable no-console */
|
2
|
+
import process from 'node:process'
|
3
|
+
import fs from 'node:fs'
|
4
|
+
import path from 'node:path'
|
5
|
+
import { fileURLToPath } from 'node:url'
|
6
|
+
import { execa } from 'execa'
|
7
|
+
import { blue, bold, cyan, dim, green, red, reset } from 'kolorist'
|
8
|
+
import minimist from 'minimist'
|
9
|
+
import prompts from 'prompts'
|
10
|
+
import { version } from '../package.json'
|
11
|
+
import { copy, emptyDir, formatTargetDir, isEmpty, isValidPackageName, pkgFromUserAgent, toValidPackageName } from './utils'
|
12
|
+
import { TEMPLATES, TEMPLATE_CHOICES, renameFiles } from './config'
|
13
|
+
|
14
|
+
const argv = minimist(process.argv.slice(2))
|
15
|
+
|
16
|
+
const cwd = process.cwd()
|
17
|
+
const defaultTargetDir = 'valaxy-blog'
|
18
|
+
|
19
|
+
export async function init() {
|
20
|
+
console.log()
|
21
|
+
console.log(` ${bold('🌌 Valaxy')} ${blue(`v${version}`)}`)
|
22
|
+
console.log()
|
23
|
+
|
24
|
+
const argTargetDir = formatTargetDir(argv._[0])
|
25
|
+
const argTemplate = argv.template || argv.t
|
26
|
+
|
27
|
+
let targetDir = argTargetDir || defaultTargetDir
|
28
|
+
const getProjectName = () =>
|
29
|
+
targetDir === '.' ? path.basename(path.resolve()) : targetDir
|
30
|
+
|
31
|
+
let result: prompts.Answers<
|
32
|
+
'projectName' | 'overwrite' | 'packageName'
|
33
|
+
>
|
34
|
+
|
35
|
+
// get template
|
36
|
+
const { template } = await prompts({
|
37
|
+
type:
|
38
|
+
argTemplate && TEMPLATE_CHOICES.includes(argTemplate) ? null : 'select',
|
39
|
+
name: 'template',
|
40
|
+
message:
|
41
|
+
typeof argTemplate === 'string' && !TEMPLATE_CHOICES.includes(argTemplate)
|
42
|
+
? reset(
|
43
|
+
`"${argTemplate}" isn't a valid template. Please choose from below: `,
|
44
|
+
)
|
45
|
+
: reset('Select a type:'),
|
46
|
+
initial: 0,
|
47
|
+
choices: TEMPLATES.map((template) => {
|
48
|
+
const tColor = template.color
|
49
|
+
return {
|
50
|
+
title: tColor(template.display || template.name) + dim(` - ${template.desc}`),
|
51
|
+
value: template,
|
52
|
+
}
|
53
|
+
}),
|
54
|
+
})
|
55
|
+
|
56
|
+
try {
|
57
|
+
result = await prompts([
|
58
|
+
{
|
59
|
+
type: argTargetDir ? null : 'text',
|
60
|
+
name: 'projectName',
|
61
|
+
message: reset(template.message),
|
62
|
+
initial: template.initial,
|
63
|
+
onState: (state) => {
|
64
|
+
targetDir = formatTargetDir(template.prefix ? template.prefix + state.value : state.value) || (template.initial)
|
65
|
+
},
|
66
|
+
},
|
67
|
+
{
|
68
|
+
type: () =>
|
69
|
+
!fs.existsSync(targetDir) || isEmpty(targetDir) ? null : 'confirm',
|
70
|
+
name: 'overwrite',
|
71
|
+
message: () =>
|
72
|
+
`${targetDir === '.'
|
73
|
+
? 'Current directory'
|
74
|
+
: `Target directory "${targetDir}"`
|
75
|
+
} is not empty. Remove existing files and continue?`,
|
76
|
+
},
|
77
|
+
{
|
78
|
+
type: (_, { overwrite }: { overwrite?: boolean }) => {
|
79
|
+
if (overwrite === false)
|
80
|
+
throw new Error(`${red('✖')} Operation cancelled`)
|
81
|
+
|
82
|
+
return null
|
83
|
+
},
|
84
|
+
name: 'overwriteChecker',
|
85
|
+
},
|
86
|
+
{
|
87
|
+
type: () => (isValidPackageName(getProjectName()) ? null : 'text'),
|
88
|
+
name: 'packageName',
|
89
|
+
message: reset('Package name:'),
|
90
|
+
initial: () => toValidPackageName(getProjectName()),
|
91
|
+
validate: dir =>
|
92
|
+
isValidPackageName(dir) || 'Invalid package.json name',
|
93
|
+
},
|
94
|
+
], {
|
95
|
+
onCancel: () => {
|
96
|
+
throw new Error(`${red('✖')} Operation cancelled`)
|
97
|
+
},
|
98
|
+
})
|
99
|
+
}
|
100
|
+
catch (cancelled: any) {
|
101
|
+
console.log(cancelled.message)
|
102
|
+
return
|
103
|
+
}
|
104
|
+
|
105
|
+
const { projectName, overwrite } = result
|
106
|
+
const dirName = template.prefix ? template.prefix + projectName : projectName
|
107
|
+
const root = path.join(cwd, dirName)
|
108
|
+
|
109
|
+
console.log(root, overwrite)
|
110
|
+
|
111
|
+
if (overwrite)
|
112
|
+
emptyDir(root)
|
113
|
+
else if (!fs.existsSync(root))
|
114
|
+
fs.mkdirSync(root, { recursive: true })
|
115
|
+
|
116
|
+
// custom
|
117
|
+
if (template.customInit) {
|
118
|
+
await template.customInit({ themeName: projectName })
|
119
|
+
}
|
120
|
+
else {
|
121
|
+
const templateDir = path.resolve(fileURLToPath(new URL('.', import.meta.url)), '..', `template-${template.name}`)
|
122
|
+
const write = (filename: string, content?: string) => {
|
123
|
+
const targetPath = renameFiles[filename]
|
124
|
+
? path.join(root, renameFiles[filename])
|
125
|
+
: path.join(root, filename)
|
126
|
+
|
127
|
+
if (content)
|
128
|
+
fs.writeFileSync(targetPath, content)
|
129
|
+
else
|
130
|
+
copy(path.join(templateDir, filename), targetPath)
|
131
|
+
}
|
132
|
+
|
133
|
+
const files = fs.readdirSync(templateDir)
|
134
|
+
for (const file of files.filter(f => f !== 'package.json'))
|
135
|
+
write(file)
|
136
|
+
|
137
|
+
// write pkg name & version
|
138
|
+
const pkg = await import(path.join(templateDir, 'package.json'))
|
139
|
+
pkg.name = projectName || getProjectName()
|
140
|
+
|
141
|
+
write('package.json', JSON.stringify(pkg, null, 2))
|
142
|
+
}
|
143
|
+
|
144
|
+
console.log(` ${dim('📁')} ${dim(root)}`)
|
145
|
+
console.log()
|
146
|
+
console.log(dim(' Scaffolding project in ') + targetDir + dim(' ...'))
|
147
|
+
|
148
|
+
const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent)
|
149
|
+
const pkgManager = pkgInfo ? pkgInfo.name : 'npm'
|
150
|
+
|
151
|
+
const related = path.relative(cwd, root)
|
152
|
+
console.log(green(' Done.\n'))
|
153
|
+
|
154
|
+
// addon not start
|
155
|
+
if (template.name === 'addon')
|
156
|
+
return
|
157
|
+
|
158
|
+
/**
|
159
|
+
* @type {{ yes: boolean }}
|
160
|
+
*/
|
161
|
+
const { yes } = await prompts({
|
162
|
+
type: 'confirm',
|
163
|
+
name: 'yes',
|
164
|
+
initial: 'Y',
|
165
|
+
message: 'Install and start it now?',
|
166
|
+
})
|
167
|
+
|
168
|
+
if (yes) {
|
169
|
+
const { agent } = await prompts({
|
170
|
+
name: 'agent',
|
171
|
+
type: 'select',
|
172
|
+
message: 'Choose the agent',
|
173
|
+
choices: ['npm', 'yarn', 'pnpm'].map(i => ({ value: i, title: i })),
|
174
|
+
})
|
175
|
+
|
176
|
+
if (!agent)
|
177
|
+
return
|
178
|
+
|
179
|
+
await execa(agent, ['install'], { stdio: 'inherit', cwd: root })
|
180
|
+
await execa(agent, ['run', 'dev'], { stdio: 'inherit', cwd: root })
|
181
|
+
}
|
182
|
+
else {
|
183
|
+
console.log(dim('\n start it later by:\n'))
|
184
|
+
if (root !== cwd)
|
185
|
+
console.log(` ${green('cd')} ${blue(related)}`)
|
186
|
+
|
187
|
+
switch (pkgManager) {
|
188
|
+
case 'yarn':
|
189
|
+
console.log(` ${green('yarn')}`)
|
190
|
+
console.log(` ${green('yarn')} dev`)
|
191
|
+
break
|
192
|
+
default:
|
193
|
+
console.log(` ${green(pkgManager)} install`)
|
194
|
+
console.log(` ${green(pkgManager)} run dev`)
|
195
|
+
break
|
196
|
+
}
|
197
|
+
console.log()
|
198
|
+
console.log(` ${cyan('✨')}`)
|
199
|
+
}
|
200
|
+
}
|
@@ -1,21 +1,21 @@
|
|
1
1
|
{
|
2
2
|
"compilerOptions": {
|
3
|
-
"baseUrl": ".",
|
4
|
-
"module": "ESNext",
|
5
3
|
"target": "ESNext",
|
6
4
|
"lib": ["DOM", "ESNext"],
|
7
|
-
"strict": true,
|
8
|
-
"esModuleInterop": true,
|
9
5
|
"jsx": "preserve",
|
10
|
-
"
|
6
|
+
"module": "ESNext",
|
11
7
|
"moduleResolution": "node",
|
12
|
-
"
|
13
|
-
"noUnusedLocals": true,
|
14
|
-
"strictNullChecks": true,
|
15
|
-
"forceConsistentCasingInFileNames": true,
|
8
|
+
"baseUrl": ".",
|
16
9
|
"paths": {
|
17
10
|
"~/*": ["./*"]
|
18
|
-
}
|
11
|
+
},
|
12
|
+
"resolveJsonModule": true,
|
13
|
+
"esModuleInterop": true,
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
15
|
+
"strict": true,
|
16
|
+
"strictNullChecks": true,
|
17
|
+
"noUnusedLocals": true,
|
18
|
+
"skipLibCheck": true
|
19
19
|
},
|
20
20
|
"exclude": ["dist", "node_modules"]
|
21
21
|
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# create-valaxy-theme
|
2
|
+
|
3
|
+
Theme template generator.
|
4
|
+
|
5
|
+
> Make a valaxy theme is so easy!
|
6
|
+
|
7
|
+
```bash
|
8
|
+
npx degit valaxyjs/valaxy-theme-starter valaxy-theme-name
|
9
|
+
```
|
10
|
+
|
11
|
+
## FAQ
|
12
|
+
|
13
|
+
If you want to create a valaxy theme, you should use the theme name as prefix of components to avoid conflicts.
|
14
|
+
|
15
|
+
For example:
|
16
|
+
|
17
|
+
`valaxy-theme-yun` component name is `YunComponent`, `YunPost`, etc.
|
package/index.js
DELETED
@@ -1,198 +0,0 @@
|
|
1
|
-
#!/usr/bin/env node
|
2
|
-
/* eslint-disable no-console */
|
3
|
-
|
4
|
-
const process = require('node:process')
|
5
|
-
const fs = require('node:fs')
|
6
|
-
const path = require('node:path')
|
7
|
-
const argv = require('minimist')(process.argv.slice(2))
|
8
|
-
const prompts = require('prompts')
|
9
|
-
const execa = require('execa')
|
10
|
-
const { bold, dim, blue, yellow, green, cyan, red, gray } = require('kolorist')
|
11
|
-
const { version } = require('./package.json')
|
12
|
-
|
13
|
-
const cwd = process.cwd()
|
14
|
-
|
15
|
-
const renameFiles = {
|
16
|
-
_gitignore: '.gitignore',
|
17
|
-
_npmrc: '.npmrc',
|
18
|
-
}
|
19
|
-
|
20
|
-
async function init() {
|
21
|
-
console.log()
|
22
|
-
console.log(` ${bold('🌌 Valaxy')} ${blue(`v${version}`)}`)
|
23
|
-
console.log()
|
24
|
-
|
25
|
-
let targetDir = argv._[0]
|
26
|
-
if (!targetDir) {
|
27
|
-
/**
|
28
|
-
* @type {{ projectName: string }}
|
29
|
-
*/
|
30
|
-
const { projectName } = await prompts({
|
31
|
-
type: 'text',
|
32
|
-
name: 'projectName',
|
33
|
-
message: 'Project name:',
|
34
|
-
initial: 'valaxy-blog',
|
35
|
-
})
|
36
|
-
targetDir = projectName.trim()
|
37
|
-
|
38
|
-
const packageName = await getValidPackageName(targetDir)
|
39
|
-
const root = path.join(cwd, targetDir)
|
40
|
-
|
41
|
-
if (!fs.existsSync(root)) {
|
42
|
-
fs.mkdirSync(root, { recursive: true })
|
43
|
-
}
|
44
|
-
else {
|
45
|
-
const existing = fs.readdirSync(root)
|
46
|
-
if (existing.length) {
|
47
|
-
console.log(yellow(` Target directory "${targetDir}" is not empty.`))
|
48
|
-
/**
|
49
|
-
* @type {{ yes: boolean }}
|
50
|
-
*/
|
51
|
-
const { yes } = await prompts({
|
52
|
-
type: 'confirm',
|
53
|
-
name: 'yes',
|
54
|
-
initial: 'Y',
|
55
|
-
message: 'Remove existing files and continue?',
|
56
|
-
})
|
57
|
-
if (yes)
|
58
|
-
emptyDir(root)
|
59
|
-
|
60
|
-
else
|
61
|
-
return
|
62
|
-
}
|
63
|
-
}
|
64
|
-
|
65
|
-
console.log(` ${dim('📁')} ${dim(root)}`)
|
66
|
-
console.log()
|
67
|
-
console.log(dim(' Scaffolding project in ') + targetDir + dim(' ...'))
|
68
|
-
|
69
|
-
const templateDir = path.join(__dirname, 'template')
|
70
|
-
const write = (file, content) => {
|
71
|
-
const targetPath = renameFiles[file]
|
72
|
-
? path.join(root, renameFiles[file])
|
73
|
-
: path.join(root, file)
|
74
|
-
if (content)
|
75
|
-
fs.writeFileSync(targetPath, content)
|
76
|
-
|
77
|
-
else
|
78
|
-
copy(path.join(templateDir, file), targetPath)
|
79
|
-
}
|
80
|
-
|
81
|
-
const files = fs.readdirSync(templateDir)
|
82
|
-
for (const file of files.filter(f => f !== 'package.json'))
|
83
|
-
write(file)
|
84
|
-
|
85
|
-
// write pkg name & version
|
86
|
-
const pkg = require(path.join(templateDir, 'package.json'))
|
87
|
-
if (packageName)
|
88
|
-
pkg.name = packageName
|
89
|
-
pkg.version = version
|
90
|
-
|
91
|
-
write('package.json', JSON.stringify(pkg, null, 2))
|
92
|
-
|
93
|
-
const pkgManager = (/pnpm/.test(process.env.npm_execpath) || /pnpm/.test(process.env.npm_config_user_agent))
|
94
|
-
? 'pnpm'
|
95
|
-
: /yarn/.test(process.env.npm_execpath) ? 'yarn' : 'npm'
|
96
|
-
|
97
|
-
const related = path.relative(cwd, root)
|
98
|
-
console.log(green(' Done.\n'))
|
99
|
-
|
100
|
-
/**
|
101
|
-
* @type {{ yes: boolean }}
|
102
|
-
*/
|
103
|
-
const { yes } = await prompts({
|
104
|
-
type: 'confirm',
|
105
|
-
name: 'yes',
|
106
|
-
initial: 'Y',
|
107
|
-
message: 'Install and start it now?',
|
108
|
-
})
|
109
|
-
|
110
|
-
if (yes) {
|
111
|
-
const { agent } = await prompts({
|
112
|
-
name: 'agent',
|
113
|
-
type: 'select',
|
114
|
-
message: 'Choose the agent',
|
115
|
-
choices: ['npm', 'yarn', 'pnpm'].map(i => ({ value: i, title: i })),
|
116
|
-
})
|
117
|
-
|
118
|
-
if (!agent)
|
119
|
-
return
|
120
|
-
|
121
|
-
await execa(agent, ['install'], { stdio: 'inherit', cwd: root })
|
122
|
-
await execa(agent, ['run', 'dev'], { stdio: 'inherit', cwd: root })
|
123
|
-
}
|
124
|
-
else {
|
125
|
-
console.log(dim('\n start it later by:\n'))
|
126
|
-
if (root !== cwd)
|
127
|
-
console.log(blue(` cd ${bold(related)}`))
|
128
|
-
|
129
|
-
console.log(blue(` ${pkgManager === 'yarn' ? 'yarn' : `${pkgManager} install`}`))
|
130
|
-
console.log(blue(` ${pkgManager === 'yarn' ? 'yarn dev' : `${pkgManager} run dev`}`))
|
131
|
-
console.log()
|
132
|
-
console.log(` ${cyan('✨')}`)
|
133
|
-
console.log()
|
134
|
-
}
|
135
|
-
}
|
136
|
-
}
|
137
|
-
|
138
|
-
function copy(src, dest) {
|
139
|
-
const stat = fs.statSync(src)
|
140
|
-
if (stat.isDirectory())
|
141
|
-
copyDir(src, dest)
|
142
|
-
|
143
|
-
else
|
144
|
-
fs.copyFileSync(src, dest)
|
145
|
-
}
|
146
|
-
|
147
|
-
function copyDir(srcDir, destDir) {
|
148
|
-
fs.mkdirSync(destDir, { recursive: true })
|
149
|
-
for (const file of fs.readdirSync(srcDir)) {
|
150
|
-
const srcFile = path.resolve(srcDir, file)
|
151
|
-
const destFile = path.resolve(destDir, file)
|
152
|
-
copy(srcFile, destFile)
|
153
|
-
}
|
154
|
-
}
|
155
|
-
|
156
|
-
async function getValidPackageName(projectName) {
|
157
|
-
projectName = path.basename(projectName)
|
158
|
-
const packageNameRegExp = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/
|
159
|
-
if (packageNameRegExp.test(projectName)) {
|
160
|
-
return projectName
|
161
|
-
}
|
162
|
-
else {
|
163
|
-
const suggestedPackageName = projectName
|
164
|
-
.trim()
|
165
|
-
.toLowerCase()
|
166
|
-
.replace(/\s+/g, '-')
|
167
|
-
.replace(/^[._]/, '')
|
168
|
-
.replace(/[^a-z0-9-~]+/g, '-')
|
169
|
-
|
170
|
-
/**
|
171
|
-
* @type {{ inputPackageName: string }}
|
172
|
-
*/
|
173
|
-
const { inputPackageName } = await prompts({
|
174
|
-
type: 'text',
|
175
|
-
name: 'inputPackageName',
|
176
|
-
message: 'Package name:',
|
177
|
-
initial: suggestedPackageName,
|
178
|
-
// validate: input =>
|
179
|
-
// packageNameRegExp.test(input) ? true : 'Invalid package.json name',
|
180
|
-
})
|
181
|
-
return inputPackageName
|
182
|
-
}
|
183
|
-
}
|
184
|
-
|
185
|
-
/**
|
186
|
-
* @param {string} dir
|
187
|
-
* @returns
|
188
|
-
*/
|
189
|
-
function emptyDir(dir) {
|
190
|
-
if (!fs.existsSync(dir))
|
191
|
-
return
|
192
|
-
console.log(red(' Remove ') + gray(dir))
|
193
|
-
fs.rmSync(dir, { recursive: true, force: true })
|
194
|
-
}
|
195
|
-
|
196
|
-
init().catch((e) => {
|
197
|
-
console.error(e)
|
198
|
-
})
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|