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

Implementing Vue-Query and adding option to skip package.json #52

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ npx rapini react-query v5 -p path/to/openapi.yaml

# For SWR
npx rapini swr -p path/to/openapi.yaml

# For TanStack Vue Query
npx rapini vue-query -p path/to/openapi.yaml
```

This will generate the package code based on an OpenAPI file at `path/to/openapi.yaml`. The outputted code will be packaged in a way to just publish it as your own NPM package and then import it in your React project.
Expand Down Expand Up @@ -64,7 +67,7 @@ Commands:
```
Usage: rapini react-query [options] [react-query-version]

Generate a Package for TanStack Query V4 or V5, or legacy React Query V3
Generate a Package for TanStack Query V4 or V5, legacy React Query V3, or Vue Query

Options:
-p, --path <path> Path to OpenAPI file
Expand All @@ -74,6 +77,7 @@ Options:
-b, --base-url [url] Prefix every request with this url
-r, --replacer [oldString] [newString...] Replace part(s) of any route's path with simple string replacements. Ex: `-r /api/v1 /api/v2` would replace the v1 with v2 in every route
-h, --help display help for command
-s, --skip-package-json Skip generating package.json file
```

### `rapini help swr` outputs the following:
Expand Down
56 changes: 28 additions & 28 deletions package-lock.json

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

16 changes: 15 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env node
import { generate as generateReactQuery } from "./react-query/generator";
import { generate as generateVueQuery } from "./vue-query/generator";
import { generate as generateSWR } from "./swr/generator";
import { Argument, Command, Option } from "commander";

Expand All @@ -11,6 +12,7 @@ export type CLIOptions = {
baseUrl: string;
replacer: string[];
reactQueryVersion: "v3" | "v4" | "v5";
"skipPackage": boolean;
};

const program = new Command();
Expand All @@ -34,8 +36,18 @@ const sharedOptions = [
"-r, --replacer [oldString] [newString...]",
"Replace part(s) of any route's path with simple string replacements. Ex: `-r /api/v1 /api/v2` would replace the v1 with v2 in every route"
),
new Option("-s, --skip-package", "Skip generating package.json file, usable when you want to generate types in existing project.")
];

let vqCommand = new Command("vue-query")
.description("Generate a Package for TanStack Vue Query")
.action((options) => {
console.log(
`Generating Vue Query package using OpenAPI file ${options.path}`
);
generateVueQuery(options);
});

let rqCommand = new Command("react-query")
.description("Generate a Package for TanStack React Query")
.addArgument(
Expand All @@ -61,13 +73,15 @@ let swrComamnd = new Command("swr")
sharedOptions.forEach((option) => {
rqCommand = rqCommand.addOption(option);
swrComamnd = swrComamnd.addOption(option);
vqCommand = vqCommand.addOption(option);
});

program
.name("rapini")
.description("Generate a package based on OpenAPI")
.version("3.5.0")
.addCommand(rqCommand)
.addCommand(swrComamnd);
.addCommand(swrComamnd)
.addCommand(vqCommand);

program.parse();
2 changes: 1 addition & 1 deletion src/react-query/print.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,5 @@ export function print(result: string, options: CLIOptions) {
}

printGeneratedTS(result, options);
printPackageJson(options);
if(!options.skipPackage) printPackageJson(options);
}
2 changes: 1 addition & 1 deletion src/swr/print.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,5 @@ export function print(result: string, options: CLIOptions) {
}

printGeneratedTS(result, options);
printPackageJson(options);
if(!options.skipPackage) printPackageJson(options);
}
52 changes: 52 additions & 0 deletions src/vue-query/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import ts from "typescript";

export function makeConfigTypes() {
return [
ts.factory.createTypeAliasDeclaration(
/*modifiers*/ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
/*name*/ ts.factory.createIdentifier("AxiosConfig"),
/*typeParameters*/ undefined,
/*type*/ ts.factory.createTypeLiteralNode([
ts.factory.createPropertySignature(
/*modifiers*/ undefined,
/*name*/ ts.factory.createIdentifier("paramsSerializer"),
/*questionToken*/ ts.factory.createToken(ts.SyntaxKind.QuestionToken),
/*type*/ ts.factory.createIndexedAccessTypeNode(
/*objectType*/ ts.factory.createTypeReferenceNode(
/*typeName*/ ts.factory.createIdentifier("AxiosRequestConfig"),
/*typeArguments*/ undefined
),
/*indexType*/ ts.factory.createLiteralTypeNode(
/*literal*/ ts.factory.createStringLiteral("paramsSerializer")
)
)
),
])
),
ts.factory.createTypeAliasDeclaration(
/*modifiers*/ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
/*name*/ ts.factory.createIdentifier("Config"),
/*typeParameters*/ undefined,
/*type*/ ts.factory.createTypeLiteralNode([
ts.factory.createPropertySignature(
/*modifiers*/ undefined,
/*name*/ ts.factory.createIdentifier("mutations"),
/*questionToken*/ ts.factory.createToken(ts.SyntaxKind.QuestionToken),
/*type*/ ts.factory.createTypeReferenceNode(
/*typeName*/ ts.factory.createIdentifier("MutationConfigs"),
/*typeArguments*/ undefined
)
),
ts.factory.createPropertySignature(
/*modifiers*/ undefined,
/*name*/ ts.factory.createIdentifier("axios"),
/*questionToken*/ ts.factory.createToken(ts.SyntaxKind.QuestionToken),
/*type*/ ts.factory.createTypeReferenceNode(
/*typeName*/ ts.factory.createIdentifier("AxiosConfig"),
/*typeArguments*/ undefined
)
),
])
),
];
}
93 changes: 93 additions & 0 deletions src/vue-query/generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import ts from "typescript";
import type { OpenAPI, OpenAPIV3 } from "openapi-types";
import SwaggerParser from "@apidevtools/swagger-parser";
import { print } from "./print";
import { makeImports } from "./imports";
import { isOpenApiV3Document } from "../common/util";
import { makeQueryKeys } from "../common/queryKeys";
import { makeRequests } from "../common/requests";
import { makeQueries } from "./queries";
import { makeInitialize } from "./initialize";
import { makeMutations } from "./mutations";
import { makeTypes } from "../common/types";
import { makeRapiniMutation } from "./rapini-mutation";
import { makeConfigTypes } from "./config";
import { CLIOptions } from "../cli";

function parse(
$refs: SwaggerParser.$Refs,
doc: OpenAPI.Document,
options: CLIOptions
) {
if (isOpenApiV3Document(doc)) {
return parseOpenApiV3Doc($refs, doc, options);
}

throw "OpenAPI Document version not supported";
}

function parseOpenApiV3Doc(
$refs: SwaggerParser.$Refs,
doc: OpenAPIV3.Document,
options: CLIOptions
) {
return {
imports: makeImports(options),
queryKeys: makeQueryKeys($refs, doc.paths),
requests: makeRequests($refs, doc.paths, options),
queries: makeQueries($refs, doc.paths),
mutations: makeMutations($refs, doc.paths),
types: makeTypes($refs, doc),
};
}

function makeSourceFile(data: ReturnType<typeof parse>) {
return ts.factory.createSourceFile(
/*statements*/ [
...data.imports,
...data.types,
...makeConfigTypes(),
makeInitialize(),
makeRapiniMutation(),
...data.queryKeys,
...data.requests,
data.queries,
...data.mutations,
],
/*endOfFileToken*/ ts.factory.createToken(ts.SyntaxKind.EndOfFileToken),
/*flags*/ ts.NodeFlags.None
);
}

function makeSource(data: ReturnType<typeof parse>) {
const resultFile = ts.createSourceFile(
"client.ts",
"",
ts.ScriptTarget.Latest,
/*setParentNodes*/ false,
ts.ScriptKind.TS
);
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });

const result = printer.printNode(
ts.EmitHint.Unspecified,
makeSourceFile(data),
resultFile
);

return result;
}

export async function generate(options: CLIOptions) {
const parser = new SwaggerParser();
const api = await parser.bundle(options.path);

console.log("API name: %s, Version: %s", api.info.title, api.info.version);
try {
const data = parse(parser.$refs, api, options);
const source = makeSource(data);
print(source, options);
} catch (e) {
console.error("Failed to parse API document.", e);
}
}
Loading
Loading