feat: 发布自研 Knife4j 适配包
This commit is contained in:
parent
628fd36796
commit
6b651fc612
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,3 +6,5 @@ dist
|
|||||||
**/.turbo
|
**/.turbo
|
||||||
*.log
|
*.log
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
|
*.tgz
|
||||||
|
packages/nestjs-knife4j/ui
|
||||||
|
|||||||
1
.husky/commit-msg
Normal file
1
.husky/commit-msg
Normal file
@ -0,0 +1 @@
|
|||||||
|
node scripts/validate-commit-msg.mjs "$1"
|
||||||
1
.husky/pre-commit
Normal file
1
.husky/pre-commit
Normal file
@ -0,0 +1 @@
|
|||||||
|
pnpm run verify:commit
|
||||||
@ -3,7 +3,13 @@ import { createRouter, createWebHashHistory, RouterView } from 'vue-router';
|
|||||||
import { Knife4jSwaggerApp } from '@kt/knife4j-vue3-ui';
|
import { Knife4jSwaggerApp } from '@kt/knife4j-vue3-ui';
|
||||||
import type { Knife4jService } from '@kt/knife4j-vue3-ui';
|
import type { Knife4jService } from '@kt/knife4j-vue3-ui';
|
||||||
|
|
||||||
const services: Knife4jService[] = [
|
declare global {
|
||||||
|
interface Window {
|
||||||
|
__KNIFE4J_SERVICES__?: Knife4jService[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fallbackServices: Knife4jService[] = [
|
||||||
{
|
{
|
||||||
name: '全量接口',
|
name: '全量接口',
|
||||||
url: '/api-json',
|
url: '/api-json',
|
||||||
@ -26,12 +32,27 @@ const services: Knife4jService[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const Knife4jPlayground = defineComponent({
|
const loadServices = async () => {
|
||||||
|
if (Array.isArray(window.__KNIFE4J_SERVICES__)) {
|
||||||
|
return window.__KNIFE4J_SERVICES__;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch('services.json', { cache: 'no-store' });
|
||||||
|
const data = (await response.json()) as Knife4jService[];
|
||||||
|
return Array.isArray(data) && data.length ? data : fallbackServices;
|
||||||
|
} catch {
|
||||||
|
return fallbackServices;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadServices().then((services) => {
|
||||||
|
const Knife4jPlayground = defineComponent({
|
||||||
name: 'Knife4jPlayground',
|
name: 'Knife4jPlayground',
|
||||||
setup: () => () => <Knife4jSwaggerApp services={services} />,
|
setup: () => () => <Knife4jSwaggerApp services={services} />,
|
||||||
});
|
});
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHashHistory(),
|
history: createWebHashHistory(),
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
@ -55,8 +76,9 @@ const router = createRouter({
|
|||||||
path: '/:pathMatch(.*)*',
|
path: '/:pathMatch(.*)*',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
createApp(() => <RouterView />)
|
createApp(() => <RouterView />)
|
||||||
.use(router)
|
.use(router)
|
||||||
.mount('#app');
|
.mount('#app');
|
||||||
|
});
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx';
|
|||||||
import { fileURLToPath, URL } from 'node:url';
|
import { fileURLToPath, URL } from 'node:url';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
base: './',
|
||||||
plugins: [vue(), vueJsx()],
|
plugins: [vue(), vueJsx()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
@ -25,6 +26,10 @@ export default defineConfig({
|
|||||||
target: 'http://127.0.0.1:48085',
|
target: 'http://127.0.0.1:48085',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
|
'/services.json': {
|
||||||
|
target: 'http://127.0.0.1:48085',
|
||||||
|
changeOrigin: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -5,13 +5,18 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"packageManager": "pnpm@10.33.0",
|
"packageManager": "pnpm@10.33.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "pnpm -r --sort run build",
|
"build": "pnpm --filter @kt/openapi-parser run build && pnpm --filter @kt/knife4j-vue3-ui run build && pnpm --filter @kt/knife4j-swagger-vue3-playground run build && pnpm --filter @kwitsukasa/knife4j-swagger-vue3 run build",
|
||||||
"dev": "pnpm --filter @kt/knife4j-swagger-vue3-playground dev",
|
"dev": "pnpm --filter @kt/knife4j-swagger-vue3-playground dev",
|
||||||
"typecheck": "pnpm -r --sort run typecheck"
|
"lint": "pnpm run typecheck",
|
||||||
|
"prepare": "husky",
|
||||||
|
"typecheck": "pnpm -r --sort run typecheck",
|
||||||
|
"verify:commit": "pnpm run lint && pnpm run build"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.14.12",
|
||||||
"@vitejs/plugin-vue": "^6.0.3",
|
"@vitejs/plugin-vue": "^6.0.3",
|
||||||
"@vitejs/plugin-vue-jsx": "^5.1.5",
|
"@vitejs/plugin-vue-jsx": "^5.1.5",
|
||||||
|
"husky": "^9.1.7",
|
||||||
"sass": "^1.97.3",
|
"sass": "^1.97.3",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
Input,
|
Input,
|
||||||
Radio,
|
|
||||||
RadioGroup,
|
RadioGroup,
|
||||||
Select,
|
Select,
|
||||||
SelectOption,
|
SelectOption,
|
||||||
@ -30,7 +29,6 @@ import type {
|
|||||||
} from "@kt/openapi-parser";
|
} from "@kt/openapi-parser";
|
||||||
import type {
|
import type {
|
||||||
Knife4jDebugConfig,
|
Knife4jDebugConfig,
|
||||||
Knife4jDebugParameter,
|
|
||||||
Knife4jParameterIn,
|
Knife4jParameterIn,
|
||||||
Knife4jService,
|
Knife4jService,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
|
|||||||
27
packages/nestjs-knife4j/package.json
Normal file
27
packages/nestjs-knife4j/package.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"name": "@kwitsukasa/knife4j-swagger-vue3",
|
||||||
|
"version": "0.1.2",
|
||||||
|
"description": "KT Knife4j Swagger Vue3 UI integration for NestJS",
|
||||||
|
"main": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"license": "MIT",
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"ui"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc -p tsconfig.json && node scripts/copy-ui.mjs",
|
||||||
|
"prepack": "pnpm --dir ../.. run build",
|
||||||
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@nestjs/common": "*"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nestjs/common": "^9.4.3"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public",
|
||||||
|
"registry": "https://registry.npmjs.org/"
|
||||||
|
}
|
||||||
|
}
|
||||||
23
packages/nestjs-knife4j/scripts/copy-ui.mjs
Normal file
23
packages/nestjs-knife4j/scripts/copy-ui.mjs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { cpSync, existsSync, mkdirSync, rmSync, renameSync } from 'node:fs';
|
||||||
|
import { dirname, resolve } from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
const currentDir = dirname(fileURLToPath(import.meta.url));
|
||||||
|
const packageDir = resolve(currentDir, '..');
|
||||||
|
const workspaceDir = resolve(packageDir, '../..');
|
||||||
|
const playgroundDist = resolve(workspaceDir, 'apps/playground/dist');
|
||||||
|
const packageUi = resolve(packageDir, 'ui');
|
||||||
|
const indexHtml = resolve(packageUi, 'index.html');
|
||||||
|
const docHtml = resolve(packageUi, 'doc.html');
|
||||||
|
|
||||||
|
if (!existsSync(playgroundDist)) {
|
||||||
|
throw new Error('Playground dist not found. Run playground build before packaging.');
|
||||||
|
}
|
||||||
|
|
||||||
|
rmSync(packageUi, { force: true, recursive: true });
|
||||||
|
mkdirSync(packageUi, { recursive: true });
|
||||||
|
cpSync(playgroundDist, packageUi, { recursive: true });
|
||||||
|
|
||||||
|
if (existsSync(indexHtml)) {
|
||||||
|
renameSync(indexHtml, docHtml);
|
||||||
|
}
|
||||||
139
packages/nestjs-knife4j/src/index.ts
Normal file
139
packages/nestjs-knife4j/src/index.ts
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import type { INestApplication } from '@nestjs/common';
|
||||||
|
import { createReadStream } from 'node:fs';
|
||||||
|
import { stat } from 'node:fs/promises';
|
||||||
|
import { extname, isAbsolute, relative, resolve } from 'node:path';
|
||||||
|
|
||||||
|
export interface Service {
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NormalizedPrefix {
|
||||||
|
prefix: string;
|
||||||
|
servicePath: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ExpressStaticResponse {
|
||||||
|
end: () => void;
|
||||||
|
setHeader: (name: string, value: string) => void;
|
||||||
|
status?: (code: number) => { end: () => void };
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizePrefix = (prefix: string): NormalizedPrefix => {
|
||||||
|
const trimmedPrefix = prefix.trim() || '/';
|
||||||
|
const withLeadingSlash = trimmedPrefix.startsWith('/')
|
||||||
|
? trimmedPrefix
|
||||||
|
: `/${trimmedPrefix}`;
|
||||||
|
const normalizedPrefix = withLeadingSlash.endsWith('/')
|
||||||
|
? withLeadingSlash.slice(0, -1)
|
||||||
|
: withLeadingSlash;
|
||||||
|
const prefixPath = normalizedPrefix || '/';
|
||||||
|
const servicePath =
|
||||||
|
prefixPath === '/' ? '/services.json' : `${prefixPath}/services.json`;
|
||||||
|
|
||||||
|
return {
|
||||||
|
prefix: prefixPath,
|
||||||
|
servicePath,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getContentType = (filePath: string) => {
|
||||||
|
const ext = extname(filePath).toLowerCase();
|
||||||
|
const contentTypes: Record<string, string> = {
|
||||||
|
'.css': 'text/css; charset=utf-8',
|
||||||
|
'.gif': 'image/gif',
|
||||||
|
'.html': 'text/html; charset=utf-8',
|
||||||
|
'.ico': 'image/x-icon',
|
||||||
|
'.js': 'application/javascript; charset=utf-8',
|
||||||
|
'.json': 'application/json; charset=utf-8',
|
||||||
|
'.map': 'application/json; charset=utf-8',
|
||||||
|
'.png': 'image/png',
|
||||||
|
'.svg': 'image/svg+xml',
|
||||||
|
'.webp': 'image/webp',
|
||||||
|
'.woff': 'font/woff',
|
||||||
|
'.woff2': 'font/woff2',
|
||||||
|
};
|
||||||
|
|
||||||
|
return contentTypes[ext] || 'application/octet-stream';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getStaticPathname = (url = '/') => {
|
||||||
|
const pathname = decodeURIComponent(url.split('?')[0] || '/');
|
||||||
|
return pathname === '/' ? '/doc.html' : pathname;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveStaticFile = async (uiRoot: string, url = '/') => {
|
||||||
|
const pathname = getStaticPathname(url);
|
||||||
|
const filePath = resolve(uiRoot, `.${pathname}`);
|
||||||
|
const relativePath = relative(uiRoot, filePath);
|
||||||
|
|
||||||
|
if (relativePath.startsWith('..') || isAbsolute(relativePath)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fileStat = await stat(filePath).catch(() => undefined);
|
||||||
|
return fileStat?.isFile() ? filePath : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const sendExpressStaticFile = async (
|
||||||
|
uiRoot: string,
|
||||||
|
request: { url?: string },
|
||||||
|
response: ExpressStaticResponse,
|
||||||
|
next?: () => void,
|
||||||
|
) => {
|
||||||
|
const filePath = await resolveStaticFile(uiRoot, request.url);
|
||||||
|
|
||||||
|
if (!filePath) {
|
||||||
|
next?.();
|
||||||
|
if (!next) response.status?.(404).end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
response.setHeader('Content-Type', getContentType(filePath));
|
||||||
|
createReadStream(filePath).pipe(response as unknown as NodeJS.WritableStream);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const knife4jSetup = async (
|
||||||
|
app: INestApplication,
|
||||||
|
services: Service[],
|
||||||
|
prefix = '/',
|
||||||
|
) => {
|
||||||
|
const { prefix: staticPrefix, servicePath } = normalizePrefix(prefix);
|
||||||
|
const uiRoot = resolve(__dirname, '../ui');
|
||||||
|
const adapter = app.getHttpAdapter();
|
||||||
|
const adapterType = adapter.getType();
|
||||||
|
const instance = adapter.getInstance();
|
||||||
|
|
||||||
|
if (!['express', 'fastify'].includes(adapterType)) {
|
||||||
|
throw new Error('Knife4j only supports express and fastify adapters.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (adapterType === 'express') {
|
||||||
|
app.use(servicePath, (_request: unknown, response: any) => {
|
||||||
|
response.send(services);
|
||||||
|
});
|
||||||
|
app.use(staticPrefix, (request: any, response: any, next: () => void) => {
|
||||||
|
void sendExpressStaticFile(uiRoot, request, response, next);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.get(servicePath, (_request: unknown, reply: any) => {
|
||||||
|
reply.send(services);
|
||||||
|
});
|
||||||
|
instance.get(`${staticPrefix === '/' ? '' : staticPrefix}/*`, async (
|
||||||
|
request: { params?: Record<string, string> },
|
||||||
|
reply: any,
|
||||||
|
) => {
|
||||||
|
const wildcardPath = request.params?.['*'] || 'doc.html';
|
||||||
|
const filePath = await resolveStaticFile(uiRoot, `/${wildcardPath}`);
|
||||||
|
|
||||||
|
if (!filePath) {
|
||||||
|
reply.code(404).send();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
reply.header('Content-Type', getContentType(filePath));
|
||||||
|
reply.send(createReadStream(filePath));
|
||||||
|
});
|
||||||
|
};
|
||||||
14
packages/nestjs-knife4j/tsconfig.json
Normal file
14
packages/nestjs-knife4j/tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"isolatedModules": false,
|
||||||
|
"module": "Node16",
|
||||||
|
"moduleResolution": "Node16",
|
||||||
|
"outDir": "dist",
|
||||||
|
"rootDir": "src",
|
||||||
|
"types": ["node"]
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts"]
|
||||||
|
}
|
||||||
126
pnpm-lock.yaml
126
pnpm-lock.yaml
@ -8,12 +8,18 @@ importers:
|
|||||||
|
|
||||||
.:
|
.:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^20.14.12
|
||||||
|
version: 20.19.41
|
||||||
'@vitejs/plugin-vue':
|
'@vitejs/plugin-vue':
|
||||||
specifier: ^6.0.3
|
specifier: ^6.0.3
|
||||||
version: 6.0.7(vite@7.3.5(sass@1.100.0))(vue@3.5.35(typescript@5.9.3))
|
version: 6.0.7(vite@7.3.5(@types/node@20.19.41)(sass@1.100.0))(vue@3.5.35(typescript@5.9.3))
|
||||||
'@vitejs/plugin-vue-jsx':
|
'@vitejs/plugin-vue-jsx':
|
||||||
specifier: ^5.1.5
|
specifier: ^5.1.5
|
||||||
version: 5.1.5(vite@7.3.5(sass@1.100.0))(vue@3.5.35(typescript@5.9.3))
|
version: 5.1.5(vite@7.3.5(@types/node@20.19.41)(sass@1.100.0))(vue@3.5.35(typescript@5.9.3))
|
||||||
|
husky:
|
||||||
|
specifier: ^9.1.7
|
||||||
|
version: 9.1.7
|
||||||
sass:
|
sass:
|
||||||
specifier: ^1.97.3
|
specifier: ^1.97.3
|
||||||
version: 1.100.0
|
version: 1.100.0
|
||||||
@ -22,7 +28,7 @@ importers:
|
|||||||
version: 5.9.3
|
version: 5.9.3
|
||||||
vite:
|
vite:
|
||||||
specifier: ^7.3.1
|
specifier: ^7.3.1
|
||||||
version: 7.3.5(sass@1.100.0)
|
version: 7.3.5(@types/node@20.19.41)(sass@1.100.0)
|
||||||
vue-tsc:
|
vue-tsc:
|
||||||
specifier: ^3.2.4
|
specifier: ^3.2.4
|
||||||
version: 3.3.3(typescript@5.9.3)
|
version: 3.3.3(typescript@5.9.3)
|
||||||
@ -41,13 +47,13 @@ importers:
|
|||||||
devDependencies:
|
devDependencies:
|
||||||
'@vitejs/plugin-vue':
|
'@vitejs/plugin-vue':
|
||||||
specifier: ^6.0.3
|
specifier: ^6.0.3
|
||||||
version: 6.0.7(vite@7.3.5(sass@1.100.0))(vue@3.5.35(typescript@5.9.3))
|
version: 6.0.7(vite@7.3.5(@types/node@20.19.41)(sass@1.100.0))(vue@3.5.35(typescript@5.9.3))
|
||||||
'@vitejs/plugin-vue-jsx':
|
'@vitejs/plugin-vue-jsx':
|
||||||
specifier: ^5.1.5
|
specifier: ^5.1.5
|
||||||
version: 5.1.5(vite@7.3.5(sass@1.100.0))(vue@3.5.35(typescript@5.9.3))
|
version: 5.1.5(vite@7.3.5(@types/node@20.19.41)(sass@1.100.0))(vue@3.5.35(typescript@5.9.3))
|
||||||
vite:
|
vite:
|
||||||
specifier: ^7.3.1
|
specifier: ^7.3.1
|
||||||
version: 7.3.5(sass@1.100.0)
|
version: 7.3.5(@types/node@20.19.41)(sass@1.100.0)
|
||||||
vue-tsc:
|
vue-tsc:
|
||||||
specifier: ^3.2.4
|
specifier: ^3.2.4
|
||||||
version: 3.3.3(typescript@5.9.3)
|
version: 3.3.3(typescript@5.9.3)
|
||||||
@ -72,17 +78,23 @@ importers:
|
|||||||
devDependencies:
|
devDependencies:
|
||||||
'@vitejs/plugin-vue':
|
'@vitejs/plugin-vue':
|
||||||
specifier: ^6.0.3
|
specifier: ^6.0.3
|
||||||
version: 6.0.7(vite@7.3.5(sass@1.100.0))(vue@3.5.35(typescript@5.9.3))
|
version: 6.0.7(vite@7.3.5(@types/node@20.19.41)(sass@1.100.0))(vue@3.5.35(typescript@5.9.3))
|
||||||
'@vitejs/plugin-vue-jsx':
|
'@vitejs/plugin-vue-jsx':
|
||||||
specifier: ^5.1.5
|
specifier: ^5.1.5
|
||||||
version: 5.1.5(vite@7.3.5(sass@1.100.0))(vue@3.5.35(typescript@5.9.3))
|
version: 5.1.5(vite@7.3.5(@types/node@20.19.41)(sass@1.100.0))(vue@3.5.35(typescript@5.9.3))
|
||||||
vite:
|
vite:
|
||||||
specifier: ^7.3.1
|
specifier: ^7.3.1
|
||||||
version: 7.3.5(sass@1.100.0)
|
version: 7.3.5(@types/node@20.19.41)(sass@1.100.0)
|
||||||
vue-tsc:
|
vue-tsc:
|
||||||
specifier: ^3.2.4
|
specifier: ^3.2.4
|
||||||
version: 3.3.3(typescript@5.9.3)
|
version: 3.3.3(typescript@5.9.3)
|
||||||
|
|
||||||
|
packages/nestjs-knife4j:
|
||||||
|
devDependencies:
|
||||||
|
'@nestjs/common':
|
||||||
|
specifier: ^9.4.3
|
||||||
|
version: 9.4.3(reflect-metadata@0.1.14)(rxjs@7.8.2)
|
||||||
|
|
||||||
packages/openapi-parser: {}
|
packages/openapi-parser: {}
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
@ -413,6 +425,26 @@ packages:
|
|||||||
'@jridgewell/trace-mapping@0.3.31':
|
'@jridgewell/trace-mapping@0.3.31':
|
||||||
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||||
|
|
||||||
|
'@lukeed/csprng@1.1.0':
|
||||||
|
resolution: {integrity: sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
'@nestjs/common@9.4.3':
|
||||||
|
resolution: {integrity: sha512-Gd6D4IaYj01o14Bwv81ukidn4w3bPHCblMUq+SmUmWLyosK+XQmInCS09SbDDZyL8jy86PngtBLTdhJ2bXSUig==}
|
||||||
|
peerDependencies:
|
||||||
|
cache-manager: <=5
|
||||||
|
class-transformer: '*'
|
||||||
|
class-validator: '*'
|
||||||
|
reflect-metadata: ^0.1.12
|
||||||
|
rxjs: ^7.1.0
|
||||||
|
peerDependenciesMeta:
|
||||||
|
cache-manager:
|
||||||
|
optional: true
|
||||||
|
class-transformer:
|
||||||
|
optional: true
|
||||||
|
class-validator:
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@parcel/watcher-android-arm64@2.5.6':
|
'@parcel/watcher-android-arm64@2.5.6':
|
||||||
resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==}
|
resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==}
|
||||||
engines: {node: '>= 10.0.0'}
|
engines: {node: '>= 10.0.0'}
|
||||||
@ -645,6 +677,9 @@ packages:
|
|||||||
'@types/estree@1.0.9':
|
'@types/estree@1.0.9':
|
||||||
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
|
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
|
||||||
|
|
||||||
|
'@types/node@20.19.41':
|
||||||
|
resolution: {integrity: sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==}
|
||||||
|
|
||||||
'@types/web-bluetooth@0.0.21':
|
'@types/web-bluetooth@0.0.21':
|
||||||
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
|
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
|
||||||
|
|
||||||
@ -1037,6 +1072,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
|
||||||
|
husky@9.1.7:
|
||||||
|
resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
immutable@5.1.6:
|
immutable@5.1.6:
|
||||||
resolution: {integrity: sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ==}
|
resolution: {integrity: sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ==}
|
||||||
|
|
||||||
@ -1048,6 +1088,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
iterare@1.2.1:
|
||||||
|
resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
js-tokens@4.0.0:
|
js-tokens@4.0.0:
|
||||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||||
|
|
||||||
@ -1103,6 +1147,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==}
|
resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==}
|
||||||
engines: {node: '>= 20.19.0'}
|
engines: {node: '>= 20.19.0'}
|
||||||
|
|
||||||
|
reflect-metadata@0.1.14:
|
||||||
|
resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==}
|
||||||
|
|
||||||
resize-observer-polyfill@1.5.1:
|
resize-observer-polyfill@1.5.1:
|
||||||
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
|
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
|
||||||
|
|
||||||
@ -1111,6 +1158,9 @@ packages:
|
|||||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
rxjs@7.8.2:
|
||||||
|
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
|
||||||
|
|
||||||
sass@1.100.0:
|
sass@1.100.0:
|
||||||
resolution: {integrity: sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==}
|
resolution: {integrity: sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==}
|
||||||
engines: {node: '>=20.19.0'}
|
engines: {node: '>=20.19.0'}
|
||||||
@ -1138,11 +1188,24 @@ packages:
|
|||||||
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
|
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
||||||
|
tslib@2.5.3:
|
||||||
|
resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==}
|
||||||
|
|
||||||
|
tslib@2.8.1:
|
||||||
|
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
|
||||||
|
|
||||||
typescript@5.9.3:
|
typescript@5.9.3:
|
||||||
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
uid@2.0.2:
|
||||||
|
resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
undici-types@6.21.0:
|
||||||
|
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||||
|
|
||||||
update-browserslist-db@1.2.3:
|
update-browserslist-db@1.2.3:
|
||||||
resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
|
resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
@ -1518,6 +1581,16 @@ snapshots:
|
|||||||
'@jridgewell/resolve-uri': 3.1.2
|
'@jridgewell/resolve-uri': 3.1.2
|
||||||
'@jridgewell/sourcemap-codec': 1.5.5
|
'@jridgewell/sourcemap-codec': 1.5.5
|
||||||
|
|
||||||
|
'@lukeed/csprng@1.1.0': {}
|
||||||
|
|
||||||
|
'@nestjs/common@9.4.3(reflect-metadata@0.1.14)(rxjs@7.8.2)':
|
||||||
|
dependencies:
|
||||||
|
iterare: 1.2.1
|
||||||
|
reflect-metadata: 0.1.14
|
||||||
|
rxjs: 7.8.2
|
||||||
|
tslib: 2.5.3
|
||||||
|
uid: 2.0.2
|
||||||
|
|
||||||
'@parcel/watcher-android-arm64@2.5.6':
|
'@parcel/watcher-android-arm64@2.5.6':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@ -1658,6 +1731,10 @@ snapshots:
|
|||||||
|
|
||||||
'@types/estree@1.0.9': {}
|
'@types/estree@1.0.9': {}
|
||||||
|
|
||||||
|
'@types/node@20.19.41':
|
||||||
|
dependencies:
|
||||||
|
undici-types: 6.21.0
|
||||||
|
|
||||||
'@types/web-bluetooth@0.0.21': {}
|
'@types/web-bluetooth@0.0.21': {}
|
||||||
|
|
||||||
'@v-c/async-validator@1.0.1': {}
|
'@v-c/async-validator@1.0.1': {}
|
||||||
@ -1902,22 +1979,22 @@ snapshots:
|
|||||||
'@v-c/util': 1.0.19(vue@3.5.35(typescript@5.9.3))
|
'@v-c/util': 1.0.19(vue@3.5.35(typescript@5.9.3))
|
||||||
vue: 3.5.35(typescript@5.9.3)
|
vue: 3.5.35(typescript@5.9.3)
|
||||||
|
|
||||||
'@vitejs/plugin-vue-jsx@5.1.5(vite@7.3.5(sass@1.100.0))(vue@3.5.35(typescript@5.9.3))':
|
'@vitejs/plugin-vue-jsx@5.1.5(vite@7.3.5(@types/node@20.19.41)(sass@1.100.0))(vue@3.5.35(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/core': 7.29.7
|
'@babel/core': 7.29.7
|
||||||
'@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7)
|
'@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7)
|
||||||
'@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7)
|
'@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7)
|
||||||
'@rolldown/pluginutils': 1.0.1
|
'@rolldown/pluginutils': 1.0.1
|
||||||
'@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.7)
|
'@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.7)
|
||||||
vite: 7.3.5(sass@1.100.0)
|
vite: 7.3.5(@types/node@20.19.41)(sass@1.100.0)
|
||||||
vue: 3.5.35(typescript@5.9.3)
|
vue: 3.5.35(typescript@5.9.3)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@vitejs/plugin-vue@6.0.7(vite@7.3.5(sass@1.100.0))(vue@3.5.35(typescript@5.9.3))':
|
'@vitejs/plugin-vue@6.0.7(vite@7.3.5(@types/node@20.19.41)(sass@1.100.0))(vue@3.5.35(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rolldown/pluginutils': 1.0.1
|
'@rolldown/pluginutils': 1.0.1
|
||||||
vite: 7.3.5(sass@1.100.0)
|
vite: 7.3.5(@types/node@20.19.41)(sass@1.100.0)
|
||||||
vue: 3.5.35(typescript@5.9.3)
|
vue: 3.5.35(typescript@5.9.3)
|
||||||
|
|
||||||
'@volar/language-core@2.4.28':
|
'@volar/language-core@2.4.28':
|
||||||
@ -2176,6 +2253,8 @@ snapshots:
|
|||||||
|
|
||||||
gensync@1.0.0-beta.2: {}
|
gensync@1.0.0-beta.2: {}
|
||||||
|
|
||||||
|
husky@9.1.7: {}
|
||||||
|
|
||||||
immutable@5.1.6: {}
|
immutable@5.1.6: {}
|
||||||
|
|
||||||
is-extglob@2.1.1:
|
is-extglob@2.1.1:
|
||||||
@ -2186,6 +2265,8 @@ snapshots:
|
|||||||
is-extglob: 2.1.1
|
is-extglob: 2.1.1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
iterare@1.2.1: {}
|
||||||
|
|
||||||
js-tokens@4.0.0: {}
|
js-tokens@4.0.0: {}
|
||||||
|
|
||||||
jsesc@3.1.0: {}
|
jsesc@3.1.0: {}
|
||||||
@ -2225,6 +2306,8 @@ snapshots:
|
|||||||
|
|
||||||
readdirp@5.0.0: {}
|
readdirp@5.0.0: {}
|
||||||
|
|
||||||
|
reflect-metadata@0.1.14: {}
|
||||||
|
|
||||||
resize-observer-polyfill@1.5.1: {}
|
resize-observer-polyfill@1.5.1: {}
|
||||||
|
|
||||||
rollup@4.61.0:
|
rollup@4.61.0:
|
||||||
@ -2258,6 +2341,10 @@ snapshots:
|
|||||||
'@rollup/rollup-win32-x64-msvc': 4.61.0
|
'@rollup/rollup-win32-x64-msvc': 4.61.0
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
|
|
||||||
|
rxjs@7.8.2:
|
||||||
|
dependencies:
|
||||||
|
tslib: 2.8.1
|
||||||
|
|
||||||
sass@1.100.0:
|
sass@1.100.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
chokidar: 5.0.0
|
chokidar: 5.0.0
|
||||||
@ -2283,15 +2370,25 @@ snapshots:
|
|||||||
fdir: 6.5.0(picomatch@4.0.4)
|
fdir: 6.5.0(picomatch@4.0.4)
|
||||||
picomatch: 4.0.4
|
picomatch: 4.0.4
|
||||||
|
|
||||||
|
tslib@2.5.3: {}
|
||||||
|
|
||||||
|
tslib@2.8.1: {}
|
||||||
|
|
||||||
typescript@5.9.3: {}
|
typescript@5.9.3: {}
|
||||||
|
|
||||||
|
uid@2.0.2:
|
||||||
|
dependencies:
|
||||||
|
'@lukeed/csprng': 1.1.0
|
||||||
|
|
||||||
|
undici-types@6.21.0: {}
|
||||||
|
|
||||||
update-browserslist-db@1.2.3(browserslist@4.28.2):
|
update-browserslist-db@1.2.3(browserslist@4.28.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
browserslist: 4.28.2
|
browserslist: 4.28.2
|
||||||
escalade: 3.2.0
|
escalade: 3.2.0
|
||||||
picocolors: 1.1.1
|
picocolors: 1.1.1
|
||||||
|
|
||||||
vite@7.3.5(sass@1.100.0):
|
vite@7.3.5(@types/node@20.19.41)(sass@1.100.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
esbuild: 0.27.7
|
esbuild: 0.27.7
|
||||||
fdir: 6.5.0(picomatch@4.0.4)
|
fdir: 6.5.0(picomatch@4.0.4)
|
||||||
@ -2300,6 +2397,7 @@ snapshots:
|
|||||||
rollup: 4.61.0
|
rollup: 4.61.0
|
||||||
tinyglobby: 0.2.17
|
tinyglobby: 0.2.17
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
|
'@types/node': 20.19.41
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
sass: 1.100.0
|
sass: 1.100.0
|
||||||
|
|
||||||
|
|||||||
24
scripts/validate-commit-msg.mjs
Normal file
24
scripts/validate-commit-msg.mjs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
|
||||||
|
const messageFile = process.argv[2];
|
||||||
|
const firstLine = readFileSync(messageFile, 'utf8').split(/\r?\n/)[0].trim();
|
||||||
|
|
||||||
|
const releaseMessages = /^(?:Merge|Revert|fixup!|squash!)/;
|
||||||
|
const ktCommitPattern =
|
||||||
|
/^(?:feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(?:\([\w./-]+\))?: .+/;
|
||||||
|
const hasChineseText = /[\u4E00-\u9FFF]/;
|
||||||
|
|
||||||
|
if (releaseMessages.test(firstLine)) {
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ktCommitPattern.test(firstLine) || !hasChineseText.test(firstLine)) {
|
||||||
|
console.error(
|
||||||
|
[
|
||||||
|
'提交信息格式不正确。',
|
||||||
|
'要求:英文类型前缀 + 可选 scope + 冒号空格 + 中文描述。',
|
||||||
|
'示例:feat(knife4j): 增加提交校验',
|
||||||
|
].join('\n'),
|
||||||
|
);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
@ -11,6 +11,8 @@
|
|||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"forceConsistentCasingInFileNames": true
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,9 @@
|
|||||||
{
|
{
|
||||||
"path": "./packages/knife4j-vue3-ui"
|
"path": "./packages/knife4j-vue3-ui"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "./packages/nestjs-knife4j"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "./apps/playground"
|
"path": "./apps/playground"
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user