create-wp-typia 0.1.0
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/README.md +33 -0
- package/dist/cli.js +87837 -0
- package/dist/highlights-eq9cgrbb.scm +604 -0
- package/dist/highlights-ghv9g403.scm +205 -0
- package/dist/highlights-hk7bwhj4.scm +284 -0
- package/dist/highlights-r812a2qc.scm +150 -0
- package/dist/highlights-x6tmsnaa.scm +115 -0
- package/dist/injections-73j83es3.scm +27 -0
- package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
- package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
- package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
- package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
- package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
- package/lib/entry.js +29 -0
- package/lib/node-cli.js +326 -0
- package/lib/package-managers.d.ts +29 -0
- package/lib/package-managers.js +170 -0
- package/lib/scaffold.d.ts +64 -0
- package/lib/scaffold.js +565 -0
- package/lib/template-registry.d.ts +18 -0
- package/lib/template-registry.js +58 -0
- package/package.json +64 -0
- package/src/cli.ts +329 -0
- package/templates/advanced/README.md.mustache +70 -0
- package/templates/advanced/block.json.mustache +42 -0
- package/templates/advanced/index.js +21 -0
- package/templates/advanced/package.json.mustache +48 -0
- package/templates/advanced/scripts/generate-migrations.ts.mustache +267 -0
- package/templates/advanced/scripts/lib/typia-metadata-core.ts +806 -0
- package/templates/advanced/scripts/migration-cli.ts.mustache +260 -0
- package/templates/advanced/scripts/sync-types-to-block-json.ts.mustache +25 -0
- package/templates/advanced/src/admin/migration-dashboard.tsx.mustache +450 -0
- package/templates/advanced/src/components/ErrorBoundary.tsx.mustache +47 -0
- package/templates/advanced/src/deprecated.ts.mustache +184 -0
- package/templates/advanced/src/edit.tsx.mustache +93 -0
- package/templates/advanced/src/hooks/useDebounce.ts.mustache +20 -0
- package/templates/advanced/src/hooks/useLocalStorage.ts.mustache +31 -0
- package/templates/advanced/src/hooks.ts.mustache +56 -0
- package/templates/advanced/src/index.tsx.mustache +16 -0
- package/templates/advanced/src/migration-detector.ts.mustache +417 -0
- package/templates/advanced/src/migrations/index.ts.mustache +361 -0
- package/templates/advanced/src/save.tsx.mustache +40 -0
- package/templates/advanced/src/style.scss.mustache +84 -0
- package/templates/advanced/src/types/versions.ts.mustache +108 -0
- package/templates/advanced/src/types.ts.mustache +45 -0
- package/templates/advanced/src/utils/classnames.ts.mustache +51 -0
- package/templates/advanced/src/utils/debounce.ts.mustache +37 -0
- package/templates/advanced/src/utils/index.ts.mustache +7 -0
- package/templates/advanced/src/utils/uuid.ts.mustache +17 -0
- package/templates/advanced/src/validators.ts.mustache +39 -0
- package/templates/advanced/src/view.ts.mustache +59 -0
- package/templates/advanced/tsconfig.json.mustache +9 -0
- package/templates/advanced/webpack.config.js.mustache +85 -0
- package/templates/basic/package.json.mustache +39 -0
- package/templates/basic/scripts/lib/typia-metadata-core.ts +806 -0
- package/templates/basic/scripts/sync-types-to-block-json.ts +25 -0
- package/templates/basic/src/block.json +51 -0
- package/templates/basic/src/edit.tsx +85 -0
- package/templates/basic/src/hooks.ts +75 -0
- package/templates/basic/src/index.tsx +37 -0
- package/templates/basic/src/save.tsx +27 -0
- package/templates/basic/src/style.scss +42 -0
- package/templates/basic/src/types.ts +47 -0
- package/templates/basic/src/validators.ts +39 -0
- package/templates/basic/tsconfig.json +20 -0
- package/templates/basic/webpack.config.js +85 -0
- package/templates/full/package.json.mustache +40 -0
- package/templates/full/scripts/lib/typia-metadata-core.ts +806 -0
- package/templates/full/scripts/sync-types-to-block-json.ts.mustache +25 -0
- package/templates/full/src/block.json.mustache +121 -0
- package/templates/full/src/edit.tsx.mustache +300 -0
- package/templates/full/src/editor.scss.mustache +251 -0
- package/templates/full/src/hooks.ts.mustache +140 -0
- package/templates/full/src/index.tsx.mustache +27 -0
- package/templates/full/src/save.tsx.mustache +39 -0
- package/templates/full/src/style.scss.mustache +224 -0
- package/templates/full/src/types.ts.mustache +34 -0
- package/templates/full/src/validators.ts.mustache +84 -0
- package/templates/full/tsconfig.json.mustache +9 -0
- package/templates/full/webpack.config.js.mustache +85 -0
- package/templates/interactivity/package.json.mustache +41 -0
- package/templates/interactivity/scripts/lib/typia-metadata-core.ts +806 -0
- package/templates/interactivity/scripts/sync-types-to-block-json.ts.mustache +25 -0
- package/templates/interactivity/src/block.json.mustache +75 -0
- package/templates/interactivity/src/edit.tsx.mustache +206 -0
- package/templates/interactivity/src/interactivity.ts.mustache +183 -0
- package/templates/interactivity/src/save.tsx.mustache +87 -0
- package/templates/interactivity/src/types.ts.mustache +29 -0
- package/templates/interactivity/tsconfig.json.mustache +9 -0
- package/templates/interactivity/webpack.config.js.mustache +85 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
; Query from: https://raw.githubusercontent.com/nvim-treesitter/nvim-treesitter/99ddf573531c4dbe53f743ecbc1595af5eb1d32f/queries/markdown_inline/highlights.scm
|
|
2
|
+
; From MDeiml/tree-sitter-markdown
|
|
3
|
+
(code_span) @markup.raw @nospell
|
|
4
|
+
|
|
5
|
+
(emphasis) @markup.italic
|
|
6
|
+
|
|
7
|
+
(strong_emphasis) @markup.strong
|
|
8
|
+
|
|
9
|
+
(strikethrough) @markup.strikethrough
|
|
10
|
+
|
|
11
|
+
(shortcut_link
|
|
12
|
+
(link_text) @nospell)
|
|
13
|
+
|
|
14
|
+
[
|
|
15
|
+
(backslash_escape)
|
|
16
|
+
(hard_line_break)
|
|
17
|
+
] @string.escape
|
|
18
|
+
|
|
19
|
+
; Conceal codeblock and text style markers
|
|
20
|
+
([
|
|
21
|
+
(code_span_delimiter)
|
|
22
|
+
(emphasis_delimiter)
|
|
23
|
+
] @conceal
|
|
24
|
+
(#set! conceal ""))
|
|
25
|
+
|
|
26
|
+
; Inline links - style all parts
|
|
27
|
+
(inline_link
|
|
28
|
+
["[" "(" ")"] @markup.link)
|
|
29
|
+
|
|
30
|
+
(inline_link
|
|
31
|
+
"]" @markup.link.bracket.close)
|
|
32
|
+
|
|
33
|
+
; Conceal opening bracket
|
|
34
|
+
((inline_link
|
|
35
|
+
"[" @conceal)
|
|
36
|
+
(#set! conceal ""))
|
|
37
|
+
|
|
38
|
+
; Conceal closing bracket with space replacement
|
|
39
|
+
((inline_link
|
|
40
|
+
"]" @conceal)
|
|
41
|
+
(#set! conceal " "))
|
|
42
|
+
|
|
43
|
+
; Conceal image links
|
|
44
|
+
(image
|
|
45
|
+
[
|
|
46
|
+
"!"
|
|
47
|
+
"["
|
|
48
|
+
"]"
|
|
49
|
+
"("
|
|
50
|
+
(link_destination)
|
|
51
|
+
")"
|
|
52
|
+
] @markup.link
|
|
53
|
+
(#set! conceal ""))
|
|
54
|
+
|
|
55
|
+
; Conceal full reference links
|
|
56
|
+
(full_reference_link
|
|
57
|
+
[
|
|
58
|
+
"["
|
|
59
|
+
"]"
|
|
60
|
+
(link_label)
|
|
61
|
+
] @markup.link
|
|
62
|
+
(#set! conceal ""))
|
|
63
|
+
|
|
64
|
+
; Conceal collapsed reference links
|
|
65
|
+
(collapsed_reference_link
|
|
66
|
+
[
|
|
67
|
+
"["
|
|
68
|
+
"]"
|
|
69
|
+
] @markup.link
|
|
70
|
+
(#set! conceal ""))
|
|
71
|
+
|
|
72
|
+
; Conceal shortcut links
|
|
73
|
+
(shortcut_link
|
|
74
|
+
[
|
|
75
|
+
"["
|
|
76
|
+
"]"
|
|
77
|
+
] @markup.link
|
|
78
|
+
(#set! conceal ""))
|
|
79
|
+
|
|
80
|
+
[
|
|
81
|
+
(link_destination)
|
|
82
|
+
(uri_autolink)
|
|
83
|
+
] @markup.link.url @nospell
|
|
84
|
+
|
|
85
|
+
[
|
|
86
|
+
(link_label)
|
|
87
|
+
(link_text)
|
|
88
|
+
(link_title)
|
|
89
|
+
(image_description)
|
|
90
|
+
] @markup.link.label
|
|
91
|
+
|
|
92
|
+
; Replace common HTML entities.
|
|
93
|
+
((entity_reference) @character.special
|
|
94
|
+
(#eq? @character.special " ")
|
|
95
|
+
(#set! conceal ""))
|
|
96
|
+
|
|
97
|
+
((entity_reference) @character.special
|
|
98
|
+
(#eq? @character.special "<")
|
|
99
|
+
(#set! conceal "<"))
|
|
100
|
+
|
|
101
|
+
((entity_reference) @character.special
|
|
102
|
+
(#eq? @character.special ">")
|
|
103
|
+
(#set! conceal ">"))
|
|
104
|
+
|
|
105
|
+
((entity_reference) @character.special
|
|
106
|
+
(#eq? @character.special "&")
|
|
107
|
+
(#set! conceal "&"))
|
|
108
|
+
|
|
109
|
+
((entity_reference) @character.special
|
|
110
|
+
(#eq? @character.special """)
|
|
111
|
+
(#set! conceal "\""))
|
|
112
|
+
|
|
113
|
+
((entity_reference) @character.special
|
|
114
|
+
(#any-of? @character.special " " " ")
|
|
115
|
+
(#set! conceal " "))
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
; Query from: https://raw.githubusercontent.com/nvim-treesitter/nvim-treesitter/refs/heads/master/queries/markdown/injections.scm
|
|
2
|
+
(fenced_code_block
|
|
3
|
+
(info_string
|
|
4
|
+
(language) @_lang)
|
|
5
|
+
(code_fence_content) @injection.content
|
|
6
|
+
(#set-lang-from-info-string! @_lang))
|
|
7
|
+
|
|
8
|
+
((html_block) @injection.content
|
|
9
|
+
(#set! injection.language "html")
|
|
10
|
+
(#set! injection.combined)
|
|
11
|
+
(#set! injection.include-children))
|
|
12
|
+
|
|
13
|
+
((minus_metadata) @injection.content
|
|
14
|
+
(#set! injection.language "yaml")
|
|
15
|
+
(#offset! @injection.content 1 0 -1 0)
|
|
16
|
+
(#set! injection.include-children))
|
|
17
|
+
|
|
18
|
+
((plus_metadata) @injection.content
|
|
19
|
+
(#set! injection.language "toml")
|
|
20
|
+
(#offset! @injection.content 1 0 -1 0)
|
|
21
|
+
(#set! injection.include-children))
|
|
22
|
+
|
|
23
|
+
([
|
|
24
|
+
(inline)
|
|
25
|
+
(pipe_table_cell)
|
|
26
|
+
] @injection.content
|
|
27
|
+
(#set! injection.language "markdown_inline"))
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/lib/entry.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
async function main() {
|
|
4
|
+
if (typeof globalThis.Bun !== "undefined") {
|
|
5
|
+
const translatedArgs = process.argv.slice(2).map((arg) => {
|
|
6
|
+
if (arg === "--no-install") {
|
|
7
|
+
return "--noInstall";
|
|
8
|
+
}
|
|
9
|
+
if (arg === "--package-manager") {
|
|
10
|
+
return "--packageManager";
|
|
11
|
+
}
|
|
12
|
+
if (arg.startsWith("--package-manager=")) {
|
|
13
|
+
return `--packageManager=${arg.slice("--package-manager=".length)}`;
|
|
14
|
+
}
|
|
15
|
+
return arg;
|
|
16
|
+
});
|
|
17
|
+
process.argv = [process.argv[0], process.argv[1], ...translatedArgs];
|
|
18
|
+
await import("../src/cli.ts");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const { runNodeCli } = await import("./node-cli.js");
|
|
23
|
+
await runNodeCli(process.argv.slice(2));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
main().catch((error) => {
|
|
27
|
+
console.error("❌ create-wp-typia failed:", error instanceof Error ? error.message : error);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
});
|
package/lib/node-cli.js
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import readline from "node:readline";
|
|
5
|
+
import { access, constants as fsConstants, rm, writeFile } from "node:fs/promises";
|
|
6
|
+
import { execFileSync } from "node:child_process";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
collectScaffoldAnswers,
|
|
10
|
+
resolvePackageManagerId,
|
|
11
|
+
resolveTemplateId,
|
|
12
|
+
scaffoldProject,
|
|
13
|
+
} from "./scaffold.js";
|
|
14
|
+
import {
|
|
15
|
+
PACKAGE_MANAGER_IDS,
|
|
16
|
+
formatInstallCommand,
|
|
17
|
+
formatRunScript,
|
|
18
|
+
getPackageManagerSelectOptions,
|
|
19
|
+
} from "./package-managers.js";
|
|
20
|
+
import {
|
|
21
|
+
TEMPLATE_IDS,
|
|
22
|
+
getTemplateById,
|
|
23
|
+
getTemplateSelectOptions,
|
|
24
|
+
listTemplates,
|
|
25
|
+
} from "./template-registry.js";
|
|
26
|
+
|
|
27
|
+
function parseArgs(argv) {
|
|
28
|
+
const parsed = {
|
|
29
|
+
help: false,
|
|
30
|
+
noInstall: false,
|
|
31
|
+
packageManager: undefined,
|
|
32
|
+
positionals: [],
|
|
33
|
+
template: undefined,
|
|
34
|
+
yes: false,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
38
|
+
const arg = argv[index];
|
|
39
|
+
|
|
40
|
+
if (!arg.startsWith("-")) {
|
|
41
|
+
parsed.positionals.push(arg);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (arg === "--help" || arg === "-h") {
|
|
46
|
+
parsed.help = true;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (arg === "--yes" || arg === "-y") {
|
|
50
|
+
parsed.yes = true;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (arg === "--no-install") {
|
|
54
|
+
parsed.noInstall = true;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (arg === "--template" || arg === "-t") {
|
|
58
|
+
parsed.template = argv[index + 1];
|
|
59
|
+
index += 1;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (arg.startsWith("--template=")) {
|
|
63
|
+
parsed.template = arg.split("=", 2)[1];
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (arg === "--package-manager" || arg === "-p") {
|
|
67
|
+
parsed.packageManager = argv[index + 1];
|
|
68
|
+
index += 1;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (arg.startsWith("--package-manager=")) {
|
|
72
|
+
parsed.packageManager = arg.split("=", 2)[1];
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
throw new Error(`Unknown flag: ${arg}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return parsed;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function createPrompt() {
|
|
83
|
+
const rl = readline.createInterface({
|
|
84
|
+
input: process.stdin,
|
|
85
|
+
output: process.stdout,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
async text(message, defaultValue, validate) {
|
|
90
|
+
const suffix = defaultValue ? ` (${defaultValue})` : "";
|
|
91
|
+
const answer = await new Promise((resolve) => {
|
|
92
|
+
rl.question(`${message}${suffix}: `, resolve);
|
|
93
|
+
});
|
|
94
|
+
const value = String(answer).trim() || defaultValue;
|
|
95
|
+
if (validate) {
|
|
96
|
+
const result = validate(value);
|
|
97
|
+
if (result !== true) {
|
|
98
|
+
console.error(`❌ ${result}`);
|
|
99
|
+
return this.text(message, defaultValue, validate);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return value;
|
|
103
|
+
},
|
|
104
|
+
async select(message, options, defaultIndex = 1) {
|
|
105
|
+
console.log(message);
|
|
106
|
+
options.forEach((option, index) => {
|
|
107
|
+
const hint = option.hint ? ` - ${option.hint}` : "";
|
|
108
|
+
console.log(` ${index + 1}. ${option.label}${hint}`);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const answer = await this.text("Choice", String(defaultIndex));
|
|
112
|
+
const numeric = Number(answer);
|
|
113
|
+
if (!Number.isNaN(numeric) && options[numeric - 1]) {
|
|
114
|
+
return options[numeric - 1].value;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const direct = options.find((option) => option.value === answer);
|
|
118
|
+
if (direct) {
|
|
119
|
+
return direct.value;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.error(`❌ Invalid selection: ${answer}`);
|
|
123
|
+
return this.select(message, options, defaultIndex);
|
|
124
|
+
},
|
|
125
|
+
close() {
|
|
126
|
+
rl.close();
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function printHelp() {
|
|
132
|
+
console.log(`Usage:
|
|
133
|
+
create-wp-typia <project-dir> [--template <id>] [--yes] [--no-install] [--package-manager <id>]
|
|
134
|
+
create-wp-typia templates list
|
|
135
|
+
create-wp-typia templates inspect <id>
|
|
136
|
+
create-wp-typia doctor
|
|
137
|
+
|
|
138
|
+
Templates: ${TEMPLATE_IDS.join(", ")}
|
|
139
|
+
Package managers: ${PACKAGE_MANAGER_IDS.join(", ")}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function printTemplate(template) {
|
|
143
|
+
console.log(`${template.id}`);
|
|
144
|
+
console.log(template.description);
|
|
145
|
+
console.log(`Category: ${template.defaultCategory}`);
|
|
146
|
+
console.log(`Path: ${template.templateDir}`);
|
|
147
|
+
console.log(`Features: ${template.features.join(", ")}`);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function readCommandVersion(command, args = ["--version"]) {
|
|
151
|
+
try {
|
|
152
|
+
return execFileSync(command, args, {
|
|
153
|
+
encoding: "utf8",
|
|
154
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
155
|
+
}).trim();
|
|
156
|
+
} catch {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function compareMajorVersion(actualVersion, minimumMajor) {
|
|
162
|
+
const parsed = Number.parseInt(actualVersion.replace(/^v/, "").split(".")[0] ?? "", 10);
|
|
163
|
+
return Number.isFinite(parsed) && parsed >= minimumMajor;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function checkWritableDirectory(directory) {
|
|
167
|
+
try {
|
|
168
|
+
await access(directory, fsConstants.W_OK);
|
|
169
|
+
return true;
|
|
170
|
+
} catch {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function checkTempDirectory() {
|
|
176
|
+
const tempFile = path.join(os.tmpdir(), `create-wp-typia-${Date.now()}.tmp`);
|
|
177
|
+
try {
|
|
178
|
+
await writeFile(tempFile, "ok", "utf8");
|
|
179
|
+
await rm(tempFile, { force: true });
|
|
180
|
+
return true;
|
|
181
|
+
} catch {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function printDoctorLine(status, label, detail) {
|
|
187
|
+
const prefix = status === "PASS" ? "PASS" : "FAIL";
|
|
188
|
+
console.log(`${prefix} ${label}: ${detail}`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function runDoctor(cwd) {
|
|
192
|
+
const bunVersion = readCommandVersion("bun");
|
|
193
|
+
const nodeVersion = readCommandVersion("node");
|
|
194
|
+
const gitVersion = readCommandVersion("git");
|
|
195
|
+
const cwdWritable = await checkWritableDirectory(cwd);
|
|
196
|
+
const tempWritable = await checkTempDirectory();
|
|
197
|
+
let hasFailures = false;
|
|
198
|
+
|
|
199
|
+
printDoctorLine(
|
|
200
|
+
bunVersion && compareMajorVersion(bunVersion, 1) ? "PASS" : "FAIL",
|
|
201
|
+
"Bun",
|
|
202
|
+
bunVersion ? `Detected ${bunVersion}` : "Not available",
|
|
203
|
+
);
|
|
204
|
+
hasFailures ||= !(bunVersion && compareMajorVersion(bunVersion, 1));
|
|
205
|
+
|
|
206
|
+
printDoctorLine(
|
|
207
|
+
nodeVersion && compareMajorVersion(nodeVersion, 16) ? "PASS" : "FAIL",
|
|
208
|
+
"Node",
|
|
209
|
+
nodeVersion ? `Detected ${nodeVersion}` : "Not available",
|
|
210
|
+
);
|
|
211
|
+
hasFailures ||= !(nodeVersion && compareMajorVersion(nodeVersion, 16));
|
|
212
|
+
|
|
213
|
+
printDoctorLine(gitVersion ? "PASS" : "FAIL", "git", gitVersion ?? "Not available");
|
|
214
|
+
hasFailures ||= !gitVersion;
|
|
215
|
+
|
|
216
|
+
printDoctorLine(cwdWritable ? "PASS" : "FAIL", "Current directory", cwdWritable ? "Writable" : "Not writable");
|
|
217
|
+
hasFailures ||= !cwdWritable;
|
|
218
|
+
|
|
219
|
+
printDoctorLine(tempWritable ? "PASS" : "FAIL", "Temp directory", tempWritable ? "Writable" : "Not writable");
|
|
220
|
+
hasFailures ||= !tempWritable;
|
|
221
|
+
|
|
222
|
+
for (const template of listTemplates()) {
|
|
223
|
+
const hasAssets =
|
|
224
|
+
fs.existsSync(template.templateDir) &&
|
|
225
|
+
fs.existsSync(path.join(template.templateDir, "package.json.mustache"));
|
|
226
|
+
printDoctorLine(
|
|
227
|
+
hasAssets ? "PASS" : "FAIL",
|
|
228
|
+
`Template ${template.id}`,
|
|
229
|
+
hasAssets ? template.templateDir : "Missing core template assets",
|
|
230
|
+
);
|
|
231
|
+
hasFailures ||= !hasAssets;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (hasFailures) {
|
|
235
|
+
throw new Error("Doctor found one or more failing checks.");
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async function runScaffold(parsed, cwd) {
|
|
240
|
+
const isInteractive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
241
|
+
const prompt = createPrompt();
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
const projectInput = parsed.positionals[0];
|
|
245
|
+
if (!projectInput) {
|
|
246
|
+
throw new Error("Project directory is required. Usage: create-wp-typia <project-dir>");
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const templateId = await resolveTemplateId({
|
|
250
|
+
templateId: parsed.template,
|
|
251
|
+
yes: parsed.yes,
|
|
252
|
+
isInteractive,
|
|
253
|
+
selectTemplate: () =>
|
|
254
|
+
prompt.select("Select a template", getTemplateSelectOptions(), 1),
|
|
255
|
+
});
|
|
256
|
+
const packageManager = await resolvePackageManagerId({
|
|
257
|
+
packageManager: parsed.packageManager,
|
|
258
|
+
yes: parsed.yes,
|
|
259
|
+
isInteractive,
|
|
260
|
+
selectPackageManager: () =>
|
|
261
|
+
prompt.select("Choose a package manager", getPackageManagerSelectOptions(), 1),
|
|
262
|
+
});
|
|
263
|
+
const projectDir = path.resolve(cwd, projectInput);
|
|
264
|
+
const projectName = path.basename(projectDir);
|
|
265
|
+
const answers = await collectScaffoldAnswers({
|
|
266
|
+
projectName,
|
|
267
|
+
templateId,
|
|
268
|
+
yes: parsed.yes,
|
|
269
|
+
promptText: (message, defaultValue, validate) =>
|
|
270
|
+
prompt.text(message, defaultValue, validate),
|
|
271
|
+
});
|
|
272
|
+
const result = await scaffoldProject({
|
|
273
|
+
answers,
|
|
274
|
+
noInstall: parsed.noInstall,
|
|
275
|
+
packageManager,
|
|
276
|
+
projectDir,
|
|
277
|
+
templateId,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
console.log(`\n✅ Created ${result.variables.title} in ${projectDir}`);
|
|
281
|
+
console.log("Next steps:");
|
|
282
|
+
console.log(` cd ${path.isAbsolute(projectInput) ? projectDir : projectInput}`);
|
|
283
|
+
if (parsed.noInstall) {
|
|
284
|
+
console.log(` ${formatInstallCommand(packageManager)}`);
|
|
285
|
+
}
|
|
286
|
+
console.log(` ${formatRunScript(packageManager, "start")}`);
|
|
287
|
+
} finally {
|
|
288
|
+
prompt.close();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export async function runNodeCli(argv = process.argv.slice(2), cwd = process.cwd()) {
|
|
293
|
+
const parsed = parseArgs(argv);
|
|
294
|
+
|
|
295
|
+
if (parsed.help) {
|
|
296
|
+
printHelp();
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const [first, second] = parsed.positionals;
|
|
301
|
+
if (first === "templates") {
|
|
302
|
+
if (second === "list") {
|
|
303
|
+
for (const template of listTemplates()) {
|
|
304
|
+
console.log(`${template.id.padEnd(14)} ${template.description}`);
|
|
305
|
+
console.log(` ${template.features.join(" • ")}`);
|
|
306
|
+
}
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
if (second === "inspect") {
|
|
310
|
+
const templateId = parsed.positionals[2];
|
|
311
|
+
if (!templateId) {
|
|
312
|
+
throw new Error(`Template ID is required. Use one of: ${TEMPLATE_IDS.join(", ")}`);
|
|
313
|
+
}
|
|
314
|
+
printTemplate(getTemplateById(templateId));
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
throw new Error("Usage: create-wp-typia templates <list|inspect>");
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (first === "doctor") {
|
|
321
|
+
await runDoctor(cwd);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
await runScaffold(parsed, cwd);
|
|
326
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type PackageManagerId = "bun" | "npm" | "pnpm" | "yarn";
|
|
2
|
+
|
|
3
|
+
export interface PackageManagerConfig {
|
|
4
|
+
id: PackageManagerId;
|
|
5
|
+
label: string;
|
|
6
|
+
packageManagerField: string;
|
|
7
|
+
installCommand: string;
|
|
8
|
+
frozenInstallCommand: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const PACKAGE_MANAGER_IDS: PackageManagerId[];
|
|
12
|
+
export const PACKAGE_MANAGERS: Record<PackageManagerId, PackageManagerConfig>;
|
|
13
|
+
|
|
14
|
+
export function getPackageManager(id: PackageManagerId | string): PackageManagerConfig;
|
|
15
|
+
export function getPackageManagerSelectOptions(): Array<{
|
|
16
|
+
label: string;
|
|
17
|
+
value: PackageManagerId;
|
|
18
|
+
hint: string;
|
|
19
|
+
}>;
|
|
20
|
+
export function formatRunScript(
|
|
21
|
+
packageManagerId: PackageManagerId,
|
|
22
|
+
scriptName: string,
|
|
23
|
+
extraArgs?: string,
|
|
24
|
+
): string;
|
|
25
|
+
export function formatInstallCommand(packageManagerId: PackageManagerId): string;
|
|
26
|
+
export function transformPackageManagerText(
|
|
27
|
+
content: string,
|
|
28
|
+
packageManagerId: PackageManagerId,
|
|
29
|
+
): string;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const PACKAGE_MANAGER_DATA = [
|
|
2
|
+
{
|
|
3
|
+
id: "bun",
|
|
4
|
+
label: "Bun",
|
|
5
|
+
packageManagerField: "bun@1.3.10",
|
|
6
|
+
installCommand: "bun install",
|
|
7
|
+
frozenInstallCommand: "bun install --frozen-lockfile",
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
id: "npm",
|
|
11
|
+
label: "npm",
|
|
12
|
+
packageManagerField: "npm@11.6.1",
|
|
13
|
+
installCommand: "npm install",
|
|
14
|
+
frozenInstallCommand: "npm ci",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: "pnpm",
|
|
18
|
+
label: "pnpm",
|
|
19
|
+
packageManagerField: "pnpm@8.3.1",
|
|
20
|
+
installCommand: "pnpm install",
|
|
21
|
+
frozenInstallCommand: "pnpm install --frozen-lockfile",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "yarn",
|
|
25
|
+
label: "Yarn",
|
|
26
|
+
packageManagerField: "yarn@3.2.4",
|
|
27
|
+
installCommand: "yarn install",
|
|
28
|
+
frozenInstallCommand: "yarn install --frozen-lockfile",
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
export const PACKAGE_MANAGER_IDS = PACKAGE_MANAGER_DATA.map((manager) => manager.id);
|
|
33
|
+
export const PACKAGE_MANAGERS = Object.freeze(
|
|
34
|
+
Object.fromEntries(PACKAGE_MANAGER_DATA.map((manager) => [manager.id, manager])),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const DEV_INSTALL_FLAGS = {
|
|
38
|
+
bun: "add -d",
|
|
39
|
+
npm: "install --save-dev",
|
|
40
|
+
pnpm: "add -D",
|
|
41
|
+
yarn: "add -D",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const STOP_CHARS = new Set(["\n", "\r", "`", "\"", "'", ")", "]", "}", "!", ",", "."]);
|
|
45
|
+
|
|
46
|
+
export function getPackageManager(id) {
|
|
47
|
+
const manager = PACKAGE_MANAGERS[id];
|
|
48
|
+
if (!manager) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`Unknown package manager "${id}". Expected one of: ${PACKAGE_MANAGER_IDS.join(", ")}`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return manager;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function getPackageManagerSelectOptions() {
|
|
57
|
+
return PACKAGE_MANAGER_DATA.map((manager) => ({
|
|
58
|
+
label: manager.label,
|
|
59
|
+
value: manager.id,
|
|
60
|
+
hint: manager.installCommand,
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function formatRunScript(packageManagerId, scriptName, extraArgs = "") {
|
|
65
|
+
const args = extraArgs.trim();
|
|
66
|
+
|
|
67
|
+
if (packageManagerId === "bun") {
|
|
68
|
+
return args ? `bun run ${scriptName} ${args}` : `bun run ${scriptName}`;
|
|
69
|
+
}
|
|
70
|
+
if (packageManagerId === "npm") {
|
|
71
|
+
return args ? `npm run ${scriptName} -- ${args}` : `npm run ${scriptName}`;
|
|
72
|
+
}
|
|
73
|
+
if (packageManagerId === "pnpm") {
|
|
74
|
+
return args ? `pnpm run ${scriptName} -- ${args}` : `pnpm run ${scriptName}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return args ? `yarn run ${scriptName} ${args}` : `yarn run ${scriptName}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function formatInstallCommand(packageManagerId) {
|
|
81
|
+
return getPackageManager(packageManagerId).installCommand;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function consumeCommandArguments(content, startIndex) {
|
|
85
|
+
let cursor = startIndex;
|
|
86
|
+
let args = "";
|
|
87
|
+
|
|
88
|
+
while (cursor < content.length) {
|
|
89
|
+
const current = content[cursor];
|
|
90
|
+
if (
|
|
91
|
+
STOP_CHARS.has(current) ||
|
|
92
|
+
content.startsWith("&&", cursor) ||
|
|
93
|
+
content.startsWith("||", cursor) ||
|
|
94
|
+
current === ";"
|
|
95
|
+
) {
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
args += current;
|
|
100
|
+
cursor += 1;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
args: args.trim(),
|
|
105
|
+
cursor,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function replaceBunRunCommands(content, packageManagerId) {
|
|
110
|
+
const marker = "bun run ";
|
|
111
|
+
let result = "";
|
|
112
|
+
let cursor = 0;
|
|
113
|
+
|
|
114
|
+
while (cursor < content.length) {
|
|
115
|
+
const index = content.indexOf(marker, cursor);
|
|
116
|
+
if (index === -1) {
|
|
117
|
+
result += content.slice(cursor);
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (index > 0 && /[A-Za-z0-9_-]/.test(content[index - 1])) {
|
|
122
|
+
result += content.slice(cursor, index + marker.length);
|
|
123
|
+
cursor = index + marker.length;
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
result += content.slice(cursor, index);
|
|
128
|
+
const scriptNameStart = index + marker.length;
|
|
129
|
+
const scriptNameMatch = /^[A-Za-z0-9:_-]+/.exec(content.slice(scriptNameStart));
|
|
130
|
+
if (!scriptNameMatch) {
|
|
131
|
+
result += marker;
|
|
132
|
+
cursor = scriptNameStart;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const scriptName = scriptNameMatch[0];
|
|
137
|
+
const argsStart = scriptNameStart + scriptName.length;
|
|
138
|
+
const { args, cursor: nextCursor } = consumeCommandArguments(content, argsStart);
|
|
139
|
+
result += formatRunScript(packageManagerId, scriptName, args);
|
|
140
|
+
cursor = nextCursor;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function replaceDevDependencyInstalls(content, packageManagerId) {
|
|
147
|
+
return content.replace(/\bbun add -d ([^\s&|;`"'()]+)\b/g, (_, packageName) => {
|
|
148
|
+
if (packageManagerId === "bun") {
|
|
149
|
+
return `bun add -d ${packageName}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return `${packageManagerId} ${DEV_INSTALL_FLAGS[packageManagerId]} ${packageName}`;
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function transformPackageManagerText(content, packageManagerId) {
|
|
157
|
+
const manager = getPackageManager(packageManagerId);
|
|
158
|
+
|
|
159
|
+
return replaceDevDependencyInstalls(
|
|
160
|
+
replaceBunRunCommands(
|
|
161
|
+
content
|
|
162
|
+
.replace(/\bbun install --frozen-lockfile\b/g, manager.frozenInstallCommand)
|
|
163
|
+
.replace(/\bbun install\b/g, manager.installCommand),
|
|
164
|
+
packageManagerId,
|
|
165
|
+
),
|
|
166
|
+
packageManagerId,
|
|
167
|
+
)
|
|
168
|
+
.replace(/\s*&&\s*/g, " && ")
|
|
169
|
+
.replace(/\s*\|\|\s*/g, " || ");
|
|
170
|
+
}
|