@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.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
- const isRelation = !/^[A-Z]/.test(prop.fieldType.toString()) && typeof prop.fieldType !== "string";
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(prop.fieldType),
19565
+ type: mapPrismaTypeToTsType(fieldTypeStr),
19560
19566
  isId: hasAttribute(prop, "id"),
19561
- isRequired: !(prop.optional || hasAttribute(prop, "default")),
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
- const spinner = logger5.spinner(`Creating empty feature '${featureName}'...`);
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
- spinner.success(`Scaffolded empty feature '${featureName}'`);
19885
+ logger5.success(`Scaffolded empty feature '${featureName}'`);
19836
19886
  } catch (error) {
19837
- spinner.error(`Failed to create empty feature '${featureName}'`);
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
- const spinner = logger5.spinner(`Scaffolding feature '${featureName}' from schema...`);
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
- spinner.update("Generating files from model schema...");
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
- spinner.success(`Successfully scaffolded feature '${featureName}' from '${modelName}' model.`);
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
- spinner.error(`Failed to scaffold feature from schema`);
19926
+ logger5.error(`Failed to scaffold feature from schema`);
19878
19927
  throw error;
19879
19928
  }
19880
19929
  }
19881
- async function handleGenerateFeature(featureName, options) {
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("<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) => {
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) => {