@buoy-design/cli 0.1.1 → 0.1.2
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/commands/audit.d.ts +3 -0
- package/dist/commands/audit.d.ts.map +1 -0
- package/dist/commands/audit.js +235 -0
- package/dist/commands/audit.js.map +1 -0
- package/dist/commands/check.d.ts +16 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +168 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/ci.d.ts.map +1 -1
- package/dist/commands/ci.js +85 -81
- package/dist/commands/ci.js.map +1 -1
- package/dist/commands/compare.d.ts +3 -0
- package/dist/commands/compare.d.ts.map +1 -0
- package/dist/commands/compare.js +170 -0
- package/dist/commands/compare.js.map +1 -0
- package/dist/commands/drift.d.ts.map +1 -1
- package/dist/commands/drift.js +40 -55
- package/dist/commands/drift.js.map +1 -1
- package/dist/commands/explain.d.ts +3 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +212 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/graph.d.ts +3 -0
- package/dist/commands/graph.d.ts.map +1 -0
- package/dist/commands/graph.js +430 -0
- package/dist/commands/graph.js.map +1 -0
- package/dist/commands/index.d.ts +6 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +6 -1
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +368 -176
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/scan.d.ts.map +1 -1
- package/dist/commands/scan.js +56 -16
- package/dist/commands/scan.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +40 -11
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/tokens.d.ts +3 -0
- package/dist/commands/tokens.d.ts.map +1 -0
- package/dist/commands/tokens.js +261 -0
- package/dist/commands/tokens.js.map +1 -0
- package/dist/config/auto-detect.d.ts +21 -0
- package/dist/config/auto-detect.d.ts.map +1 -0
- package/dist/config/auto-detect.js +278 -0
- package/dist/config/auto-detect.js.map +1 -0
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +17 -0
- package/dist/config/loader.js.map +1 -1
- package/dist/config/schema.d.ts +63 -63
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +20 -2
- package/dist/config/schema.js.map +1 -1
- package/dist/constants.d.ts +36 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +37 -0
- package/dist/constants.js.map +1 -0
- package/dist/detect/frameworks.d.ts +5 -1
- package/dist/detect/frameworks.d.ts.map +1 -1
- package/dist/detect/frameworks.js +35 -9
- package/dist/detect/frameworks.js.map +1 -1
- package/dist/detect/index.d.ts +1 -0
- package/dist/detect/index.d.ts.map +1 -1
- package/dist/detect/index.js +3 -0
- package/dist/detect/index.js.map +1 -1
- package/dist/detect/monorepo-patterns.d.ts +54 -0
- package/dist/detect/monorepo-patterns.d.ts.map +1 -0
- package/dist/detect/monorepo-patterns.js +209 -0
- package/dist/detect/monorepo-patterns.js.map +1 -0
- package/dist/detect/project-detector.d.ts +1 -1
- package/dist/detect/project-detector.d.ts.map +1 -1
- package/dist/detect/project-detector.js +132 -0
- package/dist/detect/project-detector.js.map +1 -1
- package/dist/explain/agents.d.ts +31 -0
- package/dist/explain/agents.d.ts.map +1 -0
- package/dist/explain/agents.js +507 -0
- package/dist/explain/agents.js.map +1 -0
- package/dist/hooks/index.d.ts +26 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +283 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/integrations/github-formatter.d.ts +54 -2
- package/dist/integrations/github-formatter.d.ts.map +1 -1
- package/dist/integrations/github-formatter.js +369 -47
- package/dist/integrations/github-formatter.js.map +1 -1
- package/dist/integrations/github.d.ts +75 -0
- package/dist/integrations/github.d.ts.map +1 -1
- package/dist/integrations/github.js +212 -0
- package/dist/integrations/github.js.map +1 -1
- package/dist/integrations/index.d.ts +3 -3
- package/dist/integrations/index.d.ts.map +1 -1
- package/dist/integrations/index.js +2 -2
- package/dist/integrations/index.js.map +1 -1
- package/dist/output/formatters.d.ts.map +1 -1
- package/dist/output/formatters.js +5 -10
- package/dist/output/formatters.js.map +1 -1
- package/dist/services/ai-analysis.d.ts +54 -0
- package/dist/services/ai-analysis.d.ts.map +1 -0
- package/dist/services/ai-analysis.js +239 -0
- package/dist/services/ai-analysis.js.map +1 -0
- package/dist/services/drift-analysis.d.ts +77 -0
- package/dist/services/drift-analysis.d.ts.map +1 -0
- package/dist/services/drift-analysis.js +145 -0
- package/dist/services/drift-analysis.js.map +1 -0
- package/package.json +16 -13
- package/LICENSE +0 -21
- package/dist/commands/__tests__/ci.test.d.ts +0 -2
- package/dist/commands/__tests__/ci.test.d.ts.map +0 -1
- package/dist/commands/__tests__/ci.test.js +0 -33
- package/dist/commands/__tests__/ci.test.js.map +0 -1
- package/dist/commands/bootstrap.d.ts +0 -3
- package/dist/commands/bootstrap.d.ts.map +0 -1
- package/dist/commands/bootstrap.js +0 -458
- package/dist/commands/bootstrap.js.map +0 -1
package/dist/commands/init.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import { Command } from
|
|
2
|
-
import { writeFileSync, existsSync } from
|
|
3
|
-
import { resolve } from
|
|
4
|
-
import chalk from
|
|
5
|
-
import ora from
|
|
6
|
-
import { createInterface } from
|
|
7
|
-
import { success, error, info, warning } from
|
|
8
|
-
import { ProjectDetector } from
|
|
9
|
-
import { detectFrameworks, getPluginInstallCommand, PLUGIN_INFO, BUILTIN_SCANNERS } from
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { writeFileSync, existsSync } from "fs";
|
|
3
|
+
import { resolve } from "path";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import ora from "ora";
|
|
6
|
+
import { createInterface } from "readline";
|
|
7
|
+
import { success, error, info, warning } from "../output/reporters.js";
|
|
8
|
+
import { ProjectDetector, detectMonorepoConfig, expandPatternsForMonorepo, } from "../detect/index.js";
|
|
9
|
+
import { detectFrameworks, getPluginInstallCommand, PLUGIN_INFO, BUILTIN_SCANNERS, } from "../detect/frameworks.js";
|
|
10
|
+
import { setupHooks, generateStandaloneHook, detectHookSystem, } from "../hooks/index.js";
|
|
10
11
|
function generateConfig(project) {
|
|
11
12
|
const lines = [];
|
|
13
|
+
// Detect monorepo configuration for pattern expansion
|
|
14
|
+
const monorepoConfig = detectMonorepoConfig(project.root);
|
|
12
15
|
lines.push(`/** @type {import('@buoy-design/cli').BuoyConfig} */`);
|
|
13
16
|
lines.push(`export default {`);
|
|
14
17
|
lines.push(` project: {`);
|
|
@@ -18,44 +21,53 @@ function generateConfig(project) {
|
|
|
18
21
|
// Determine the correct source key based on framework
|
|
19
22
|
const getSourceKey = (frameworkName) => {
|
|
20
23
|
// React-based frameworks
|
|
21
|
-
if ([
|
|
22
|
-
|
|
24
|
+
if ([
|
|
25
|
+
"react",
|
|
26
|
+
"nextjs",
|
|
27
|
+
"remix",
|
|
28
|
+
"gatsby",
|
|
29
|
+
"react-native",
|
|
30
|
+
"expo",
|
|
31
|
+
"preact",
|
|
32
|
+
"solid",
|
|
33
|
+
].includes(frameworkName)) {
|
|
34
|
+
return "react";
|
|
23
35
|
}
|
|
24
36
|
// Vue-based frameworks
|
|
25
|
-
if ([
|
|
26
|
-
return
|
|
37
|
+
if (["vue", "nuxt"].includes(frameworkName)) {
|
|
38
|
+
return "vue";
|
|
27
39
|
}
|
|
28
40
|
// Svelte-based frameworks
|
|
29
|
-
if ([
|
|
30
|
-
return
|
|
41
|
+
if (["svelte", "sveltekit"].includes(frameworkName)) {
|
|
42
|
+
return "svelte";
|
|
31
43
|
}
|
|
32
44
|
// Angular
|
|
33
|
-
if (frameworkName ===
|
|
34
|
-
return
|
|
45
|
+
if (frameworkName === "angular") {
|
|
46
|
+
return "angular";
|
|
35
47
|
}
|
|
36
48
|
// Web Components
|
|
37
|
-
if ([
|
|
38
|
-
return
|
|
49
|
+
if (["lit", "stencil"].includes(frameworkName)) {
|
|
50
|
+
return "webcomponent";
|
|
39
51
|
}
|
|
40
52
|
// Astro is special - can use multiple frameworks
|
|
41
|
-
if (frameworkName ===
|
|
42
|
-
return
|
|
53
|
+
if (frameworkName === "astro") {
|
|
54
|
+
return "react"; // Default to React for Astro
|
|
43
55
|
}
|
|
44
56
|
return null;
|
|
45
57
|
};
|
|
46
58
|
// File extensions by framework
|
|
47
59
|
const getExtensions = (sourceKey, typescript) => {
|
|
48
60
|
switch (sourceKey) {
|
|
49
|
-
case
|
|
50
|
-
return [
|
|
51
|
-
case
|
|
52
|
-
return [
|
|
53
|
-
case
|
|
54
|
-
return [
|
|
55
|
-
case
|
|
56
|
-
return [
|
|
61
|
+
case "vue":
|
|
62
|
+
return ["vue"];
|
|
63
|
+
case "svelte":
|
|
64
|
+
return ["svelte"];
|
|
65
|
+
case "angular":
|
|
66
|
+
return ["component.ts"];
|
|
67
|
+
case "webcomponent":
|
|
68
|
+
return ["ts"];
|
|
57
69
|
default: // react
|
|
58
|
-
return typescript ? [
|
|
70
|
+
return typescript ? ["tsx", "jsx"] : ["jsx", "tsx"];
|
|
59
71
|
}
|
|
60
72
|
};
|
|
61
73
|
// JS Framework config (React, Vue, Svelte, Angular, Web Components)
|
|
@@ -66,23 +78,34 @@ function generateConfig(project) {
|
|
|
66
78
|
if (sourceKey && !addedSourceKeys.has(sourceKey)) {
|
|
67
79
|
addedSourceKeys.add(sourceKey);
|
|
68
80
|
const extensions = getExtensions(sourceKey, framework.typescript);
|
|
69
|
-
const jsComponents = project.components.filter(c => c.type ===
|
|
81
|
+
const jsComponents = project.components.filter((c) => c.type === "jsx" ||
|
|
82
|
+
c.type === "vue" ||
|
|
83
|
+
c.type === "svelte" ||
|
|
84
|
+
!c.type);
|
|
70
85
|
let includePatterns;
|
|
71
86
|
if (jsComponents.length > 0) {
|
|
72
|
-
includePatterns = jsComponents.flatMap(c => extensions.map(ext => `${c.path}/**/*.${ext}`));
|
|
87
|
+
includePatterns = jsComponents.flatMap((c) => extensions.map((ext) => `${c.path}/**/*.${ext}`));
|
|
73
88
|
}
|
|
74
89
|
else {
|
|
75
|
-
|
|
90
|
+
// Use default patterns, but expand for monorepo if detected
|
|
91
|
+
const defaultPatterns = extensions.map((ext) => `src/**/*.${ext}`);
|
|
92
|
+
if (monorepoConfig.type) {
|
|
93
|
+
const expanded = expandPatternsForMonorepo(defaultPatterns, monorepoConfig);
|
|
94
|
+
includePatterns = expanded.allPatterns;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
includePatterns = defaultPatterns;
|
|
98
|
+
}
|
|
76
99
|
}
|
|
77
100
|
lines.push(` ${sourceKey}: {`);
|
|
78
101
|
lines.push(` enabled: true,`);
|
|
79
|
-
lines.push(` include: [${includePatterns.map((p) => `'${p}'`).join(
|
|
102
|
+
lines.push(` include: [${includePatterns.map((p) => `'${p}'`).join(", ")}],`);
|
|
80
103
|
lines.push(` exclude: ['**/*.test.*', '**/*.spec.*', '**/*.stories.*'],`);
|
|
81
104
|
if (project.designSystem) {
|
|
82
105
|
lines.push(` designSystemPackage: '${project.designSystem.package}',`);
|
|
83
106
|
}
|
|
84
|
-
if (sourceKey ===
|
|
85
|
-
const wcFramework = framework.name ===
|
|
107
|
+
if (sourceKey === "webcomponent") {
|
|
108
|
+
const wcFramework = framework.name === "lit" ? "lit" : "stencil";
|
|
86
109
|
lines.push(` framework: '${wcFramework}',`);
|
|
87
110
|
}
|
|
88
111
|
lines.push(` },`);
|
|
@@ -90,40 +113,97 @@ function generateConfig(project) {
|
|
|
90
113
|
}
|
|
91
114
|
// Server-side / template-based framework config
|
|
92
115
|
const serverFrameworks = [
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
116
|
+
"php",
|
|
117
|
+
"laravel",
|
|
118
|
+
"symfony",
|
|
119
|
+
"rails",
|
|
120
|
+
"django",
|
|
121
|
+
"flask",
|
|
122
|
+
"fastapi",
|
|
123
|
+
"express",
|
|
124
|
+
"nestjs",
|
|
125
|
+
"spring",
|
|
126
|
+
"aspnet",
|
|
127
|
+
"go",
|
|
128
|
+
"hugo",
|
|
129
|
+
"jekyll",
|
|
130
|
+
"eleventy",
|
|
100
131
|
];
|
|
101
132
|
// Map framework to template type
|
|
102
133
|
const getTemplateType = (frameworkName, componentType) => {
|
|
103
|
-
if
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
134
|
+
// Return component type directly if it's a known template type
|
|
135
|
+
const knownTypes = [
|
|
136
|
+
// Server-side templates
|
|
137
|
+
"blade", "erb", "twig", "njk", "razor", "hbs", "mustache",
|
|
138
|
+
"ejs", "pug", "liquid", "slim", "haml", "jinja", "django",
|
|
139
|
+
"thymeleaf", "freemarker", "go-template", "edge", "eta", "heex",
|
|
140
|
+
"velocity", "xslt",
|
|
141
|
+
// JS frameworks
|
|
142
|
+
"astro", "solid", "qwik", "marko", "lit", "fast", "angular",
|
|
143
|
+
"stencil", "alpine", "htmx",
|
|
144
|
+
// Static site generators
|
|
145
|
+
"hugo", "jekyll", "eleventy", "shopify",
|
|
146
|
+
// Documentation
|
|
147
|
+
"markdown", "mdx", "asciidoc",
|
|
148
|
+
// Graphics
|
|
149
|
+
"svg",
|
|
150
|
+
// Data templates
|
|
151
|
+
"yaml-template", "json-template"
|
|
152
|
+
];
|
|
153
|
+
if (componentType && knownTypes.includes(componentType)) {
|
|
154
|
+
return componentType;
|
|
155
|
+
}
|
|
111
156
|
// Framework-based defaults
|
|
112
|
-
if (frameworkName ===
|
|
113
|
-
return
|
|
114
|
-
if (frameworkName ===
|
|
115
|
-
return
|
|
116
|
-
if (frameworkName ===
|
|
117
|
-
return
|
|
118
|
-
if (frameworkName ===
|
|
119
|
-
return
|
|
120
|
-
|
|
157
|
+
if (frameworkName === "laravel")
|
|
158
|
+
return "blade";
|
|
159
|
+
if (frameworkName === "rails")
|
|
160
|
+
return "erb";
|
|
161
|
+
if (frameworkName === "symfony")
|
|
162
|
+
return "twig";
|
|
163
|
+
if (frameworkName === "eleventy")
|
|
164
|
+
return "eleventy";
|
|
165
|
+
if (frameworkName === "aspnet")
|
|
166
|
+
return "razor";
|
|
167
|
+
if (frameworkName === "express")
|
|
168
|
+
return "ejs";
|
|
169
|
+
if (frameworkName === "flask")
|
|
170
|
+
return "jinja";
|
|
171
|
+
if (frameworkName === "django")
|
|
172
|
+
return "django";
|
|
173
|
+
if (frameworkName === "spring")
|
|
174
|
+
return "thymeleaf";
|
|
175
|
+
if (frameworkName === "go")
|
|
176
|
+
return "go-template";
|
|
177
|
+
if (frameworkName === "astro")
|
|
178
|
+
return "astro";
|
|
179
|
+
if (frameworkName === "hugo")
|
|
180
|
+
return "hugo";
|
|
181
|
+
if (frameworkName === "jekyll")
|
|
182
|
+
return "jekyll";
|
|
183
|
+
return "html";
|
|
121
184
|
};
|
|
122
185
|
// Check if any framework is a server-side framework
|
|
123
|
-
const serverFramework = project.frameworks.find(f => serverFrameworks.includes(f.name));
|
|
186
|
+
const serverFramework = project.frameworks.find((f) => serverFrameworks.includes(f.name));
|
|
124
187
|
if (serverFramework) {
|
|
125
|
-
const
|
|
126
|
-
|
|
188
|
+
const templateTypes = [
|
|
189
|
+
// Server-side templates
|
|
190
|
+
"php", "blade", "erb", "twig", "html", "njk", "razor", "hbs",
|
|
191
|
+
"mustache", "ejs", "pug", "liquid", "slim", "haml", "jinja",
|
|
192
|
+
"django", "thymeleaf", "freemarker", "go-template", "edge",
|
|
193
|
+
"eta", "heex", "velocity", "xslt",
|
|
194
|
+
// JS frameworks
|
|
195
|
+
"astro", "solid", "qwik", "marko", "lit", "fast", "angular",
|
|
196
|
+
"stencil", "alpine", "htmx",
|
|
197
|
+
// Static site generators
|
|
198
|
+
"hugo", "jekyll", "eleventy", "shopify",
|
|
199
|
+
// Documentation
|
|
200
|
+
"markdown", "mdx", "asciidoc",
|
|
201
|
+
// Graphics
|
|
202
|
+
"svg",
|
|
203
|
+
// Data templates
|
|
204
|
+
"yaml-template", "json-template"
|
|
205
|
+
];
|
|
206
|
+
const templateComponents = project.components.filter((c) => c.type && templateTypes.includes(c.type));
|
|
127
207
|
if (templateComponents.length > 0) {
|
|
128
208
|
// Use the first component's type to determine template type
|
|
129
209
|
const templateType = getTemplateType(serverFramework.name, templateComponents[0]?.type);
|
|
@@ -145,8 +225,8 @@ function generateConfig(project) {
|
|
|
145
225
|
lines.push(` },`);
|
|
146
226
|
}
|
|
147
227
|
// Token files config
|
|
148
|
-
const tokenFiles = project.tokens.filter((t) => t.type !==
|
|
149
|
-
const hasTailwind = project.tokens.some((t) => t.type ===
|
|
228
|
+
const tokenFiles = project.tokens.filter((t) => t.type !== "tailwind");
|
|
229
|
+
const hasTailwind = project.tokens.some((t) => t.type === "tailwind");
|
|
150
230
|
if (tokenFiles.length > 0 || hasTailwind) {
|
|
151
231
|
lines.push(` tokens: {`);
|
|
152
232
|
lines.push(` enabled: true,`);
|
|
@@ -172,66 +252,84 @@ function generateConfig(project) {
|
|
|
172
252
|
lines.push(` },`);
|
|
173
253
|
lines.push(`};`);
|
|
174
254
|
lines.push(``);
|
|
175
|
-
return lines.join(
|
|
255
|
+
return lines.join("\n");
|
|
176
256
|
}
|
|
177
257
|
function printDetectionResults(project) {
|
|
178
|
-
console.log(
|
|
179
|
-
console.log(chalk.bold(
|
|
258
|
+
console.log("");
|
|
259
|
+
console.log(chalk.bold(" Detected:"));
|
|
180
260
|
const frameworkNames = {
|
|
181
261
|
// JS frameworks
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
262
|
+
react: "React",
|
|
263
|
+
vue: "Vue",
|
|
264
|
+
svelte: "Svelte",
|
|
265
|
+
angular: "Angular",
|
|
266
|
+
solid: "Solid",
|
|
267
|
+
preact: "Preact",
|
|
188
268
|
// Meta-frameworks
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
269
|
+
nextjs: "Next.js",
|
|
270
|
+
nuxt: "Nuxt",
|
|
271
|
+
astro: "Astro",
|
|
272
|
+
remix: "Remix",
|
|
273
|
+
sveltekit: "SvelteKit",
|
|
274
|
+
gatsby: "Gatsby",
|
|
195
275
|
// Mobile
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
276
|
+
"react-native": "React Native",
|
|
277
|
+
flutter: "Flutter",
|
|
278
|
+
expo: "Expo",
|
|
199
279
|
// Web Components
|
|
200
|
-
|
|
201
|
-
|
|
280
|
+
lit: "Lit",
|
|
281
|
+
stencil: "Stencil",
|
|
202
282
|
// Server-side
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
283
|
+
php: "PHP",
|
|
284
|
+
laravel: "Laravel",
|
|
285
|
+
symfony: "Symfony",
|
|
286
|
+
rails: "Ruby on Rails",
|
|
287
|
+
django: "Django",
|
|
288
|
+
flask: "Flask",
|
|
289
|
+
fastapi: "FastAPI",
|
|
290
|
+
express: "Express",
|
|
291
|
+
nestjs: "NestJS",
|
|
292
|
+
spring: "Spring Boot",
|
|
293
|
+
aspnet: "ASP.NET",
|
|
294
|
+
go: "Go",
|
|
215
295
|
// Static site generators
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
296
|
+
hugo: "Hugo",
|
|
297
|
+
jekyll: "Jekyll",
|
|
298
|
+
eleventy: "Eleventy",
|
|
219
299
|
};
|
|
220
300
|
// Frameworks - show all detected
|
|
221
301
|
if (project.frameworks.length > 0) {
|
|
222
302
|
// Show warning if multiple UI frameworks detected (framework sprawl)
|
|
223
|
-
const uiFrameworks = [
|
|
224
|
-
|
|
225
|
-
|
|
303
|
+
const uiFrameworks = [
|
|
304
|
+
"react",
|
|
305
|
+
"vue",
|
|
306
|
+
"svelte",
|
|
307
|
+
"angular",
|
|
308
|
+
"solid",
|
|
309
|
+
"preact",
|
|
310
|
+
"lit",
|
|
311
|
+
"stencil",
|
|
312
|
+
"nextjs",
|
|
313
|
+
"nuxt",
|
|
314
|
+
"astro",
|
|
315
|
+
"remix",
|
|
316
|
+
"sveltekit",
|
|
317
|
+
"gatsby",
|
|
318
|
+
"react-native",
|
|
319
|
+
"expo",
|
|
320
|
+
"flutter",
|
|
321
|
+
];
|
|
322
|
+
const uiCount = project.frameworks.filter((f) => uiFrameworks.includes(f.name)).length;
|
|
226
323
|
if (uiCount > 1) {
|
|
227
|
-
console.log(chalk.yellow(
|
|
324
|
+
console.log(chalk.yellow(" ⚠ ") +
|
|
325
|
+
chalk.yellow.bold("Multiple UI frameworks detected (framework sprawl)"));
|
|
228
326
|
}
|
|
229
327
|
for (const framework of project.frameworks) {
|
|
230
|
-
const ts = framework.typescript ?
|
|
328
|
+
const ts = framework.typescript ? " + TypeScript" : "";
|
|
231
329
|
const frameworkName = frameworkNames[framework.name] || capitalize(framework.name);
|
|
232
|
-
const meta = framework.meta ? chalk.dim(` (${framework.meta})`) :
|
|
233
|
-
const version = framework.version !==
|
|
234
|
-
console.log(chalk.green(
|
|
330
|
+
const meta = framework.meta ? chalk.dim(` (${framework.meta})`) : "";
|
|
331
|
+
const version = framework.version !== "unknown" ? ` ${framework.version}` : "";
|
|
332
|
+
console.log(chalk.green(" ✓ ") +
|
|
235
333
|
chalk.bold(frameworkName) +
|
|
236
334
|
ts +
|
|
237
335
|
meta +
|
|
@@ -242,18 +340,66 @@ function printDetectionResults(project) {
|
|
|
242
340
|
if (project.components.length > 0) {
|
|
243
341
|
for (const comp of project.components) {
|
|
244
342
|
const typeLabels = {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
343
|
+
// JS frameworks
|
|
344
|
+
jsx: "component files",
|
|
345
|
+
tsx: "TypeScript components",
|
|
346
|
+
vue: "Vue components",
|
|
347
|
+
svelte: "Svelte components",
|
|
348
|
+
astro: "Astro components",
|
|
349
|
+
solid: "Solid components",
|
|
350
|
+
qwik: "Qwik components",
|
|
351
|
+
marko: "Marko components",
|
|
352
|
+
lit: "Lit elements",
|
|
353
|
+
fast: "FAST elements",
|
|
354
|
+
// Server-side templates
|
|
355
|
+
php: "PHP templates",
|
|
356
|
+
blade: "Blade templates",
|
|
357
|
+
erb: "ERB templates",
|
|
358
|
+
twig: "Twig templates",
|
|
359
|
+
html: "HTML templates",
|
|
360
|
+
njk: "Nunjucks templates",
|
|
361
|
+
razor: "Razor views",
|
|
362
|
+
hbs: "Handlebars templates",
|
|
363
|
+
mustache: "Mustache templates",
|
|
364
|
+
ejs: "EJS templates",
|
|
365
|
+
pug: "Pug templates",
|
|
366
|
+
liquid: "Liquid templates",
|
|
367
|
+
slim: "Slim templates",
|
|
368
|
+
haml: "Haml templates",
|
|
369
|
+
jinja: "Jinja templates",
|
|
370
|
+
django: "Django templates",
|
|
371
|
+
thymeleaf: "Thymeleaf templates",
|
|
372
|
+
freemarker: "Freemarker templates",
|
|
373
|
+
velocity: "Velocity templates",
|
|
374
|
+
"go-template": "Go templates",
|
|
375
|
+
edge: "Edge.js templates",
|
|
376
|
+
eta: "Eta templates",
|
|
377
|
+
heex: "HEEx templates",
|
|
378
|
+
xslt: "XSLT stylesheets",
|
|
379
|
+
// Static site generators
|
|
380
|
+
hugo: "Hugo layouts",
|
|
381
|
+
jekyll: "Jekyll layouts",
|
|
382
|
+
eleventy: "Eleventy templates",
|
|
383
|
+
shopify: "Shopify templates",
|
|
384
|
+
// Documentation
|
|
385
|
+
markdown: "Markdown files",
|
|
386
|
+
mdx: "MDX files",
|
|
387
|
+
asciidoc: "AsciiDoc files",
|
|
388
|
+
// Data templates
|
|
389
|
+
"yaml-template": "YAML templates",
|
|
390
|
+
"json-template": "JSON templates",
|
|
391
|
+
// Additional JS frameworks
|
|
392
|
+
angular: "Angular components",
|
|
393
|
+
stencil: "Stencil components",
|
|
394
|
+
alpine: "Alpine.js templates",
|
|
395
|
+
htmx: "HTMX templates",
|
|
396
|
+
// Graphics
|
|
397
|
+
svg: "SVG components",
|
|
254
398
|
};
|
|
255
|
-
const typeLabel = comp.type
|
|
256
|
-
|
|
399
|
+
const typeLabel = comp.type
|
|
400
|
+
? typeLabels[comp.type] || "template files"
|
|
401
|
+
: "component files";
|
|
402
|
+
console.log(chalk.green(" ✓ ") +
|
|
257
403
|
`${comp.fileCount} ${typeLabel} in ` +
|
|
258
404
|
chalk.cyan(comp.path));
|
|
259
405
|
}
|
|
@@ -261,36 +407,47 @@ function printDetectionResults(project) {
|
|
|
261
407
|
// Tokens
|
|
262
408
|
if (project.tokens.length > 0) {
|
|
263
409
|
for (const token of project.tokens) {
|
|
264
|
-
|
|
265
|
-
console.log(chalk.green(icon) + `${token.name}: ` + chalk.cyan(token.path));
|
|
410
|
+
console.log(chalk.green(" ✓ ") + `${token.name}: ` + chalk.cyan(token.path));
|
|
266
411
|
}
|
|
267
412
|
}
|
|
268
413
|
// Storybook
|
|
269
414
|
if (project.storybook) {
|
|
270
|
-
const version = project.storybook.version
|
|
271
|
-
|
|
415
|
+
const version = project.storybook.version
|
|
416
|
+
? ` (${project.storybook.version})`
|
|
417
|
+
: "";
|
|
418
|
+
console.log(chalk.green(" ✓ ") + `Storybook` + chalk.dim(version));
|
|
272
419
|
}
|
|
273
420
|
// Design system
|
|
274
421
|
if (project.designSystem) {
|
|
275
|
-
console.log(chalk.green(
|
|
422
|
+
console.log(chalk.green(" ✓ ") +
|
|
276
423
|
`Design system: ` +
|
|
277
424
|
chalk.cyan(project.designSystem.package));
|
|
278
425
|
}
|
|
279
426
|
// Monorepo
|
|
280
427
|
if (project.monorepo) {
|
|
281
|
-
|
|
428
|
+
// Show basic monorepo info
|
|
429
|
+
console.log(chalk.green(" ✓ ") +
|
|
282
430
|
capitalize(project.monorepo.type) +
|
|
283
431
|
` monorepo (${project.monorepo.packages.length} packages)`);
|
|
432
|
+
// Show monorepo component paths if detected
|
|
433
|
+
const monorepoComponents = project.components.filter((c) => c.path.startsWith("packages/") ||
|
|
434
|
+
c.path.startsWith("apps/") ||
|
|
435
|
+
c.path.startsWith("libs/") ||
|
|
436
|
+
c.path.startsWith("modules/"));
|
|
437
|
+
if (monorepoComponents.length > 0) {
|
|
438
|
+
console.log(chalk.dim(" ") +
|
|
439
|
+
chalk.dim(`Scanning: ${monorepoComponents.map((c) => c.path).slice(0, 3).join(", ")}${monorepoComponents.length > 3 ? ` +${monorepoComponents.length - 3} more` : ""}`));
|
|
440
|
+
}
|
|
284
441
|
}
|
|
285
442
|
// Nothing found
|
|
286
443
|
if (project.frameworks.length === 0 &&
|
|
287
444
|
project.components.length === 0 &&
|
|
288
445
|
project.tokens.length === 0 &&
|
|
289
446
|
!project.storybook) {
|
|
290
|
-
console.log(chalk.yellow(
|
|
291
|
-
console.log(chalk.dim(
|
|
447
|
+
console.log(chalk.yellow(" ⚠ ") + "No sources auto-detected");
|
|
448
|
+
console.log(chalk.dim(" You can manually configure sources in buoy.config.mjs"));
|
|
292
449
|
}
|
|
293
|
-
console.log(
|
|
450
|
+
console.log("");
|
|
294
451
|
}
|
|
295
452
|
function capitalize(s) {
|
|
296
453
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
@@ -300,35 +457,36 @@ async function promptConfirm(message, defaultValue = true) {
|
|
|
300
457
|
input: process.stdin,
|
|
301
458
|
output: process.stdout,
|
|
302
459
|
});
|
|
303
|
-
const suffix = defaultValue ?
|
|
460
|
+
const suffix = defaultValue ? "[Y/n]" : "[y/N]";
|
|
304
461
|
return new Promise((resolve) => {
|
|
305
462
|
rl.question(`${message} ${suffix} `, (answer) => {
|
|
306
463
|
rl.close();
|
|
307
464
|
const trimmed = answer.trim().toLowerCase();
|
|
308
|
-
if (trimmed ===
|
|
465
|
+
if (trimmed === "") {
|
|
309
466
|
resolve(defaultValue);
|
|
310
467
|
}
|
|
311
468
|
else {
|
|
312
|
-
resolve(trimmed ===
|
|
469
|
+
resolve(trimmed === "y" || trimmed === "yes");
|
|
313
470
|
}
|
|
314
471
|
});
|
|
315
472
|
});
|
|
316
473
|
}
|
|
317
474
|
export function createInitCommand() {
|
|
318
|
-
const cmd = new Command(
|
|
319
|
-
.description(
|
|
320
|
-
.option(
|
|
321
|
-
.option(
|
|
322
|
-
.option(
|
|
323
|
-
.option(
|
|
324
|
-
.option(
|
|
475
|
+
const cmd = new Command("init")
|
|
476
|
+
.description("Initialize Buoy configuration in the current project")
|
|
477
|
+
.option("-f, --force", "Overwrite existing configuration")
|
|
478
|
+
.option("-n, --name <name>", "Project name")
|
|
479
|
+
.option("--skip-detect", "Skip auto-detection and create minimal config")
|
|
480
|
+
.option("-y, --yes", "Auto-install recommended plugins without prompting")
|
|
481
|
+
.option("--no-install", "Skip plugin installation prompts")
|
|
482
|
+
.option("--hooks", "Setup pre-commit hook for drift checking")
|
|
325
483
|
.action(async (options) => {
|
|
326
484
|
const cwd = process.cwd();
|
|
327
|
-
const configPath = resolve(cwd,
|
|
485
|
+
const configPath = resolve(cwd, "buoy.config.mjs");
|
|
328
486
|
// Check if config already exists
|
|
329
487
|
if (existsSync(configPath) && !options.force) {
|
|
330
488
|
warning(`Configuration already exists at ${configPath}`);
|
|
331
|
-
info(
|
|
489
|
+
info("Use --force to overwrite");
|
|
332
490
|
return;
|
|
333
491
|
}
|
|
334
492
|
let project;
|
|
@@ -349,7 +507,7 @@ export function createInitCommand() {
|
|
|
349
507
|
}
|
|
350
508
|
else {
|
|
351
509
|
// Run auto-detection
|
|
352
|
-
const spinner = ora(
|
|
510
|
+
const spinner = ora("Scanning project...").start();
|
|
353
511
|
try {
|
|
354
512
|
const detector = new ProjectDetector(cwd);
|
|
355
513
|
project = await detector.detect();
|
|
@@ -360,7 +518,7 @@ export function createInitCommand() {
|
|
|
360
518
|
printDetectionResults(project);
|
|
361
519
|
}
|
|
362
520
|
catch (err) {
|
|
363
|
-
spinner.fail(
|
|
521
|
+
spinner.fail("Detection failed");
|
|
364
522
|
const message = err instanceof Error ? err.message : String(err);
|
|
365
523
|
error(message);
|
|
366
524
|
process.exit(1);
|
|
@@ -370,49 +528,50 @@ export function createInitCommand() {
|
|
|
370
528
|
const detectedFrameworks = await detectFrameworks(cwd);
|
|
371
529
|
if (detectedFrameworks.length > 0) {
|
|
372
530
|
// Separate built-in scanners from optional plugins
|
|
373
|
-
const builtIn = detectedFrameworks.filter(fw => fw.scanner);
|
|
374
|
-
const optionalPlugins = detectedFrameworks.filter(fw => fw.plugin && !fw.scanner);
|
|
531
|
+
const builtIn = detectedFrameworks.filter((fw) => fw.scanner);
|
|
532
|
+
const optionalPlugins = detectedFrameworks.filter((fw) => fw.plugin && !fw.scanner);
|
|
375
533
|
// Show built-in scanners (no install needed)
|
|
376
534
|
if (builtIn.length > 0) {
|
|
377
|
-
console.log(chalk.bold(
|
|
378
|
-
|
|
535
|
+
console.log(chalk.bold(" Built-in Scanners") +
|
|
536
|
+
chalk.dim(" (no install needed)"));
|
|
537
|
+
console.log("");
|
|
379
538
|
for (const fw of builtIn) {
|
|
380
539
|
const scannerInfo = BUILTIN_SCANNERS[fw.scanner];
|
|
381
540
|
const scannerLabel = scannerInfo?.description || capitalize(fw.name);
|
|
382
|
-
console.log(` ${chalk.green(
|
|
541
|
+
console.log(` ${chalk.green("✓")} ${chalk.cyan.bold(scannerLabel)}`);
|
|
383
542
|
console.log(` ${chalk.dim(fw.evidence)}`);
|
|
384
|
-
console.log(
|
|
543
|
+
console.log("");
|
|
385
544
|
}
|
|
386
545
|
}
|
|
387
546
|
// Show optional plugins (need install)
|
|
388
547
|
if (optionalPlugins.length > 0) {
|
|
389
|
-
console.log(chalk.bold(
|
|
390
|
-
console.log(
|
|
548
|
+
console.log(chalk.bold(" Optional Plugins"));
|
|
549
|
+
console.log("");
|
|
391
550
|
for (const fw of optionalPlugins) {
|
|
392
551
|
const pluginInfo = PLUGIN_INFO[fw.plugin];
|
|
393
552
|
const pluginName = pluginInfo?.name || `@buoy-design/plugin-${fw.plugin}`;
|
|
394
|
-
console.log(` ${chalk.dim(
|
|
395
|
-
console.log(` ${chalk.dim(
|
|
553
|
+
console.log(` ${chalk.dim("┌")} ${chalk.cyan.bold(pluginName)}`);
|
|
554
|
+
console.log(` ${chalk.dim("│")}`);
|
|
396
555
|
// What was detected
|
|
397
556
|
const detectsLabel = pluginInfo?.detects || capitalize(fw.name);
|
|
398
|
-
console.log(` ${chalk.dim(
|
|
399
|
-
console.log(` ${chalk.dim(
|
|
557
|
+
console.log(` ${chalk.dim("│")} ${chalk.white("Detected:")} ${detectsLabel} ${chalk.dim(`(${fw.evidence.toLowerCase()})`)}`);
|
|
558
|
+
console.log(` ${chalk.dim("│")}`);
|
|
400
559
|
// What the plugin does
|
|
401
560
|
if (pluginInfo?.description) {
|
|
402
|
-
console.log(` ${chalk.dim(
|
|
561
|
+
console.log(` ${chalk.dim("│")} ${chalk.dim(pluginInfo.description)}`);
|
|
403
562
|
}
|
|
404
|
-
console.log(` ${chalk.dim(
|
|
405
|
-
console.log(
|
|
563
|
+
console.log(` ${chalk.dim("└─")} ${chalk.dim(getPluginInstallCommand([fw.plugin]))}`);
|
|
564
|
+
console.log("");
|
|
406
565
|
}
|
|
407
566
|
const missingPlugins = optionalPlugins
|
|
408
567
|
.map((fw) => fw.plugin)
|
|
409
568
|
.filter((plugin, index, self) => self.indexOf(plugin) === index);
|
|
410
569
|
if (missingPlugins.length > 0) {
|
|
411
|
-
console.log(chalk.dim(
|
|
412
|
-
console.log(
|
|
413
|
-
console.log(chalk.bold(
|
|
570
|
+
console.log(chalk.dim(" " + "─".repeat(65)));
|
|
571
|
+
console.log("");
|
|
572
|
+
console.log(chalk.bold(" Install all optional plugins:"));
|
|
414
573
|
console.log(` ${chalk.cyan(getPluginInstallCommand(missingPlugins))}`);
|
|
415
|
-
console.log(
|
|
574
|
+
console.log("");
|
|
416
575
|
// Determine if we should install plugins
|
|
417
576
|
let shouldInstall = false;
|
|
418
577
|
if (options.yes) {
|
|
@@ -421,37 +580,70 @@ export function createInitCommand() {
|
|
|
421
580
|
else if (options.install !== false) {
|
|
422
581
|
// Only prompt if --no-install was not passed and stdin is a TTY
|
|
423
582
|
if (process.stdin.isTTY) {
|
|
424
|
-
shouldInstall = await promptConfirm(
|
|
583
|
+
shouldInstall = await promptConfirm("Install optional plugins now?", true);
|
|
425
584
|
}
|
|
426
585
|
}
|
|
427
586
|
if (shouldInstall) {
|
|
428
|
-
const { execSync } = await import(
|
|
429
|
-
console.log(
|
|
430
|
-
console.log(
|
|
587
|
+
const { execSync } = await import("node:child_process");
|
|
588
|
+
console.log("");
|
|
589
|
+
console.log("Installing plugins...");
|
|
431
590
|
try {
|
|
432
|
-
execSync(getPluginInstallCommand(missingPlugins), {
|
|
433
|
-
|
|
591
|
+
execSync(getPluginInstallCommand(missingPlugins), {
|
|
592
|
+
stdio: "inherit",
|
|
593
|
+
});
|
|
594
|
+
success("Plugins installed successfully");
|
|
434
595
|
}
|
|
435
596
|
catch {
|
|
436
|
-
warning(
|
|
597
|
+
warning("Plugin installation failed. You can install manually with the command above.");
|
|
437
598
|
}
|
|
438
599
|
}
|
|
439
600
|
}
|
|
440
601
|
}
|
|
441
|
-
console.log(
|
|
602
|
+
console.log("");
|
|
442
603
|
}
|
|
443
604
|
// Generate and write config
|
|
444
605
|
const content = generateConfig(project);
|
|
445
606
|
try {
|
|
446
|
-
writeFileSync(configPath, content,
|
|
607
|
+
writeFileSync(configPath, content, "utf-8");
|
|
447
608
|
success(`Created buoy.config.mjs`);
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
609
|
+
// Setup hooks if --hooks flag is provided
|
|
610
|
+
if (options.hooks) {
|
|
611
|
+
console.log("");
|
|
612
|
+
const hookSystem = detectHookSystem(cwd);
|
|
613
|
+
if (hookSystem) {
|
|
614
|
+
info(`Detected hook system: ${hookSystem}`);
|
|
615
|
+
const hookResult = setupHooks(cwd);
|
|
616
|
+
if (hookResult.success) {
|
|
617
|
+
success(hookResult.message);
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
warning(hookResult.message);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
// No hook system detected, create standalone hook
|
|
625
|
+
const standaloneResult = generateStandaloneHook(cwd);
|
|
626
|
+
if (standaloneResult.success) {
|
|
627
|
+
success(standaloneResult.message);
|
|
628
|
+
info("To use this hook, copy it to .git/hooks/pre-commit or configure your hook system");
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
warning(standaloneResult.message);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
console.log("");
|
|
636
|
+
info("Next steps:");
|
|
637
|
+
info(" 1. Run " + chalk.cyan("buoy scan") + " to scan your codebase");
|
|
638
|
+
info(" 2. Run " + chalk.cyan("buoy drift check") + " to detect drift");
|
|
639
|
+
if (!options.hooks) {
|
|
640
|
+
info(" 3. Run " +
|
|
641
|
+
chalk.cyan("buoy init --hooks") +
|
|
642
|
+
" to setup pre-commit hooks");
|
|
643
|
+
}
|
|
452
644
|
if (!project.storybook) {
|
|
453
|
-
console.log(
|
|
454
|
-
info(chalk.dim(
|
|
645
|
+
console.log("");
|
|
646
|
+
info(chalk.dim("Optional: Connect Figma by adding your API key to the config"));
|
|
455
647
|
}
|
|
456
648
|
}
|
|
457
649
|
catch (err) {
|