Skip to content

Commit

Permalink
feature: generate prettified code, ignore generated code in watch mode
Browse files Browse the repository at this point in the history
  • Loading branch information
DJankauskas committed Jul 14, 2023
1 parent ad5ef4f commit 7887d5d
Show file tree
Hide file tree
Showing 12 changed files with 142 additions and 50 deletions.
15 changes: 13 additions & 2 deletions demo/stl-api-gen/api/posts/create.ts
@@ -1,6 +1,17 @@
import { z } from "stainless";
import { PostType as __symbol_PostType } from "./create";
export const Query: z.ZodTypeAny = z.object({ include: z.includes(z.lazy(() => __symbol_PostType), 3).optional() });
export const Query: z.ZodTypeAny = z.object({
include: z
.includes(
z.lazy(() => __symbol_PostType),
3
)
.optional(),
});
export const Body: z.ZodTypeAny = z.object({ body: z.string() });
export const PostType: z.ZodTypeAny = z.any();
export const post__api_posts: any = { query: z.lazy(() => Query), body: z.lazy(() => Body), response: z.lazy(() => __symbol_PostType) };
export const post__api_posts: any = {
query: z.lazy(() => Query),
body: z.lazy(() => Body),
response: z.lazy(() => __symbol_PostType),
};
23 changes: 21 additions & 2 deletions demo/stl-api-gen/api/posts/models.ts
@@ -1,4 +1,23 @@
import { z } from "stainless";
import { IncludableUserSchema, SelectableUserSchema, IncludableCommentsSchema, IncludableCommentsFieldSchema } from "../../../api/posts/models";
import {
IncludableUserSchema,
SelectableUserSchema,
IncludableCommentsSchema,
IncludableCommentsFieldSchema,
} from "../../../api/posts/models";
import { prisma } from "../../../libs/prismadb";
export const PostType: z.ZodTypeAny = z.object({ id: z.string().uuid(), body: z.string(), createdAt: z.date(), updatedAt: z.date(), userId: z.string().uuid(), likedIds: z.array(z.string().uuid()), image: z.string().nullable().optional(), user: z.lazy(() => IncludableUserSchema), user_fields: z.lazy(() => SelectableUserSchema), comments: z.lazy(() => IncludableCommentsSchema), comments_field: z.lazy(() => IncludableCommentsFieldSchema) }).prismaModel(prisma.post);
export const PostType: z.ZodTypeAny = z
.object({
id: z.string().uuid(),
body: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
userId: z.string().uuid(),
likedIds: z.array(z.string().uuid()),
image: z.string().nullable().optional(),
user: z.lazy(() => IncludableUserSchema),
user_fields: z.lazy(() => SelectableUserSchema),
comments: z.lazy(() => IncludableCommentsSchema),
comments_field: z.lazy(() => IncludableCommentsFieldSchema),
})
.prismaModel(prisma.post);
25 changes: 22 additions & 3 deletions demo/stl-api-gen/api/posts/retrieve.ts
@@ -1,7 +1,26 @@
import { z } from "stainless";
import { PostType as __symbol_PostType } from "./retrieve";
import { prisma } from "../../../libs/prismadb";
export const Query: z.ZodTypeAny = z.object({ include: z.includes(z.lazy(() => __symbol_PostType), 3).optional(), select: z.selects(z.lazy(() => __symbol_PostType), 3).optional() });
export const Path: z.ZodTypeAny = z.object({ post: z.string().prismaModelLoader(prisma.post) });
export const Query: z.ZodTypeAny = z.object({
include: z
.includes(
z.lazy(() => __symbol_PostType),
3
)
.optional(),
select: z
.selects(
z.lazy(() => __symbol_PostType),
3
)
.optional(),
});
export const Path: z.ZodTypeAny = z.object({
post: z.string().prismaModelLoader(prisma.post),
});
export const PostType: z.ZodTypeAny = z.any();
export const get__api_posts_$post$: any = { query: z.lazy(() => Query), path: z.lazy(() => Path), response: z.lazy(() => __symbol_PostType) };
export const get__api_posts_$post$: any = {
query: z.lazy(() => Query),
path: z.lazy(() => Path),
response: z.lazy(() => __symbol_PostType),
};
7 changes: 6 additions & 1 deletion demo/stl-api-gen/index.ts
@@ -1 +1,6 @@
export const typeSchemas = { "post /api/posts": () => import("./api/posts/create").then(mod => mod.post__api_posts), "get /api/posts/{post}": () => import("./api/posts/retrieve").then(mod => mod.get__api_posts_$post$) };
export const typeSchemas = {
"post /api/posts": () =>
import("./api/posts/create").then((mod) => mod.post__api_posts),
"get /api/posts/{post}": () =>
import("./api/posts/retrieve").then((mod) => mod.get__api_posts_$post$),
};
2 changes: 2 additions & 0 deletions packages/cli/package.json
Expand Up @@ -30,13 +30,15 @@
"commander": "^11.0.0",
"lodash": "^4.17.21",
"pkg-up": "3.1",
"resolve": "^1.22.2",
"ts-morph": "^19.0.0",
"ts-to-zod": "workspace:*"
},
"devDependencies": {
"@swc/core": "^1.3.66",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
"@types/resolve": "^1.20.2",
"jest": "^29.5.0",
"jest-watch-typeahead": "^2.2.2",
"nodemon": "^2.0.22",
Expand Down
23 changes: 23 additions & 0 deletions packages/cli/src/format.ts
@@ -0,0 +1,23 @@
import { promisify } from "util";
import path from "path";
import resolve from "resolve";
import defaultPrettier from "prettier";

export async function format(
source: string,
filepath: string
): Promise<string> {
let prettier = defaultPrettier;

try {
const prettierPath = await promisify<string | undefined>((cb) =>
resolve("prettier", { basedir: path.dirname(filepath) }, cb)
)();
if (prettierPath) {
prettier = await import(prettierPath);
}
} catch (error) {}

const config = await prettier.resolveConfig(filepath);
return prettier.format(source, config || { filepath });
}
49 changes: 25 additions & 24 deletions packages/cli/src/index.ts
Expand Up @@ -31,6 +31,7 @@ import {

import {
GenOptions,
GenerationConfig,
createGenerationConfig,
} from "ts-to-zod/dist/filePathConfig";

Expand All @@ -48,6 +49,7 @@ import {
mangleRouteToIdentifier,
statOrExit,
} from "./utils";
import { format } from "./format";

// TODO: add dry run functionality?
argParser.option("-w, --watch", "enables watch mode");
Expand Down Expand Up @@ -106,9 +108,19 @@ async function main() {
console.error(`Error: '${tsConfigFilePath}' is not a file.`);
}

const generationOptions = {
genLocation: {
type: "folder",
genPath: FOLDER_GEN_PATH,
},
rootPath,
zPackage: "stainless",
} as const;
const generationConfig = createGenerationConfig(generationOptions);

let watcher: Watcher | undefined;
if (options.watch) {
watcher = new Watcher(rootPath);
watcher = new Watcher(rootPath, generationConfig.basePath);
}

const project =
Expand All @@ -120,21 +132,12 @@ async function main() {

const baseCtx = new SchemaGenContext(project);

const generationOptions = {
genLocation: {
type: "folder",
genPath: FOLDER_GEN_PATH,
},
rootPath,
zPackage: "stainless",
} as const;

const printer = tm.ts.createPrinter();

const succeeded = await evaluate(
project,
baseCtx,
generationOptions,
generationConfig,
rootPath,
printer
);
Expand All @@ -147,7 +150,7 @@ async function main() {
const succeeded = await evaluate(
watcher.project,
watcher.baseCtx,
generationOptions,
generationConfig,
rootPath,
printer
);
Expand Down Expand Up @@ -182,12 +185,10 @@ function generateIncidentLocation(
async function evaluate(
project: tm.Project,
baseCtx: SchemaGenContext,
generationOptions: GenOptions,
generationConfig: GenerationConfig,
rootPath: string,
printer: ts.Printer
): Promise<boolean> {
const generationConfig = createGenerationConfig(generationOptions);

// accumulated diagnostics to emit
const callDiagnostics: CallDiagnostics[] = [];
// every stl.types.endpoint call found per file
Expand Down Expand Up @@ -365,8 +366,7 @@ async function evaluate(
imports.set(name, {
...importInfo,
sourceFile: Path.join(
rootPath,
FOLDER_GEN_PATH,
generationConfig.basePath,
Path.relative(rootPath, importInfo.sourceFile)
),
});
Expand All @@ -379,7 +379,6 @@ async function evaluate(
let importDeclarations = generateImportStatements(
generationConfig,
file.getFilePath(),
"stainless",
imports,
namespacedImports
);
Expand Down Expand Up @@ -426,7 +425,7 @@ async function evaluate(
// Commit all operations potentially destructive to AST visiting.
fileOperations.forEach((op) => op());

const generatedFileContents = generateFiles(baseCtx, generationOptions);
const generatedFileContents = generateFiles(baseCtx, generationConfig);

if (callDiagnostics.length) {
const output = [];
Expand Down Expand Up @@ -511,8 +510,7 @@ async function evaluate(
if (endpointCalls.size) {
const mapEntries = [];

const genPath = Path.join(rootPath, FOLDER_GEN_PATH);
const endpointMapGenPath = Path.join(genPath, "index.ts");
const endpointMapGenPath = Path.join(generationConfig.basePath, "index.ts");

for (const [file, calls] of endpointCalls) {
for (const call of calls) {
Expand Down Expand Up @@ -578,11 +576,11 @@ async function evaluate(
0
);

await fs.promises.mkdir(genPath, { recursive: true });
await fs.promises.mkdir(generationConfig.basePath, { recursive: true });

await fs.promises.writeFile(
endpointMapGenPath,
printer.printFile(mapSourceFile)
await format(printer.printFile(mapSourceFile), endpointMapGenPath)
);
}

Expand All @@ -600,7 +598,10 @@ async function evaluate(
);

// write sourceFile to file
await fs.promises.writeFile(file, printer.printFile(sourceFile));
await fs.promises.writeFile(
file,
await format(printer.printFile(sourceFile), file)
);
}

project.save();
Expand Down
14 changes: 8 additions & 6 deletions packages/cli/src/watch.ts
@@ -1,4 +1,4 @@
import chokidar, {FSWatcher} from "chokidar";
import chokidar, { FSWatcher } from "chokidar";
import * as tm from "ts-morph";
import path from "path";
import { SchemaGenContext } from "ts-to-zod/dist/convertType";
Expand Down Expand Up @@ -46,15 +46,17 @@ export class Watcher {
}
}

constructor(rootPath: string) {
constructor(rootPath: string, genFolderPath: string) {
this.tsConfigFilePath = path.join(rootPath, "tsconfig.json");
this.project = new tm.Project({
tsConfigFilePath: this.tsConfigFilePath,
});

this.baseCtx = new SchemaGenContext(this.project);

this.watcher = chokidar.watch(path.join(rootPath, "**"));
this.watcher = chokidar.watch(path.join(rootPath, "**"), {
ignored: path.join(genFolderPath, "**")
});
this.watcher.on("ready", () => {
console.log("Watching for file changes...");
this.ready = true;
Expand Down Expand Up @@ -96,7 +98,7 @@ export class Watcher {
if (sourceFile) {
this.project.removeSourceFile(sourceFile);
}
this.pushEvent({path, type: "unlink"});
this.pushEvent({ path, type: "unlink" });
});

// handle an error occuring during the file watching process
Expand All @@ -107,12 +109,12 @@ export class Watcher {
}
async *getEvents(): AsyncGenerator<Event, never, unknown> {
while (true) {
const event = this.eventQueue.shift()
const event = this.eventQueue.shift();
if (event) {
yield event;
} else {
yield new Promise<Event>((resolve) => {
this.resolvers.push(resolve)
this.resolvers.push(resolve);
});
}
}
Expand Down
5 changes: 3 additions & 2 deletions packages/ts-to-zod/src/__tests__/multiFileTestCase.ts
Expand Up @@ -4,7 +4,7 @@ const factory = ts.factory;
import { SchemaGenContext, convertSymbol } from "../convertType";
import { testProject } from "./testProject";
import { generateFiles } from "../generateFiles";
import { GenOptions } from "../filePathConfig";
import { GenOptions, createGenerationConfig } from "../filePathConfig";
import * as path from "path";
import pkgUp from "pkg-up";

Expand Down Expand Up @@ -49,7 +49,8 @@ export const multiFileTestCase = async (options: {
},
rootPath,
};
for (const [file, statements] of generateFiles(ctx, genOptions)) {
const generationConfig = createGenerationConfig(genOptions);
for (const [file, statements] of generateFiles(ctx, generationConfig)) {
const relativeFile = path.relative(rootPath, file);
const sourceFile = factory.createSourceFile(
statements,
Expand Down
4 changes: 3 additions & 1 deletion packages/ts-to-zod/src/filePathConfig.ts
Expand Up @@ -24,6 +24,7 @@ export interface GenerationConfig {
baseDependenciesPath: string;
rootPath: string;
suffix?: string;
zPackage?: string;
}

export function createGenerationConfig(options: GenOptions): GenerationConfig {
Expand Down Expand Up @@ -58,6 +59,7 @@ export function createGenerationConfig(options: GenOptions): GenerationConfig {
basePath,
baseDependenciesPath,
suffix,
rootPath: options.rootPath
rootPath: options.rootPath,
zPackage: options.zPackage
}
}

0 comments on commit 7887d5d

Please sign in to comment.