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

feat: generate just typescript #92

Merged
merged 1 commit into from
Feb 12, 2024
Merged
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
78 changes: 72 additions & 6 deletions src/__snapshots__/router.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,38 @@ export type Endpoints = {
};
};
export declare class Client {
constructor(client: AxiosInstance);
/* eslint-disable */
const substituteParams = (url: string, params: Object) =>
Object.entries(params).reduce(
(url, [name, value]) => url.replace(\\":\\" + name, encodeURIComponent(value)),
url
);
const removePathParams = (url: string, params: Object) =>
Object.entries(params)
.filter(([key, value]) => value !== undefined)
.reduce(
(accum, [name, value]) =>
url.includes(\\":\\" + name) ? accum : { ...accum, [name]: value },
{}
);
const parseQueryParamsFromPagingLink = (link: string) => {
const params = new URLSearchParams(link.split(\\"?\\")[1]);
return {
nextPageToken: params.get(\\"nextPageToken\\"),
pageSize: params.get(\\"pageSize\\"),
};
};
export class Client {
client: AxiosInstance;
constructor(client: AxiosInstance) {
this.client = client;
}
/**
* Executes the \`POST /items\` endpoint.
Expand All @@ -44,7 +74,14 @@ export declare class Client {
data: Endpoints[\\"POST /items\\"][\\"Request\\"] &
Endpoints[\\"POST /items\\"][\\"PathParams\\"],
config?: AxiosRequestConfig
): Promise<AxiosResponse<Endpoints[\\"POST /items\\"][\\"Response\\"]>>;
): Promise<AxiosResponse<Endpoints[\\"POST /items\\"][\\"Response\\"]>> {
return this.client.request({
...config,
method: \\"POST\\",
data: removePathParams(\\"/items\\", data),
url: substituteParams(\\"/items\\", data),
});
}
/**
* Executes the \`GET /items/:id\` endpoint.
Expand All @@ -58,14 +95,21 @@ export declare class Client {
data: Endpoints[\\"GET /items/:id\\"][\\"Request\\"] &
Endpoints[\\"GET /items/:id\\"][\\"PathParams\\"],
config?: AxiosRequestConfig
): Promise<AxiosResponse<Endpoints[\\"GET /items/:id\\"][\\"Response\\"]>>;
): Promise<AxiosResponse<Endpoints[\\"GET /items/:id\\"][\\"Response\\"]>> {
return this.client.request({
...config,
method: \\"GET\\",
params: removePathParams(\\"/items/:id\\", data),
url: substituteParams(\\"/items/:id\\", data),
});
}
/**
* Paginates exhaustively through the provided \`request\`, using the specified
* \`data\`. A \`pageSize\` can be specified in the \`data\` to customize the
* page size for pagination.
*/
paginate<T extends { nextPageToken?: string; pageSize?: string }, Item>(
async paginate<T extends { nextPageToken?: string; pageSize?: string }, Item>(
request: (
data: T,
config?: AxiosRequestConfig
Expand All @@ -74,7 +118,29 @@ export declare class Client {
>,
data: T,
config?: AxiosRequestConfig
): Promise<Item[]>;
): Promise<Item[]> {
const result = [];
let nextPageParams = {};
do {
// @ts-expect-error
const response = await this[request.name](
{ ...nextPageParams, ...data },
config
);
result.push(...response.data.items);
nextPageParams = response.data.links.next
? parseQueryParamsFromPagingLink(response.data.links.next)
: {};
// @ts-expect-error
} while (!!nextPageParams.nextPageToken);
return result;
}
}
module.exports.Client = Client;
"
`;
9 changes: 1 addition & 8 deletions src/bin/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,9 @@ const program = yargs(process.argv.slice(2))
outputClass: argv.name,
});

writeGeneratedFile(argv.output.replace('.ts', '.js'), output.javascript, {
writeGeneratedFile(argv.output, output.typescript, {
format: argv.format,
});
writeGeneratedFile(
argv.output.replace('.ts', '.d.ts'),
output.declaration,
{
format: argv.format,
},
);
},
)
.command(
Expand Down
163 changes: 77 additions & 86 deletions src/generate-axios-client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,12 @@ It contains newlines.
describe('generate', () => {
const generateAndFormat = (input: GenerateAxiosClientInput) =>
generateAxiosClient(input).then((source) => ({
javascript: format(source.javascript, { parser: 'babel' }),
declaration: format(source.declaration, { parser: 'typescript' }),
typescript: format(source.typescript, { parser: 'typescript' }),
}));

const FIXTURES: {
input: GenerateAxiosClientInput;
expected: { javascript: string; declaration: string };
expected: { typescript: string };
}[] = [
{
input: {
Expand Down Expand Up @@ -70,79 +69,7 @@ describe('generate', () => {
},
},
expected: {
javascript: `/* eslint-disable */
const substituteParams = (url, params) =>
Object.entries(params).reduce(
(url, [name, value]) => url.replace(":" + name, encodeURIComponent(value)),
url
);
const removePathParams = (url, params) =>
Object.entries(params)
.filter(([key, value]) => value !== undefined)
.reduce(
(accum, [name, value]) =>
url.includes(":" + name) ? accum : { ...accum, [name]: value },
{}
);
const parseQueryParamsFromPagingLink = (link) => {
const params = new URLSearchParams(link.split("?")[1]);
return {
nextPageToken: params.get("nextPageToken"),
pageSize: params.get("pageSize"),
};
};
class Client {
constructor(client) {
this.client = client;
}
getPosts(data, config) {
return this.client.request({
...config,
method: "GET",
params: removePathParams("/posts", data),
url: substituteParams("/posts", data),
});
}
putPost(data, config) {
return this.client.request({
...config,
method: "PUT",
data: removePathParams("/posts/:id", data),
url: substituteParams("/posts/:id", data),
});
}
async paginate(request, data, config) {
const result = [];
let nextPageParams = {};
do {
const response = await this[request.name](
{ ...nextPageParams, ...data },
config
);
result.push(...response.data.items);
nextPageParams = response.data.links.next
? parseQueryParamsFromPagingLink(response.data.links.next)
: {};
} while (!!nextPageParams.nextPageToken);
return result;
}
}
module.exports.Client = Client;
`,
declaration: `/* eslint-disable */
typescript: `/* eslint-disable */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we still need this?

Copy link
Member Author

@aecorredor aecorredor Feb 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we do since eslint does also check typescript files. If we don't, it might be worth putting it as another change.

import { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
export type Endpoints = {
Expand Down Expand Up @@ -176,8 +103,38 @@ export type Endpoints = {
};
};
export declare class Client {
constructor(client: AxiosInstance);
/* eslint-disable */
const substituteParams = (url: string, params: Object) =>
Object.entries(params).reduce(
(url, [name, value]) => url.replace(":" + name, encodeURIComponent(value)),
url
);
const removePathParams = (url: string, params: Object) =>
Object.entries(params)
.filter(([key, value]) => value !== undefined)
.reduce(
(accum, [name, value]) =>
url.includes(":" + name) ? accum : { ...accum, [name]: value },
{}
);
const parseQueryParamsFromPagingLink = (link: string) => {
const params = new URLSearchParams(link.split("?")[1]);
return {
nextPageToken: params.get("nextPageToken"),
pageSize: params.get("pageSize"),
};
};
export class Client {
client: AxiosInstance;
constructor(client: AxiosInstance) {
this.client = client;
}
/**
* Executes the \`GET /posts\` endpoint.
Expand All @@ -191,7 +148,14 @@ export declare class Client {
data: Endpoints["GET /posts"]["Request"] &
Endpoints["GET /posts"]["PathParams"],
config?: AxiosRequestConfig
): Promise<AxiosResponse<Endpoints["GET /posts"]["Response"]>>;
): Promise<AxiosResponse<Endpoints["GET /posts"]["Response"]>> {
return this.client.request({
...config,
method: "GET",
params: removePathParams("/posts", data),
url: substituteParams("/posts", data),
});
}
/**
* This is a long description about a field. It contains lots of very long text. Sometimes the text might be over the desired line length.
Expand All @@ -209,14 +173,21 @@ export declare class Client {
data: Endpoints["PUT /posts/:id"]["Request"] &
Endpoints["PUT /posts/:id"]["PathParams"],
config?: AxiosRequestConfig
): Promise<AxiosResponse<Endpoints["PUT /posts/:id"]["Response"]>>;
): Promise<AxiosResponse<Endpoints["PUT /posts/:id"]["Response"]>> {
return this.client.request({
...config,
method: "PUT",
data: removePathParams("/posts/:id", data),
url: substituteParams("/posts/:id", data),
});
}
/**
* Paginates exhaustively through the provided \`request\`, using the specified
* \`data\`. A \`pageSize\` can be specified in the \`data\` to customize the
* page size for pagination.
*/
paginate<T extends { nextPageToken?: string; pageSize?: string }, Item>(
async paginate<T extends { nextPageToken?: string; pageSize?: string }, Item>(
request: (
data: T,
config?: AxiosRequestConfig
Expand All @@ -225,8 +196,30 @@ export declare class Client {
>,
data: T,
config?: AxiosRequestConfig
): Promise<Item[]>;
): Promise<Item[]> {
const result = [];
let nextPageParams = {};
do {
// @ts-expect-error
const response = await this[request.name](
{ ...nextPageParams, ...data },
config
);
result.push(...response.data.items);
nextPageParams = response.data.links.next
? parseQueryParamsFromPagingLink(response.data.links.next)
: {};
// @ts-expect-error
} while (!!nextPageParams.nextPageToken);
return result;
}
}
module.exports.Client = Client;
`,
},
},
Expand All @@ -235,11 +228,9 @@ export declare class Client {
FIXTURES.forEach(({ input, expected }, idx) => {
test(`fixture ${idx}`, async () => {
const result = await generateAndFormat(input);
expect(result.javascript).toStrictEqual(expected.javascript);
expect(result.declaration).toStrictEqual(expected.declaration);
expect(result.typescript).toStrictEqual(expected.typescript);

writeFileSync(`${__dirname}/test-generated.js`, result.javascript);
writeFileSync(`${__dirname}/test-generated.d.ts`, result.declaration);
writeFileSync(`${__dirname}/test-generated.ts`, result.typescript);
});
});
});
Loading
Loading