Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to load schemas from file in generate command #160

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,16 @@ Kirimase generates:
- Scaffolds views using Shadcn-UI to enable immediate CRUD operations (including select fields for adding relations and datepickers for dates).
- Option to use either React Hook Form with tRPC or plain React (useOptimistic and useValidated Form hooks)

### Generate schemas from JSON file

You can also import and generate schemas from a JSON file via the command

```sh
kirimase generate -f <path-to-schema.json>
```

More info [here](commands/generate/generate-schema-from-file.md)

## Run in non-interactive mode

As of v0.0.23, you can run `kirimase init` and `kirimase add` entirely via the command line as follows:
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"figlet": "^1.7.0",
"ora": "^8.0.1",
"pluralize": "^8.0.0",
"strip-json-comments": "^5.0.1"
"strip-json-comments": "^5.0.1",
"zod": "^3.22.4"
},
"devDependencies": {
"@types/node": "^20.4.5",
Expand Down
7 changes: 7 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

77 changes: 77 additions & 0 deletions src/commands/generate/generate-schema-from-file.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
## Generating schemas from JSON file

You can also import and generate schemas from a JSON file via the command

```sh
kirimase generate -f <path-to-schema.json>
```

### Input format

You must provide an array of schema, and each of them must follows the `Schema` type definition

```typescript
type Schema = {
tableName: string;
fields: {
name: string;
type: T;
references?: string;
notNull?: boolean;
cascade?: boolean;
};
index: string;
belongsToUser?: boolean;
includeTimestamps: boolean;
children?: Schema[];
};
```

As you can guess, providing more than one schema will make Kirimase generates multiple schemas in a single `generate` command

### Examples

Here's a sample of a valid `schema.json`

```json
[
{
"tableName": "products",
"fields": [
{ "name": "name", "type": "varchar", "notNull": true },
{ "name": "price", "type": "number", "notNull": true }
],
"index": "name",
"belongsToUser": false,
"includeTimestamps": true,
"children": []
}
]
```

Another example with children

```json
[
{
"tableName": "product_discounts",
"fields": [
{ "name": "code", "type": "number", "notNull": true },
{ "name": "amount", "type": "number", "notNull": true }
],
"index": null,
"belongsToUser": false,
"includeTimestamps": false,
"children": [
{
"tableName": "product_discount_analytics",
"fields": [{ "name": "used_count", "type": "number", "notNull": true }],
"index": null,
"belongsToUser": false,
"includeTimestamps": false,
"children": []
}
]
}
]
```
52 changes: 44 additions & 8 deletions src/commands/generate/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { checkbox, confirm, input, select } from "@inquirer/prompts";
import { consola } from "consola";
import fs from "fs";
import pluralize from "pluralize";
import { z } from "zod";
import {
Config,
DBField,
DBType,
DrizzleColumnType,
GenerateOptions,
ORMType,
PrismaColumnType,
} from "../../types.js";
Expand All @@ -24,10 +27,12 @@ import { ExtendedSchema, Schema } from "./types.js";
import { scaffoldViewsAndComponents } from "./generators/views.js";
import {
camelCaseToSnakeCase,
formatSchemaValidationError,
formatTableName,
getCurrentSchemas,
printGenerateNextSteps,
toCamelCase,
validateSchemas,
} from "./utils.js";
import { scaffoldModel } from "./generators/model/index.js";
import { scaffoldServerActions } from "./generators/serverActions.js";
Expand Down Expand Up @@ -175,8 +180,8 @@ async function askForResourceType() {
disabled: !packages.includes("trpc")
? "[You need to have tRPC installed. Run 'kirimase add']"
: viewRequested === "views_and_components_trpc"
? "[Already generated with your selected view]"
: false,
? "[Already generated with your selected view]"
: false,
},
].filter((item) =>
viewRequested ? !viewRequested.includes(item.value.split("_")[0]) : item
Expand Down Expand Up @@ -495,7 +500,30 @@ async function generateResources(
await installShadcnComponentList();
}

export async function buildSchema() {
function parseSchemaFile(jsonString: string): Schema[] | null {
let schemas: Schema[] = [];
try {
schemas = JSON.parse(jsonString);
const validatedSchemas = validateSchemas(schemas);
return validatedSchemas;
} catch (error) {
if (error instanceof z.ZodError) {
consola.error(
`Error parsing schema file:\n${formatSchemaValidationError(
error,
schemas
)}`
);
} else if (error instanceof SyntaxError) {
consola.error(`Failed to parse JSON: ${error.message}`);
} else {
consola.error(`Unexpected error: ${error}`);
}
return null;
}
}

export async function buildSchema(options: GenerateOptions) {
const ready = preBuild();
if (!ready) return;

Expand All @@ -504,23 +532,31 @@ export async function buildSchema() {
if (config.orm !== null) {
provideInstructions();
const resourceType = await askForResourceType();
const schema = await getSchema(config, resourceType);
const schemas = options.fromFile
? parseSchemaFile(fs.readFileSync(options.fromFile, "utf8"))
: [await getSchema(config, resourceType)];

// would want to have something that formatted the schema object into:
// an array of items that needed to be created using code commented below
// would also need extra stuff like urls
// TODO

const schemas = formatSchemaForGeneration(schema);
// Stop generate if schema parsing failed
if (!schemas) return;

const formattedSchemas = schemas.flatMap((schema) =>
formatSchemaForGeneration(schema)
);

await sendEvent("generate", {
schemas: JSON.stringify(anonymiseSchemas(schemas)),
schemas: JSON.stringify(anonymiseSchemas(formattedSchemas)),
resources: resourceType,
});

for (let schema of schemas) {
for (let schema of formattedSchemas) {
await generateResources(schema, resourceType);
}
printGenerateNextSteps(schema, resourceType);
printGenerateNextSteps(schemas, resourceType);
} else {
consola.warn(
"You need to have an ORM installed in order to use the scaffold command."
Expand Down
Loading