@korajs/cli 0.1.15 → 0.2.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 +49 -0
- package/dist/bin.cjs +2517 -332
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +12 -10
- package/dist/bin.js.map +1 -1
- package/dist/chunk-E4JG7THU.js +1550 -0
- package/dist/chunk-E4JG7THU.js.map +1 -0
- package/dist/chunk-KTSRAPSE.js +752 -0
- package/dist/chunk-KTSRAPSE.js.map +1 -0
- package/dist/chunk-ZGYRDYXS.js +609 -0
- package/dist/chunk-ZGYRDYXS.js.map +1 -0
- package/dist/create.cjs +974 -159
- package/dist/create.cjs.map +1 -1
- package/dist/create.js +2 -2
- package/dist/index.cjs +2103 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +617 -1
- package/dist/index.d.ts +617 -1
- package/dist/index.js +91 -5
- package/package.json +8 -2
- package/dist/chunk-N36PFOSA.js +0 -103
- package/dist/chunk-N36PFOSA.js.map +0 -1
- package/dist/chunk-SYOFLJLB.js +0 -437
- package/dist/chunk-SYOFLJLB.js.map +0 -1
- package/dist/chunk-VGOOQ56H.js +0 -103
- package/dist/chunk-VGOOQ56H.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,26 +1,112 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
DEPLOY_PLATFORMS,
|
|
4
|
+
FlyAdapter,
|
|
5
|
+
NodeFlyCommandRunner,
|
|
6
|
+
NodeRailwayCommandRunner,
|
|
7
|
+
RailwayAdapter,
|
|
8
|
+
StubDeployAdapter,
|
|
9
|
+
buildClient,
|
|
10
|
+
bundleServer,
|
|
11
|
+
createDeployAdapter,
|
|
12
|
+
deployCommand,
|
|
13
|
+
generateDeployPackageJson,
|
|
14
|
+
generateDockerIgnore,
|
|
15
|
+
generateDockerfile,
|
|
16
|
+
generateFlyToml,
|
|
17
|
+
generateRailwayJson,
|
|
18
|
+
generateTypes,
|
|
19
|
+
isDeployPlatform,
|
|
20
|
+
readDeployState,
|
|
21
|
+
resetDeployState,
|
|
22
|
+
resolveDeployDirectory,
|
|
23
|
+
resolveDeployStatePath,
|
|
24
|
+
updateDeployState,
|
|
25
|
+
writeDeployState,
|
|
26
|
+
writeDockerIgnoreArtifact,
|
|
27
|
+
writeDockerfileArtifact,
|
|
28
|
+
writeFlyTomlArtifact,
|
|
29
|
+
writeRailwayJsonArtifact
|
|
30
|
+
} from "./chunk-E4JG7THU.js";
|
|
5
31
|
import {
|
|
32
|
+
ClackPromptClient,
|
|
6
33
|
CliError,
|
|
7
34
|
DevServerError,
|
|
8
35
|
InvalidProjectError,
|
|
9
36
|
PACKAGE_MANAGERS,
|
|
37
|
+
PreferenceStore,
|
|
10
38
|
ProjectExistsError,
|
|
39
|
+
PromptCancelledError,
|
|
40
|
+
ReadlinePromptClient,
|
|
11
41
|
SchemaNotFoundError,
|
|
12
42
|
TEMPLATES,
|
|
13
|
-
TEMPLATE_INFO
|
|
14
|
-
|
|
43
|
+
TEMPLATE_INFO,
|
|
44
|
+
applySyncProviderPreset,
|
|
45
|
+
createPromptClient,
|
|
46
|
+
determineTemplateFromSelections,
|
|
47
|
+
getCreatePreferencesOrDefault,
|
|
48
|
+
getDefaultCreatePreferences,
|
|
49
|
+
isAuthValue,
|
|
50
|
+
isDatabaseProviderValue,
|
|
51
|
+
isDatabaseValue,
|
|
52
|
+
isFrameworkValue,
|
|
53
|
+
resolveCreatePreferencesFlow,
|
|
54
|
+
saveResolvedPreferences,
|
|
55
|
+
shouldSavePreferences,
|
|
56
|
+
validateProjectName
|
|
57
|
+
} from "./chunk-KTSRAPSE.js";
|
|
15
58
|
export {
|
|
59
|
+
ClackPromptClient,
|
|
16
60
|
CliError,
|
|
61
|
+
DEPLOY_PLATFORMS,
|
|
17
62
|
DevServerError,
|
|
63
|
+
FlyAdapter,
|
|
18
64
|
InvalidProjectError,
|
|
65
|
+
NodeFlyCommandRunner,
|
|
66
|
+
NodeRailwayCommandRunner,
|
|
19
67
|
PACKAGE_MANAGERS,
|
|
68
|
+
PreferenceStore,
|
|
20
69
|
ProjectExistsError,
|
|
70
|
+
PromptCancelledError,
|
|
71
|
+
RailwayAdapter,
|
|
72
|
+
ReadlinePromptClient,
|
|
21
73
|
SchemaNotFoundError,
|
|
74
|
+
StubDeployAdapter,
|
|
22
75
|
TEMPLATES,
|
|
23
76
|
TEMPLATE_INFO,
|
|
24
|
-
|
|
77
|
+
applySyncProviderPreset,
|
|
78
|
+
buildClient,
|
|
79
|
+
bundleServer,
|
|
80
|
+
createDeployAdapter,
|
|
81
|
+
createPromptClient,
|
|
82
|
+
deployCommand,
|
|
83
|
+
determineTemplateFromSelections,
|
|
84
|
+
generateDeployPackageJson,
|
|
85
|
+
generateDockerIgnore,
|
|
86
|
+
generateDockerfile,
|
|
87
|
+
generateFlyToml,
|
|
88
|
+
generateRailwayJson,
|
|
89
|
+
generateTypes,
|
|
90
|
+
getCreatePreferencesOrDefault,
|
|
91
|
+
getDefaultCreatePreferences,
|
|
92
|
+
isAuthValue,
|
|
93
|
+
isDatabaseProviderValue,
|
|
94
|
+
isDatabaseValue,
|
|
95
|
+
isDeployPlatform,
|
|
96
|
+
isFrameworkValue,
|
|
97
|
+
readDeployState,
|
|
98
|
+
resetDeployState,
|
|
99
|
+
resolveCreatePreferencesFlow,
|
|
100
|
+
resolveDeployDirectory,
|
|
101
|
+
resolveDeployStatePath,
|
|
102
|
+
saveResolvedPreferences,
|
|
103
|
+
shouldSavePreferences,
|
|
104
|
+
updateDeployState,
|
|
105
|
+
validateProjectName,
|
|
106
|
+
writeDeployState,
|
|
107
|
+
writeDockerIgnoreArtifact,
|
|
108
|
+
writeDockerfileArtifact,
|
|
109
|
+
writeFlyTomlArtifact,
|
|
110
|
+
writeRailwayJsonArtifact
|
|
25
111
|
};
|
|
26
112
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@korajs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Kora.js CLI tooling and project scaffolding",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -31,12 +31,18 @@
|
|
|
31
31
|
"templates"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
+
"@clack/prompts": "^1.2.0",
|
|
34
35
|
"citty": "^0.1.6",
|
|
36
|
+
"conf": "^15.1.0",
|
|
37
|
+
"esbuild": "^0.28.0",
|
|
38
|
+
"validate-npm-package-name": "^7.0.2",
|
|
35
39
|
"@korajs/core": "0.1.3"
|
|
36
40
|
},
|
|
37
41
|
"devDependencies": {
|
|
38
|
-
"
|
|
42
|
+
"@types/node": "^25.5.2",
|
|
43
|
+
"@types/validate-npm-package-name": "^4.0.2",
|
|
39
44
|
"tsup": "^8.3.6",
|
|
45
|
+
"typescript": "^5.7.3",
|
|
40
46
|
"vitest": "^3.0.4"
|
|
41
47
|
},
|
|
42
48
|
"license": "MIT",
|
package/dist/chunk-N36PFOSA.js
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/commands/generate/type-generator.ts
|
|
4
|
-
function generateTypes(schema) {
|
|
5
|
-
const lines = [
|
|
6
|
-
"// Auto-generated by @korajs/cli \u2014 do not edit manually",
|
|
7
|
-
`// Generated from schema version ${String(schema.version)}`,
|
|
8
|
-
""
|
|
9
|
-
];
|
|
10
|
-
const collectionNames = Object.keys(schema.collections);
|
|
11
|
-
if (collectionNames.length === 0) {
|
|
12
|
-
return lines.join("\n");
|
|
13
|
-
}
|
|
14
|
-
for (const [name, collection] of Object.entries(schema.collections)) {
|
|
15
|
-
const pascal = toPascalCase(name);
|
|
16
|
-
const fields = collection.fields;
|
|
17
|
-
lines.push(`export interface ${pascal}Record {`);
|
|
18
|
-
lines.push(" readonly id: string");
|
|
19
|
-
for (const [fieldName, descriptor] of Object.entries(fields)) {
|
|
20
|
-
const tsType = fieldKindToTypeScript(descriptor);
|
|
21
|
-
const optional = !descriptor.required && !descriptor.auto ? "?" : "";
|
|
22
|
-
lines.push(` readonly ${fieldName}${optional}: ${tsType}`);
|
|
23
|
-
}
|
|
24
|
-
lines.push("}");
|
|
25
|
-
lines.push("");
|
|
26
|
-
lines.push(`export interface ${pascal}InsertInput {`);
|
|
27
|
-
for (const [fieldName, descriptor] of Object.entries(fields)) {
|
|
28
|
-
if (descriptor.auto) continue;
|
|
29
|
-
const tsType = fieldKindToTypeScript(descriptor);
|
|
30
|
-
const optional = !descriptor.required || descriptor.defaultValue !== void 0 ? "?" : "";
|
|
31
|
-
lines.push(` ${fieldName}${optional}: ${tsType}`);
|
|
32
|
-
}
|
|
33
|
-
lines.push("}");
|
|
34
|
-
lines.push("");
|
|
35
|
-
lines.push(`export interface ${pascal}UpdateInput {`);
|
|
36
|
-
for (const [fieldName, descriptor] of Object.entries(fields)) {
|
|
37
|
-
if (descriptor.auto) continue;
|
|
38
|
-
const tsType = fieldKindToTypeScript(descriptor);
|
|
39
|
-
lines.push(` ${fieldName}?: ${tsType}`);
|
|
40
|
-
}
|
|
41
|
-
lines.push("}");
|
|
42
|
-
lines.push("");
|
|
43
|
-
}
|
|
44
|
-
return lines.join("\n");
|
|
45
|
-
}
|
|
46
|
-
function fieldKindToTypeScript(descriptor) {
|
|
47
|
-
switch (descriptor.kind) {
|
|
48
|
-
case "string":
|
|
49
|
-
return "string";
|
|
50
|
-
case "number":
|
|
51
|
-
return "number";
|
|
52
|
-
case "boolean":
|
|
53
|
-
return "boolean";
|
|
54
|
-
case "timestamp":
|
|
55
|
-
return "number";
|
|
56
|
-
case "richtext":
|
|
57
|
-
return "string";
|
|
58
|
-
case "enum": {
|
|
59
|
-
if (descriptor.enumValues && descriptor.enumValues.length > 0) {
|
|
60
|
-
return descriptor.enumValues.map((v) => `'${v}'`).join(" | ");
|
|
61
|
-
}
|
|
62
|
-
return "string";
|
|
63
|
-
}
|
|
64
|
-
case "array": {
|
|
65
|
-
const itemType = itemKindToTypeScript(descriptor.itemKind);
|
|
66
|
-
return `Array<${itemType}>`;
|
|
67
|
-
}
|
|
68
|
-
default:
|
|
69
|
-
return "unknown";
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
function itemKindToTypeScript(kind) {
|
|
73
|
-
switch (kind) {
|
|
74
|
-
case "string":
|
|
75
|
-
return "string";
|
|
76
|
-
case "number":
|
|
77
|
-
return "number";
|
|
78
|
-
case "boolean":
|
|
79
|
-
return "boolean";
|
|
80
|
-
case "timestamp":
|
|
81
|
-
return "number";
|
|
82
|
-
case "richtext":
|
|
83
|
-
return "string";
|
|
84
|
-
case "enum":
|
|
85
|
-
return "string";
|
|
86
|
-
case "array":
|
|
87
|
-
return "unknown[]";
|
|
88
|
-
default:
|
|
89
|
-
return "unknown";
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
function toPascalCase(name) {
|
|
93
|
-
return name.split(/[_-]/).map((part) => {
|
|
94
|
-
if (part.length === 0) return "";
|
|
95
|
-
const first = part[0];
|
|
96
|
-
return first ? first.toUpperCase() + part.slice(1) : "";
|
|
97
|
-
}).join("");
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export {
|
|
101
|
-
generateTypes
|
|
102
|
-
};
|
|
103
|
-
//# sourceMappingURL=chunk-N36PFOSA.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/generate/type-generator.ts"],"sourcesContent":["import type { FieldDescriptor, FieldKind, SchemaDefinition } from '@korajs/core'\n\n/**\n * Generates TypeScript interfaces from a SchemaDefinition.\n * For each collection, produces three interfaces:\n * - {Name}Record: full record type (all fields + id)\n * - {Name}InsertInput: insert input (omit auto fields, optional for fields with defaults)\n * - {Name}UpdateInput: update input (all non-auto fields optional)\n *\n * @param schema - A validated SchemaDefinition from defineSchema()\n * @returns A complete TypeScript file as a string\n */\nexport function generateTypes(schema: SchemaDefinition): string {\n\tconst lines: string[] = [\n\t\t'// Auto-generated by @korajs/cli — do not edit manually',\n\t\t`// Generated from schema version ${String(schema.version)}`,\n\t\t'',\n\t]\n\n\tconst collectionNames = Object.keys(schema.collections)\n\tif (collectionNames.length === 0) {\n\t\treturn lines.join('\\n')\n\t}\n\n\tfor (const [name, collection] of Object.entries(schema.collections)) {\n\t\tconst pascal = toPascalCase(name)\n\t\tconst fields = collection.fields\n\n\t\t// Record type: all fields + id\n\t\tlines.push(`export interface ${pascal}Record {`)\n\t\tlines.push('\\treadonly id: string')\n\t\tfor (const [fieldName, descriptor] of Object.entries(fields)) {\n\t\t\tconst tsType = fieldKindToTypeScript(descriptor)\n\t\t\tconst optional = !descriptor.required && !descriptor.auto ? '?' : ''\n\t\t\tlines.push(`\\treadonly ${fieldName}${optional}: ${tsType}`)\n\t\t}\n\t\tlines.push('}')\n\t\tlines.push('')\n\n\t\t// Insert input: omit auto fields, optional for fields with defaults\n\t\tlines.push(`export interface ${pascal}InsertInput {`)\n\t\tfor (const [fieldName, descriptor] of Object.entries(fields)) {\n\t\t\tif (descriptor.auto) continue\n\t\t\tconst tsType = fieldKindToTypeScript(descriptor)\n\t\t\tconst optional = !descriptor.required || descriptor.defaultValue !== undefined ? '?' : ''\n\t\t\tlines.push(`\\t${fieldName}${optional}: ${tsType}`)\n\t\t}\n\t\tlines.push('}')\n\t\tlines.push('')\n\n\t\t// Update input: all non-auto fields optional\n\t\tlines.push(`export interface ${pascal}UpdateInput {`)\n\t\tfor (const [fieldName, descriptor] of Object.entries(fields)) {\n\t\t\tif (descriptor.auto) continue\n\t\t\tconst tsType = fieldKindToTypeScript(descriptor)\n\t\t\tlines.push(`\\t${fieldName}?: ${tsType}`)\n\t\t}\n\t\tlines.push('}')\n\t\tlines.push('')\n\t}\n\n\treturn lines.join('\\n')\n}\n\n/** Maps a FieldDescriptor to its TypeScript type string */\nfunction fieldKindToTypeScript(descriptor: FieldDescriptor): string {\n\tswitch (descriptor.kind) {\n\t\tcase 'string':\n\t\t\treturn 'string'\n\t\tcase 'number':\n\t\t\treturn 'number'\n\t\tcase 'boolean':\n\t\t\treturn 'boolean'\n\t\tcase 'timestamp':\n\t\t\treturn 'number'\n\t\tcase 'richtext':\n\t\t\treturn 'string'\n\t\tcase 'enum': {\n\t\t\tif (descriptor.enumValues && descriptor.enumValues.length > 0) {\n\t\t\t\treturn descriptor.enumValues.map((v) => `'${v}'`).join(' | ')\n\t\t\t}\n\t\t\treturn 'string'\n\t\t}\n\t\tcase 'array': {\n\t\t\tconst itemType = itemKindToTypeScript(descriptor.itemKind)\n\t\t\treturn `Array<${itemType}>`\n\t\t}\n\t\tdefault:\n\t\t\treturn 'unknown'\n\t}\n}\n\n/** Maps an item FieldKind to its TypeScript type string */\nfunction itemKindToTypeScript(kind: FieldKind | null): string {\n\tswitch (kind) {\n\t\tcase 'string':\n\t\t\treturn 'string'\n\t\tcase 'number':\n\t\t\treturn 'number'\n\t\tcase 'boolean':\n\t\t\treturn 'boolean'\n\t\tcase 'timestamp':\n\t\t\treturn 'number'\n\t\tcase 'richtext':\n\t\t\treturn 'string'\n\t\tcase 'enum':\n\t\t\treturn 'string'\n\t\tcase 'array':\n\t\t\treturn 'unknown[]'\n\t\tdefault:\n\t\t\treturn 'unknown'\n\t}\n}\n\n/** Converts a snake_case or kebab-case name to PascalCase */\nfunction toPascalCase(name: string): string {\n\treturn name\n\t\t.split(/[_-]/)\n\t\t.map((part) => {\n\t\t\tif (part.length === 0) return ''\n\t\t\tconst first = part[0]\n\t\t\treturn first ? first.toUpperCase() + part.slice(1) : ''\n\t\t})\n\t\t.join('')\n}\n"],"mappings":"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","names":[]}
|
package/dist/chunk-SYOFLJLB.js
DELETED
|
@@ -1,437 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
PACKAGE_MANAGERS,
|
|
4
|
-
ProjectExistsError,
|
|
5
|
-
TEMPLATES,
|
|
6
|
-
TEMPLATE_INFO
|
|
7
|
-
} from "./chunk-VGOOQ56H.js";
|
|
8
|
-
|
|
9
|
-
// src/commands/create/create-command.ts
|
|
10
|
-
import { execSync as execSync2 } from "child_process";
|
|
11
|
-
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
12
|
-
import { dirname as dirname3, resolve as resolve3 } from "path";
|
|
13
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
14
|
-
import { defineCommand } from "citty";
|
|
15
|
-
|
|
16
|
-
// src/utils/fs-helpers.ts
|
|
17
|
-
import { access, readFile } from "fs/promises";
|
|
18
|
-
import { dirname, join, resolve } from "path";
|
|
19
|
-
async function directoryExists(path) {
|
|
20
|
-
try {
|
|
21
|
-
await access(path);
|
|
22
|
-
return true;
|
|
23
|
-
} catch {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
async function findProjectRoot(startDir) {
|
|
28
|
-
let current = resolve(startDir ?? process.cwd());
|
|
29
|
-
for (; ; ) {
|
|
30
|
-
const pkgPath = join(current, "package.json");
|
|
31
|
-
try {
|
|
32
|
-
const content = await readFile(pkgPath, "utf-8");
|
|
33
|
-
const pkg = JSON.parse(content);
|
|
34
|
-
if (isKoraProject(pkg)) {
|
|
35
|
-
return current;
|
|
36
|
-
}
|
|
37
|
-
} catch {
|
|
38
|
-
}
|
|
39
|
-
const parent = dirname(current);
|
|
40
|
-
if (parent === current) break;
|
|
41
|
-
current = parent;
|
|
42
|
-
}
|
|
43
|
-
return null;
|
|
44
|
-
}
|
|
45
|
-
async function findSchemaFile(projectRoot) {
|
|
46
|
-
const candidates = [
|
|
47
|
-
join(projectRoot, "src", "schema.ts"),
|
|
48
|
-
join(projectRoot, "schema.ts"),
|
|
49
|
-
join(projectRoot, "src", "schema.js"),
|
|
50
|
-
join(projectRoot, "schema.js")
|
|
51
|
-
];
|
|
52
|
-
for (const candidate of candidates) {
|
|
53
|
-
try {
|
|
54
|
-
await access(candidate);
|
|
55
|
-
return candidate;
|
|
56
|
-
} catch {
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
async function hasTsxInstalled(projectRoot) {
|
|
62
|
-
try {
|
|
63
|
-
await access(join(projectRoot, "node_modules", "tsx", "package.json"));
|
|
64
|
-
return true;
|
|
65
|
-
} catch {
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
async function resolveProjectBinaryEntryPoint(projectRoot, packageName, binaryName) {
|
|
70
|
-
const pkgJsonPath = join(projectRoot, "node_modules", packageName, "package.json");
|
|
71
|
-
try {
|
|
72
|
-
const content = await readFile(pkgJsonPath, "utf-8");
|
|
73
|
-
const pkg = JSON.parse(content);
|
|
74
|
-
let binPath;
|
|
75
|
-
if (typeof pkg.bin === "string") {
|
|
76
|
-
binPath = pkg.bin;
|
|
77
|
-
} else if (typeof pkg.bin === "object" && pkg.bin !== null) {
|
|
78
|
-
binPath = pkg.bin[binaryName];
|
|
79
|
-
}
|
|
80
|
-
if (!binPath) return null;
|
|
81
|
-
const fullPath = join(projectRoot, "node_modules", packageName, binPath);
|
|
82
|
-
await access(fullPath);
|
|
83
|
-
return fullPath;
|
|
84
|
-
} catch {
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
function isKoraProject(pkg) {
|
|
89
|
-
if (typeof pkg !== "object" || pkg === null) return false;
|
|
90
|
-
const record = pkg;
|
|
91
|
-
return hasKoraDep(record.dependencies) || hasKoraDep(record.devDependencies);
|
|
92
|
-
}
|
|
93
|
-
function hasKoraDep(deps) {
|
|
94
|
-
if (typeof deps !== "object" || deps === null) return false;
|
|
95
|
-
return Object.keys(deps).some((key) => key === "kora" || key.startsWith("@korajs/"));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// src/utils/logger.ts
|
|
99
|
-
var RESET = "\x1B[0m";
|
|
100
|
-
var BOLD = "\x1B[1m";
|
|
101
|
-
var DIM = "\x1B[2m";
|
|
102
|
-
var GREEN = "\x1B[32m";
|
|
103
|
-
var YELLOW = "\x1B[33m";
|
|
104
|
-
var RED = "\x1B[31m";
|
|
105
|
-
var CYAN = "\x1B[36m";
|
|
106
|
-
function createLogger(options) {
|
|
107
|
-
const colorDisabled = options?.noColor === true || process.env.NO_COLOR !== void 0 || !process.stdout.isTTY;
|
|
108
|
-
function color(code, text) {
|
|
109
|
-
return colorDisabled ? text : `${code}${text}${RESET}`;
|
|
110
|
-
}
|
|
111
|
-
return {
|
|
112
|
-
info(message) {
|
|
113
|
-
console.log(color(CYAN, message));
|
|
114
|
-
},
|
|
115
|
-
success(message) {
|
|
116
|
-
console.log(color(GREEN, ` \u2713 ${message}`));
|
|
117
|
-
},
|
|
118
|
-
warn(message) {
|
|
119
|
-
console.warn(color(YELLOW, ` \u26A0 ${message}`));
|
|
120
|
-
},
|
|
121
|
-
error(message) {
|
|
122
|
-
console.error(color(RED, ` \u2717 ${message}`));
|
|
123
|
-
},
|
|
124
|
-
step(message) {
|
|
125
|
-
console.log(color(DIM, ` ${message}`));
|
|
126
|
-
},
|
|
127
|
-
blank() {
|
|
128
|
-
console.log();
|
|
129
|
-
},
|
|
130
|
-
banner() {
|
|
131
|
-
console.log();
|
|
132
|
-
console.log(
|
|
133
|
-
color(BOLD + CYAN, " Kora.js") + color(DIM, " \u2014 Offline-first application framework")
|
|
134
|
-
);
|
|
135
|
-
console.log();
|
|
136
|
-
}
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// src/utils/package-manager.ts
|
|
141
|
-
import { execSync } from "child_process";
|
|
142
|
-
function detectPackageManager() {
|
|
143
|
-
const userAgent = process.env.npm_config_user_agent;
|
|
144
|
-
if (!userAgent) return "npm";
|
|
145
|
-
if (userAgent.startsWith("pnpm/")) return "pnpm";
|
|
146
|
-
if (userAgent.startsWith("yarn/")) return "yarn";
|
|
147
|
-
if (userAgent.startsWith("bun/")) return "bun";
|
|
148
|
-
return "npm";
|
|
149
|
-
}
|
|
150
|
-
function getInstallCommand(pm) {
|
|
151
|
-
return pm === "yarn" ? "yarn" : `${pm} install`;
|
|
152
|
-
}
|
|
153
|
-
function getRunDevCommand(pm) {
|
|
154
|
-
if (pm === "npm") return "npm run dev";
|
|
155
|
-
return `${pm} dev`;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// src/utils/prompt.ts
|
|
159
|
-
import { createInterface } from "readline";
|
|
160
|
-
function promptText(message, defaultValue, options) {
|
|
161
|
-
return new Promise((resolve4) => {
|
|
162
|
-
const rl = createReadline(options);
|
|
163
|
-
const suffix = defaultValue !== void 0 ? ` (${defaultValue})` : "";
|
|
164
|
-
rl.question(` ? ${message}${suffix}: `, (answer) => {
|
|
165
|
-
rl.close();
|
|
166
|
-
const trimmed = answer.trim();
|
|
167
|
-
resolve4(trimmed || defaultValue || "");
|
|
168
|
-
});
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
function promptSelect(message, choices, options) {
|
|
172
|
-
return new Promise((resolve4) => {
|
|
173
|
-
const rl = createReadline(options);
|
|
174
|
-
const out = options?.output ?? process.stdout;
|
|
175
|
-
out.write(` ? ${message}
|
|
176
|
-
`);
|
|
177
|
-
for (let i = 0; i < choices.length; i++) {
|
|
178
|
-
const choice = choices[i];
|
|
179
|
-
if (choice) {
|
|
180
|
-
out.write(` ${i + 1}) ${choice.label}
|
|
181
|
-
`);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
const ask = () => {
|
|
185
|
-
rl.question(" > ", (answer) => {
|
|
186
|
-
const index = Number.parseInt(answer.trim(), 10) - 1;
|
|
187
|
-
const selected = choices[index];
|
|
188
|
-
if (selected) {
|
|
189
|
-
rl.close();
|
|
190
|
-
resolve4(selected.value);
|
|
191
|
-
} else {
|
|
192
|
-
out.write(` Please enter a number between 1 and ${choices.length}
|
|
193
|
-
`);
|
|
194
|
-
ask();
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
};
|
|
198
|
-
ask();
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
function promptConfirm(message, defaultValue = false, options) {
|
|
202
|
-
return new Promise((resolve4) => {
|
|
203
|
-
const rl = createReadline(options);
|
|
204
|
-
const suffix = defaultValue ? "Y/n" : "y/N";
|
|
205
|
-
const ask = () => {
|
|
206
|
-
rl.question(` ? ${message} (${suffix}): `, (answer) => {
|
|
207
|
-
const normalized = answer.trim().toLowerCase();
|
|
208
|
-
if (normalized.length === 0) {
|
|
209
|
-
rl.close();
|
|
210
|
-
resolve4(defaultValue);
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
if (normalized === "y" || normalized === "yes") {
|
|
214
|
-
rl.close();
|
|
215
|
-
resolve4(true);
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
if (normalized === "n" || normalized === "no") {
|
|
219
|
-
rl.close();
|
|
220
|
-
resolve4(false);
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
(options?.output ?? process.stdout).write(" Please answer with y or n\n");
|
|
224
|
-
ask();
|
|
225
|
-
});
|
|
226
|
-
};
|
|
227
|
-
ask();
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
function createReadline(options) {
|
|
231
|
-
return createInterface({
|
|
232
|
-
input: options?.input ?? process.stdin,
|
|
233
|
-
output: options?.output ?? process.stdout
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// src/commands/create/template-engine.ts
|
|
238
|
-
import { existsSync } from "fs";
|
|
239
|
-
import { copyFile, mkdir, readFile as readFile2, readdir, stat, writeFile } from "fs/promises";
|
|
240
|
-
import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
|
|
241
|
-
import { fileURLToPath } from "url";
|
|
242
|
-
function substituteVariables(template, context) {
|
|
243
|
-
return template.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
|
|
244
|
-
const value = context[key];
|
|
245
|
-
return value !== void 0 ? value : `{{${key}}}`;
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
function getTemplatePath(templateName) {
|
|
249
|
-
let dir = dirname2(fileURLToPath(import.meta.url));
|
|
250
|
-
for (let i = 0; i < 5; i++) {
|
|
251
|
-
if (existsSync(resolve2(dir, "templates"))) {
|
|
252
|
-
return resolve2(dir, "templates", templateName);
|
|
253
|
-
}
|
|
254
|
-
dir = dirname2(dir);
|
|
255
|
-
}
|
|
256
|
-
const currentDir = dirname2(fileURLToPath(import.meta.url));
|
|
257
|
-
return resolve2(currentDir, "..", "templates", templateName);
|
|
258
|
-
}
|
|
259
|
-
async function scaffoldTemplate(templateName, targetDir, context) {
|
|
260
|
-
const templateDir = getTemplatePath(templateName);
|
|
261
|
-
const vars = {
|
|
262
|
-
projectName: context.projectName,
|
|
263
|
-
packageManager: context.packageManager,
|
|
264
|
-
koraVersion: context.koraVersion
|
|
265
|
-
};
|
|
266
|
-
await copyDirectory(templateDir, targetDir, vars);
|
|
267
|
-
}
|
|
268
|
-
async function copyDirectory(src, dest, vars) {
|
|
269
|
-
await mkdir(dest, { recursive: true });
|
|
270
|
-
const entries = await readdir(src);
|
|
271
|
-
for (const entry of entries) {
|
|
272
|
-
const srcPath = join2(src, entry);
|
|
273
|
-
const srcStat = await stat(srcPath);
|
|
274
|
-
if (srcStat.isDirectory()) {
|
|
275
|
-
await copyDirectory(srcPath, join2(dest, entry), vars);
|
|
276
|
-
} else if (entry.endsWith(".hbs")) {
|
|
277
|
-
const content = await readFile2(srcPath, "utf-8");
|
|
278
|
-
const outputName = entry.slice(0, -4);
|
|
279
|
-
await writeFile(join2(dest, outputName), substituteVariables(content, vars), "utf-8");
|
|
280
|
-
} else {
|
|
281
|
-
await copyFile(srcPath, join2(dest, entry));
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// src/commands/create/create-command.ts
|
|
287
|
-
var createCommand = defineCommand({
|
|
288
|
-
meta: {
|
|
289
|
-
name: "create",
|
|
290
|
-
description: "Create a new Kora application"
|
|
291
|
-
},
|
|
292
|
-
args: {
|
|
293
|
-
name: {
|
|
294
|
-
type: "positional",
|
|
295
|
-
description: "Project directory name",
|
|
296
|
-
required: false
|
|
297
|
-
},
|
|
298
|
-
template: {
|
|
299
|
-
type: "string",
|
|
300
|
-
description: "Project template (react-tailwind-sync, react-tailwind, react-sync, react-basic)"
|
|
301
|
-
},
|
|
302
|
-
pm: {
|
|
303
|
-
type: "string",
|
|
304
|
-
description: "Package manager (pnpm, npm, yarn, bun)"
|
|
305
|
-
},
|
|
306
|
-
"skip-install": {
|
|
307
|
-
type: "boolean",
|
|
308
|
-
description: "Skip installing dependencies",
|
|
309
|
-
default: false
|
|
310
|
-
},
|
|
311
|
-
yes: {
|
|
312
|
-
type: "boolean",
|
|
313
|
-
alias: "y",
|
|
314
|
-
description: "Accept all defaults (react-tailwind-sync + detected package manager)",
|
|
315
|
-
default: false
|
|
316
|
-
},
|
|
317
|
-
tailwind: {
|
|
318
|
-
type: "boolean",
|
|
319
|
-
description: "Use Tailwind CSS (use --no-tailwind to skip)"
|
|
320
|
-
},
|
|
321
|
-
sync: {
|
|
322
|
-
type: "boolean",
|
|
323
|
-
description: "Include sync server (use --no-sync to skip)"
|
|
324
|
-
}
|
|
325
|
-
},
|
|
326
|
-
async run({ args }) {
|
|
327
|
-
const logger = createLogger();
|
|
328
|
-
logger.banner();
|
|
329
|
-
const useDefaults = args.yes === true;
|
|
330
|
-
const projectName = args.name || (useDefaults ? "my-kora-app" : await promptText("Project name", "my-kora-app"));
|
|
331
|
-
if (!projectName) {
|
|
332
|
-
logger.error("Project name is required");
|
|
333
|
-
process.exitCode = 1;
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
let template;
|
|
337
|
-
if (args.template && isValidTemplate(args.template)) {
|
|
338
|
-
template = args.template;
|
|
339
|
-
} else if (useDefaults) {
|
|
340
|
-
template = "react-tailwind-sync";
|
|
341
|
-
} else if (args.tailwind !== void 0 || args.sync !== void 0) {
|
|
342
|
-
template = resolveTemplateFromFlags(args.tailwind, args.sync);
|
|
343
|
-
} else {
|
|
344
|
-
template = await promptSelect(
|
|
345
|
-
"Select a template:",
|
|
346
|
-
TEMPLATE_INFO.map((t) => ({ label: `${t.label} \u2014 ${t.description}`, value: t.name }))
|
|
347
|
-
);
|
|
348
|
-
}
|
|
349
|
-
let pm;
|
|
350
|
-
if (args.pm && isValidPackageManager(args.pm)) {
|
|
351
|
-
pm = args.pm;
|
|
352
|
-
} else if (useDefaults) {
|
|
353
|
-
pm = detectPackageManager();
|
|
354
|
-
} else {
|
|
355
|
-
const detected = detectPackageManager();
|
|
356
|
-
pm = await promptSelect(
|
|
357
|
-
"Package manager:",
|
|
358
|
-
PACKAGE_MANAGERS.map((p) => ({
|
|
359
|
-
label: p === detected ? `${p} (detected)` : p,
|
|
360
|
-
value: p
|
|
361
|
-
}))
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
const targetDir = resolve3(process.cwd(), projectName);
|
|
365
|
-
if (await directoryExists(targetDir)) {
|
|
366
|
-
throw new ProjectExistsError(projectName);
|
|
367
|
-
}
|
|
368
|
-
const koraVersion = resolveKoraVersion();
|
|
369
|
-
logger.step(`Creating ${projectName} with ${template} template...`);
|
|
370
|
-
await scaffoldTemplate(template, targetDir, {
|
|
371
|
-
projectName,
|
|
372
|
-
packageManager: pm,
|
|
373
|
-
koraVersion
|
|
374
|
-
});
|
|
375
|
-
logger.success("Project scaffolded");
|
|
376
|
-
if (!args["skip-install"]) {
|
|
377
|
-
logger.step("Installing dependencies...");
|
|
378
|
-
try {
|
|
379
|
-
execSync2(getInstallCommand(pm), { cwd: targetDir, stdio: "inherit" });
|
|
380
|
-
logger.success("Dependencies installed");
|
|
381
|
-
} catch {
|
|
382
|
-
logger.warn("Failed to install dependencies. Run install manually.");
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
logger.blank();
|
|
386
|
-
logger.info("Done! Next steps:");
|
|
387
|
-
logger.blank();
|
|
388
|
-
logger.step(` cd ${projectName}`);
|
|
389
|
-
logger.step(` ${getRunDevCommand(pm)}`);
|
|
390
|
-
logger.blank();
|
|
391
|
-
}
|
|
392
|
-
});
|
|
393
|
-
function isValidTemplate(value) {
|
|
394
|
-
return TEMPLATES.includes(value);
|
|
395
|
-
}
|
|
396
|
-
function isValidPackageManager(value) {
|
|
397
|
-
return PACKAGE_MANAGERS.includes(value);
|
|
398
|
-
}
|
|
399
|
-
function resolveTemplateFromFlags(tailwind, sync) {
|
|
400
|
-
const useTailwind = tailwind !== false;
|
|
401
|
-
const useSync = sync !== false;
|
|
402
|
-
if (useTailwind && useSync) return "react-tailwind-sync";
|
|
403
|
-
if (useTailwind && !useSync) return "react-tailwind";
|
|
404
|
-
if (!useTailwind && useSync) return "react-sync";
|
|
405
|
-
return "react-basic";
|
|
406
|
-
}
|
|
407
|
-
function resolveKoraVersion() {
|
|
408
|
-
try {
|
|
409
|
-
let dir = dirname3(fileURLToPath2(import.meta.url));
|
|
410
|
-
for (let i = 0; i < 5; i++) {
|
|
411
|
-
const pkgPath = resolve3(dir, "package.json");
|
|
412
|
-
if (existsSync2(pkgPath)) {
|
|
413
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
414
|
-
if (pkg.name === "@korajs/cli") {
|
|
415
|
-
if (pkg.version === "0.0.0") return "latest";
|
|
416
|
-
const parts = pkg.version.split(".");
|
|
417
|
-
return `^${parts[0]}.${parts[1]}.0`;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
dir = dirname3(dir);
|
|
421
|
-
}
|
|
422
|
-
return "latest";
|
|
423
|
-
} catch {
|
|
424
|
-
return "latest";
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
export {
|
|
429
|
-
findProjectRoot,
|
|
430
|
-
findSchemaFile,
|
|
431
|
-
hasTsxInstalled,
|
|
432
|
-
resolveProjectBinaryEntryPoint,
|
|
433
|
-
createLogger,
|
|
434
|
-
promptConfirm,
|
|
435
|
-
createCommand
|
|
436
|
-
};
|
|
437
|
-
//# sourceMappingURL=chunk-SYOFLJLB.js.map
|