@roboticela/devkit 3.0.0 → 4.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 +12 -0
- package/dist/commands/add.js +103 -4
- package/dist/commands/eject.d.ts +1 -0
- package/dist/commands/eject.js +60 -0
- package/dist/index.js +10 -19
- package/dist/lib/bundled-registry.d.ts +18 -0
- package/dist/lib/bundled-registry.js +82 -0
- package/dist/lib/component-entry.d.ts +16 -0
- package/dist/lib/component-entry.js +1 -0
- package/dist/lib/config.d.ts +2 -0
- package/dist/lib/config.js +3 -1
- package/dist/lib/installer.js +9 -7
- package/dist/lib/registry.d.ts +2 -16
- package/dist/lib/registry.js +10 -0
- package/package.json +4 -2
- package/registry/components/auth/component.json +29 -0
- package/registry/components/auth/nextjs-compact/v1/files/app/api/auth/[...route]/route.ts +140 -0
- package/registry/components/auth/nextjs-compact/v1/files/app/auth/forgot-password/page.tsx +150 -0
- package/registry/components/auth/nextjs-compact/v1/files/app/auth/login/page.tsx +45 -0
- package/registry/components/auth/nextjs-compact/v1/files/app/auth/register/page.tsx +45 -0
- package/registry/components/auth/nextjs-compact/v1/files/app/auth/reset-password/page.tsx +139 -0
- package/registry/components/auth/nextjs-compact/v1/files/components/auth/AuthProvider.tsx +89 -0
- package/registry/components/auth/nextjs-compact/v1/files/components/auth/LoginForm.tsx +123 -0
- package/registry/components/auth/nextjs-compact/v1/files/components/auth/RegisterForm.tsx +106 -0
- package/registry/components/auth/nextjs-compact/v1/files/lib/auth/authService.ts +43 -0
- package/registry/components/auth/nextjs-compact/v1/manifest.json +37 -0
- package/registry/components/auth/vite-express-tauri/v1/files/server/middleware/requireAuth.ts +14 -0
- package/registry/components/auth/vite-express-tauri/v1/files/server/routes/auth.ts +83 -0
- package/registry/components/auth/vite-express-tauri/v1/files/server/services/jwtService.ts +19 -0
- package/registry/components/auth/vite-express-tauri/v1/files/src/contexts/AuthContext.tsx +72 -0
- package/registry/components/auth/vite-express-tauri/v1/manifest.json +47 -0
- package/registry/components/hero-section/component.json +28 -0
- package/registry/components/hero-section/nextjs-compact/v1/variants/centered/files/components/hero/HeroSection.tsx +97 -0
- package/registry/components/hero-section/nextjs-compact/v1/variants/centered/manifest.json +17 -0
- package/registry/components/hero-section/nextjs-compact/v1/variants/gradient-mesh/files/components/hero/HeroSection.tsx +146 -0
- package/registry/components/hero-section/nextjs-compact/v1/variants/gradient-mesh/manifest.json +17 -0
- package/registry/components/hero-section/nextjs-compact/v1/variants/split-image/files/components/hero/HeroSection.tsx +110 -0
- package/registry/components/hero-section/nextjs-compact/v1/variants/split-image/manifest.json +17 -0
- package/registry/components/registry.json +31 -0
- package/schemas/devkit-config.json +63 -0
package/README.md
CHANGED
|
@@ -180,6 +180,12 @@ devkit init
|
|
|
180
180
|
Install one or more components into the current project.
|
|
181
181
|
|
|
182
182
|
```bash
|
|
183
|
+
# Interactive — browse the registry for your template, multi-select, then pick variants when needed
|
|
184
|
+
devkit add
|
|
185
|
+
|
|
186
|
+
# Same as above but only print what would be installed
|
|
187
|
+
devkit add --dry-run
|
|
188
|
+
|
|
183
189
|
# Single component
|
|
184
190
|
devkit add auth
|
|
185
191
|
|
|
@@ -196,6 +202,8 @@ devkit add auth@2.0.0
|
|
|
196
202
|
devkit add auth --dry-run
|
|
197
203
|
```
|
|
198
204
|
|
|
205
|
+
**Interactive mode** (no component names): DevKit loads components from the registry filtered by your template in `devkit.config.json`, excludes anything already in `devkit.lock.json`, and opens a terminal multiselect (space to toggle, enter to confirm). For each selected package that has variants, you get a follow-up prompt unless you passed `--variant=<id>` and that id exists for that component.
|
|
206
|
+
|
|
199
207
|
**What happens under the hood:**
|
|
200
208
|
|
|
201
209
|
1. Resolves the component version (latest unless pinned)
|
|
@@ -365,6 +373,10 @@ devkit install
|
|
|
365
373
|
Stop DevKit from tracking a component without deleting its files. After ejecting, the files become entirely yours — no more updates via `devkit update`.
|
|
366
374
|
|
|
367
375
|
```bash
|
|
376
|
+
# Interactive — multiselect from installed components (from devkit.lock.json)
|
|
377
|
+
devkit eject
|
|
378
|
+
|
|
379
|
+
# Eject one component by name
|
|
368
380
|
devkit eject auth
|
|
369
381
|
```
|
|
370
382
|
|
package/dist/commands/add.js
CHANGED
|
@@ -1,27 +1,126 @@
|
|
|
1
1
|
import ora from "ora";
|
|
2
2
|
import chalk from "chalk";
|
|
3
|
+
import { intro, outro, multiselect, select, isCancel } from "@clack/prompts";
|
|
3
4
|
import { readConfig, isComponentInstalled } from "../lib/config.js";
|
|
4
5
|
import { installComponent } from "../lib/installer.js";
|
|
6
|
+
import { listComponents } from "../lib/registry.js";
|
|
5
7
|
import { log } from "../lib/logger.js";
|
|
8
|
+
/** `undefined` = no variant; string = chosen id; `"cancel"` = user aborted */
|
|
9
|
+
async function pickVariantForComponent(meta, optsVariant) {
|
|
10
|
+
if (!meta.hasVariants || !meta.variants?.length)
|
|
11
|
+
return undefined;
|
|
12
|
+
if (meta.variants.length === 1)
|
|
13
|
+
return meta.variants[0].id;
|
|
14
|
+
if (optsVariant) {
|
|
15
|
+
const match = meta.variants.find((v) => v.id === optsVariant);
|
|
16
|
+
if (match)
|
|
17
|
+
return match.id;
|
|
18
|
+
}
|
|
19
|
+
const v = await select({
|
|
20
|
+
message: `Variant for ${meta.name}`,
|
|
21
|
+
options: meta.variants.map((x) => ({
|
|
22
|
+
value: x.id,
|
|
23
|
+
label: x.label,
|
|
24
|
+
hint: x.description,
|
|
25
|
+
})),
|
|
26
|
+
});
|
|
27
|
+
if (isCancel(v))
|
|
28
|
+
return "cancel";
|
|
29
|
+
return v;
|
|
30
|
+
}
|
|
31
|
+
async function interactiveAddSelections(config, opts, cwd) {
|
|
32
|
+
let all;
|
|
33
|
+
try {
|
|
34
|
+
all = await listComponents(config.template);
|
|
35
|
+
}
|
|
36
|
+
catch (e) {
|
|
37
|
+
log.error(`Could not reach registry: ${e.message}`);
|
|
38
|
+
log.info("Check DEVKIT_REGISTRY or start the registry locally (see repo README).");
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const installable = all.filter((c) => !isComponentInstalled(c.name, cwd));
|
|
42
|
+
if (installable.length === 0) {
|
|
43
|
+
log.info("Every component from the registry for your template is already installed.");
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
console.log();
|
|
47
|
+
intro(chalk.cyan("Add components"));
|
|
48
|
+
const pickedRaw = await multiselect({
|
|
49
|
+
message: "Select components to install (space to toggle, enter to confirm)",
|
|
50
|
+
options: installable.map((c) => ({
|
|
51
|
+
value: c.name,
|
|
52
|
+
label: c.name,
|
|
53
|
+
hint: c.description.length > 64 ? `${c.description.slice(0, 61)}…` : c.description,
|
|
54
|
+
})),
|
|
55
|
+
required: false,
|
|
56
|
+
});
|
|
57
|
+
if (isCancel(pickedRaw)) {
|
|
58
|
+
outro("Cancelled.");
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
const picked = pickedRaw;
|
|
62
|
+
if (picked.length === 0) {
|
|
63
|
+
outro("No components selected.");
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
const results = [];
|
|
67
|
+
for (const compName of picked) {
|
|
68
|
+
const meta = installable.find((x) => x.name === compName);
|
|
69
|
+
const variant = await pickVariantForComponent(meta, opts.variant);
|
|
70
|
+
if (variant === "cancel") {
|
|
71
|
+
outro("Cancelled.");
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
results.push({ name: compName, version: "latest", variant });
|
|
75
|
+
}
|
|
76
|
+
return results;
|
|
77
|
+
}
|
|
6
78
|
export async function addCommand(names, opts, cwd = process.cwd()) {
|
|
7
79
|
const config = readConfig(cwd);
|
|
8
|
-
|
|
9
|
-
|
|
80
|
+
const usedInteractive = names.length === 0;
|
|
81
|
+
let todo;
|
|
82
|
+
if (usedInteractive) {
|
|
83
|
+
const picked = await interactiveAddSelections(config, opts, cwd);
|
|
84
|
+
if (picked === null)
|
|
85
|
+
return;
|
|
86
|
+
if (picked.length === 0)
|
|
87
|
+
return;
|
|
88
|
+
todo = picked;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
todo = names.map((nameAtVersion) => {
|
|
92
|
+
const [name, version = "latest"] = nameAtVersion.split("@");
|
|
93
|
+
return { name, version, variant: opts.variant };
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
let anyOk = false;
|
|
97
|
+
for (const { name, version, variant } of todo) {
|
|
10
98
|
if (isComponentInstalled(name, cwd)) {
|
|
11
99
|
log.warn(`${name} is already installed. Run 'devkit update ${name}' to update.`);
|
|
12
100
|
continue;
|
|
13
101
|
}
|
|
14
102
|
if (opts.dryRun) {
|
|
15
|
-
log.info(`[dry-run] Would install: ${chalk.cyan(name)}@${version} (${config.template})`
|
|
103
|
+
log.info(`[dry-run] Would install: ${chalk.cyan(name)}@${version} (${config.template})` +
|
|
104
|
+
(variant ? chalk.dim(` variant=${variant}`) : ""));
|
|
105
|
+
anyOk = true;
|
|
16
106
|
continue;
|
|
17
107
|
}
|
|
18
108
|
const spinner = ora({ text: `Installing ${chalk.cyan(name)}…`, color: "cyan" }).start();
|
|
19
109
|
try {
|
|
20
|
-
await installComponent(name, config.template, version,
|
|
110
|
+
await installComponent(name, config.template, version, variant, cwd);
|
|
21
111
|
spinner.succeed(chalk.green(`${name}@${version} installed`));
|
|
112
|
+
anyOk = true;
|
|
22
113
|
}
|
|
23
114
|
catch (e) {
|
|
24
115
|
spinner.fail(chalk.red(`Failed to install ${name}: ${e.message}`));
|
|
25
116
|
}
|
|
26
117
|
}
|
|
118
|
+
if (usedInteractive) {
|
|
119
|
+
if (anyOk) {
|
|
120
|
+
outro(opts.dryRun ? chalk.dim("Dry run complete.") : chalk.green("Done."));
|
|
121
|
+
}
|
|
122
|
+
else if (!opts.dryRun) {
|
|
123
|
+
outro(chalk.yellow("Nothing was installed."));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
27
126
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function ejectCommand(name: string | undefined, cwd?: string): Promise<void>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { intro, outro, multiselect, isCancel } from "@clack/prompts";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { unlinkSync, existsSync } from "fs";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
import { readLock, writeLock } from "../lib/config.js";
|
|
6
|
+
import { log } from "../lib/logger.js";
|
|
7
|
+
export async function ejectCommand(name, cwd = process.cwd()) {
|
|
8
|
+
const lock = readLock(cwd);
|
|
9
|
+
const installed = Object.keys(lock.components);
|
|
10
|
+
if (installed.length === 0) {
|
|
11
|
+
log.info("No installed components to eject.");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
let toEject;
|
|
15
|
+
if (name) {
|
|
16
|
+
if (!(name in lock.components)) {
|
|
17
|
+
log.warn(`'${name}' is not an installed DevKit component.`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
toEject = [name];
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
console.log();
|
|
24
|
+
intro(chalk.cyan("Eject components"));
|
|
25
|
+
const picked = await multiselect({
|
|
26
|
+
message: "Select components to eject (DevKit stops tracking them; files stay on disk — space to toggle, enter to confirm)",
|
|
27
|
+
options: installed.map((n) => ({
|
|
28
|
+
value: n,
|
|
29
|
+
label: n,
|
|
30
|
+
hint: `v${lock.components[n].version}`,
|
|
31
|
+
})),
|
|
32
|
+
required: false,
|
|
33
|
+
});
|
|
34
|
+
if (isCancel(picked)) {
|
|
35
|
+
outro("Cancelled.");
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const list = picked;
|
|
39
|
+
if (list.length === 0) {
|
|
40
|
+
outro("No components selected.");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
toEject = list;
|
|
44
|
+
}
|
|
45
|
+
for (const n of toEject) {
|
|
46
|
+
if (!(n in lock.components)) {
|
|
47
|
+
log.warn(`Skipping '${n}' — not in lock file.`);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const manifestPath = join(cwd, ".devkit", `${n}.manifest.json`);
|
|
51
|
+
if (existsSync(manifestPath))
|
|
52
|
+
unlinkSync(manifestPath);
|
|
53
|
+
delete lock.components[n];
|
|
54
|
+
log.success(`${n} ejected. Files are yours — DevKit will no longer manage them.`);
|
|
55
|
+
}
|
|
56
|
+
writeLock(lock, cwd);
|
|
57
|
+
if (!name) {
|
|
58
|
+
outro(chalk.green("Done."));
|
|
59
|
+
}
|
|
60
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { listCommand } from "./commands/list.js";
|
|
|
9
9
|
import { infoCommand } from "./commands/info.js";
|
|
10
10
|
import { updateCommand, upgradeAllCommand } from "./commands/update.js";
|
|
11
11
|
import { doctorCommand } from "./commands/doctor.js";
|
|
12
|
+
import { ejectCommand } from "./commands/eject.js";
|
|
12
13
|
import { themeApplyCommand, themePreviewCommand, themePresetCommand, themeSetCommand, themeListCommand, themeAuditCommand, } from "./commands/theme.js";
|
|
13
14
|
const program = new Command();
|
|
14
15
|
program
|
|
@@ -36,11 +37,11 @@ program
|
|
|
36
37
|
.action(() => initCommand());
|
|
37
38
|
// ── devkit add ────────────────────────────────────────────────────────────────
|
|
38
39
|
program
|
|
39
|
-
.command("add
|
|
40
|
-
.description("Install
|
|
41
|
-
.option("--variant <id>", "Variant
|
|
40
|
+
.command("add [components...]")
|
|
41
|
+
.description("Install components — run with no args for an interactive picker")
|
|
42
|
+
.option("--variant <id>", "Variant for components with variants (non-interactive or as default when prompting)")
|
|
42
43
|
.option("--dry-run", "Preview what would be installed without writing files")
|
|
43
|
-
.action((names, opts) => addCommand(names, opts));
|
|
44
|
+
.action((names, opts) => addCommand(names ?? [], opts));
|
|
44
45
|
// ── devkit remove ─────────────────────────────────────────────────────────────
|
|
45
46
|
program
|
|
46
47
|
.command("remove <name>")
|
|
@@ -124,28 +125,18 @@ program
|
|
|
124
125
|
});
|
|
125
126
|
// ── devkit eject ──────────────────────────────────────────────────────────────
|
|
126
127
|
program
|
|
127
|
-
.command("eject
|
|
128
|
-
.description("Stop DevKit from tracking
|
|
129
|
-
.action(
|
|
130
|
-
const { readLock, writeLock } = await import("./lib/config.js");
|
|
131
|
-
const { unlinkSync, existsSync } = await import("fs");
|
|
132
|
-
const { join } = await import("path");
|
|
133
|
-
const { log } = await import("./lib/logger.js");
|
|
134
|
-
const manifestPath = join(process.cwd(), ".devkit", `${name}.manifest.json`);
|
|
135
|
-
if (existsSync(manifestPath))
|
|
136
|
-
unlinkSync(manifestPath);
|
|
137
|
-
const lock = readLock();
|
|
138
|
-
delete lock.components[name];
|
|
139
|
-
writeLock(lock);
|
|
140
|
-
log.success(`${name} ejected. Files are yours — DevKit will no longer manage them.`);
|
|
141
|
-
});
|
|
128
|
+
.command("eject [name]")
|
|
129
|
+
.description("Stop DevKit from tracking component(s) — run with no args to pick interactively")
|
|
130
|
+
.action((name) => ejectCommand(name));
|
|
142
131
|
// ── Help footer ───────────────────────────────────────────────────────────────
|
|
143
132
|
program.addHelpText("after", `
|
|
144
133
|
${chalk.bold("Examples:")}
|
|
145
134
|
${chalk.cyan("devkit create my-app")} Interactive project creation
|
|
146
135
|
${chalk.cyan("devkit create my-app --template=nextjs-compact --yes")} Non-interactive
|
|
136
|
+
${chalk.cyan("devkit add")} Interactive component picker
|
|
147
137
|
${chalk.cyan("devkit add auth")} Install auth component
|
|
148
138
|
${chalk.cyan("devkit add hero-section --variant=split-image")}
|
|
139
|
+
${chalk.cyan("devkit eject")} Pick component(s) to eject
|
|
149
140
|
${chalk.cyan("devkit theme set colors.primary #e11d48")} Change primary color
|
|
150
141
|
${chalk.cyan("devkit doctor")} Check configuration
|
|
151
142
|
${chalk.cyan("devkit list")} Browse all components
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ComponentEntry } from "./component-entry.js";
|
|
2
|
+
/** Directory containing `dist/` (the published CLI package root). */
|
|
3
|
+
export declare function getCliPackageRoot(fromImportMetaUrl?: string): string;
|
|
4
|
+
/**
|
|
5
|
+
* On-disk component payloads: either shipped inside the npm package (`cli/registry/components`)
|
|
6
|
+
* or the monorepo sibling (`DevKit/registry/components`).
|
|
7
|
+
*/
|
|
8
|
+
export declare function getBundledComponentsDir(fromImportMetaUrl?: string): string | null;
|
|
9
|
+
export declare function listBundledComponents(template: string | undefined, fromImportMetaUrl?: string): ComponentEntry[] | null;
|
|
10
|
+
export declare function getBundledComponentMeta(name: string, fromImportMetaUrl?: string): Record<string, unknown> | null;
|
|
11
|
+
export declare function resolveBundledVersion(name: string, requested: string, fromImportMetaUrl?: string): string | null;
|
|
12
|
+
export declare function getBundledManifest(name: string, template: string, version: string, variant: string | undefined, fromImportMetaUrl?: string): {
|
|
13
|
+
name: string;
|
|
14
|
+
template: string;
|
|
15
|
+
version: string;
|
|
16
|
+
variant: string | null;
|
|
17
|
+
manifest: Record<string, unknown>;
|
|
18
|
+
} | null;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { dirname, join } from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
/** Directory containing `dist/` (the published CLI package root). */
|
|
5
|
+
export function getCliPackageRoot(fromImportMetaUrl = import.meta.url) {
|
|
6
|
+
return join(dirname(fileURLToPath(fromImportMetaUrl)), "..", "..");
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* On-disk component payloads: either shipped inside the npm package (`cli/registry/components`)
|
|
10
|
+
* or the monorepo sibling (`DevKit/registry/components`).
|
|
11
|
+
*/
|
|
12
|
+
export function getBundledComponentsDir(fromImportMetaUrl = import.meta.url) {
|
|
13
|
+
const pkgRoot = getCliPackageRoot(fromImportMetaUrl);
|
|
14
|
+
const nested = join(pkgRoot, "registry", "components");
|
|
15
|
+
const sibling = join(pkgRoot, "..", "registry", "components");
|
|
16
|
+
if (existsSync(join(nested, "registry.json")))
|
|
17
|
+
return nested;
|
|
18
|
+
if (existsSync(join(sibling, "registry.json")))
|
|
19
|
+
return sibling;
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
export function listBundledComponents(template, fromImportMetaUrl = import.meta.url) {
|
|
23
|
+
const root = getBundledComponentsDir(fromImportMetaUrl);
|
|
24
|
+
if (!root)
|
|
25
|
+
return null;
|
|
26
|
+
const index = JSON.parse(readFileSync(join(root, "registry.json"), "utf-8"));
|
|
27
|
+
const out = [];
|
|
28
|
+
for (const c of index.components) {
|
|
29
|
+
if (template && !c.templates.includes(template))
|
|
30
|
+
continue;
|
|
31
|
+
const metaPath = join(root, c.name, "component.json");
|
|
32
|
+
if (!existsSync(metaPath))
|
|
33
|
+
continue;
|
|
34
|
+
const meta = JSON.parse(readFileSync(metaPath, "utf-8"));
|
|
35
|
+
out.push({
|
|
36
|
+
name: meta.name,
|
|
37
|
+
displayName: meta.displayName,
|
|
38
|
+
description: meta.description,
|
|
39
|
+
category: meta.category,
|
|
40
|
+
templates: meta.templates,
|
|
41
|
+
platforms: meta.platforms,
|
|
42
|
+
hasVariants: meta.hasVariants,
|
|
43
|
+
variants: meta.variants,
|
|
44
|
+
latestVersion: meta.versions.latest,
|
|
45
|
+
tags: meta.tags,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
export function getBundledComponentMeta(name, fromImportMetaUrl = import.meta.url) {
|
|
51
|
+
const root = getBundledComponentsDir(fromImportMetaUrl);
|
|
52
|
+
if (!root)
|
|
53
|
+
return null;
|
|
54
|
+
const path = join(root, name, "component.json");
|
|
55
|
+
if (!existsSync(path))
|
|
56
|
+
return null;
|
|
57
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
58
|
+
}
|
|
59
|
+
export function resolveBundledVersion(name, requested, fromImportMetaUrl = import.meta.url) {
|
|
60
|
+
const meta = getBundledComponentMeta(name, fromImportMetaUrl);
|
|
61
|
+
if (!meta)
|
|
62
|
+
return null;
|
|
63
|
+
const versions = meta["versions"];
|
|
64
|
+
if (requested === "latest")
|
|
65
|
+
return versions.latest;
|
|
66
|
+
const found = versions.history.find((h) => h.version === requested);
|
|
67
|
+
return found ? found.version : null;
|
|
68
|
+
}
|
|
69
|
+
export function getBundledManifest(name, template, version, variant, fromImportMetaUrl = import.meta.url) {
|
|
70
|
+
const root = getBundledComponentsDir(fromImportMetaUrl);
|
|
71
|
+
if (!root)
|
|
72
|
+
return null;
|
|
73
|
+
const resolved = resolveBundledVersion(name, version, fromImportMetaUrl);
|
|
74
|
+
if (!resolved)
|
|
75
|
+
return null;
|
|
76
|
+
const base = join(root, name, template, `v${resolved.split(".")[0]}`);
|
|
77
|
+
const manifestPath = variant ? join(base, "variants", variant, "manifest.json") : join(base, "manifest.json");
|
|
78
|
+
if (!existsSync(manifestPath))
|
|
79
|
+
return null;
|
|
80
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
|
|
81
|
+
return { name, template, version: resolved, variant: variant ?? null, manifest };
|
|
82
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface ComponentEntry {
|
|
2
|
+
name: string;
|
|
3
|
+
displayName: string;
|
|
4
|
+
description: string;
|
|
5
|
+
category: string;
|
|
6
|
+
templates: string[];
|
|
7
|
+
platforms: string[];
|
|
8
|
+
hasVariants: boolean;
|
|
9
|
+
variants?: {
|
|
10
|
+
id: string;
|
|
11
|
+
label: string;
|
|
12
|
+
description: string;
|
|
13
|
+
}[];
|
|
14
|
+
latestVersion: string;
|
|
15
|
+
tags: string[];
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/lib/config.d.ts
CHANGED
|
@@ -41,6 +41,8 @@ export interface DevKitLock {
|
|
|
41
41
|
installedAt: string;
|
|
42
42
|
}>;
|
|
43
43
|
}
|
|
44
|
+
/** Stable URL for editors (works before/without the public registry host). */
|
|
45
|
+
export declare const DEVKIT_CONFIG_SCHEMA_URL = "https://raw.githubusercontent.com/Roboticela/DevKit/main/cli/schemas/devkit-config.json";
|
|
44
46
|
export declare function configExists(cwd?: string): boolean;
|
|
45
47
|
export declare function readConfig(cwd?: string): DevKitConfig;
|
|
46
48
|
export declare function writeConfig(config: DevKitConfig, cwd?: string): void;
|
package/dist/lib/config.js
CHANGED
|
@@ -2,6 +2,8 @@ import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
|
2
2
|
import { join } from "path";
|
|
3
3
|
const CONFIG_FILE = "devkit.config.json";
|
|
4
4
|
const LOCK_FILE = "devkit.lock.json";
|
|
5
|
+
/** Stable URL for editors (works before/without the public registry host). */
|
|
6
|
+
export const DEVKIT_CONFIG_SCHEMA_URL = "https://raw.githubusercontent.com/Roboticela/DevKit/main/cli/schemas/devkit-config.json";
|
|
5
7
|
export function configExists(cwd = process.cwd()) {
|
|
6
8
|
return existsSync(join(cwd, CONFIG_FILE));
|
|
7
9
|
}
|
|
@@ -32,7 +34,7 @@ export function isComponentInstalled(name, cwd = process.cwd()) {
|
|
|
32
34
|
}
|
|
33
35
|
export function defaultConfig(template, siteName, siteUrl) {
|
|
34
36
|
return {
|
|
35
|
-
$schema:
|
|
37
|
+
$schema: DEVKIT_CONFIG_SCHEMA_URL,
|
|
36
38
|
devkit: "1.0",
|
|
37
39
|
template,
|
|
38
40
|
site: { name: siteName, url: siteUrl },
|
package/dist/lib/installer.js
CHANGED
|
@@ -4,6 +4,7 @@ import { execSync } from "child_process";
|
|
|
4
4
|
import { createHash } from "crypto";
|
|
5
5
|
import { x as extractTar } from "tar";
|
|
6
6
|
import { pipeline } from "stream/promises";
|
|
7
|
+
import { getBundledComponentsDir } from "./bundled-registry.js";
|
|
7
8
|
import { getManifest } from "./registry.js";
|
|
8
9
|
import { readLock, writeLock } from "./config.js";
|
|
9
10
|
import { log } from "./logger.js";
|
|
@@ -127,14 +128,15 @@ export function removeComponent(name, cwd = process.cwd()) {
|
|
|
127
128
|
}
|
|
128
129
|
async function downloadComponentFiles(name, template, version, variant, tmpDir) {
|
|
129
130
|
mkdirSync(tmpDir, { recursive: true });
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
const registryDir = join(dirname(new URL(import.meta.url).pathname), "../../../registry/components");
|
|
131
|
+
// Prefer on-disk payloads (bundled in the npm package or monorepo sibling) before HTTP tarball.
|
|
132
|
+
const registryDir = getBundledComponentsDir();
|
|
133
133
|
const major = `v${version.split(".")[0]}`;
|
|
134
|
-
const compDir =
|
|
135
|
-
?
|
|
136
|
-
|
|
137
|
-
|
|
134
|
+
const compDir = registryDir
|
|
135
|
+
? variant
|
|
136
|
+
? join(registryDir, name, template, major, "variants", variant)
|
|
137
|
+
: join(registryDir, name, template, major)
|
|
138
|
+
: "";
|
|
139
|
+
if (compDir && existsSync(compDir)) {
|
|
138
140
|
cpSync(compDir, tmpDir, { recursive: true });
|
|
139
141
|
}
|
|
140
142
|
else {
|
package/dist/lib/registry.d.ts
CHANGED
|
@@ -1,19 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
displayName: string;
|
|
4
|
-
description: string;
|
|
5
|
-
category: string;
|
|
6
|
-
templates: string[];
|
|
7
|
-
platforms: string[];
|
|
8
|
-
hasVariants: boolean;
|
|
9
|
-
variants?: {
|
|
10
|
-
id: string;
|
|
11
|
-
label: string;
|
|
12
|
-
description: string;
|
|
13
|
-
}[];
|
|
14
|
-
latestVersion: string;
|
|
15
|
-
tags: string[];
|
|
16
|
-
}
|
|
1
|
+
import type { ComponentEntry } from "./component-entry.js";
|
|
2
|
+
export type { ComponentEntry };
|
|
17
3
|
export declare function listComponents(template?: string): Promise<ComponentEntry[]>;
|
|
18
4
|
export declare function getComponentInfo(name: string): Promise<Record<string, unknown>>;
|
|
19
5
|
export declare function getManifest(name: string, template: string, version?: string, variant?: string): Promise<{
|
package/dist/lib/registry.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getBundledComponentMeta, getBundledManifest, listBundledComponents, } from "./bundled-registry.js";
|
|
1
2
|
const REGISTRY_URL = process.env.DEVKIT_REGISTRY ?? "https://registry.devkit.roboticela.com";
|
|
2
3
|
async function get(path) {
|
|
3
4
|
const res = await fetch(`${REGISTRY_URL}${path}`);
|
|
@@ -6,14 +7,23 @@ async function get(path) {
|
|
|
6
7
|
return res.json();
|
|
7
8
|
}
|
|
8
9
|
export async function listComponents(template) {
|
|
10
|
+
const local = listBundledComponents(template);
|
|
11
|
+
if (local)
|
|
12
|
+
return local;
|
|
9
13
|
const qs = template ? `?template=${template}` : "";
|
|
10
14
|
const data = await get(`/api/v1/components${qs}`);
|
|
11
15
|
return data.components;
|
|
12
16
|
}
|
|
13
17
|
export async function getComponentInfo(name) {
|
|
18
|
+
const local = getBundledComponentMeta(name);
|
|
19
|
+
if (local)
|
|
20
|
+
return local;
|
|
14
21
|
return get(`/api/v1/components/${name}`);
|
|
15
22
|
}
|
|
16
23
|
export async function getManifest(name, template, version = "latest", variant) {
|
|
24
|
+
const local = getBundledManifest(name, template, version, variant);
|
|
25
|
+
if (local)
|
|
26
|
+
return local;
|
|
17
27
|
const qs = new URLSearchParams({ template, version });
|
|
18
28
|
if (variant)
|
|
19
29
|
qs.set("variant", variant);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roboticela/devkit",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Roboticela DevKit CLI — scaffold, extend, and theme full-stack projects with one command",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -53,6 +53,8 @@
|
|
|
53
53
|
},
|
|
54
54
|
"files": [
|
|
55
55
|
"dist",
|
|
56
|
-
"README.md"
|
|
56
|
+
"README.md",
|
|
57
|
+
"registry",
|
|
58
|
+
"schemas"
|
|
57
59
|
]
|
|
58
60
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "auth",
|
|
3
|
+
"displayName": "Authentication",
|
|
4
|
+
"description": "Complete authentication system: email/password login, registration, email verification, forgot/reset password, and Google OAuth.",
|
|
5
|
+
"category": "authentication",
|
|
6
|
+
"icon": "lock",
|
|
7
|
+
"author": "Roboticela",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"tags": ["auth", "login", "register", "oauth", "jwt", "email"],
|
|
10
|
+
"templates": ["nextjs-compact", "vite-express-tauri"],
|
|
11
|
+
"platforms": ["web", "desktop"],
|
|
12
|
+
"hasVariants": false,
|
|
13
|
+
"versions": {
|
|
14
|
+
"latest": "1.0.0",
|
|
15
|
+
"stable": "1.0.0",
|
|
16
|
+
"history": [
|
|
17
|
+
{ "version": "1.0.0", "releaseDate": "2026-04-05", "breaking": false }
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"requiredConfig": ["site.name", "site.url"],
|
|
21
|
+
"optionalConfig": [
|
|
22
|
+
"auth.google.clientId",
|
|
23
|
+
"auth.google.clientSecret",
|
|
24
|
+
"auth.smtp.host",
|
|
25
|
+
"auth.smtp.port",
|
|
26
|
+
"auth.smtp.user",
|
|
27
|
+
"auth.smtp.password"
|
|
28
|
+
]
|
|
29
|
+
}
|