@bitstack/ng-query-codegen-openapi 0.0.30
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/README.md +77 -0
- package/lib/bin/cli.mjs +186 -0
- package/lib/bin/cli.mjs.map +1 -0
- package/lib/index.d.mts +191 -0
- package/lib/index.d.ts +191 -0
- package/lib/index.js +1392 -0
- package/lib/index.js.map +1 -0
- package/lib/index.mjs +1376 -0
- package/lib/index.mjs.map +1 -0
- package/package.json +91 -0
- package/src/bin/cli.ts +77 -0
- package/src/bin/utils.ts +85 -0
- package/src/generators/api-service-generator.ts +112 -0
- package/src/generators/cache-keys-generator.ts +43 -0
- package/src/generators/common-types-generator.ts +33 -0
- package/src/generators/component-schema-generator.ts +25 -0
- package/src/generators/do-not-modify-generator.ts +27 -0
- package/src/generators/index-generator.ts +11 -0
- package/src/generators/query-service-generator.ts +108 -0
- package/src/generators/types-generator.ts +285 -0
- package/src/generators/utils-generator.ts +33 -0
- package/src/index.ts +21 -0
- package/src/services/api-code-generator.ts +157 -0
- package/src/services/api-service-generator.ts +24 -0
- package/src/services/endpoint-info-extractor.ts +119 -0
- package/src/services/file-writer-service.ts +148 -0
- package/src/services/group-service.ts +84 -0
- package/src/services/openapi-parser-service.ts +72 -0
- package/src/services/openapi-service.ts +61 -0
- package/src/services/query-code-generator.ts +24 -0
- package/src/services/unified-code-generator.ts +353 -0
- package/src/types.ts +248 -0
- package/src/utils/capitalize.ts +3 -0
- package/src/utils/directory.ts +75 -0
- package/src/utils/downloadSchema.ts +33 -0
- package/src/utils/factory.ts +29 -0
- package/src/utils/getOperationDefinitions.ts +20 -0
- package/src/utils/getV3Doc.ts +24 -0
- package/src/utils/http.ts +86 -0
- package/src/utils/index.ts +9 -0
- package/src/utils/isQuery.ts +16 -0
- package/src/utils/isValidUrl.ts +9 -0
- package/src/utils/messages.ts +7 -0
- package/src/utils/prettier.ts +51 -0
- package/src/utils/removeUndefined.ts +3 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import semver from 'semver';
|
|
3
|
+
|
|
4
|
+
const originalFactory = ts.factory;
|
|
5
|
+
|
|
6
|
+
function createImportSpecifier(propertyName: ts.Identifier | undefined, name: ts.Identifier): ts.ImportSpecifier {
|
|
7
|
+
if (semver.satisfies(ts.version, '>= 4.5'))
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
return originalFactory.createImportSpecifier(false, propertyName, name);
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
return originalFactory.createImportSpecifier(propertyName, name);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function createExportSpecifier(
|
|
15
|
+
propertyName: string | ts.Identifier | undefined,
|
|
16
|
+
name: string | ts.Identifier
|
|
17
|
+
): ts.ExportSpecifier {
|
|
18
|
+
if (semver.satisfies(ts.version, '>= 4.5'))
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
return originalFactory.createExportSpecifier(false, propertyName, name);
|
|
21
|
+
// @ts-ignore
|
|
22
|
+
return originalFactory.createExportSpecifier(propertyName, name);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const factory = {
|
|
26
|
+
...originalFactory,
|
|
27
|
+
createImportSpecifier,
|
|
28
|
+
createExportSpecifier,
|
|
29
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { OpenAPIV3 } from 'openapi-types';
|
|
2
|
+
import type { OperationDefinition } from '../types';
|
|
3
|
+
import { operationKeys } from '../types';
|
|
4
|
+
|
|
5
|
+
export function getOperationDefinitions(v3Doc: OpenAPIV3.Document): OperationDefinition[] {
|
|
6
|
+
return Object.entries(v3Doc.paths).flatMap(([path, pathItem]) =>
|
|
7
|
+
!pathItem
|
|
8
|
+
? []
|
|
9
|
+
: Object.entries(pathItem)
|
|
10
|
+
.filter((arg): arg is [(typeof operationKeys)[number], OpenAPIV3.OperationObject] =>
|
|
11
|
+
operationKeys.includes(arg[0] as any)
|
|
12
|
+
)
|
|
13
|
+
.map(([verb, operation]) => ({
|
|
14
|
+
path,
|
|
15
|
+
verb,
|
|
16
|
+
pathItem,
|
|
17
|
+
operation,
|
|
18
|
+
}))
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import SwaggerParser from '@apidevtools/swagger-parser';
|
|
2
|
+
import type { OpenAPIV3 } from 'openapi-types';
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
import converter from 'swagger2openapi';
|
|
5
|
+
|
|
6
|
+
export async function getV3Doc(
|
|
7
|
+
spec: string,
|
|
8
|
+
httpResolverOptions?: SwaggerParser.HTTPResolverOptions
|
|
9
|
+
): Promise<OpenAPIV3.Document> {
|
|
10
|
+
const doc = await SwaggerParser.bundle(spec, {
|
|
11
|
+
resolve: {
|
|
12
|
+
http: httpResolverOptions,
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const isOpenApiV3 = 'openapi' in doc && doc.openapi.startsWith('3');
|
|
17
|
+
|
|
18
|
+
if (isOpenApiV3) {
|
|
19
|
+
return doc as OpenAPIV3.Document;
|
|
20
|
+
} else {
|
|
21
|
+
const result = await converter.convertObj(doc, {});
|
|
22
|
+
return result.openapi as OpenAPIV3.Document;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
EndpointMatcher, EndpointOverrides,
|
|
3
|
+
OperationDefinition,
|
|
4
|
+
ParameterDefinition,
|
|
5
|
+
ParameterMatcher,
|
|
6
|
+
TextMatcher,
|
|
7
|
+
} from '../types';
|
|
8
|
+
import {
|
|
9
|
+
getOperationName as _getOperationName,
|
|
10
|
+
} from 'oazapfts/generate';
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* 判斷 HTTP 狀態碼是否為成功的數據響應
|
|
15
|
+
*/
|
|
16
|
+
export function defaultIsDataResponse(code: string, includeDefault: boolean) {
|
|
17
|
+
if (includeDefault && code === 'default') {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
const parsedCode = Number(code);
|
|
21
|
+
return !Number.isNaN(parsedCode) && parsedCode >= 200 && parsedCode < 300;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 根據 HTTP 方法和路徑生成操作名稱
|
|
26
|
+
*/
|
|
27
|
+
export function getOperationName({ verb, path }: Pick<OperationDefinition, 'verb' | 'path'>) {
|
|
28
|
+
return _getOperationName(verb, path, undefined);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* 從路徑項目中提取標籤(tags)
|
|
36
|
+
*/
|
|
37
|
+
export function getTags({ verb, pathItem }: Pick<OperationDefinition, 'verb' | 'pathItem'>): string[] {
|
|
38
|
+
return verb ? pathItem[verb]?.tags || [] : [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 創建文本匹配器函數,用於過濾操作名稱
|
|
43
|
+
*/
|
|
44
|
+
function patternMatches(pattern?: TextMatcher) {
|
|
45
|
+
const filters = Array.isArray(pattern) ? pattern : [pattern];
|
|
46
|
+
return function matcher(operationName: string) {
|
|
47
|
+
if (!pattern) return true;
|
|
48
|
+
return filters.some((filter) =>
|
|
49
|
+
typeof filter === 'string' ? filter === operationName : filter?.test(operationName)
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* 創建操作匹配器函數,用於過濾操作定義
|
|
56
|
+
*/
|
|
57
|
+
export function operationMatches(pattern?: EndpointMatcher) {
|
|
58
|
+
const checkMatch = typeof pattern === 'function' ? pattern : patternMatches(pattern);
|
|
59
|
+
return function matcher(operationDefinition: OperationDefinition) {
|
|
60
|
+
if (!pattern) return true;
|
|
61
|
+
const operationName = getOperationName(operationDefinition);
|
|
62
|
+
return checkMatch(operationName, operationDefinition);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 創建參數匹配器函數,用於過濾參數定義
|
|
68
|
+
*/
|
|
69
|
+
export function argumentMatches(pattern?: ParameterMatcher) {
|
|
70
|
+
const checkMatch = typeof pattern === 'function' ? pattern : patternMatches(pattern);
|
|
71
|
+
return function matcher(argumentDefinition: ParameterDefinition) {
|
|
72
|
+
if (!pattern || argumentDefinition.in === 'path') return true;
|
|
73
|
+
const argumentName = argumentDefinition.name;
|
|
74
|
+
return checkMatch(argumentName, argumentDefinition);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 獲取操作的覆蓋配置
|
|
80
|
+
*/
|
|
81
|
+
export function getOverrides(
|
|
82
|
+
operation: OperationDefinition,
|
|
83
|
+
endpointOverrides?: EndpointOverrides[]
|
|
84
|
+
): EndpointOverrides | undefined {
|
|
85
|
+
return endpointOverrides?.find((override) => operationMatches(override.pattern)(operation));
|
|
86
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from './capitalize';
|
|
2
|
+
export * from './downloadSchema';
|
|
3
|
+
export * from './getOperationDefinitions';
|
|
4
|
+
export * from './getV3Doc';
|
|
5
|
+
export * from './isQuery';
|
|
6
|
+
export * from './isValidUrl';
|
|
7
|
+
export * from './prettier';
|
|
8
|
+
export * from './messages';
|
|
9
|
+
export * from './removeUndefined';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { EndpointOverrides, operationKeys } from '../types';
|
|
2
|
+
|
|
3
|
+
export function isQuery(
|
|
4
|
+
verb: (typeof operationKeys)[number],
|
|
5
|
+
path: string,
|
|
6
|
+
overrides: EndpointOverrides | undefined,
|
|
7
|
+
queryMatch?: (method: string, path: string) => boolean
|
|
8
|
+
) {
|
|
9
|
+
if (queryMatch) {
|
|
10
|
+
return queryMatch(verb, path);
|
|
11
|
+
}
|
|
12
|
+
if (overrides?.type) {
|
|
13
|
+
return overrides.type === 'query';
|
|
14
|
+
}
|
|
15
|
+
return verb === 'get';
|
|
16
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const MESSAGES = {
|
|
2
|
+
NAMED_EXPORT_MISSING: `You specified a named export that does not exist or was empty.`,
|
|
3
|
+
DEFAULT_EXPORT_MISSING: `Specified file exists, but no default export was found for the --baseQuery`,
|
|
4
|
+
FILE_NOT_FOUND: `Unable to locate the specified file provided to --baseQuery`,
|
|
5
|
+
TSCONFIG_FILE_NOT_FOUND: `Unable to locate the specified file provided to -c, --config`,
|
|
6
|
+
BASE_URL_IGNORED: `The url provided to --baseUrl is ignored when using --baseQuery`,
|
|
7
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import prettier from 'prettier';
|
|
3
|
+
import type { BuiltInParserName } from 'prettier';
|
|
4
|
+
|
|
5
|
+
const EXTENSION_TO_PARSER: Record<string, BuiltInParserName> = {
|
|
6
|
+
ts: 'typescript',
|
|
7
|
+
tsx: 'typescript',
|
|
8
|
+
js: 'babel',
|
|
9
|
+
jsx: 'babel',
|
|
10
|
+
'js.flow': 'flow',
|
|
11
|
+
flow: 'flow',
|
|
12
|
+
gql: 'graphql',
|
|
13
|
+
graphql: 'graphql',
|
|
14
|
+
css: 'scss',
|
|
15
|
+
scss: 'scss',
|
|
16
|
+
less: 'scss',
|
|
17
|
+
stylus: 'scss',
|
|
18
|
+
markdown: 'markdown',
|
|
19
|
+
md: 'markdown',
|
|
20
|
+
json: 'json',
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export async function prettify(filePath: string | null, content: string, prettierConfigFile?: string): Promise<string> {
|
|
24
|
+
let config = null;
|
|
25
|
+
let parser: BuiltInParserName = 'typescript';
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
if (filePath) {
|
|
29
|
+
const fileExtension = path.extname(filePath).slice(1);
|
|
30
|
+
parser = EXTENSION_TO_PARSER[fileExtension] || 'typescript';
|
|
31
|
+
config = await prettier.resolveConfig(process.cwd(), {
|
|
32
|
+
useCache: true,
|
|
33
|
+
editorconfig: !prettierConfigFile,
|
|
34
|
+
config: prettierConfigFile,
|
|
35
|
+
});
|
|
36
|
+
} else if (prettierConfigFile) {
|
|
37
|
+
config = await prettier.resolveConfig(process.cwd(), {
|
|
38
|
+
useCache: true,
|
|
39
|
+
config: prettierConfigFile,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return prettier.format(content, {
|
|
44
|
+
parser,
|
|
45
|
+
...config,
|
|
46
|
+
});
|
|
47
|
+
} catch (error) {
|
|
48
|
+
console.warn('Prettier formatting failed, returning original content:', error);
|
|
49
|
+
return content;
|
|
50
|
+
}
|
|
51
|
+
}
|