Skip to content

Commit 22f6876

Browse files
authored
Merge pull request #205 from devforth/next
Next
2 parents fc0348a + 7fb5238 commit 22f6876

File tree

26 files changed

+1256
-250
lines changed

26 files changed

+1256
-250
lines changed

adminforth/commands/createCustomComponent/configUpdater.js

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,282 @@ export async function updateResourceConfig(resourceId, columnName, fieldType, co
205205
throw new Error(`Failed to update resource file ${path.basename(filePath)}: ${error.message}`);
206206
}
207207
}
208+
209+
210+
export async function injectLoginComponent(indexFilePath, componentPath) {
211+
console.log(chalk.dim(`Reading file: ${indexFilePath}`));
212+
const content = await fs.readFile(indexFilePath, 'utf-8');
213+
const ast = recast.parse(content, {
214+
parser: typescriptParser,
215+
});
216+
217+
let updated = false;
218+
219+
recast.visit(ast, {
220+
visitNewExpression(path) {
221+
if (
222+
n.Identifier.check(path.node.callee) &&
223+
path.node.callee.name === 'AdminForth' &&
224+
path.node.arguments.length > 0 &&
225+
n.ObjectExpression.check(path.node.arguments[0])
226+
) {
227+
const configObject = path.node.arguments[0];
228+
229+
let customizationProp = configObject.properties.find(
230+
p => n.ObjectProperty.check(p) && n.Identifier.check(p.key) && p.key.name === 'customization'
231+
);
232+
233+
if (!customizationProp) {
234+
const customizationObj = b.objectExpression([]);
235+
customizationProp = b.objectProperty(b.identifier('customization'), customizationObj);
236+
configObject.properties.push(customizationProp);
237+
console.log(chalk.dim(`Added missing 'customization' property.`));
238+
}
239+
240+
const customizationValue = customizationProp.value;
241+
if (!n.ObjectExpression.check(customizationValue)) return false;
242+
243+
let loginPageInjections = customizationValue.properties.find(
244+
p => n.ObjectProperty.check(p) && n.Identifier.check(p.key) && p.key.name === 'loginPageInjections'
245+
);
246+
247+
if (!loginPageInjections) {
248+
const injectionsObj = b.objectExpression([]);
249+
loginPageInjections = b.objectProperty(b.identifier('loginPageInjections'), injectionsObj);
250+
customizationValue.properties.push(loginPageInjections);
251+
console.log(chalk.dim(`Added missing 'loginPageInjections'.`));
252+
}
253+
254+
const injectionsValue = loginPageInjections.value;
255+
if (!n.ObjectExpression.check(injectionsValue)) return false;
256+
257+
let underInputsProp = injectionsValue.properties.find(
258+
p => n.ObjectProperty.check(p) && n.Identifier.check(p.key) && p.key.name === 'underInputs'
259+
);
260+
261+
if (underInputsProp) {
262+
underInputsProp.value = b.stringLiteral(componentPath);
263+
console.log(chalk.dim(`Updated 'underInputs' to ${componentPath}`));
264+
} else {
265+
injectionsValue.properties.push(
266+
b.objectProperty(b.identifier('underInputs'), b.stringLiteral(componentPath))
267+
);
268+
console.log(chalk.dim(`Added 'underInputs': ${componentPath}`));
269+
}
270+
271+
updated = true;
272+
this.abort();
273+
}
274+
return false;
275+
}
276+
});
277+
278+
if (!updated) {
279+
throw new Error(`Could not find AdminForth configuration in file: ${indexFilePath}`);
280+
}
281+
282+
const outputCode = recast.print(ast).code;
283+
await fs.writeFile(indexFilePath, outputCode, 'utf-8');
284+
console.log(chalk.green(`✅ Successfully updated login injection in: ${indexFilePath}`));
285+
}
286+
287+
288+
export async function injectGlobalComponent(indexFilePath, injectionType, componentPath) {
289+
console.log(chalk.dim(`Reading file: ${indexFilePath}`));
290+
const content = await fs.readFile(indexFilePath, 'utf-8');
291+
const ast = recast.parse(content, {
292+
parser: typescriptParser,
293+
});
294+
295+
let updated = false;
296+
297+
console.log(JSON.stringify(injectionType));
298+
recast.visit(ast, {
299+
visitNewExpression(path) {
300+
if (
301+
n.Identifier.check(path.node.callee) &&
302+
path.node.callee.name === 'AdminForth' &&
303+
path.node.arguments.length > 0 &&
304+
n.ObjectExpression.check(path.node.arguments[0])
305+
) {
306+
const configObject = path.node.arguments[0];
307+
308+
let customizationProp = configObject.properties.find(
309+
p => n.ObjectProperty.check(p) && n.Identifier.check(p.key) && p.key.name === 'customization'
310+
);
311+
312+
if (!customizationProp) {
313+
const customizationObj = b.objectExpression([]);
314+
customizationProp = b.objectProperty(b.identifier('customization'), customizationObj);
315+
configObject.properties.push(customizationProp);
316+
console.log(chalk.dim(`Added missing 'customization' property.`));
317+
}
318+
319+
const customizationValue = customizationProp.value;
320+
if (!n.ObjectExpression.check(customizationValue)) return false;
321+
322+
let globalInjections = customizationValue.properties.find(
323+
p => n.ObjectProperty.check(p) && n.Identifier.check(p.key) && p.key.name === 'globalInjections'
324+
);
325+
326+
if (!globalInjections) {
327+
const injectionsObj = b.objectExpression([]);
328+
globalInjections = b.objectProperty(b.identifier('globalInjections'), injectionsObj);
329+
customizationValue.properties.push(globalInjections);
330+
console.log(chalk.dim(`Added missing 'globalInjections'.`));
331+
}
332+
333+
const injectionsValue = globalInjections.value;
334+
if (!n.ObjectExpression.check(injectionsValue)) return false;
335+
console.log(JSON.stringify(injectionType));
336+
let injectionProp = injectionsValue.properties.find(
337+
p => n.ObjectProperty.check(p) && n.Identifier.check(p.key) && p.key.name === injectionType
338+
);
339+
if (injectionProp) {
340+
const currentValue = injectionProp.value;
341+
342+
if (n.ArrayExpression.check(currentValue)) {
343+
currentValue.elements.push(b.stringLiteral(componentPath));
344+
console.log(chalk.dim(`Added '${componentPath}' to existing array in '${injectionType}'`));
345+
} else if (n.StringLiteral.check(currentValue)) {
346+
injectionProp.value = b.arrayExpression([
347+
b.stringLiteral(currentValue.value),
348+
b.stringLiteral(componentPath)
349+
]);
350+
console.log(chalk.dim(`Converted '${injectionType}' from string to array and added '${componentPath}'`));
351+
} else {
352+
throw new Error(`Unsupported value type for '${injectionType}'. Must be string or array.`);
353+
}
354+
} else {
355+
injectionsValue.properties.push(
356+
b.objectProperty(
357+
b.identifier(injectionType),
358+
b.arrayExpression([b.stringLiteral(componentPath)])
359+
)
360+
);
361+
console.log(chalk.dim(`Added new array for '${injectionType}' with '${componentPath}'`));
362+
}
363+
364+
updated = true;
365+
this.abort();
366+
}
367+
return false;
368+
}
369+
});
370+
371+
if (!updated) {
372+
throw new Error(`Could not find AdminForth configuration in file: ${indexFilePath}`);
373+
}
374+
375+
const outputCode = recast.print(ast).code;
376+
await fs.writeFile(indexFilePath, outputCode, 'utf-8');
377+
console.log(chalk.green(`✅ Successfully updated global injection '${injectionType}' in: ${indexFilePath}`));
378+
}
379+
380+
export async function updateCrudInjectionConfig(resourceId, crudType, injectionPosition, componentPathForConfig, isThin) {
381+
const filePath = await findResourceFilePath(resourceId);
382+
console.log(chalk.dim(`Attempting to update resource CRUD injection: ${filePath}`));
383+
384+
let content;
385+
try {
386+
content = await fs.readFile(filePath, 'utf-8');
387+
} catch (error) {
388+
console.error(chalk.red(`❌ Error reading resource file: ${filePath}`));
389+
throw new Error(`Could not read resource file ${filePath}.`);
390+
}
391+
392+
try {
393+
const ast = recast.parse(content, {
394+
parser: typescriptParser
395+
});
396+
397+
let updateApplied = false;
398+
399+
recast.visit(ast, {
400+
visitExportDefaultDeclaration(path) {
401+
const declaration = path.node.declaration;
402+
let objectExpressionNode = null;
403+
404+
if (n.TSAsExpression.check(declaration) && n.ObjectExpression.check(declaration.expression)) {
405+
objectExpressionNode = declaration.expression;
406+
} else if (n.ObjectExpression.check(declaration)) {
407+
objectExpressionNode = declaration;
408+
}
409+
410+
if (!objectExpressionNode) {
411+
console.warn(chalk.yellow(`Warning: Default export in ${filePath} is not an ObjectExpression. Skipping update.`));
412+
return false;
413+
}
414+
415+
const getOrCreateObjectProp = (obj, propName) => {
416+
let prop = obj.properties.find(p => n.ObjectProperty.check(p) && n.Identifier.check(p.key) && p.key.name === propName);
417+
if (!prop) {
418+
const newObject = b.objectExpression([]);
419+
prop = b.objectProperty(b.identifier(propName), newObject);
420+
obj.properties.push(prop);
421+
}
422+
return prop.value;
423+
};
424+
425+
const options = getOrCreateObjectProp(objectExpressionNode, 'options');
426+
if (!n.ObjectExpression.check(options)) return false;
427+
428+
const pageInjections = getOrCreateObjectProp(options, 'pageInjections');
429+
if (!n.ObjectExpression.check(pageInjections)) return false;
430+
431+
let crudProp = pageInjections.properties.find(p =>
432+
n.ObjectProperty.check(p) && n.Identifier.check(p.key) && p.key.name === crudType
433+
);
434+
435+
if (!crudProp) {
436+
crudProp = b.objectProperty(
437+
b.identifier(crudType),
438+
b.objectExpression([])
439+
);
440+
pageInjections.properties.push(crudProp);
441+
}
442+
443+
const crudValue = crudProp.value;
444+
if (!n.ObjectExpression.check(crudValue)) return false;
445+
446+
let injectionProp = crudValue.properties.find(p =>
447+
n.ObjectProperty.check(p) && n.Identifier.check(p.key) && p.key.name === injectionPosition
448+
);
449+
450+
const newInjectionObject = b.objectExpression([
451+
b.objectProperty(b.identifier('file'), b.stringLiteral(componentPathForConfig)),
452+
b.objectProperty(
453+
b.identifier('meta'),
454+
b.objectExpression([
455+
b.objectProperty(b.identifier('thinEnoughToShrinkTable'), b.booleanLiteral(!!isThin)),
456+
])
457+
),
458+
]);
459+
460+
if (injectionProp) {
461+
injectionProp.value = newInjectionObject;
462+
console.log(chalk.dim(`Updated '${injectionPosition}' injection for '${crudType}'.`));
463+
} else {
464+
crudValue.properties.push(b.objectProperty(b.identifier(injectionPosition), newInjectionObject));
465+
console.log(chalk.dim(`Added '${injectionPosition}' injection for '${crudType}'.`));
466+
}
467+
468+
updateApplied = true;
469+
this.abort();
470+
return false;
471+
}
472+
});
473+
474+
if (!updateApplied) {
475+
throw new Error(`Could not inject CRUD component in resource ${resourceId}.`);
476+
}
477+
478+
const outputCode = recast.print(ast).code;
479+
await fs.writeFile(filePath, outputCode, 'utf-8');
480+
console.log(chalk.dim(`✅ Successfully updated CRUD injection in resource file: ${filePath}`));
481+
482+
} catch (error) {
483+
console.error(chalk.red(`❌ Error processing resource file: ${filePath}`));
484+
throw new Error(`Failed to inject CRUD component in ${path.basename(filePath)}: ${error.message}`);
485+
}
486+
}

0 commit comments

Comments
 (0)