@igniter-js/cli 0.2.62 → 0.2.63
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/index.js +112 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +112 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -19553,12 +19553,18 @@ var PrismaProvider = class {
|
|
|
19553
19553
|
return null;
|
|
19554
19554
|
}
|
|
19555
19555
|
const fields = model.properties.filter((prop) => prop.type === "field").map((prop) => {
|
|
19556
|
-
|
|
19556
|
+
if (prop.type !== "field" || !("fieldType" in prop) || !("name" in prop)) {
|
|
19557
|
+
throw new Error(`Invalid field property structure for ${prop}`);
|
|
19558
|
+
}
|
|
19559
|
+
const fieldType = prop.fieldType;
|
|
19560
|
+
const fieldTypeStr = typeof fieldType === "string" ? fieldType : fieldType?.toString() || "string";
|
|
19561
|
+
const isOptional = "optional" in prop ? prop.optional : false;
|
|
19562
|
+
const isRelation = !/^[A-Z]/.test(fieldTypeStr) && typeof fieldType !== "string";
|
|
19557
19563
|
return {
|
|
19558
19564
|
name: prop.name,
|
|
19559
|
-
type: mapPrismaTypeToTsType(
|
|
19565
|
+
type: mapPrismaTypeToTsType(fieldTypeStr),
|
|
19560
19566
|
isId: hasAttribute(prop, "id"),
|
|
19561
|
-
isRequired: !(
|
|
19567
|
+
isRequired: !(isOptional || hasAttribute(prop, "default")),
|
|
19562
19568
|
isUnique: hasAttribute(prop, "unique"),
|
|
19563
19569
|
isRelation,
|
|
19564
19570
|
hasDefault: hasAttribute(prop, "default"),
|
|
@@ -19578,6 +19584,28 @@ var PrismaProvider = class {
|
|
|
19578
19584
|
throw new Error(`Could not process Prisma schema. Make sure '${this.schemaPath}' exists and is valid.`);
|
|
19579
19585
|
}
|
|
19580
19586
|
}
|
|
19587
|
+
/**
|
|
19588
|
+
* Lists all available model names in the Prisma schema.
|
|
19589
|
+
*
|
|
19590
|
+
* @returns A promise that resolves to an array of model names.
|
|
19591
|
+
*/
|
|
19592
|
+
async listModels() {
|
|
19593
|
+
try {
|
|
19594
|
+
const schemaContent = await fs4.readFile(this.schemaPath, "utf-8");
|
|
19595
|
+
const ast = getSchema(schemaContent);
|
|
19596
|
+
const models = ast.list.filter((node) => node.type === "model").map((model) => model.name);
|
|
19597
|
+
logger4.debug(`Found ${models.length} models in schema: ${models.join(", ")}`);
|
|
19598
|
+
return models;
|
|
19599
|
+
} catch (error) {
|
|
19600
|
+
if (error.code === "ENOENT") {
|
|
19601
|
+
logger4.error(`Prisma schema file not found at: ${this.schemaPath}`);
|
|
19602
|
+
return [];
|
|
19603
|
+
} else {
|
|
19604
|
+
logger4.error("Failed to parse Prisma schema for model listing", { error });
|
|
19605
|
+
throw new Error(`Could not process Prisma schema. Make sure '${this.schemaPath}' exists and is valid.`);
|
|
19606
|
+
}
|
|
19607
|
+
}
|
|
19608
|
+
}
|
|
19581
19609
|
/**
|
|
19582
19610
|
* Determines if a field's value is automatically managed by the database.
|
|
19583
19611
|
*
|
|
@@ -19603,6 +19631,29 @@ var PrismaProvider = class {
|
|
|
19603
19631
|
|
|
19604
19632
|
// src/adapters/scaffold.ts
|
|
19605
19633
|
var logger5 = createChildLogger({ component: "scaffold" });
|
|
19634
|
+
async function hasPrismaSchema() {
|
|
19635
|
+
const possiblePaths = [
|
|
19636
|
+
path6.join(process.cwd(), "prisma", "schema.prisma"),
|
|
19637
|
+
path6.join(process.cwd(), "schema.prisma")
|
|
19638
|
+
];
|
|
19639
|
+
for (const schemaPath of possiblePaths) {
|
|
19640
|
+
try {
|
|
19641
|
+
await fs5.access(schemaPath);
|
|
19642
|
+
return true;
|
|
19643
|
+
} catch {
|
|
19644
|
+
}
|
|
19645
|
+
}
|
|
19646
|
+
return false;
|
|
19647
|
+
}
|
|
19648
|
+
async function getPrismaModels() {
|
|
19649
|
+
try {
|
|
19650
|
+
const provider = new PrismaProvider();
|
|
19651
|
+
return await provider.listModels();
|
|
19652
|
+
} catch (error) {
|
|
19653
|
+
logger5.debug("Failed to get Prisma models", { error });
|
|
19654
|
+
return [];
|
|
19655
|
+
}
|
|
19656
|
+
}
|
|
19606
19657
|
function toPascalCase(str) {
|
|
19607
19658
|
return str.replace(/(^\w|-\w)/g, (g) => g.replace(/-/, "").toUpperCase());
|
|
19608
19659
|
}
|
|
@@ -19815,8 +19866,7 @@ function generateEmptyIndexTemplate(featureName) {
|
|
|
19815
19866
|
`;
|
|
19816
19867
|
}
|
|
19817
19868
|
async function scaffoldEmptyFeature(featureName, featureDir) {
|
|
19818
|
-
|
|
19819
|
-
spinner.start();
|
|
19869
|
+
logger5.info(`Creating empty feature '${featureName}'...`);
|
|
19820
19870
|
try {
|
|
19821
19871
|
await fs5.mkdir(path6.join(featureDir, "controllers"), { recursive: true });
|
|
19822
19872
|
await fs5.mkdir(path6.join(featureDir, "procedures"), { recursive: true });
|
|
@@ -19832,15 +19882,14 @@ async function scaffoldEmptyFeature(featureName, featureDir) {
|
|
|
19832
19882
|
path6.join(featureDir, "index.ts"),
|
|
19833
19883
|
generateEmptyIndexTemplate(featureName)
|
|
19834
19884
|
);
|
|
19835
|
-
|
|
19885
|
+
logger5.success(`Scaffolded empty feature '${featureName}'`);
|
|
19836
19886
|
} catch (error) {
|
|
19837
|
-
|
|
19887
|
+
logger5.error(`Failed to create empty feature '${featureName}'`);
|
|
19838
19888
|
throw error;
|
|
19839
19889
|
}
|
|
19840
19890
|
}
|
|
19841
19891
|
async function scaffoldFeatureFromSchema(featureName, schemaString, featureDir) {
|
|
19842
|
-
|
|
19843
|
-
spinner.start();
|
|
19892
|
+
logger5.info(`Scaffolding feature '${featureName}' from schema...`);
|
|
19844
19893
|
try {
|
|
19845
19894
|
const [providerName, modelName] = schemaString.split(":");
|
|
19846
19895
|
if (!providerName || !modelName) {
|
|
@@ -19851,7 +19900,7 @@ async function scaffoldFeatureFromSchema(featureName, schemaString, featureDir)
|
|
|
19851
19900
|
if (!model) {
|
|
19852
19901
|
throw new Error(`Model '${modelName}' not found using provider '${providerName}'.`);
|
|
19853
19902
|
}
|
|
19854
|
-
|
|
19903
|
+
logger5.info("Generating files from model schema...");
|
|
19855
19904
|
await fs5.mkdir(path6.join(featureDir, "controllers"), { recursive: true });
|
|
19856
19905
|
await fs5.mkdir(path6.join(featureDir, "procedures"), { recursive: true });
|
|
19857
19906
|
await writeFile2(
|
|
@@ -19870,15 +19919,64 @@ async function scaffoldFeatureFromSchema(featureName, schemaString, featureDir)
|
|
|
19870
19919
|
path6.join(featureDir, "index.ts"),
|
|
19871
19920
|
generateCrudIndexTemplate(featureName)
|
|
19872
19921
|
);
|
|
19873
|
-
|
|
19922
|
+
logger5.success(`Successfully scaffolded feature '${featureName}' from '${modelName}' model.`);
|
|
19874
19923
|
console.log(chalk5.cyan(`
|
|
19875
19924
|
\u2705 Next step: Register the '${toCamelCase(featureName)}Controller' in 'src/igniter.router.ts'`));
|
|
19876
19925
|
} catch (error) {
|
|
19877
|
-
|
|
19926
|
+
logger5.error(`Failed to scaffold feature from schema`);
|
|
19878
19927
|
throw error;
|
|
19879
19928
|
}
|
|
19880
19929
|
}
|
|
19881
|
-
async function handleGenerateFeature(
|
|
19930
|
+
async function handleGenerateFeature(name, options = {}) {
|
|
19931
|
+
let featureName = name;
|
|
19932
|
+
if (!featureName) {
|
|
19933
|
+
const prompts2 = await import("prompts");
|
|
19934
|
+
const hasPrisma = await hasPrismaSchema();
|
|
19935
|
+
const prismaModels = hasPrisma ? await getPrismaModels() : [];
|
|
19936
|
+
const questions = [
|
|
19937
|
+
{
|
|
19938
|
+
type: "text",
|
|
19939
|
+
name: "featureName",
|
|
19940
|
+
message: "What is the name of your feature?",
|
|
19941
|
+
validate: (input) => {
|
|
19942
|
+
if (!input.trim()) {
|
|
19943
|
+
return "Feature name is required";
|
|
19944
|
+
}
|
|
19945
|
+
if (!/^[a-zA-Z][a-zA-Z0-9-_]*$/.test(input)) {
|
|
19946
|
+
return "Feature name must start with a letter and contain only letters, numbers, hyphens, and underscores";
|
|
19947
|
+
}
|
|
19948
|
+
return true;
|
|
19949
|
+
}
|
|
19950
|
+
}
|
|
19951
|
+
];
|
|
19952
|
+
if (hasPrisma && prismaModels.length > 0) {
|
|
19953
|
+
questions.push({
|
|
19954
|
+
type: "select",
|
|
19955
|
+
name: "useModel",
|
|
19956
|
+
message: "Would you like to generate CRUD operations from a Prisma model?",
|
|
19957
|
+
choices: [
|
|
19958
|
+
{ title: "No, create an empty feature", value: "none" },
|
|
19959
|
+
{ title: "Yes, select a Prisma model", value: "select" }
|
|
19960
|
+
],
|
|
19961
|
+
initial: 0
|
|
19962
|
+
});
|
|
19963
|
+
questions.push({
|
|
19964
|
+
type: (prev) => prev === "select" ? "select" : null,
|
|
19965
|
+
name: "selectedModel",
|
|
19966
|
+
message: "Which Prisma model would you like to use?",
|
|
19967
|
+
choices: prismaModels.map((model) => ({ title: model, value: model }))
|
|
19968
|
+
});
|
|
19969
|
+
}
|
|
19970
|
+
const response = await prompts2.default(questions);
|
|
19971
|
+
if (!response.featureName) {
|
|
19972
|
+
logger5.error("Feature name is required");
|
|
19973
|
+
process.exit(1);
|
|
19974
|
+
}
|
|
19975
|
+
featureName = response.featureName;
|
|
19976
|
+
if (response.useModel === "select" && response.selectedModel) {
|
|
19977
|
+
options.schema = `prisma:${response.selectedModel}`;
|
|
19978
|
+
}
|
|
19979
|
+
}
|
|
19882
19980
|
const normalizedName = featureName.toLowerCase();
|
|
19883
19981
|
const featureDir = path6.join(process.cwd(), "src", "features", normalizedName);
|
|
19884
19982
|
logger5.info(`Scaffolding feature: ${chalk5.cyan(normalizedName)}`);
|
|
@@ -20115,7 +20213,7 @@ generate.command("docs").description("Generate OpenAPI specification and/or inte
|
|
|
20115
20213
|
process.exit(1);
|
|
20116
20214
|
}
|
|
20117
20215
|
});
|
|
20118
|
-
generate.command("feature").description("Scaffold a new feature module").argument("
|
|
20216
|
+
generate.command("feature").description("Scaffold a new feature module").argument("[name]", "The name of the feature (e.g., 'user', 'products')").option("--schema <value>", "Generate from a schema provider (e.g., 'prisma:User')").action(async (name, options) => {
|
|
20119
20217
|
await handleGenerateFeature(name, options);
|
|
20120
20218
|
});
|
|
20121
20219
|
generate.command("controller").description("Scaffold a new controller within a feature").argument("<name>", "The name of the controller (e.g., 'profile')").option("-f, --feature <feature>", "The parent feature name", "").action(async (name, options) => {
|