wlmaker 1.6.0 → 1.8.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.
@@ -0,0 +1,202 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ dartFormat,
4
+ detectDesignSystem,
5
+ tierPlural,
6
+ updateSortedBarrelFile
7
+ } from "./chunk-ZYOPXWRK.mjs";
8
+ import "./chunk-V4S7DQDN.mjs";
9
+ import {
10
+ parseRegions
11
+ } from "./chunk-6JXSEAVX.mjs";
12
+
13
+ // src/flows/compose-flow.ts
14
+ import * as fs2 from "fs";
15
+ import * as path2 from "path";
16
+ import * as clack from "@clack/prompts";
17
+ import chalk from "chalk";
18
+ import { pascalCase } from "change-case";
19
+
20
+ // src/analyzer/component-regions.ts
21
+ import * as fs from "fs";
22
+ import * as path from "path";
23
+ function analyzeComponent(filePath) {
24
+ if (!fs.existsSync(filePath)) return null;
25
+ const content = fs.readFileSync(filePath, "utf-8");
26
+ const { regions, errors } = parseRegions(content);
27
+ if (errors.length > 0) {
28
+ console.warn(`\u26A0 Regions warning in ${filePath}:`);
29
+ for (const err of errors) console.warn(` ${err}`);
30
+ }
31
+ const componentName = path.basename(path.dirname(filePath)) === path.basename(filePath, ".dart") ? path.basename(path.dirname(filePath)) : path.basename(filePath, ".dart");
32
+ return {
33
+ componentName,
34
+ filePath,
35
+ props: regions["Props"] ?? null,
36
+ constructor: regions["Constructor"] ?? null,
37
+ fields: regions["Fields"] ?? regions["Props"] ?? null,
38
+ allRegions: regions
39
+ };
40
+ }
41
+ function listComponents(projectRoot) {
42
+ const ds = detectDesignSystem(projectRoot);
43
+ if (!ds) return [];
44
+ const results = [];
45
+ for (const tierDir of ds.availableTiers) {
46
+ const dir = path.join(ds.componentsDir, tierDir);
47
+ if (!fs.existsSync(dir)) continue;
48
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
49
+ for (const entry of entries) {
50
+ if (entry.isDirectory() && entry.name.startsWith("wl_")) {
51
+ results.push(entry.name);
52
+ }
53
+ }
54
+ }
55
+ return results.sort();
56
+ }
57
+
58
+ // src/flows/compose-flow.ts
59
+ async function composeFlow() {
60
+ const projectRoot = process.cwd();
61
+ const ds = detectDesignSystem(projectRoot);
62
+ if (!ds) {
63
+ clack.outro(chalk.red("No design system detected."));
64
+ return;
65
+ }
66
+ const rawName = await clack.text({
67
+ message: "Parent component name (snake_case, without wl_ prefix)",
68
+ placeholder: "e.g. confirmation_card",
69
+ validate: (v) => {
70
+ if (!v?.trim()) return "Name is required";
71
+ if (!/^[a-z][a-z0-9_]*$/.test(v)) return "Must be snake_case";
72
+ }
73
+ });
74
+ if (clack.isCancel(rawName)) {
75
+ clack.cancel("Cancelled");
76
+ return;
77
+ }
78
+ const name = rawName;
79
+ const pascal = pascalCase(name);
80
+ const fileName = `wl_${name}`;
81
+ const allTiers = ["atom", "molecule", "organism", "template"];
82
+ const tierChoice = await clack.select({
83
+ message: "Select tier",
84
+ options: allTiers.map((t) => ({ value: t, label: t })),
85
+ initialValue: "molecule"
86
+ });
87
+ if (clack.isCancel(tierChoice)) {
88
+ clack.cancel("Cancelled");
89
+ return;
90
+ }
91
+ const tier = tierChoice;
92
+ const availableComponents = listComponents(projectRoot).filter((c) => c !== fileName);
93
+ if (availableComponents.length === 0) {
94
+ clack.outro(chalk.yellow("No components found in the design system."));
95
+ return;
96
+ }
97
+ const selected = await clack.multiselect({
98
+ message: "Select child components to compose from:",
99
+ options: availableComponents.map((c) => ({ value: c, label: c })),
100
+ required: true
101
+ });
102
+ if (clack.isCancel(selected)) {
103
+ clack.cancel("Cancelled");
104
+ return;
105
+ }
106
+ const childNames = selected;
107
+ const allProps = [];
108
+ const allConstructorParams = [];
109
+ const fieldNames = /* @__PURE__ */ new Set();
110
+ function findAnyComponent(name2) {
111
+ for (const t of ["atoms", "molecules", "organisms", "templates"]) {
112
+ const p = path2.join(ds.componentsDir, t, name2, `${name2}.dart`);
113
+ if (fs2.existsSync(p)) return p;
114
+ }
115
+ return null;
116
+ }
117
+ for (const child of childNames) {
118
+ const childFile = findAnyComponent(child);
119
+ if (!childFile) {
120
+ clack.log.warn(`Component "${child}" not found in any tier, skipping.`);
121
+ continue;
122
+ }
123
+ const analysis = analyzeComponent(childFile);
124
+ if (!analysis) continue;
125
+ if (analysis.props || analysis.fields) {
126
+ const region = analysis.props ?? analysis.fields;
127
+ const lines = region.content.split("\n");
128
+ for (const line of lines) {
129
+ const trimmed = line.trim();
130
+ if (!trimmed || trimmed.startsWith("//")) continue;
131
+ const fieldMatch = trimmed.match(/final\s+\S+\s+(\w+)/);
132
+ if (fieldMatch && !fieldNames.has(fieldMatch[1])) {
133
+ fieldNames.add(fieldMatch[1]);
134
+ allProps.push(` ${trimmed}`);
135
+ }
136
+ }
137
+ }
138
+ if (analysis.constructor) {
139
+ const lines = analysis.constructor.content.split("\n");
140
+ for (const line of lines) {
141
+ const trimmed = line.trim();
142
+ if (!trimmed || trimmed === "const WlXxx({" || trimmed === "});" || trimmed.startsWith("//")) continue;
143
+ const paramMatch = trimmed.match(/this\.(\w+)/);
144
+ if (paramMatch && fieldNames.has(paramMatch[1])) {
145
+ allConstructorParams.push(` ${trimmed.replace(/,\s*$/, "")},`);
146
+ }
147
+ }
148
+ }
149
+ }
150
+ if (allProps.length === 0) {
151
+ clack.outro(chalk.yellow("No props found in selected components. Make sure they have //#region Props markers."));
152
+ return;
153
+ }
154
+ const plural = tierPlural(tier);
155
+ const tierDir = path2.join(ds.componentsDir, plural);
156
+ const outDir = path2.join(tierDir, fileName);
157
+ if (!fs2.existsSync(outDir)) fs2.mkdirSync(outDir, { recursive: true });
158
+ const targetFile = path2.join(outDir, `${fileName}.dart`);
159
+ if (fs2.existsSync(targetFile)) {
160
+ const overwrite = await clack.confirm({
161
+ message: `${fileName} already exists. Overwrite?`,
162
+ initialValue: false
163
+ });
164
+ if (!overwrite || clack.isCancel(overwrite)) {
165
+ clack.cancel("Cancelled");
166
+ return;
167
+ }
168
+ }
169
+ const componentName = `Wl${pascal}`;
170
+ const code = `import 'package:design_system/design_system.dart';
171
+ import 'package:flutter/material.dart';
172
+
173
+ class ${componentName} extends StatelessWidget {
174
+ //#region Constructor
175
+ const ${componentName}({
176
+ super.key,
177
+ ${allConstructorParams.join("\n")}
178
+ });
179
+ //#endregion
180
+
181
+ //#region Props
182
+ ${allProps.join("\n")}
183
+ //#endregion
184
+
185
+ @override
186
+ Widget build(BuildContext context) {
187
+ final theme = context.theme;
188
+ // TODO: compose children from ${childNames.join(", ")}
189
+ return const SizedBox.shrink();
190
+ }
191
+ }
192
+ `;
193
+ fs2.writeFileSync(targetFile, code);
194
+ dartFormat(targetFile);
195
+ const barrelFileName = `${plural}.dart`;
196
+ const exportLine = `export '${fileName}/${fileName}.dart';`;
197
+ updateSortedBarrelFile(tierDir, barrelFileName, exportLine);
198
+ clack.outro(chalk.green(`${componentName} created in ${plural}/${fileName}/ with ${allProps.length} combined props from ${childNames.length} children.`));
199
+ }
200
+ export {
201
+ composeFlow
202
+ };
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ createCollaborativeBloc
4
+ } from "./chunk-554WKK3Z.mjs";
5
+ import "./chunk-7KUDXB2F.mjs";
6
+ import "./chunk-ELYQ3PL5.mjs";
7
+ export {
8
+ createCollaborativeBloc
9
+ };
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ createCollaborativeFeature
4
+ } from "./chunk-4AOS33ZX.mjs";
5
+ import "./chunk-ELYQ3PL5.mjs";
6
+ import "./chunk-V4S7DQDN.mjs";
7
+ export {
8
+ createCollaborativeFeature
9
+ };
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ createCollaborativePage
4
+ } from "./chunk-P7W5YVTZ.mjs";
5
+ import "./chunk-7KUDXB2F.mjs";
6
+ import "./chunk-ELYQ3PL5.mjs";
7
+ export {
8
+ createCollaborativePage
9
+ };
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ analyzeProject,
4
+ discoverCollaborativeFeatures,
5
+ discoverPackages,
6
+ discoverProjects,
7
+ findMonorepoRoot,
8
+ isMonorepoRoot,
9
+ resolveWorkspaceVersions
10
+ } from "./chunk-V4S7DQDN.mjs";
11
+ export {
12
+ analyzeProject,
13
+ discoverCollaborativeFeatures,
14
+ discoverPackages,
15
+ discoverProjects,
16
+ findMonorepoRoot,
17
+ isMonorepoRoot,
18
+ resolveWorkspaceVersions
19
+ };
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ parseRegions
4
+ } from "./chunk-6JXSEAVX.mjs";
5
+
6
+ // src/commands/validate-regions.ts
7
+ import * as fs from "fs";
8
+ import * as path from "path";
9
+ import chalk from "chalk";
10
+ function validateAllRegions(dir) {
11
+ const results = [];
12
+ function walk(currentDir) {
13
+ if (!fs.existsSync(currentDir)) return;
14
+ const entries = fs.readdirSync(currentDir, { withFileTypes: true });
15
+ for (const entry of entries) {
16
+ const fullPath = path.join(currentDir, entry.name);
17
+ if (entry.isDirectory()) {
18
+ walk(fullPath);
19
+ } else if (entry.name.endsWith(".dart") && !entry.name.endsWith(".g.dart")) {
20
+ const content = fs.readFileSync(fullPath, "utf-8");
21
+ const { errors } = parseRegions(content);
22
+ if (errors.length > 0) {
23
+ results.push({ file: fullPath, errors });
24
+ }
25
+ }
26
+ }
27
+ }
28
+ walk(dir);
29
+ return results;
30
+ }
31
+ function displayRegionValidation(results) {
32
+ if (results.length === 0) {
33
+ console.log(chalk.green("\u2713 Todas las regions est\xE1n balanceadas."));
34
+ return;
35
+ }
36
+ console.log(chalk.red(`\u2717 ${results.length} archivo(s) con errores:
37
+ `));
38
+ for (const { file, errors } of results) {
39
+ console.log(chalk.cyan(file));
40
+ for (const error of errors) {
41
+ console.log(chalk.yellow(` ${error}`));
42
+ }
43
+ console.log();
44
+ }
45
+ }
46
+ export {
47
+ displayRegionValidation,
48
+ validateAllRegions
49
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wlmaker",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "Create Flutter BLoCs with Freezed sealed classes from the terminal",
5
5
  "keywords": [
6
6
  "flutter",
@@ -24,16 +24,16 @@
24
24
  ],
25
25
  "scripts": {
26
26
  "build": "tsup",
27
- "dev": "tsup --watch",
28
- "postinstall": "node ./dist/postinstall.mjs"
27
+ "dev": "tsup --watch"
29
28
  },
30
29
  "dependencies": {
31
30
  "@clack/prompts": "^1.2.0",
32
- "@modelcontextprotocol/sdk": "^1.0.4",
33
31
  "chalk": "^5.3.0",
34
32
  "change-case": "^5.4.0",
35
33
  "commander": "^12.0.0",
36
- "yaml": "^2.8.3"
34
+ "wlmaker": "link:",
35
+ "yaml": "^2.8.3",
36
+ "zod": "^4.4.3"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^22.0.0",
@@ -1,81 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- // src/mcp-install.ts
4
- import * as fs from "fs";
5
- import * as path from "path";
6
- import * as os from "os";
7
- import { fileURLToPath } from "url";
8
- async function installMcpServer() {
9
- const homeDir = os.homedir();
10
- const isMac = process.platform === "darwin";
11
- const isWin = process.platform === "win32";
12
- const __filename = fileURLToPath(import.meta.url);
13
- const __dirname = path.dirname(__filename);
14
- const cliPath = path.resolve(__dirname, "cli.mjs");
15
- const wlmakerConfig = {
16
- command: process.execPath,
17
- args: [cliPath, "mcp"]
18
- };
19
- const configs = [
20
- {
21
- name: "Claude Desktop",
22
- paths: isMac ? [path.join(homeDir, "Library/Application Support/Claude/claude_desktop_config.json")] : isWin ? [path.join(homeDir, "AppData/Roaming/Claude/claude_desktop_config.json")] : []
23
- },
24
- {
25
- name: "Claude Code",
26
- paths: [path.join(homeDir, ".claude.json")]
27
- },
28
- {
29
- name: "Gemini CLI",
30
- paths: [path.join(homeDir, ".gemini/settings.json")]
31
- },
32
- {
33
- name: "Cursor",
34
- paths: [path.join(homeDir, ".cursor/mcp.json")]
35
- },
36
- {
37
- name: "Cline / RooCode (VSCode)",
38
- paths: isMac ? [
39
- path.join(homeDir, "Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json"),
40
- path.join(homeDir, "Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json")
41
- ] : isWin ? [
42
- path.join(homeDir, "AppData/Roaming/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json"),
43
- path.join(homeDir, "AppData/Roaming/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json")
44
- ] : [
45
- path.join(homeDir, ".config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json"),
46
- path.join(homeDir, ".config/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json")
47
- ]
48
- }
49
- ];
50
- let anyInstalled = false;
51
- for (const client of configs) {
52
- for (const configPath of client.paths) {
53
- if (fs.existsSync(configPath)) {
54
- try {
55
- const content = fs.readFileSync(configPath, "utf8");
56
- const json = JSON.parse(content);
57
- json.mcpServers = json.mcpServers || {};
58
- json.mcpServers["wlmaker-cli"] = wlmakerConfig;
59
- fs.writeFileSync(configPath, JSON.stringify(json, null, 2));
60
- console.log(`\u2705 Successfully added wlmaker-cli to ${client.name} configuration.`);
61
- anyInstalled = true;
62
- } catch (error) {
63
- console.error(`\u274C Failed to update ${client.name} configuration at ${configPath}:`, error);
64
- }
65
- }
66
- }
67
- }
68
- if (!anyInstalled) {
69
- console.log("\u26A0\uFE0F No supported MCP clients (Claude Desktop, Cursor, Cline) found to auto-configure.");
70
- console.log("You can manually add the following configuration to your client:");
71
- console.log(JSON.stringify({
72
- mcpServers: {
73
- "wlmaker-cli": wlmakerConfig
74
- }
75
- }, null, 2));
76
- }
77
- }
78
-
79
- export {
80
- installMcpServer
81
- };
@@ -1,11 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- installMcpServer
4
- } from "./chunk-OXBECDQE.mjs";
5
-
6
- // src/postinstall.ts
7
- async function main() {
8
- console.log("Registering wlmaker-cli as an MCP server...");
9
- await installMcpServer();
10
- }
11
- main().catch(console.error);