farmon 0.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/LICENSE +21 -0
- package/README.md +163 -0
- package/dist/bin/farmon.js +12 -0
- package/dist/bin/farmon.js.map +1 -0
- package/dist/execute/agents/index.js +19 -0
- package/dist/execute/agents/index.js.map +1 -0
- package/dist/execute/agents/instruction-classifier-agent.js +16 -0
- package/dist/execute/agents/instruction-classifier-agent.js.map +1 -0
- package/dist/execute/agents/mutation-agent.js +272 -0
- package/dist/execute/agents/mutation-agent.js.map +1 -0
- package/dist/execute/agents/query-agent.js +118 -0
- package/dist/execute/agents/query-agent.js.map +1 -0
- package/dist/execute/helpers/analyzers.js +8 -0
- package/dist/execute/helpers/analyzers.js.map +1 -0
- package/dist/execute/helpers/ensurers.js +1053 -0
- package/dist/execute/helpers/ensurers.js.map +1 -0
- package/dist/execute/helpers/finders.js +1454 -0
- package/dist/execute/helpers/finders.js.map +1 -0
- package/dist/execute/helpers/general.js +3736 -0
- package/dist/execute/helpers/general.js.map +1 -0
- package/dist/execute/helpers/import-helpers.js +183 -0
- package/dist/execute/helpers/import-helpers.js.map +1 -0
- package/dist/execute/helpers/parsers.js +840 -0
- package/dist/execute/helpers/parsers.js.map +1 -0
- package/dist/execute/helpers/prompt-maker.js +1163 -0
- package/dist/execute/helpers/prompt-maker.js.map +1 -0
- package/dist/execute/helpers/validators.js +40 -0
- package/dist/execute/helpers/validators.js.map +1 -0
- package/dist/execute/history/history-manager.js +1030 -0
- package/dist/execute/history/history-manager.js.map +1 -0
- package/dist/execute/history/rollback-handlers.js +2524 -0
- package/dist/execute/history/rollback-handlers.js.map +1 -0
- package/dist/execute/index.js +44 -0
- package/dist/execute/index.js.map +1 -0
- package/dist/execute/llm/call.js +103 -0
- package/dist/execute/llm/call.js.map +1 -0
- package/dist/execute/tasks/ast.js +3819 -0
- package/dist/execute/tasks/ast.js.map +1 -0
- package/dist/execute/tasks/generators.js +96 -0
- package/dist/execute/tasks/generators.js.map +1 -0
- package/dist/execute/tasks/index.js +7 -0
- package/dist/execute/tasks/index.js.map +1 -0
- package/dist/execute/tasks/mutations.js +8139 -0
- package/dist/execute/tasks/mutations.js.map +1 -0
- package/dist/execute/tasks/query.js +248 -0
- package/dist/execute/tasks/query.js.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/index.js +15 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/ollama.js +40 -0
- package/dist/providers/ollama.js.map +1 -0
- package/dist/providers/openai-compatible.js +52 -0
- package/dist/providers/openai-compatible.js.map +1 -0
- package/dist/runtime/inject.js +250 -0
- package/dist/runtime/inject.js.map +1 -0
- package/dist/schemas/agent/action.schema.js +935 -0
- package/dist/schemas/agent/action.schema.js.map +1 -0
- package/dist/schemas/agent/index.js +4 -0
- package/dist/schemas/agent/index.js.map +1 -0
- package/dist/schemas/agent/llm.schema.js +16 -0
- package/dist/schemas/agent/llm.schema.js.map +1 -0
- package/dist/schemas/agent/planner.schema.js +17 -0
- package/dist/schemas/agent/planner.schema.js.map +1 -0
- package/dist/schemas/index.js +7 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/project/context.schema.js +2 -0
- package/dist/schemas/project/context.schema.js.map +1 -0
- package/dist/schemas/project/index.js +2 -0
- package/dist/schemas/project/index.js.map +1 -0
- package/dist/schemas/runtime/index.js +4 -0
- package/dist/schemas/runtime/index.js.map +1 -0
- package/dist/schemas/runtime/injector.schema.js +11 -0
- package/dist/schemas/runtime/injector.schema.js.map +1 -0
- package/dist/schemas/runtime/runtime.schema.js +73 -0
- package/dist/schemas/runtime/runtime.schema.js.map +1 -0
- package/dist/schemas/runtime/sse.schema.js +15 -0
- package/dist/schemas/runtime/sse.schema.js.map +1 -0
- package/dist/schemas/system/index.js +2 -0
- package/dist/schemas/system/index.js.map +1 -0
- package/dist/schemas/system/logger.schema.js +56 -0
- package/dist/schemas/system/logger.schema.js.map +1 -0
- package/dist/schemas/task/index.js +9 -0
- package/dist/schemas/task/index.js.map +1 -0
- package/dist/server/app-context.js +254 -0
- package/dist/server/app-context.js.map +1 -0
- package/dist/server/config.js +22 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/error.js +22 -0
- package/dist/server/error.js.map +1 -0
- package/dist/server/event-bus.js +60 -0
- package/dist/server/event-bus.js.map +1 -0
- package/dist/server/logger.js +57 -0
- package/dist/server/logger.js.map +1 -0
- package/dist/server/run.js +265 -0
- package/dist/server/run.js.map +1 -0
- package/dist/server/sse.js +143 -0
- package/dist/server/sse.js.map +1 -0
- package/dist/ui/assets/index-C4ydQSAw.css +2 -0
- package/dist/ui/assets/index-Dzo7S5xs.js +85 -0
- package/dist/ui/favicon.svg +1 -0
- package/dist/ui/icons.svg +24 -0
- package/dist/ui/index.html +14 -0
- package/dist/workers/prettier.js +11 -0
- package/dist/workers/prettier.js.map +1 -0
- package/package.json +114 -0
|
@@ -0,0 +1,1053 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import healpers from "../../execute/helpers/general.js";
|
|
4
|
+
import { ERROR_CODES } from "../../schemas/index.js";
|
|
5
|
+
import finders from "./finders.js";
|
|
6
|
+
import { LoomaError } from "../../server/error.js";
|
|
7
|
+
/**
|
|
8
|
+
* Validator: Ensures that a library exists inside package.json.
|
|
9
|
+
*
|
|
10
|
+
* ------------------------------------------------------------
|
|
11
|
+
* WHAT THIS FUNCTION DOES
|
|
12
|
+
* ------------------------------------------------------------
|
|
13
|
+
*
|
|
14
|
+
* This function guarantees that a dependency exists in either:
|
|
15
|
+
*
|
|
16
|
+
* - dependencies
|
|
17
|
+
* OR
|
|
18
|
+
* - devDependencies
|
|
19
|
+
*
|
|
20
|
+
* If the library already exists:
|
|
21
|
+
* - nothing changes
|
|
22
|
+
*
|
|
23
|
+
* If the library does not exist:
|
|
24
|
+
* - it gets added
|
|
25
|
+
* - but you have to do npm install explecitily
|
|
26
|
+
*
|
|
27
|
+
* ------------------------------------------------------------
|
|
28
|
+
* WHY "ENSURE" IS BETTER THAN "INCLUDE"
|
|
29
|
+
* ------------------------------------------------------------
|
|
30
|
+
*
|
|
31
|
+
* "includeLibrary" suggests:
|
|
32
|
+
* - blindly insert
|
|
33
|
+
*
|
|
34
|
+
* "ensureLibrary" means:
|
|
35
|
+
* - verify existence first
|
|
36
|
+
* - avoid duplicates
|
|
37
|
+
* - maintain deterministic state
|
|
38
|
+
*
|
|
39
|
+
* This is the same idea as:
|
|
40
|
+
* - ensureImport
|
|
41
|
+
* - ensureDirectory
|
|
42
|
+
* - ensureVariable
|
|
43
|
+
*
|
|
44
|
+
* ------------------------------------------------------------
|
|
45
|
+
* Useful commands
|
|
46
|
+
* ------------------------------------------------------------
|
|
47
|
+
* add routing: ensureLibrary("react-router-dom")
|
|
48
|
+
* add tailwind: ensureLibrary("tailwindcss")
|
|
49
|
+
* add redux: ensureLibrary("@reduxjs/toolkit")
|
|
50
|
+
* add charts: ensureLibrary("recharts")
|
|
51
|
+
* add icons: ensureLibrary("lucide-react")
|
|
52
|
+
* add form validation: ensureLibrary("zod")
|
|
53
|
+
* add animations: ensureLibrary("framer-motion")
|
|
54
|
+
*
|
|
55
|
+
*
|
|
56
|
+
* ------------------------------------------------------------
|
|
57
|
+
* IMPORTANT NOTE
|
|
58
|
+
* ------------------------------------------------------------
|
|
59
|
+
*
|
|
60
|
+
* This function ONLY updates package.json.
|
|
61
|
+
*
|
|
62
|
+
* It DOES NOT:
|
|
63
|
+
* - run npm install
|
|
64
|
+
* - run yarn install
|
|
65
|
+
* - download packages
|
|
66
|
+
*
|
|
67
|
+
* Installation should be a separate responsibility.
|
|
68
|
+
*
|
|
69
|
+
* ------------------------------------------------------------
|
|
70
|
+
* EXAMPLE
|
|
71
|
+
* ------------------------------------------------------------
|
|
72
|
+
*
|
|
73
|
+
* ensureLibrary({
|
|
74
|
+
* projectPath: "/my-app",
|
|
75
|
+
* libraryName: "react-router-dom",
|
|
76
|
+
* version: "^7.0.0"
|
|
77
|
+
* });
|
|
78
|
+
*
|
|
79
|
+
* ------------------------------------------------------------
|
|
80
|
+
* PARAMS
|
|
81
|
+
* ------------------------------------------------------------
|
|
82
|
+
*
|
|
83
|
+
* @param {Object} params
|
|
84
|
+
*
|
|
85
|
+
* @param {string} params.projectPath
|
|
86
|
+
* Absolute or relative path of the project root.
|
|
87
|
+
*
|
|
88
|
+
* @param {string} params.libraryName
|
|
89
|
+
* Name of the npm package.
|
|
90
|
+
*
|
|
91
|
+
* @param {string} params.version
|
|
92
|
+
* Version to store in package.json.
|
|
93
|
+
*
|
|
94
|
+
* @param {"dependencies"|"devDependencies"} [params.dependencyType]
|
|
95
|
+
* Which dependency section should contain the package.
|
|
96
|
+
*
|
|
97
|
+
* defaults to:
|
|
98
|
+
* "dependencies"
|
|
99
|
+
*
|
|
100
|
+
* ------------------------------------------------------------
|
|
101
|
+
* RETURNS
|
|
102
|
+
* ------------------------------------------------------------
|
|
103
|
+
*
|
|
104
|
+
* @returns {{
|
|
105
|
+
* modified: boolean,
|
|
106
|
+
* packageJson: Object
|
|
107
|
+
* }}
|
|
108
|
+
*
|
|
109
|
+
* modified:
|
|
110
|
+
* true -> package.json changed
|
|
111
|
+
* false -> package already existed
|
|
112
|
+
*
|
|
113
|
+
*/
|
|
114
|
+
function ensureLibrary({ libraryName, version = "v1.0.0" }, context) {
|
|
115
|
+
if (!Object.prototype.hasOwnProperty.call(healpers.getProjectDependencies({
|
|
116
|
+
projectRoot: context.project.root,
|
|
117
|
+
}).allPackages, libraryName)) {
|
|
118
|
+
const message = `Package ${libraryName} in not installed. Please install it`;
|
|
119
|
+
// informUser({
|
|
120
|
+
// message,
|
|
121
|
+
// });
|
|
122
|
+
return {
|
|
123
|
+
success: false,
|
|
124
|
+
message,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
success: true,
|
|
129
|
+
};
|
|
130
|
+
// ----------------------------------------------------------
|
|
131
|
+
// STEP 1:
|
|
132
|
+
// Build absolute path to package.json
|
|
133
|
+
// ----------------------------------------------------------
|
|
134
|
+
// const packageJsonPath = path.join(projectPath, "package.json");
|
|
135
|
+
// // ----------------------------------------------------------
|
|
136
|
+
// // STEP 2:
|
|
137
|
+
// // Verify package.json exists
|
|
138
|
+
// // ----------------------------------------------------------
|
|
139
|
+
// if (!fs.existsSync(packageJsonPath)) {
|
|
140
|
+
// throw new Error(`package.json not found at: ${packageJsonPath}`);
|
|
141
|
+
// }
|
|
142
|
+
// // ----------------------------------------------------------
|
|
143
|
+
// // STEP 3:
|
|
144
|
+
// // Read package.json file as text
|
|
145
|
+
// // ----------------------------------------------------------
|
|
146
|
+
// const packageJsonContent = fs.readFileSync(packageJsonPath, "utf-8");
|
|
147
|
+
// // ----------------------------------------------------------
|
|
148
|
+
// // STEP 4:
|
|
149
|
+
// // Parse JSON string into JS object
|
|
150
|
+
// // ----------------------------------------------------------
|
|
151
|
+
// const packageJson = JSON.parse(packageJsonContent);
|
|
152
|
+
// // ----------------------------------------------------------
|
|
153
|
+
// // STEP 5:
|
|
154
|
+
// // Ensure dependency section exists
|
|
155
|
+
// //
|
|
156
|
+
// // Example:
|
|
157
|
+
// //
|
|
158
|
+
// // {
|
|
159
|
+
// // dependencies: {}
|
|
160
|
+
// // }
|
|
161
|
+
// //
|
|
162
|
+
// // If missing, create empty object
|
|
163
|
+
// // ----------------------------------------------------------
|
|
164
|
+
// if (!packageJson[dependencyType]) {
|
|
165
|
+
// packageJson[dependencyType] = {};
|
|
166
|
+
// }
|
|
167
|
+
// // ----------------------------------------------------------
|
|
168
|
+
// // STEP 6:
|
|
169
|
+
// // Check if library already exists
|
|
170
|
+
// // ----------------------------------------------------------
|
|
171
|
+
// const alreadyExists = packageJson[dependencyType][libraryName];
|
|
172
|
+
// // ----------------------------------------------------------
|
|
173
|
+
// // STEP 7:
|
|
174
|
+
// // If already exists -> return unchanged
|
|
175
|
+
// // ----------------------------------------------------------
|
|
176
|
+
// if (alreadyExists) {
|
|
177
|
+
// return {
|
|
178
|
+
// success: false,
|
|
179
|
+
// packageJson,
|
|
180
|
+
// };
|
|
181
|
+
// }
|
|
182
|
+
// // ----------------------------------------------------------
|
|
183
|
+
// // STEP 8:
|
|
184
|
+
// // Add library with version
|
|
185
|
+
// //
|
|
186
|
+
// // Example:
|
|
187
|
+
// //
|
|
188
|
+
// // "react-router-dom": "^7.0.0"
|
|
189
|
+
// // ----------------------------------------------------------
|
|
190
|
+
// packageJson[dependencyType][libraryName] = version;
|
|
191
|
+
// // ----------------------------------------------------------
|
|
192
|
+
// // STEP 9:
|
|
193
|
+
// // Sort dependencies alphabetically
|
|
194
|
+
// //
|
|
195
|
+
// // WHY?
|
|
196
|
+
// //
|
|
197
|
+
// // Deterministic ordering:
|
|
198
|
+
// // - cleaner git diffs
|
|
199
|
+
// // - stable formatting
|
|
200
|
+
// // - easier debugging
|
|
201
|
+
// // ----------------------------------------------------------
|
|
202
|
+
// packageJson[dependencyType] = Object.fromEntries(
|
|
203
|
+
// Object.entries(packageJson[dependencyType]).sort(([a], [b]) =>
|
|
204
|
+
// a.localeCompare(b)
|
|
205
|
+
// )
|
|
206
|
+
// );
|
|
207
|
+
// // ----------------------------------------------------------
|
|
208
|
+
// // STEP 10:
|
|
209
|
+
// // Convert object back into formatted JSON string
|
|
210
|
+
// //
|
|
211
|
+
// // JSON.stringify arguments:
|
|
212
|
+
// //
|
|
213
|
+
// // null -> no replacer
|
|
214
|
+
// // 2 -> 2-space indentation
|
|
215
|
+
// // ----------------------------------------------------------
|
|
216
|
+
// const updatedContent = JSON.stringify(packageJson, null, 2);
|
|
217
|
+
// // ----------------------------------------------------------
|
|
218
|
+
// // STEP 11:
|
|
219
|
+
// // Write updated package.json back to disk
|
|
220
|
+
// // ----------------------------------------------------------
|
|
221
|
+
// fs.writeFileSync(packageJsonPath, `${updatedContent}\n`, "utf-8");
|
|
222
|
+
// // ----------------------------------------------------------
|
|
223
|
+
// // STEP 12:
|
|
224
|
+
// // Return success metadata
|
|
225
|
+
// // ----------------------------------------------------------
|
|
226
|
+
// return {
|
|
227
|
+
// success: true,
|
|
228
|
+
// packageJson,
|
|
229
|
+
// };
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Validator: Ensures that a component style file exists.
|
|
233
|
+
*
|
|
234
|
+
* ------------------------------------------------------------
|
|
235
|
+
* WHAT THIS FUNCTION DOES
|
|
236
|
+
* ------------------------------------------------------------
|
|
237
|
+
*
|
|
238
|
+
* This function guarantees that:
|
|
239
|
+
*
|
|
240
|
+
* Component.css
|
|
241
|
+
*
|
|
242
|
+
* exists for a given component.
|
|
243
|
+
*
|
|
244
|
+
* If css file does not exist:
|
|
245
|
+
*
|
|
246
|
+
* - creates the css file
|
|
247
|
+
* - optionally inserts initial styles
|
|
248
|
+
*
|
|
249
|
+
* If css file already exists:
|
|
250
|
+
*
|
|
251
|
+
* - does nothing
|
|
252
|
+
*
|
|
253
|
+
* ------------------------------------------------------------
|
|
254
|
+
* WHY THIS FUNCTION EXISTS
|
|
255
|
+
* ------------------------------------------------------------
|
|
256
|
+
*
|
|
257
|
+
* Many Looma operations depend on css file existing.
|
|
258
|
+
*
|
|
259
|
+
* Example:
|
|
260
|
+
*
|
|
261
|
+
* - "make header red"
|
|
262
|
+
* - "increase spacing"
|
|
263
|
+
* - "add hover effect"
|
|
264
|
+
* - "make card responsive"
|
|
265
|
+
*
|
|
266
|
+
* Before inserting styles,
|
|
267
|
+
* we must ensure style file exists.
|
|
268
|
+
*
|
|
269
|
+
* Useful in commands like:
|
|
270
|
+
|
|
271
|
+
make header red
|
|
272
|
+
add hover effect
|
|
273
|
+
increase spacing
|
|
274
|
+
make card responsive
|
|
275
|
+
add styles to navbar
|
|
276
|
+
create component
|
|
277
|
+
extract component
|
|
278
|
+
|
|
279
|
+
It is usually called before:
|
|
280
|
+
|
|
281
|
+
insertStyles
|
|
282
|
+
updateStyles
|
|
283
|
+
removeStyles
|
|
284
|
+
createComponent
|
|
285
|
+
* ------------------------------------------------------------
|
|
286
|
+
* IMPORTANT ARCHITECTURAL IDEA
|
|
287
|
+
* ------------------------------------------------------------
|
|
288
|
+
*
|
|
289
|
+
* Looma treats every component as:
|
|
290
|
+
*
|
|
291
|
+
* Component Unit
|
|
292
|
+
*
|
|
293
|
+
* Example:
|
|
294
|
+
*
|
|
295
|
+
* Header/
|
|
296
|
+
* Header.jsx
|
|
297
|
+
* Header.css
|
|
298
|
+
* index.js
|
|
299
|
+
*
|
|
300
|
+
* This function enforces that convention.
|
|
301
|
+
*
|
|
302
|
+
* ------------------------------------------------------------
|
|
303
|
+
* PARAMS
|
|
304
|
+
* ------------------------------------------------------------
|
|
305
|
+
*
|
|
306
|
+
* @param {Object} params
|
|
307
|
+
*
|
|
308
|
+
* @param {string} params.componentPath
|
|
309
|
+
* Absolute or relative component folder path.
|
|
310
|
+
*
|
|
311
|
+
* Example:
|
|
312
|
+
*
|
|
313
|
+
* "./src/components/Header"
|
|
314
|
+
*
|
|
315
|
+
* @param {string} params.componentName
|
|
316
|
+
* Component name.
|
|
317
|
+
*
|
|
318
|
+
* Example:
|
|
319
|
+
*
|
|
320
|
+
* "Header"
|
|
321
|
+
*
|
|
322
|
+
* @param {string} [params.initialStyles]
|
|
323
|
+
* Optional initial css content.
|
|
324
|
+
*
|
|
325
|
+
* Example:
|
|
326
|
+
*
|
|
327
|
+
* ".header {}"
|
|
328
|
+
*
|
|
329
|
+
* ------------------------------------------------------------
|
|
330
|
+
* RETURNS
|
|
331
|
+
* ------------------------------------------------------------
|
|
332
|
+
*
|
|
333
|
+
* @returns {Object}
|
|
334
|
+
*
|
|
335
|
+
* {
|
|
336
|
+
* created: boolean,
|
|
337
|
+
* cssPath: string
|
|
338
|
+
* }
|
|
339
|
+
*
|
|
340
|
+
*/
|
|
341
|
+
function ensureStyleFile({ componentName }, context) {
|
|
342
|
+
// ----------------------------------------------------------
|
|
343
|
+
// STEP 1:
|
|
344
|
+
// Resolve absolute component directory path
|
|
345
|
+
// ----------------------------------------------------------
|
|
346
|
+
const { foundComponent } = finders.findComponentDirectory({
|
|
347
|
+
componentName,
|
|
348
|
+
}, context.appContext);
|
|
349
|
+
const absoluteComponentPath = foundComponent.componentPath;
|
|
350
|
+
// ----------------------------------------------------------
|
|
351
|
+
// STEP 2:
|
|
352
|
+
// Build css file path
|
|
353
|
+
//
|
|
354
|
+
// Example:
|
|
355
|
+
//
|
|
356
|
+
// Header/Header.css
|
|
357
|
+
// ----------------------------------------------------------
|
|
358
|
+
const cssPath = path.join(absoluteComponentPath, `${componentName}.css`);
|
|
359
|
+
// ----------------------------------------------------------
|
|
360
|
+
// STEP 3:
|
|
361
|
+
// Check if css file already exists
|
|
362
|
+
// ----------------------------------------------------------
|
|
363
|
+
const cssFileExists = fs.existsSync(cssPath);
|
|
364
|
+
// ----------------------------------------------------------
|
|
365
|
+
// STEP 4:
|
|
366
|
+
// If css file already exists,
|
|
367
|
+
// return early
|
|
368
|
+
// ----------------------------------------------------------
|
|
369
|
+
if (!cssFileExists) {
|
|
370
|
+
return {
|
|
371
|
+
success: false,
|
|
372
|
+
cssPath,
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
success: true,
|
|
377
|
+
cssPath,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Validator: Normalizes a component into Looma's
|
|
382
|
+
* canonical architecture format.
|
|
383
|
+
*
|
|
384
|
+
* ------------------------------------------------------------
|
|
385
|
+
* WHAT THIS FUNCTION DOES
|
|
386
|
+
* ------------------------------------------------------------
|
|
387
|
+
*
|
|
388
|
+
* This function repairs and standardizes:
|
|
389
|
+
*
|
|
390
|
+
* - component structure
|
|
391
|
+
* - imports
|
|
392
|
+
* - css imports
|
|
393
|
+
* - component naming
|
|
394
|
+
* - export structure
|
|
395
|
+
* - directory consistency
|
|
396
|
+
* - class naming
|
|
397
|
+
* - formatting cleanup
|
|
398
|
+
*
|
|
399
|
+
* into a predictable architecture.
|
|
400
|
+
*
|
|
401
|
+
* Example:
|
|
402
|
+
*
|
|
403
|
+
* BEFORE:
|
|
404
|
+
*
|
|
405
|
+
* components/
|
|
406
|
+
* header.js
|
|
407
|
+
*
|
|
408
|
+
* AFTER:
|
|
409
|
+
*
|
|
410
|
+
* Header/
|
|
411
|
+
* Header.jsx
|
|
412
|
+
* Header.css
|
|
413
|
+
* index.js
|
|
414
|
+
*
|
|
415
|
+
* ------------------------------------------------------------
|
|
416
|
+
* WHY THIS FUNCTION EXISTS
|
|
417
|
+
* ------------------------------------------------------------
|
|
418
|
+
*
|
|
419
|
+
* AI-generated code drifts rapidly.
|
|
420
|
+
*
|
|
421
|
+
* Over time projects become:
|
|
422
|
+
*
|
|
423
|
+
* - inconsistent
|
|
424
|
+
* - structurally chaotic
|
|
425
|
+
* - impossible to reason about
|
|
426
|
+
* - difficult to edit automatically
|
|
427
|
+
*
|
|
428
|
+
* Runtime AI editing REQUIRES:
|
|
429
|
+
*
|
|
430
|
+
* deterministic structure.
|
|
431
|
+
*
|
|
432
|
+
* This function acts as:
|
|
433
|
+
*
|
|
434
|
+
* architecture stabilization layer.
|
|
435
|
+
*
|
|
436
|
+
* ------------------------------------------------------------
|
|
437
|
+
* IMPORTANT ARCHITECTURAL DECISION
|
|
438
|
+
* ------------------------------------------------------------
|
|
439
|
+
*
|
|
440
|
+
* Looma should NEVER allow:
|
|
441
|
+
*
|
|
442
|
+
* arbitrary component structures.
|
|
443
|
+
*
|
|
444
|
+
* Every component should eventually
|
|
445
|
+
* converge toward:
|
|
446
|
+
*
|
|
447
|
+
* ONE predictable structure.
|
|
448
|
+
*
|
|
449
|
+
* because:
|
|
450
|
+
*
|
|
451
|
+
* predictable architecture enables:
|
|
452
|
+
*
|
|
453
|
+
* - safe runtime editing
|
|
454
|
+
* - reliable AST transforms
|
|
455
|
+
* - undo/redo
|
|
456
|
+
* - DOM synchronization
|
|
457
|
+
* - registry synchronization
|
|
458
|
+
* - scalable AI modifications
|
|
459
|
+
*
|
|
460
|
+
* ------------------------------------------------------------
|
|
461
|
+
* IMPORTANT NOTE
|
|
462
|
+
* ------------------------------------------------------------
|
|
463
|
+
*
|
|
464
|
+
* Current implementation focuses on:
|
|
465
|
+
*
|
|
466
|
+
* filesystem normalization.
|
|
467
|
+
*
|
|
468
|
+
* It does NOT yet fully normalize:
|
|
469
|
+
*
|
|
470
|
+
* - hook ordering
|
|
471
|
+
* - jsx formatting
|
|
472
|
+
* - prop ordering
|
|
473
|
+
* - state extraction
|
|
474
|
+
* - logic separation
|
|
475
|
+
* - advanced AST refactors
|
|
476
|
+
*
|
|
477
|
+
* Production version should eventually
|
|
478
|
+
* become AST-driven.
|
|
479
|
+
*
|
|
480
|
+
* ------------------------------------------------------------
|
|
481
|
+
* DEPENDENCIES
|
|
482
|
+
* ------------------------------------------------------------
|
|
483
|
+
*
|
|
484
|
+
* This function complements:
|
|
485
|
+
*
|
|
486
|
+
* - createComponent()
|
|
487
|
+
* - updateComponent()
|
|
488
|
+
* - renameComponent()
|
|
489
|
+
* - moveComponent()
|
|
490
|
+
* - extractComponent()
|
|
491
|
+
* - ensureComponentStructure()
|
|
492
|
+
* - updateComponentImports()
|
|
493
|
+
* - resolveCssClassConflicts()
|
|
494
|
+
* - syncComponentStyles()
|
|
495
|
+
*
|
|
496
|
+
* ------------------------------------------------------------
|
|
497
|
+
* WHERE THIS FUNCTION IS USEFUL
|
|
498
|
+
* ------------------------------------------------------------
|
|
499
|
+
*
|
|
500
|
+
* Useful in commands like:
|
|
501
|
+
*
|
|
502
|
+
* - "cleanup component"
|
|
503
|
+
* - "normalize architecture"
|
|
504
|
+
* - "repair generated code"
|
|
505
|
+
* - "fix component structure"
|
|
506
|
+
* - "optimize project"
|
|
507
|
+
* - "prepare for extraction"
|
|
508
|
+
* - "stabilize AI generated UI"
|
|
509
|
+
*
|
|
510
|
+
* Usually executed:
|
|
511
|
+
*
|
|
512
|
+
* AFTER major AI-generated changes.
|
|
513
|
+
*
|
|
514
|
+
* ------------------------------------------------------------
|
|
515
|
+
* PARAMS
|
|
516
|
+
* ------------------------------------------------------------
|
|
517
|
+
*
|
|
518
|
+
* @param {Object} params
|
|
519
|
+
*
|
|
520
|
+
* @param {string} params.componentPath
|
|
521
|
+
* Component directory path.
|
|
522
|
+
*
|
|
523
|
+
* Example:
|
|
524
|
+
*
|
|
525
|
+
* "./src/components/Header"
|
|
526
|
+
*
|
|
527
|
+
* @param {string} params.componentName
|
|
528
|
+
* Canonical component name.
|
|
529
|
+
*
|
|
530
|
+
* Example:
|
|
531
|
+
*
|
|
532
|
+
* "Header"
|
|
533
|
+
*
|
|
534
|
+
* @param {boolean} [params.ensureCss=true]
|
|
535
|
+
* Whether component should contain css file.
|
|
536
|
+
*
|
|
537
|
+
* @param {boolean} [params.ensureIndex=true]
|
|
538
|
+
* Whether component should contain index.js.
|
|
539
|
+
*
|
|
540
|
+
* @param {boolean} [params.normalizeExports=true]
|
|
541
|
+
* Whether export statements should be normalized.
|
|
542
|
+
*
|
|
543
|
+
* ------------------------------------------------------------
|
|
544
|
+
* RETURNS
|
|
545
|
+
* ------------------------------------------------------------
|
|
546
|
+
*
|
|
547
|
+
* @returns {Object}
|
|
548
|
+
*
|
|
549
|
+
* {
|
|
550
|
+
* normalized: boolean,
|
|
551
|
+
* repairedItems: string[],
|
|
552
|
+
* warnings: string[]
|
|
553
|
+
* }
|
|
554
|
+
*
|
|
555
|
+
*/
|
|
556
|
+
function normalizeComponent({ componentPath, componentName, ensureCss = true, ensureIndex = true, normalizeExports = true, }, context) {
|
|
557
|
+
const COMPONENT_STRUCTURE = context.appContext.config.componentStructure;
|
|
558
|
+
// ----------------------------------------------------------
|
|
559
|
+
// STEP 1:
|
|
560
|
+
// Resolve absolute component path
|
|
561
|
+
// ----------------------------------------------------------
|
|
562
|
+
const absoluteComponentPath = path.resolve(componentPath);
|
|
563
|
+
// ----------------------------------------------------------
|
|
564
|
+
// STEP 2:
|
|
565
|
+
// Validate component directory existence
|
|
566
|
+
// ----------------------------------------------------------
|
|
567
|
+
if (!fs.existsSync(absoluteComponentPath)) {
|
|
568
|
+
throw new LoomaError(ERROR_CODES.TASK_EXECUTION_FAILED, `Component directory does not exist: ${absoluteComponentPath}`, {
|
|
569
|
+
payload: {
|
|
570
|
+
componentPath,
|
|
571
|
+
componentName,
|
|
572
|
+
ensureCss: true,
|
|
573
|
+
ensureIndex: true,
|
|
574
|
+
normalizeExports: true,
|
|
575
|
+
},
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
// ----------------------------------------------------------
|
|
579
|
+
// STEP 3:
|
|
580
|
+
// Track repaired items
|
|
581
|
+
// ----------------------------------------------------------
|
|
582
|
+
const repairedItems = [];
|
|
583
|
+
// ----------------------------------------------------------
|
|
584
|
+
// STEP 4:
|
|
585
|
+
// Track warnings
|
|
586
|
+
// ----------------------------------------------------------
|
|
587
|
+
const warnings = [];
|
|
588
|
+
// ----------------------------------------------------------
|
|
589
|
+
// STEP 5:
|
|
590
|
+
// Ensure canonical component structure
|
|
591
|
+
// ----------------------------------------------------------
|
|
592
|
+
const jsxPath = path.join(absoluteComponentPath, `${componentName}${COMPONENT_STRUCTURE[0]}`);
|
|
593
|
+
const cssPath = path.join(absoluteComponentPath, `${componentName}${COMPONENT_STRUCTURE[1]}`);
|
|
594
|
+
const indexPath = path.join(absoluteComponentPath, COMPONENT_STRUCTURE[2]);
|
|
595
|
+
// ----------------------------------------------------------
|
|
596
|
+
// STEP 6:
|
|
597
|
+
// Ensure JSX file exists
|
|
598
|
+
// ----------------------------------------------------------
|
|
599
|
+
if (!fs.existsSync(jsxPath)) {
|
|
600
|
+
// --------------------------------------------------------
|
|
601
|
+
// Create minimal JSX template
|
|
602
|
+
// --------------------------------------------------------
|
|
603
|
+
const jsxTemplate = `
|
|
604
|
+
import "./${componentName}.css";
|
|
605
|
+
|
|
606
|
+
function ${componentName}() {
|
|
607
|
+
return (
|
|
608
|
+
<div className="${componentName.toLowerCase()}">
|
|
609
|
+
${componentName}
|
|
610
|
+
</div>
|
|
611
|
+
);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
export default ${componentName};
|
|
615
|
+
`;
|
|
616
|
+
fs.writeFileSync(jsxPath, jsxTemplate.trim(), "utf8");
|
|
617
|
+
repairedItems.push("Created missing JSX file");
|
|
618
|
+
}
|
|
619
|
+
// ----------------------------------------------------------
|
|
620
|
+
// STEP 7:
|
|
621
|
+
// Ensure CSS file exists
|
|
622
|
+
// ----------------------------------------------------------
|
|
623
|
+
if (ensureCss && !fs.existsSync(cssPath)) {
|
|
624
|
+
// --------------------------------------------------------
|
|
625
|
+
// Create empty CSS file
|
|
626
|
+
// --------------------------------------------------------
|
|
627
|
+
fs.writeFileSync(cssPath, "", "utf8");
|
|
628
|
+
repairedItems.push("Created missing CSS file");
|
|
629
|
+
}
|
|
630
|
+
// ----------------------------------------------------------
|
|
631
|
+
// STEP 8:
|
|
632
|
+
// Ensure index.js exists
|
|
633
|
+
// ----------------------------------------------------------
|
|
634
|
+
if (ensureIndex && !fs.existsSync(indexPath)) {
|
|
635
|
+
// --------------------------------------------------------
|
|
636
|
+
// Create standard export file
|
|
637
|
+
// --------------------------------------------------------
|
|
638
|
+
const indexTemplate = `
|
|
639
|
+
export { default } from "./${componentName}";
|
|
640
|
+
`;
|
|
641
|
+
fs.writeFileSync(indexPath, indexTemplate.trim(), "utf8");
|
|
642
|
+
repairedItems.push("Created missing index.js");
|
|
643
|
+
}
|
|
644
|
+
// ----------------------------------------------------------
|
|
645
|
+
// STEP 9:
|
|
646
|
+
// Read component source
|
|
647
|
+
// ----------------------------------------------------------
|
|
648
|
+
let componentCode = fs.readFileSync(jsxPath, "utf8");
|
|
649
|
+
// ----------------------------------------------------------
|
|
650
|
+
// STEP 10:
|
|
651
|
+
// Ensure CSS import exists
|
|
652
|
+
// ----------------------------------------------------------
|
|
653
|
+
const cssImportStatement = `import "./${componentName}.css";`;
|
|
654
|
+
// ----------------------------------------------------------
|
|
655
|
+
// Add css import if missing
|
|
656
|
+
// ----------------------------------------------------------
|
|
657
|
+
if (ensureCss && !componentCode.includes(cssImportStatement)) {
|
|
658
|
+
componentCode = cssImportStatement + "\n" + componentCode;
|
|
659
|
+
repairedItems.push("Added missing CSS import");
|
|
660
|
+
}
|
|
661
|
+
// ----------------------------------------------------------
|
|
662
|
+
// STEP 11:
|
|
663
|
+
// Normalize export statement
|
|
664
|
+
// ----------------------------------------------------------
|
|
665
|
+
if (normalizeExports) {
|
|
666
|
+
// --------------------------------------------------------
|
|
667
|
+
// Remove broken exports
|
|
668
|
+
// --------------------------------------------------------
|
|
669
|
+
componentCode = componentCode.replace(/export\s+default\s+[A-Za-z0-9_]+;/g, "");
|
|
670
|
+
// --------------------------------------------------------
|
|
671
|
+
// Add canonical export
|
|
672
|
+
// --------------------------------------------------------
|
|
673
|
+
componentCode += `
|
|
674
|
+
|
|
675
|
+
export default ${componentName};
|
|
676
|
+
`;
|
|
677
|
+
repairedItems.push("Normalized export statement");
|
|
678
|
+
}
|
|
679
|
+
// ----------------------------------------------------------
|
|
680
|
+
// STEP 12:
|
|
681
|
+
// Normalize component declaration
|
|
682
|
+
// ----------------------------------------------------------
|
|
683
|
+
const componentDeclarationRegex = /function\s+[A-Za-z0-9_]+\s*\(/;
|
|
684
|
+
// ----------------------------------------------------------
|
|
685
|
+
// Replace incorrect component names
|
|
686
|
+
// ----------------------------------------------------------
|
|
687
|
+
if (componentDeclarationRegex.test(componentCode)) {
|
|
688
|
+
componentCode = componentCode.replace(componentDeclarationRegex, `function ${componentName}(`);
|
|
689
|
+
repairedItems.push("Normalized component declaration");
|
|
690
|
+
}
|
|
691
|
+
else {
|
|
692
|
+
warnings.push("No function component declaration found");
|
|
693
|
+
}
|
|
694
|
+
// ----------------------------------------------------------
|
|
695
|
+
// STEP 13:
|
|
696
|
+
// Cleanup excessive blank lines
|
|
697
|
+
// ----------------------------------------------------------
|
|
698
|
+
componentCode = componentCode.replace(/\n{3,}/g, "\n\n");
|
|
699
|
+
// ----------------------------------------------------------
|
|
700
|
+
// STEP 14:
|
|
701
|
+
// Write normalized component source
|
|
702
|
+
// ----------------------------------------------------------
|
|
703
|
+
fs.writeFileSync(jsxPath, componentCode.trim(), "utf8");
|
|
704
|
+
// ----------------------------------------------------------
|
|
705
|
+
// STEP 15:
|
|
706
|
+
// Return normalization summary
|
|
707
|
+
// ----------------------------------------------------------
|
|
708
|
+
return {
|
|
709
|
+
success: repairedItems.length > 0,
|
|
710
|
+
repairedItems,
|
|
711
|
+
warnings,
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Validator: Ensures that a component follows
|
|
716
|
+
* Looma's required filesystem structure.
|
|
717
|
+
*
|
|
718
|
+
* ------------------------------------------------------------
|
|
719
|
+
* WHAT THIS FUNCTION DOES
|
|
720
|
+
* ------------------------------------------------------------
|
|
721
|
+
*
|
|
722
|
+
* This function validates and fixes:
|
|
723
|
+
*
|
|
724
|
+
* - component directory
|
|
725
|
+
* - jsx file
|
|
726
|
+
* - css file
|
|
727
|
+
* - index.js file
|
|
728
|
+
*
|
|
729
|
+
* structure consistency.
|
|
730
|
+
*
|
|
731
|
+
* Example:
|
|
732
|
+
*
|
|
733
|
+
* REQUIRED STRUCTURE:
|
|
734
|
+
*
|
|
735
|
+
* Header/
|
|
736
|
+
* Header.jsx
|
|
737
|
+
* Header.css
|
|
738
|
+
* index.js
|
|
739
|
+
*
|
|
740
|
+
* If something is missing:
|
|
741
|
+
*
|
|
742
|
+
* - it gets created
|
|
743
|
+
*
|
|
744
|
+
* If structure is invalid:
|
|
745
|
+
*
|
|
746
|
+
* - it gets normalized
|
|
747
|
+
*
|
|
748
|
+
* ------------------------------------------------------------
|
|
749
|
+
* WHY THIS FUNCTION EXISTS
|
|
750
|
+
* ------------------------------------------------------------
|
|
751
|
+
*
|
|
752
|
+
* AI-generated projects drift very quickly.
|
|
753
|
+
*
|
|
754
|
+
* Without strict structure enforcement:
|
|
755
|
+
*
|
|
756
|
+
* - imports become inconsistent
|
|
757
|
+
* - components become fragmented
|
|
758
|
+
* - css locations become unpredictable
|
|
759
|
+
* - registry synchronization breaks
|
|
760
|
+
* - runtime editing becomes unstable
|
|
761
|
+
*
|
|
762
|
+
* This function acts as:
|
|
763
|
+
*
|
|
764
|
+
* architecture enforcement layer.
|
|
765
|
+
*
|
|
766
|
+
* ------------------------------------------------------------
|
|
767
|
+
* IMPORTANT ARCHITECTURAL DECISION
|
|
768
|
+
* ------------------------------------------------------------
|
|
769
|
+
*
|
|
770
|
+
* Looma should NEVER support:
|
|
771
|
+
*
|
|
772
|
+
* random project structures.
|
|
773
|
+
*
|
|
774
|
+
* Deterministic structure is REQUIRED for:
|
|
775
|
+
*
|
|
776
|
+
* - runtime editing
|
|
777
|
+
* - component registry
|
|
778
|
+
* - DOM mapping
|
|
779
|
+
* - AI transformations
|
|
780
|
+
* - undo/redo
|
|
781
|
+
* - AST synchronization
|
|
782
|
+
*
|
|
783
|
+
* Structure consistency is one of the
|
|
784
|
+
* MOST IMPORTANT foundations of Looma.
|
|
785
|
+
*
|
|
786
|
+
* ------------------------------------------------------------
|
|
787
|
+
* IMPORTANT NOTE
|
|
788
|
+
* ------------------------------------------------------------
|
|
789
|
+
*
|
|
790
|
+
* Current implementation:
|
|
791
|
+
*
|
|
792
|
+
* handles filesystem structure only.
|
|
793
|
+
*
|
|
794
|
+
* It does NOT validate:
|
|
795
|
+
*
|
|
796
|
+
* - component correctness
|
|
797
|
+
* - jsx correctness
|
|
798
|
+
* - import correctness
|
|
799
|
+
* - css correctness
|
|
800
|
+
*
|
|
801
|
+
* Those belong to separate validators.
|
|
802
|
+
*
|
|
803
|
+
* ------------------------------------------------------------
|
|
804
|
+
* DEPENDENCIES
|
|
805
|
+
* ------------------------------------------------------------
|
|
806
|
+
*
|
|
807
|
+
* This function complements:
|
|
808
|
+
*
|
|
809
|
+
* - createComponent()
|
|
810
|
+
* - updateComponent()
|
|
811
|
+
* - renameComponent()
|
|
812
|
+
* - moveComponent()
|
|
813
|
+
* - extractComponent()
|
|
814
|
+
* - ensureStyleFile()
|
|
815
|
+
*
|
|
816
|
+
* ------------------------------------------------------------
|
|
817
|
+
* WHERE THIS FUNCTION IS USEFUL
|
|
818
|
+
* ------------------------------------------------------------
|
|
819
|
+
*
|
|
820
|
+
* Useful in commands like:
|
|
821
|
+
*
|
|
822
|
+
* - "create component"
|
|
823
|
+
* - "fix project structure"
|
|
824
|
+
* - "normalize components"
|
|
825
|
+
* - "extract reusable section"
|
|
826
|
+
* - "repair generated code"
|
|
827
|
+
* - "migrate project"
|
|
828
|
+
* - "sync architecture"
|
|
829
|
+
*
|
|
830
|
+
* Usually executed:
|
|
831
|
+
*
|
|
832
|
+
* BEFORE major component mutations.
|
|
833
|
+
*
|
|
834
|
+
* ------------------------------------------------------------
|
|
835
|
+
* PARAMS
|
|
836
|
+
* ------------------------------------------------------------
|
|
837
|
+
*
|
|
838
|
+
* @param {Object} params
|
|
839
|
+
*
|
|
840
|
+
* @param {string} params.componentPath
|
|
841
|
+
* Component directory path.
|
|
842
|
+
*
|
|
843
|
+
* Example:
|
|
844
|
+
*
|
|
845
|
+
* "./src/components/Header"
|
|
846
|
+
*
|
|
847
|
+
* @param {string} params.componentName
|
|
848
|
+
* Component name.
|
|
849
|
+
*
|
|
850
|
+
* Example:
|
|
851
|
+
*
|
|
852
|
+
* "Header"
|
|
853
|
+
*
|
|
854
|
+
* @param {boolean} [params.ensureCss=true]
|
|
855
|
+
* Whether css file should exist.
|
|
856
|
+
*
|
|
857
|
+
* @param {boolean} [params.ensureIndex=true]
|
|
858
|
+
* Whether index.js should exist.
|
|
859
|
+
*
|
|
860
|
+
* @param {boolean} [params.createIfMissing=true]
|
|
861
|
+
* Whether missing directories/files
|
|
862
|
+
* should be automatically created.
|
|
863
|
+
*
|
|
864
|
+
* ------------------------------------------------------------
|
|
865
|
+
* RETURNS
|
|
866
|
+
* ------------------------------------------------------------
|
|
867
|
+
*
|
|
868
|
+
* @returns {Object}
|
|
869
|
+
*
|
|
870
|
+
* {
|
|
871
|
+
* created: string[],
|
|
872
|
+
* existing: string[],
|
|
873
|
+
* repaired: boolean
|
|
874
|
+
* }
|
|
875
|
+
*
|
|
876
|
+
*/
|
|
877
|
+
// function ensureComponentStructure({
|
|
878
|
+
// componentPath,
|
|
879
|
+
// componentName,
|
|
880
|
+
// ensureCss = true,
|
|
881
|
+
// ensureIndex = true,
|
|
882
|
+
// createIfMissing = true,
|
|
883
|
+
// }: TaskPayload<"ensureComponentStructure">, context: ExecutionContext): TaskResponse<
|
|
884
|
+
// TaskReturn<"ensureComponentStructure">
|
|
885
|
+
// > {
|
|
886
|
+
// // ----------------------------------------------------------
|
|
887
|
+
// // STEP 1:
|
|
888
|
+
// // Resolve absolute component path
|
|
889
|
+
// // ----------------------------------------------------------
|
|
890
|
+
// const absoluteComponentPath = path.resolve(componentPath);
|
|
891
|
+
// // ----------------------------------------------------------
|
|
892
|
+
// // STEP 2:
|
|
893
|
+
// // Track created files/directories
|
|
894
|
+
// // ----------------------------------------------------------
|
|
895
|
+
// const created = [];
|
|
896
|
+
// // ----------------------------------------------------------
|
|
897
|
+
// // STEP 3:
|
|
898
|
+
// // Track existing files/directories
|
|
899
|
+
// // ----------------------------------------------------------
|
|
900
|
+
// const existing = [];
|
|
901
|
+
// // ----------------------------------------------------------
|
|
902
|
+
// // STEP 4:
|
|
903
|
+
// // Ensure component directory exists
|
|
904
|
+
// // ----------------------------------------------------------
|
|
905
|
+
// if (!fs.existsSync(absoluteComponentPath)) {
|
|
906
|
+
// // --------------------------------------------------------
|
|
907
|
+
// // Throw if auto creation disabled
|
|
908
|
+
// // --------------------------------------------------------
|
|
909
|
+
// if (!createIfMissing) {
|
|
910
|
+
// throw new Error(
|
|
911
|
+
// `Component directory does not exist: ${absoluteComponentPath}`
|
|
912
|
+
// );
|
|
913
|
+
// }
|
|
914
|
+
// // --------------------------------------------------------
|
|
915
|
+
// // Create component directory
|
|
916
|
+
// // --------------------------------------------------------
|
|
917
|
+
// fs.mkdirSync(absoluteComponentPath, {
|
|
918
|
+
// recursive: true,
|
|
919
|
+
// });
|
|
920
|
+
// created.push(absoluteComponentPath);
|
|
921
|
+
// } else {
|
|
922
|
+
// existing.push(absoluteComponentPath);
|
|
923
|
+
// }
|
|
924
|
+
// // ----------------------------------------------------------
|
|
925
|
+
// // STEP 5:
|
|
926
|
+
// // Build JSX file path
|
|
927
|
+
// // ----------------------------------------------------------
|
|
928
|
+
// const jsxPath = path.join(
|
|
929
|
+
// absoluteComponentPath,
|
|
930
|
+
// `${componentName}${CONSTENTS.COMPONENT_STRUCTURE[0]}`
|
|
931
|
+
// );
|
|
932
|
+
// // ----------------------------------------------------------
|
|
933
|
+
// // STEP 6:
|
|
934
|
+
// // Ensure JSX file exists
|
|
935
|
+
// // ----------------------------------------------------------
|
|
936
|
+
// if (!fs.existsSync(jsxPath)) {
|
|
937
|
+
// // --------------------------------------------------------
|
|
938
|
+
// // Throw if creation disabled
|
|
939
|
+
// // --------------------------------------------------------
|
|
940
|
+
// if (!createIfMissing) {
|
|
941
|
+
// throw new Error(`Component JSX file missing: ${jsxPath}`);
|
|
942
|
+
// }
|
|
943
|
+
// // --------------------------------------------------------
|
|
944
|
+
// // Create minimal component template
|
|
945
|
+
// // --------------------------------------------------------
|
|
946
|
+
// const componentTemplate = `
|
|
947
|
+
// import "./${componentName}.css";
|
|
948
|
+
// function ${componentName}() {
|
|
949
|
+
// return (
|
|
950
|
+
// <div className="${componentName.toLowerCase()}">
|
|
951
|
+
// ${componentName}
|
|
952
|
+
// </div>
|
|
953
|
+
// );
|
|
954
|
+
// }
|
|
955
|
+
// export default ${componentName};
|
|
956
|
+
// `;
|
|
957
|
+
// fs.writeFileSync(jsxPath, componentTemplate.trim(), "utf8");
|
|
958
|
+
// created.push(jsxPath);
|
|
959
|
+
// } else {
|
|
960
|
+
// existing.push(jsxPath);
|
|
961
|
+
// }
|
|
962
|
+
// // ----------------------------------------------------------
|
|
963
|
+
// // STEP 7:
|
|
964
|
+
// // Build CSS file path
|
|
965
|
+
// // ----------------------------------------------------------
|
|
966
|
+
// const cssPath = path.join(
|
|
967
|
+
// absoluteComponentPath,
|
|
968
|
+
// `${componentName}${CONSTENTS.COMPONENT_STRUCTURE[1]}`
|
|
969
|
+
// );
|
|
970
|
+
// // ----------------------------------------------------------
|
|
971
|
+
// // STEP 8:
|
|
972
|
+
// // Ensure CSS file exists
|
|
973
|
+
// // ----------------------------------------------------------
|
|
974
|
+
// if (ensureCss) {
|
|
975
|
+
// if (!fs.existsSync(cssPath)) {
|
|
976
|
+
// // ------------------------------------------------------
|
|
977
|
+
// // Throw if creation disabled
|
|
978
|
+
// // ------------------------------------------------------
|
|
979
|
+
// if (!createIfMissing) {
|
|
980
|
+
// throw new Error(`Component CSS file missing: ${cssPath}`);
|
|
981
|
+
// }
|
|
982
|
+
// // ------------------------------------------------------
|
|
983
|
+
// // Create empty css file
|
|
984
|
+
// // ------------------------------------------------------
|
|
985
|
+
// fs.writeFileSync(cssPath, "", "utf8");
|
|
986
|
+
// created.push(cssPath);
|
|
987
|
+
// } else {
|
|
988
|
+
// existing.push(cssPath);
|
|
989
|
+
// }
|
|
990
|
+
// }
|
|
991
|
+
// // ----------------------------------------------------------
|
|
992
|
+
// // STEP 9:
|
|
993
|
+
// // Build index.js path
|
|
994
|
+
// // ----------------------------------------------------------
|
|
995
|
+
// const indexPath = path.join(
|
|
996
|
+
// absoluteComponentPath,
|
|
997
|
+
// CONSTENTS.COMPONENT_STRUCTURE[2]
|
|
998
|
+
// );
|
|
999
|
+
// // ----------------------------------------------------------
|
|
1000
|
+
// // STEP 10:
|
|
1001
|
+
// // Ensure index.js exists
|
|
1002
|
+
// // ----------------------------------------------------------
|
|
1003
|
+
// if (ensureIndex) {
|
|
1004
|
+
// if (!fs.existsSync(indexPath)) {
|
|
1005
|
+
// // ------------------------------------------------------
|
|
1006
|
+
// // Throw if creation disabled
|
|
1007
|
+
// // ------------------------------------------------------
|
|
1008
|
+
// if (!createIfMissing) {
|
|
1009
|
+
// throw new Error(`Component index.js missing: ${indexPath}`);
|
|
1010
|
+
// }
|
|
1011
|
+
// // ------------------------------------------------------
|
|
1012
|
+
// // Create export file
|
|
1013
|
+
// // ------------------------------------------------------
|
|
1014
|
+
// const indexTemplate = `
|
|
1015
|
+
// export { default } from "./${componentName}";
|
|
1016
|
+
// `;
|
|
1017
|
+
// fs.writeFileSync(indexPath, indexTemplate.trim(), "utf8");
|
|
1018
|
+
// created.push(indexPath);
|
|
1019
|
+
// } else {
|
|
1020
|
+
// existing.push(indexPath);
|
|
1021
|
+
// }
|
|
1022
|
+
// }
|
|
1023
|
+
// // ----------------------------------------------------------
|
|
1024
|
+
// // STEP 11:
|
|
1025
|
+
// // Return structure validation summary
|
|
1026
|
+
// // ----------------------------------------------------------
|
|
1027
|
+
// return {
|
|
1028
|
+
// success: true,
|
|
1029
|
+
// existing,
|
|
1030
|
+
// repaired: created.length > 0,
|
|
1031
|
+
// };
|
|
1032
|
+
// }
|
|
1033
|
+
export default {
|
|
1034
|
+
// insertCode,
|
|
1035
|
+
// expose only when planner becomes mature enough to use it directly
|
|
1036
|
+
// ensureImport,
|
|
1037
|
+
// removeImport,
|
|
1038
|
+
// enrichImport,
|
|
1039
|
+
// optimizeImports,
|
|
1040
|
+
// updateComponentImports,
|
|
1041
|
+
// findNodeByLine,
|
|
1042
|
+
// findComponentByName,
|
|
1043
|
+
// findJSXElement,
|
|
1044
|
+
// resolveImportConflicts,
|
|
1045
|
+
// do not expose
|
|
1046
|
+
ensureLibrary,
|
|
1047
|
+
// ensureComponentStructure,
|
|
1048
|
+
normalizeComponent,
|
|
1049
|
+
// findComponentDirectory,
|
|
1050
|
+
// inferComponentName,
|
|
1051
|
+
ensureStyleFile,
|
|
1052
|
+
};
|
|
1053
|
+
//# sourceMappingURL=ensurers.js.map
|