@devfellowship/components 2.0.0 → 3.0.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/dist/cli.js +54 -367
- package/package.json +4 -8
package/dist/cli.js
CHANGED
|
@@ -3,321 +3,10 @@
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
|
-
// src/cli/commands/init.ts
|
|
7
|
-
import fs from "fs-extra";
|
|
8
|
-
import path from "path";
|
|
9
|
-
import chalk2 from "chalk";
|
|
10
|
-
import ora from "ora";
|
|
11
|
-
import prompts from "prompts";
|
|
12
|
-
|
|
13
|
-
// src/cli/types/config.ts
|
|
14
|
-
import { z } from "zod";
|
|
15
|
-
var configSchema = z.object({
|
|
16
|
-
typescript: z.boolean().default(true),
|
|
17
|
-
aliases: z.object({
|
|
18
|
-
components: z.string().default("@/components/dfl"),
|
|
19
|
-
hooks: z.string().default("@/hooks"),
|
|
20
|
-
providers: z.string().default("@/providers"),
|
|
21
|
-
pages: z.string().default("@/pages")
|
|
22
|
-
}),
|
|
23
|
-
registry: z.string().default("https://raw.githubusercontent.com/taigfs/dfl-components-cli/main/registry")
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
// src/cli/utils/logger.ts
|
|
27
|
-
import chalk from "chalk";
|
|
28
|
-
var logger = {
|
|
29
|
-
info: (message) => console.log(chalk.blue("info"), message),
|
|
30
|
-
success: (message) => console.log(chalk.green("\u2713"), message),
|
|
31
|
-
warn: (message) => console.log(chalk.yellow("warn"), message),
|
|
32
|
-
error: (message) => console.log(chalk.red("error"), message),
|
|
33
|
-
break: () => console.log("")
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
// src/cli/commands/init.ts
|
|
37
|
-
async function init(options) {
|
|
38
|
-
const cwd = path.resolve(options.cwd);
|
|
39
|
-
logger.info("Initializing dfl-components configuration...");
|
|
40
|
-
logger.break();
|
|
41
|
-
const configPath = path.join(cwd, "dfl-components.json");
|
|
42
|
-
if (await fs.pathExists(configPath)) {
|
|
43
|
-
const { overwrite } = await prompts({
|
|
44
|
-
type: "confirm",
|
|
45
|
-
name: "overwrite",
|
|
46
|
-
message: "dfl-components.json already exists. Overwrite?",
|
|
47
|
-
initial: false
|
|
48
|
-
});
|
|
49
|
-
if (!overwrite) {
|
|
50
|
-
logger.warn("Aborted.");
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
let config;
|
|
55
|
-
if (options.yes) {
|
|
56
|
-
config = configSchema.parse({
|
|
57
|
-
aliases: {}
|
|
58
|
-
});
|
|
59
|
-
} else {
|
|
60
|
-
const answers = await prompts([
|
|
61
|
-
{
|
|
62
|
-
type: "confirm",
|
|
63
|
-
name: "typescript",
|
|
64
|
-
message: "Would you like to use TypeScript?",
|
|
65
|
-
initial: true
|
|
66
|
-
},
|
|
67
|
-
{
|
|
68
|
-
type: "text",
|
|
69
|
-
name: "componentsPath",
|
|
70
|
-
message: "Where would you like to install components?",
|
|
71
|
-
initial: "@/components/dfl"
|
|
72
|
-
},
|
|
73
|
-
{
|
|
74
|
-
type: "text",
|
|
75
|
-
name: "hooksPath",
|
|
76
|
-
message: "Where would you like to install hooks?",
|
|
77
|
-
initial: "@/hooks"
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
type: "text",
|
|
81
|
-
name: "providersPath",
|
|
82
|
-
message: "Where would you like to install providers?",
|
|
83
|
-
initial: "@/providers"
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
type: "text",
|
|
87
|
-
name: "pagesPath",
|
|
88
|
-
message: "Where would you like to install pages?",
|
|
89
|
-
initial: "@/pages"
|
|
90
|
-
}
|
|
91
|
-
]);
|
|
92
|
-
if (!answers.typescript) {
|
|
93
|
-
logger.warn("Aborted.");
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
config = configSchema.parse({
|
|
97
|
-
typescript: answers.typescript,
|
|
98
|
-
aliases: {
|
|
99
|
-
components: answers.componentsPath,
|
|
100
|
-
hooks: answers.hooksPath,
|
|
101
|
-
providers: answers.providersPath,
|
|
102
|
-
pages: answers.pagesPath
|
|
103
|
-
}
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
const spinner = ora("Writing configuration...").start();
|
|
107
|
-
try {
|
|
108
|
-
await fs.writeJson(configPath, config, { spaces: 2 });
|
|
109
|
-
spinner.succeed("Configuration saved to dfl-components.json");
|
|
110
|
-
logger.break();
|
|
111
|
-
logger.success("Project initialized successfully!");
|
|
112
|
-
logger.break();
|
|
113
|
-
console.log("You can now add components:");
|
|
114
|
-
console.log(chalk2.cyan(" npx dfl-components add button"));
|
|
115
|
-
console.log(chalk2.cyan(" npx dfl-components add card input"));
|
|
116
|
-
console.log(chalk2.cyan(" npx dfl-components add auth-pages"));
|
|
117
|
-
logger.break();
|
|
118
|
-
} catch (error) {
|
|
119
|
-
spinner.fail("Failed to write configuration");
|
|
120
|
-
throw error;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// src/cli/commands/add.ts
|
|
125
|
-
import fs2 from "fs-extra";
|
|
126
|
-
import path2 from "path";
|
|
127
|
-
import chalk3 from "chalk";
|
|
128
|
-
import ora2 from "ora";
|
|
129
|
-
import prompts2 from "prompts";
|
|
130
|
-
|
|
131
|
-
// src/cli/utils/get-config.ts
|
|
132
|
-
import { cosmiconfig } from "cosmiconfig";
|
|
133
|
-
async function getConfig(cwd) {
|
|
134
|
-
const explorer = cosmiconfig("dfl-components", {
|
|
135
|
-
searchPlaces: ["dfl-components.json"]
|
|
136
|
-
});
|
|
137
|
-
const result = await explorer.search(cwd);
|
|
138
|
-
if (!result) {
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
return configSchema.parse(result.config);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// src/cli/utils/get-registry.ts
|
|
145
|
-
import fetch2 from "node-fetch";
|
|
146
|
-
async function getRegistry(registryUrl) {
|
|
147
|
-
const response = await fetch2(`${registryUrl}/registry.json`);
|
|
148
|
-
if (!response.ok) {
|
|
149
|
-
throw new Error(`Failed to fetch registry: ${response.statusText}`);
|
|
150
|
-
}
|
|
151
|
-
return response.json();
|
|
152
|
-
}
|
|
153
|
-
function getComponentFolderByCategory(category) {
|
|
154
|
-
switch (category) {
|
|
155
|
-
case "Hooks":
|
|
156
|
-
return "hooks";
|
|
157
|
-
case "Providers":
|
|
158
|
-
return "providers";
|
|
159
|
-
case "Pages":
|
|
160
|
-
return "pages";
|
|
161
|
-
default:
|
|
162
|
-
return "components";
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// src/cli/utils/resolve-alias.ts
|
|
167
|
-
function resolveAlias(aliasPath) {
|
|
168
|
-
if (aliasPath.startsWith("@/")) {
|
|
169
|
-
return aliasPath.replace("@/", "src/");
|
|
170
|
-
}
|
|
171
|
-
return aliasPath;
|
|
172
|
-
}
|
|
173
|
-
function getTargetPath(category, aliases) {
|
|
174
|
-
switch (category) {
|
|
175
|
-
case "Hooks":
|
|
176
|
-
return resolveAlias(aliases.hooks);
|
|
177
|
-
case "Providers":
|
|
178
|
-
return resolveAlias(aliases.providers);
|
|
179
|
-
case "Pages":
|
|
180
|
-
return resolveAlias(aliases.pages);
|
|
181
|
-
default:
|
|
182
|
-
return resolveAlias(aliases.components);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// src/cli/commands/add.ts
|
|
187
|
-
async function add(components, options) {
|
|
188
|
-
const cwd = path2.resolve(options.cwd);
|
|
189
|
-
const config = await getConfig(cwd);
|
|
190
|
-
if (!config) {
|
|
191
|
-
logger.error("No dfl-components.json found.");
|
|
192
|
-
console.log("Run", chalk3.cyan("npx dfl-components init"), "first.");
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
const spinner = ora2("Fetching registry...").start();
|
|
196
|
-
try {
|
|
197
|
-
let resolveDeps2 = function(name) {
|
|
198
|
-
if (allComponents.has(name)) return;
|
|
199
|
-
allComponents.add(name);
|
|
200
|
-
const item = registry.items.find((i) => i.name === name);
|
|
201
|
-
if (item?.registryDependencies) {
|
|
202
|
-
for (const dep of item.registryDependencies) {
|
|
203
|
-
resolveDeps2(dep);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
};
|
|
207
|
-
var resolveDeps = resolveDeps2;
|
|
208
|
-
const registry = await getRegistry(config.registry);
|
|
209
|
-
let toInstall;
|
|
210
|
-
if (options.all) {
|
|
211
|
-
toInstall = registry.items.map((item) => item.name);
|
|
212
|
-
} else if (components.length === 0) {
|
|
213
|
-
spinner.stop();
|
|
214
|
-
const { selected } = await prompts2({
|
|
215
|
-
type: "multiselect",
|
|
216
|
-
name: "selected",
|
|
217
|
-
message: "Which components would you like to add?",
|
|
218
|
-
choices: registry.items.map((item) => ({
|
|
219
|
-
title: `${item.title} (${item.category})`,
|
|
220
|
-
value: item.name,
|
|
221
|
-
description: item.description
|
|
222
|
-
})),
|
|
223
|
-
min: 1
|
|
224
|
-
});
|
|
225
|
-
if (!selected || selected.length === 0) {
|
|
226
|
-
logger.warn("No components selected.");
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
toInstall = selected;
|
|
230
|
-
spinner.start("Fetching components...");
|
|
231
|
-
} else {
|
|
232
|
-
toInstall = components;
|
|
233
|
-
}
|
|
234
|
-
const validComponents = [];
|
|
235
|
-
for (const name of toInstall) {
|
|
236
|
-
const item = registry.items.find((i) => i.name === name);
|
|
237
|
-
if (!item) {
|
|
238
|
-
logger.warn(`Component "${name}" not found in registry. Skipping.`);
|
|
239
|
-
} else {
|
|
240
|
-
validComponents.push(name);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
if (validComponents.length === 0) {
|
|
244
|
-
spinner.fail("No valid components to install.");
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
const allComponents = /* @__PURE__ */ new Set();
|
|
248
|
-
for (const component of validComponents) {
|
|
249
|
-
resolveDeps2(component);
|
|
250
|
-
}
|
|
251
|
-
spinner.text = `Installing ${allComponents.size} component(s)...`;
|
|
252
|
-
const installed = [];
|
|
253
|
-
const errors = [];
|
|
254
|
-
for (const componentName of allComponents) {
|
|
255
|
-
const registryItem = registry.items.find((i) => i.name === componentName);
|
|
256
|
-
if (!registryItem) continue;
|
|
257
|
-
try {
|
|
258
|
-
const folder = getComponentFolderByCategory(registryItem.category);
|
|
259
|
-
const url = `${config.registry}/${folder}/${componentName}.json`;
|
|
260
|
-
const response = await fetch(url);
|
|
261
|
-
if (!response.ok) {
|
|
262
|
-
throw new Error(`Failed to fetch: ${response.statusText}`);
|
|
263
|
-
}
|
|
264
|
-
const componentData = await response.json();
|
|
265
|
-
for (const file of componentData.files) {
|
|
266
|
-
const targetDir = file.target ? path2.dirname(path2.join(cwd, "src", file.target)) : path2.join(cwd, getTargetPath(registryItem.category, config.aliases));
|
|
267
|
-
const targetPath = file.target ? path2.join(cwd, "src", file.target) : path2.join(targetDir, file.path);
|
|
268
|
-
if (await fs2.pathExists(targetPath)) {
|
|
269
|
-
if (!options.overwrite && !options.yes) {
|
|
270
|
-
spinner.stop();
|
|
271
|
-
const { overwrite } = await prompts2({
|
|
272
|
-
type: "confirm",
|
|
273
|
-
name: "overwrite",
|
|
274
|
-
message: `${path2.relative(cwd, targetPath)} already exists. Overwrite?`,
|
|
275
|
-
initial: false
|
|
276
|
-
});
|
|
277
|
-
if (!overwrite) {
|
|
278
|
-
logger.info(`Skipping ${path2.basename(targetPath)}`);
|
|
279
|
-
continue;
|
|
280
|
-
}
|
|
281
|
-
spinner.start();
|
|
282
|
-
} else if (!options.overwrite) {
|
|
283
|
-
logger.info(`Skipping ${path2.basename(targetPath)} (use --overwrite to replace)`);
|
|
284
|
-
continue;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
await fs2.ensureDir(path2.dirname(targetPath));
|
|
288
|
-
await fs2.writeFile(targetPath, file.content);
|
|
289
|
-
installed.push(targetPath);
|
|
290
|
-
}
|
|
291
|
-
} catch (error) {
|
|
292
|
-
errors.push(`${componentName}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
spinner.succeed(`Installed ${installed.length} file(s)`);
|
|
296
|
-
if (installed.length > 0) {
|
|
297
|
-
logger.break();
|
|
298
|
-
console.log(chalk3.green("Files created:"));
|
|
299
|
-
for (const file of installed) {
|
|
300
|
-
console.log(chalk3.gray(" -"), path2.relative(cwd, file));
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
if (errors.length > 0) {
|
|
304
|
-
logger.break();
|
|
305
|
-
console.log(chalk3.red("Errors:"));
|
|
306
|
-
for (const error of errors) {
|
|
307
|
-
console.log(chalk3.gray(" -"), error);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
logger.break();
|
|
311
|
-
} catch (error) {
|
|
312
|
-
spinner.fail("Failed to add components");
|
|
313
|
-
throw error;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
6
|
// src/cli/ux-paths/commands/init.ts
|
|
318
7
|
import { mkdirSync, writeFileSync, existsSync } from "fs";
|
|
319
8
|
import { resolve, basename } from "path";
|
|
320
|
-
import
|
|
9
|
+
import chalk from "chalk";
|
|
321
10
|
function registerInit(program2) {
|
|
322
11
|
program2.command("init").description("Bootstrap a .dfl-ux-paths/ directory in the current cwd.").option("--app-id <id>", "App identifier (defaults to current dir name).").option("--force", "Overwrite an existing flows.json if present.", false).action((opts) => {
|
|
323
12
|
const cwd = process.cwd();
|
|
@@ -326,7 +15,7 @@ function registerInit(program2) {
|
|
|
326
15
|
const appId = opts.appId || basename(cwd);
|
|
327
16
|
if (existsSync(flowsPath) && !opts.force) {
|
|
328
17
|
console.error(
|
|
329
|
-
|
|
18
|
+
chalk.yellow(`${flowsPath} already exists. Use --force to overwrite.`)
|
|
330
19
|
);
|
|
331
20
|
process.exit(1);
|
|
332
21
|
}
|
|
@@ -342,9 +31,9 @@ function registerInit(program2) {
|
|
|
342
31
|
flows: []
|
|
343
32
|
};
|
|
344
33
|
writeFileSync(flowsPath, JSON.stringify(stub, null, 2) + "\n", "utf8");
|
|
345
|
-
console.log(
|
|
34
|
+
console.log(chalk.green("Created"), flowsPath);
|
|
346
35
|
console.log(
|
|
347
|
-
|
|
36
|
+
chalk.gray(
|
|
348
37
|
"Next steps: populate screens/flows, then run `dfl-components ux-paths validate` and `dfl-components ux-paths generate-mermaid`."
|
|
349
38
|
)
|
|
350
39
|
);
|
|
@@ -356,7 +45,7 @@ import { readFileSync, existsSync as existsSync2 } from "fs";
|
|
|
356
45
|
import { resolve as resolve2 } from "path";
|
|
357
46
|
import Ajv2020 from "ajv/dist/2020.js";
|
|
358
47
|
import addFormats from "ajv-formats";
|
|
359
|
-
import
|
|
48
|
+
import chalk2 from "chalk";
|
|
360
49
|
|
|
361
50
|
// src/cli/ux-paths/lib/load-schema.ts
|
|
362
51
|
var SCHEMA_URL = "https://raw.githubusercontent.com/devfellowship/dfl-ux-paths/main/schema/v1.json";
|
|
@@ -383,16 +72,16 @@ async function loadSchemaV1() {
|
|
|
383
72
|
// src/cli/ux-paths/commands/validate.ts
|
|
384
73
|
function registerValidate(program2) {
|
|
385
74
|
program2.command("validate [path]").description("Validate a flows.json against the DFL UX Paths v1 schema.").action(async (maybePath) => {
|
|
386
|
-
const
|
|
387
|
-
if (!existsSync2(
|
|
388
|
-
console.error(
|
|
75
|
+
const path = resolve2(process.cwd(), maybePath || ".dfl-ux-paths/flows.json");
|
|
76
|
+
if (!existsSync2(path)) {
|
|
77
|
+
console.error(chalk2.red("File not found:"), path);
|
|
389
78
|
process.exit(1);
|
|
390
79
|
}
|
|
391
80
|
let doc;
|
|
392
81
|
try {
|
|
393
|
-
doc = JSON.parse(readFileSync(
|
|
82
|
+
doc = JSON.parse(readFileSync(path, "utf8"));
|
|
394
83
|
} catch (err) {
|
|
395
|
-
console.error(
|
|
84
|
+
console.error(chalk2.red("Invalid JSON:"), err.message);
|
|
396
85
|
process.exit(1);
|
|
397
86
|
}
|
|
398
87
|
const ajv = new Ajv2020({ allErrors: true, strict: false });
|
|
@@ -401,19 +90,19 @@ function registerValidate(program2) {
|
|
|
401
90
|
try {
|
|
402
91
|
schema = await loadSchemaV1();
|
|
403
92
|
} catch (err) {
|
|
404
|
-
console.error(
|
|
93
|
+
console.error(chalk2.red("Schema error:"), err.message);
|
|
405
94
|
process.exit(1);
|
|
406
95
|
}
|
|
407
96
|
const validate = ajv.compile(schema);
|
|
408
97
|
const ok = validate(doc);
|
|
409
98
|
if (ok) {
|
|
410
|
-
console.log(
|
|
99
|
+
console.log(chalk2.green("OK"), path, "conforms to schema v1.");
|
|
411
100
|
process.exit(0);
|
|
412
101
|
}
|
|
413
|
-
console.error(
|
|
102
|
+
console.error(chalk2.red("FAIL"), path);
|
|
414
103
|
for (const err of validate.errors ?? []) {
|
|
415
104
|
console.error(
|
|
416
|
-
|
|
105
|
+
chalk2.yellow(" -"),
|
|
417
106
|
err.instancePath || "<root>",
|
|
418
107
|
err.message,
|
|
419
108
|
err.params ? JSON.stringify(err.params) : ""
|
|
@@ -426,7 +115,7 @@ function registerValidate(program2) {
|
|
|
426
115
|
// src/cli/ux-paths/commands/generate-mermaid.ts
|
|
427
116
|
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync3 } from "fs";
|
|
428
117
|
import { resolve as resolve3, dirname } from "path";
|
|
429
|
-
import
|
|
118
|
+
import chalk3 from "chalk";
|
|
430
119
|
|
|
431
120
|
// src/cli/ux-paths/lib/json-to-mermaid.ts
|
|
432
121
|
function jsonToMermaid(doc) {
|
|
@@ -516,23 +205,23 @@ function escapeLabel(raw) {
|
|
|
516
205
|
var HEADER = "%% AUTO-GENERATED by dfl-components ux-paths \u2014 do not edit by hand.\n";
|
|
517
206
|
function registerGenerateMermaid(program2) {
|
|
518
207
|
program2.command("generate-mermaid [path]").description("Generate a Mermaid graph from a flows.json file.").option("-o, --out <file>", "Output path (defaults to <dir>/flows.mmd).").action((maybePath, opts) => {
|
|
519
|
-
const
|
|
520
|
-
if (!existsSync3(
|
|
521
|
-
console.error(
|
|
208
|
+
const path = resolve3(process.cwd(), maybePath || ".dfl-ux-paths/flows.json");
|
|
209
|
+
if (!existsSync3(path)) {
|
|
210
|
+
console.error(chalk3.red("File not found:"), path);
|
|
522
211
|
process.exit(1);
|
|
523
212
|
}
|
|
524
|
-
const doc = JSON.parse(readFileSync2(
|
|
525
|
-
const out = opts.out ? resolve3(process.cwd(), opts.out) : resolve3(dirname(
|
|
213
|
+
const doc = JSON.parse(readFileSync2(path, "utf8"));
|
|
214
|
+
const out = opts.out ? resolve3(process.cwd(), opts.out) : resolve3(dirname(path), "flows.mmd");
|
|
526
215
|
const body = HEADER + jsonToMermaid(doc);
|
|
527
216
|
writeFileSync2(out, body, "utf8");
|
|
528
|
-
console.log(
|
|
217
|
+
console.log(chalk3.green("Wrote"), out);
|
|
529
218
|
});
|
|
530
219
|
}
|
|
531
220
|
|
|
532
221
|
// src/cli/ux-paths/commands/diff.ts
|
|
533
222
|
import { readFileSync as readFileSync3, existsSync as existsSync4 } from "fs";
|
|
534
223
|
import { resolve as resolve4 } from "path";
|
|
535
|
-
import
|
|
224
|
+
import chalk4 from "chalk";
|
|
536
225
|
|
|
537
226
|
// src/cli/ux-paths/lib/flows-diff.ts
|
|
538
227
|
function flowsDiff(a, b) {
|
|
@@ -612,15 +301,15 @@ function registerDiff(program2) {
|
|
|
612
301
|
console.log(JSON.stringify(result, null, 2));
|
|
613
302
|
return;
|
|
614
303
|
}
|
|
615
|
-
console.log(
|
|
616
|
-
printSection("Screens added", result.screens.added,
|
|
617
|
-
printSection("Screens removed", result.screens.removed,
|
|
618
|
-
printSection("Actions added", result.actions.added,
|
|
619
|
-
printSection("Actions removed", result.actions.removed,
|
|
620
|
-
printSection("Flows added", result.flows.added,
|
|
621
|
-
printSection("Flows removed", result.flows.removed,
|
|
304
|
+
console.log(chalk4.bold(`Diff ${a.app_id}@${a.app_version} \u2192 ${b.app_id}@${b.app_version}`));
|
|
305
|
+
printSection("Screens added", result.screens.added, chalk4.green);
|
|
306
|
+
printSection("Screens removed", result.screens.removed, chalk4.red);
|
|
307
|
+
printSection("Actions added", result.actions.added, chalk4.green);
|
|
308
|
+
printSection("Actions removed", result.actions.removed, chalk4.red);
|
|
309
|
+
printSection("Flows added", result.flows.added, chalk4.green);
|
|
310
|
+
printSection("Flows removed", result.flows.removed, chalk4.red);
|
|
622
311
|
if (result.flows.changed.length > 0) {
|
|
623
|
-
console.log(
|
|
312
|
+
console.log(chalk4.yellow("\nFlows changed:"));
|
|
624
313
|
for (const ch of result.flows.changed) {
|
|
625
314
|
console.log(` - ${ch.name}: ${ch.reason}`);
|
|
626
315
|
}
|
|
@@ -628,12 +317,12 @@ function registerDiff(program2) {
|
|
|
628
317
|
});
|
|
629
318
|
}
|
|
630
319
|
function loadDoc(p) {
|
|
631
|
-
const
|
|
632
|
-
if (!existsSync4(
|
|
633
|
-
console.error(
|
|
320
|
+
const path = resolve4(process.cwd(), p);
|
|
321
|
+
if (!existsSync4(path)) {
|
|
322
|
+
console.error(chalk4.red("File not found:"), path);
|
|
634
323
|
process.exit(1);
|
|
635
324
|
}
|
|
636
|
-
return JSON.parse(readFileSync3(
|
|
325
|
+
return JSON.parse(readFileSync3(path, "utf8"));
|
|
637
326
|
}
|
|
638
327
|
function printSection(label, items, color) {
|
|
639
328
|
if (items.length === 0) return;
|
|
@@ -648,7 +337,7 @@ ${label} (${items.length}):`));
|
|
|
648
337
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, existsSync as existsSync5 } from "fs";
|
|
649
338
|
import { resolve as resolve5 } from "path";
|
|
650
339
|
import { execSync } from "child_process";
|
|
651
|
-
import
|
|
340
|
+
import chalk5 from "chalk";
|
|
652
341
|
|
|
653
342
|
// src/cli/ux-paths/lib/preserve-format.ts
|
|
654
343
|
function replaceTopLevelStringField(raw, key, newValue) {
|
|
@@ -678,12 +367,12 @@ function registerStamp(program2) {
|
|
|
678
367
|
"--no-preserve-format",
|
|
679
368
|
"Reformat the whole file (legacy). By default, stamp surgically updates only app_version/generated_at, preserving the existing formatting (minimal diff)."
|
|
680
369
|
).action((maybePath, opts) => {
|
|
681
|
-
const
|
|
682
|
-
if (!existsSync5(
|
|
683
|
-
console.error(
|
|
370
|
+
const path = resolve5(process.cwd(), maybePath || ".dfl-ux-paths/flows.json");
|
|
371
|
+
if (!existsSync5(path)) {
|
|
372
|
+
console.error(chalk5.red("File not found:"), path);
|
|
684
373
|
process.exit(1);
|
|
685
374
|
}
|
|
686
|
-
const raw = readFileSync4(
|
|
375
|
+
const raw = readFileSync4(path, "utf8");
|
|
687
376
|
const doc = JSON.parse(raw);
|
|
688
377
|
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
689
378
|
let sha = "0000000";
|
|
@@ -709,10 +398,10 @@ function registerStamp(program2) {
|
|
|
709
398
|
};
|
|
710
399
|
output = JSON.stringify(updated, null, 2) + "\n";
|
|
711
400
|
}
|
|
712
|
-
writeFileSync3(
|
|
713
|
-
console.log(
|
|
714
|
-
console.log(
|
|
715
|
-
console.log(
|
|
401
|
+
writeFileSync3(path, output, "utf8");
|
|
402
|
+
console.log(chalk5.green("Stamped"), path, preserve ? chalk5.gray("(minimal diff)") : "");
|
|
403
|
+
console.log(chalk5.gray(` app_version: ${newVersion}`));
|
|
404
|
+
console.log(chalk5.gray(` generated_at: ${newGeneratedAt}`));
|
|
716
405
|
});
|
|
717
406
|
}
|
|
718
407
|
|
|
@@ -729,7 +418,7 @@ function registerUxPaths(program2) {
|
|
|
729
418
|
// src/cli/check-style-imports/index.ts
|
|
730
419
|
import { resolve as resolve6 } from "path";
|
|
731
420
|
import { existsSync as existsSync6 } from "fs";
|
|
732
|
-
import
|
|
421
|
+
import chalk6 from "chalk";
|
|
733
422
|
|
|
734
423
|
// src/cli/check-style-imports/detect.ts
|
|
735
424
|
import { readdirSync, readFileSync as readFileSync5 } from "fs";
|
|
@@ -821,7 +510,7 @@ function registerCheckStyleImports(program2) {
|
|
|
821
510
|
).option("--json", "Emit the raw detection result as JSON instead of a human report.").action((maybeDir, opts) => {
|
|
822
511
|
const root = resolve6(process.cwd(), maybeDir || ".");
|
|
823
512
|
if (!existsSync6(root)) {
|
|
824
|
-
console.error(
|
|
513
|
+
console.error(chalk6.red("Directory not found:"), root);
|
|
825
514
|
process.exit(2);
|
|
826
515
|
}
|
|
827
516
|
const result = detectInDir(root);
|
|
@@ -832,33 +521,33 @@ function registerCheckStyleImports(program2) {
|
|
|
832
521
|
if (!result.conflict) {
|
|
833
522
|
if (result.hits.length === 0) {
|
|
834
523
|
console.log(
|
|
835
|
-
|
|
524
|
+
chalk6.green("OK"),
|
|
836
525
|
"no @devfellowship/components/{styles,shadcn} imports found."
|
|
837
526
|
);
|
|
838
527
|
} else {
|
|
839
528
|
const which = result.styles.length > 0 ? "styles" : "shadcn";
|
|
840
529
|
console.log(
|
|
841
|
-
|
|
530
|
+
chalk6.green("OK"),
|
|
842
531
|
`app imports only @devfellowship/components/${which} (${result.hits.length} reference${result.hits.length === 1 ? "" : "s"}).`
|
|
843
532
|
);
|
|
844
533
|
}
|
|
845
534
|
process.exit(0);
|
|
846
535
|
}
|
|
847
536
|
console.error(
|
|
848
|
-
|
|
537
|
+
chalk6.red("CONFLICT"),
|
|
849
538
|
"this app imports BOTH @devfellowship/components/styles AND /shadcn."
|
|
850
539
|
);
|
|
851
540
|
console.error(
|
|
852
|
-
|
|
541
|
+
chalk6.yellow(
|
|
853
542
|
"\nThese exports define the SAME CSS vars (--background, --primary, \u2026) in INCOMPATIBLE formats:\n - /styles ships them as HEX (#0A0908)\n - /shadcn ships them as HSL CHANNELS (30 11% 4%)\nImporting both clobbers --background \u2014 e.g. hsl(#0A0908) is invalid CSS \u2192 the\ndeclaration drops \u2192 surfaces render TRANSPARENT (the transparent-dialog bug).\n"
|
|
854
543
|
)
|
|
855
544
|
);
|
|
856
|
-
console.error(
|
|
545
|
+
console.error(chalk6.bold(" /styles imports:"));
|
|
857
546
|
for (const h of result.styles) console.error(` ${h.file}:${h.line} ${h.text}`);
|
|
858
|
-
console.error(
|
|
547
|
+
console.error(chalk6.bold(" /shadcn imports:"));
|
|
859
548
|
for (const h of result.shadcn) console.error(` ${h.file}:${h.line} ${h.text}`);
|
|
860
549
|
console.error(
|
|
861
|
-
|
|
550
|
+
chalk6.cyan(
|
|
862
551
|
`
|
|
863
552
|
FIX: keep exactly ONE. A DS-native (hex) app imports /styles only; a
|
|
864
553
|
shadcn-slate (HSL-channel) app imports /shadcn only. NEVER both.
|
|
@@ -872,10 +561,8 @@ See: ${DOC_URL}`
|
|
|
872
561
|
// src/cli/index.ts
|
|
873
562
|
var program = new Command();
|
|
874
563
|
program.name("dfl-components").description(
|
|
875
|
-
|
|
876
|
-
).version("
|
|
877
|
-
program.command("init").description("Initialize your project with dfl-components configuration").option("-y, --yes", "Skip prompts and use defaults").option("-c, --cwd <path>", "Working directory", process.cwd()).action(init);
|
|
878
|
-
program.command("add").description("Add a component to your project").argument("[components...]", "Components to add").option("-y, --yes", "Skip confirmation prompts").option("-o, --overwrite", "Overwrite existing files").option("-c, --cwd <path>", "Working directory", process.cwd()).option("-a, --all", "Add all available components").action(add);
|
|
564
|
+
'DevFellowship components CLI \u2014 map app UX paths (folds the dfl-ux-paths CLI). Shipped as the `dfl-components` bin of @devfellowship/components. The component set is consumed as a library import (`import { Button } from "@devfellowship/components"`), not scaffolded.'
|
|
565
|
+
).version("3.0.0");
|
|
879
566
|
registerUxPaths(program);
|
|
880
567
|
registerCheckStyleImports(program);
|
|
881
568
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devfellowship/components",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "DFL Design System — UI components, hooks, utils and providers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": [
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
],
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "tsup && tsup --config tsup.cli.config.ts",
|
|
46
|
+
"release": "npm run build && changeset publish",
|
|
46
47
|
"build:lib": "tsup",
|
|
47
48
|
"build:cli": "tsup --config tsup.cli.config.ts",
|
|
48
49
|
"build:watch": "tsup --watch",
|
|
@@ -68,6 +69,8 @@
|
|
|
68
69
|
}
|
|
69
70
|
},
|
|
70
71
|
"devDependencies": {
|
|
72
|
+
"@changesets/changelog-github": "^0.7.0",
|
|
73
|
+
"@changesets/cli": "^2.31.0",
|
|
71
74
|
"@storybook/addon-a11y": "^9.1.20",
|
|
72
75
|
"@storybook/addon-themes": "^9.1.20",
|
|
73
76
|
"@storybook/react": "^9.1.20",
|
|
@@ -76,9 +79,7 @@
|
|
|
76
79
|
"@tailwindcss/vite": "^4.2.2",
|
|
77
80
|
"@testing-library/jest-dom": "^6.9.1",
|
|
78
81
|
"@testing-library/react": "^16.3.2",
|
|
79
|
-
"@types/fs-extra": "^11.0.4",
|
|
80
82
|
"@types/node": "^20.0.0",
|
|
81
|
-
"@types/prompts": "^2.4.9",
|
|
82
83
|
"@types/react": "^18.3.28",
|
|
83
84
|
"@types/react-dom": "^18.3.7",
|
|
84
85
|
"@vitejs/plugin-react": "^4.7.0",
|
|
@@ -128,16 +129,11 @@
|
|
|
128
129
|
"clsx": "^2.1.1",
|
|
129
130
|
"cmdk": "^1.1.1",
|
|
130
131
|
"commander": "^12.0.0",
|
|
131
|
-
"cosmiconfig": "^9.0.0",
|
|
132
132
|
"date-fns": "^3.6.0",
|
|
133
133
|
"embla-carousel-react": "^8.6.0",
|
|
134
|
-
"fs-extra": "^11.2.0",
|
|
135
134
|
"input-otp": "^1.4.2",
|
|
136
135
|
"lucide-react": "^0.462.0",
|
|
137
136
|
"next-themes": "^0.4.6",
|
|
138
|
-
"node-fetch": "^3.3.2",
|
|
139
|
-
"ora": "^8.0.1",
|
|
140
|
-
"prompts": "^2.4.2",
|
|
141
137
|
"react-day-picker": "^8.10.1",
|
|
142
138
|
"react-hook-form": "^7.72.1",
|
|
143
139
|
"react-resizable-panels": "^2.1.9",
|