create-valaxy 0.14.61 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. package/bin/index.mjs +4 -0
  2. package/build.config.ts +3 -0
  3. package/dist/index.mjs +275 -0
  4. package/package.json +11 -4
  5. package/src/config.ts +45 -0
  6. package/src/index.ts +5 -203
  7. package/src/theme.ts +46 -0
  8. package/src/utils.ts +73 -0
  9. package/src/valaxy.ts +200 -0
  10. package/{template → template-blog}/package.json +2 -2
  11. package/{template → template-blog}/tsconfig.json +10 -10
  12. package/template-theme/README.md +17 -0
  13. package/index.js +0 -198
  14. /package/{template → template-blog}/.dockerignore +0 -0
  15. /package/{template → template-blog}/.editorconfig +0 -0
  16. /package/{template → template-blog}/.github/workflows/gh-pages.yml +0 -0
  17. /package/{template → template-blog}/.vscode/extensions.json +0 -0
  18. /package/{template → template-blog}/.vscode/settings.json +0 -0
  19. /package/{template → template-blog}/Dockerfile +0 -0
  20. /package/{template → template-blog}/README.md +0 -0
  21. /package/{template → template-blog}/_gitignore +0 -0
  22. /package/{template → template-blog}/_npmrc +0 -0
  23. /package/{template → template-blog}/components/README.md +0 -0
  24. /package/{template → template-blog}/layouts/README.md +0 -0
  25. /package/{template → template-blog}/locales/README.md +0 -0
  26. /package/{template → template-blog}/locales/en.yml +0 -0
  27. /package/{template → template-blog}/locales/zh-CN.yml +0 -0
  28. /package/{template → template-blog}/netlify.toml +0 -0
  29. /package/{template → template-blog}/pages/404.md +0 -0
  30. /package/{template → template-blog}/pages/about/index.md +0 -0
  31. /package/{template → template-blog}/pages/about/site.md +0 -0
  32. /package/{template → template-blog}/pages/archives/index.md +0 -0
  33. /package/{template → template-blog}/pages/categories/index.md +0 -0
  34. /package/{template → template-blog}/pages/links/index.md +0 -0
  35. /package/{template → template-blog}/pages/posts/hello-valaxy.md +0 -0
  36. /package/{template → template-blog}/pages/tags/index.md +0 -0
  37. /package/{template → template-blog}/public/_headers +0 -0
  38. /package/{template → template-blog}/public/favicon.svg +0 -0
  39. /package/{template → template-blog}/public/pwa-192x192.png +0 -0
  40. /package/{template → template-blog}/public/pwa-512x512.png +0 -0
  41. /package/{template → template-blog}/public/safari-pinned-tab.svg +0 -0
  42. /package/{template → template-blog}/site.config.ts +0 -0
  43. /package/{template → template-blog}/styles/README.md +0 -0
  44. /package/{template → template-blog}/styles/css-vars.scss +0 -0
  45. /package/{template → template-blog}/styles/index.scss +0 -0
  46. /package/{template → template-blog}/valaxy.config.ts +0 -0
  47. /package/{template → template-blog}/vercel.json +0 -0
package/bin/index.mjs ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ 'use strict'
3
+
4
+ import '../dist/index.mjs'
@@ -0,0 +1,3 @@
1
+ export default {
2
+ entries: ['./src/index'],
3
+ }
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
- "version": "0.14.61",
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.js",
17
+ "main": "bin/index.mjs",
17
18
  "bin": {
18
- "create-valaxy": "index.js"
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": "5.1.1",
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
- // #!/usr/bin/env node
2
- // /* eslint-disable @typescript-eslint/no-var-requires */
3
- // /* eslint-disable no-console */
1
+ /* eslint-disable no-console */
2
+ import { init } from './valaxy'
4
3
 
5
- // // @todo migrate to ts
6
-
7
- // import process from 'node:process'
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
+ }
@@ -11,8 +11,8 @@
11
11
  "serve": "vite preview"
12
12
  },
13
13
  "dependencies": {
14
- "valaxy": "0.14.61",
15
- "valaxy-theme-yun": "0.14.61"
14
+ "valaxy": "0.15.1",
15
+ "valaxy-theme-yun": "0.15.1"
16
16
  },
17
17
  "devDependencies": {
18
18
  "typescript": "^5.2.2"
@@ -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
- "skipLibCheck": true,
6
+ "module": "ESNext",
11
7
  "moduleResolution": "node",
12
- "resolveJsonModule": true,
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