feat: 发布自研 Knife4j 适配包
This commit is contained in:
parent
628fd36796
commit
6b651fc612
2
.gitignore
vendored
2
.gitignore
vendored
@ -6,3 +6,5 @@ dist
|
||||
**/.turbo
|
||||
*.log
|
||||
*.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 type { Knife4jService } from '@kt/knife4j-vue3-ui';
|
||||
|
||||
const services: Knife4jService[] = [
|
||||
declare global {
|
||||
interface Window {
|
||||
__KNIFE4J_SERVICES__?: Knife4jService[];
|
||||
}
|
||||
}
|
||||
|
||||
const fallbackServices: Knife4jService[] = [
|
||||
{
|
||||
name: '全量接口',
|
||||
url: '/api-json',
|
||||
@ -26,37 +32,53 @@ const services: Knife4jService[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const Knife4jPlayground = defineComponent({
|
||||
name: 'Knife4jPlayground',
|
||||
setup: () => () => <Knife4jSwaggerApp services={services} />,
|
||||
});
|
||||
const loadServices = async () => {
|
||||
if (Array.isArray(window.__KNIFE4J_SERVICES__)) {
|
||||
return window.__KNIFE4J_SERVICES__;
|
||||
}
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/home/0',
|
||||
},
|
||||
{
|
||||
component: Knife4jPlayground,
|
||||
path: '/home/:service',
|
||||
},
|
||||
{
|
||||
component: Knife4jPlayground,
|
||||
path: '/api/:service/:operationId(.*)',
|
||||
},
|
||||
{
|
||||
component: Knife4jPlayground,
|
||||
path: '/static/:service/:staticType',
|
||||
},
|
||||
{
|
||||
component: Knife4jPlayground,
|
||||
path: '/:pathMatch(.*)*',
|
||||
},
|
||||
],
|
||||
});
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
createApp(() => <RouterView />)
|
||||
.use(router)
|
||||
.mount('#app');
|
||||
loadServices().then((services) => {
|
||||
const Knife4jPlayground = defineComponent({
|
||||
name: 'Knife4jPlayground',
|
||||
setup: () => () => <Knife4jSwaggerApp services={services} />,
|
||||
});
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/home/0',
|
||||
},
|
||||
{
|
||||
component: Knife4jPlayground,
|
||||
path: '/home/:service',
|
||||
},
|
||||
{
|
||||
component: Knife4jPlayground,
|
||||
path: '/api/:service/:operationId(.*)',
|
||||
},
|
||||
{
|
||||
component: Knife4jPlayground,
|
||||
path: '/static/:service/:staticType',
|
||||
},
|
||||
{
|
||||
component: Knife4jPlayground,
|
||||
path: '/:pathMatch(.*)*',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
createApp(() => <RouterView />)
|
||||
.use(router)
|
||||
.mount('#app');
|
||||
});
|
||||
|
||||
@ -4,6 +4,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx';
|
||||
import { fileURLToPath, URL } from 'node:url';
|
||||
|
||||
export default defineConfig({
|
||||
base: './',
|
||||
plugins: [vue(), vueJsx()],
|
||||
resolve: {
|
||||
alias: {
|
||||
@ -25,6 +26,10 @@ export default defineConfig({
|
||||
target: 'http://127.0.0.1:48085',
|
||||
changeOrigin: true,
|
||||
},
|
||||
'/services.json': {
|
||||
target: 'http://127.0.0.1:48085',
|
||||
changeOrigin: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@ -5,13 +5,18 @@
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@10.33.0",
|
||||
"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",
|
||||
"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": {
|
||||
"@types/node": "^20.14.12",
|
||||
"@vitejs/plugin-vue": "^6.0.3",
|
||||
"@vitejs/plugin-vue-jsx": "^5.1.5",
|
||||
"husky": "^9.1.7",
|
||||
"sass": "^1.97.3",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.3.1",
|
||||
|
||||
@ -3,7 +3,6 @@ import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Input,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Select,
|
||||
SelectOption,
|
||||
@ -30,7 +29,6 @@ import type {
|
||||
} from "@kt/openapi-parser";
|
||||
import type {
|
||||
Knife4jDebugConfig,
|
||||
Knife4jDebugParameter,
|
||||
Knife4jParameterIn,
|
||||
Knife4jService,
|
||||
} 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:
|
||||
'@types/node':
|
||||
specifier: ^20.14.12
|
||||
version: 20.19.41
|
||||
'@vitejs/plugin-vue':
|
||||
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':
|
||||
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:
|
||||
specifier: ^1.97.3
|
||||
version: 1.100.0
|
||||
@ -22,7 +28,7 @@ importers:
|
||||
version: 5.9.3
|
||||
vite:
|
||||
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:
|
||||
specifier: ^3.2.4
|
||||
version: 3.3.3(typescript@5.9.3)
|
||||
@ -41,13 +47,13 @@ importers:
|
||||
devDependencies:
|
||||
'@vitejs/plugin-vue':
|
||||
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':
|
||||
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:
|
||||
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:
|
||||
specifier: ^3.2.4
|
||||
version: 3.3.3(typescript@5.9.3)
|
||||
@ -72,17 +78,23 @@ importers:
|
||||
devDependencies:
|
||||
'@vitejs/plugin-vue':
|
||||
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':
|
||||
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:
|
||||
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:
|
||||
specifier: ^3.2.4
|
||||
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:
|
||||
@ -413,6 +425,26 @@ packages:
|
||||
'@jridgewell/trace-mapping@0.3.31':
|
||||
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':
|
||||
resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
@ -645,6 +677,9 @@ packages:
|
||||
'@types/estree@1.0.9':
|
||||
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
|
||||
|
||||
'@types/node@20.19.41':
|
||||
resolution: {integrity: sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==}
|
||||
|
||||
'@types/web-bluetooth@0.0.21':
|
||||
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
|
||||
|
||||
@ -1037,6 +1072,11 @@ packages:
|
||||
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
||||
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:
|
||||
resolution: {integrity: sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ==}
|
||||
|
||||
@ -1048,6 +1088,10 @@ packages:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
iterare@1.2.1:
|
||||
resolution: {integrity: sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
@ -1103,6 +1147,9 @@ packages:
|
||||
resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==}
|
||||
engines: {node: '>= 20.19.0'}
|
||||
|
||||
reflect-metadata@0.1.14:
|
||||
resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==}
|
||||
|
||||
resize-observer-polyfill@1.5.1:
|
||||
resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==}
|
||||
|
||||
@ -1111,6 +1158,9 @@ packages:
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
|
||||
rxjs@7.8.2:
|
||||
resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==}
|
||||
|
||||
sass@1.100.0:
|
||||
resolution: {integrity: sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==}
|
||||
engines: {node: '>=20.19.0'}
|
||||
@ -1138,11 +1188,24 @@ packages:
|
||||
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
|
||||
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:
|
||||
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
||||
engines: {node: '>=14.17'}
|
||||
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:
|
||||
resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
|
||||
hasBin: true
|
||||
@ -1518,6 +1581,16 @@ snapshots:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@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':
|
||||
optional: true
|
||||
|
||||
@ -1658,6 +1731,10 @@ snapshots:
|
||||
|
||||
'@types/estree@1.0.9': {}
|
||||
|
||||
'@types/node@20.19.41':
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
'@types/web-bluetooth@0.0.21': {}
|
||||
|
||||
'@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))
|
||||
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:
|
||||
'@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)
|
||||
'@rolldown/pluginutils': 1.0.1
|
||||
'@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)
|
||||
transitivePeerDependencies:
|
||||
- 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:
|
||||
'@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)
|
||||
|
||||
'@volar/language-core@2.4.28':
|
||||
@ -2176,6 +2253,8 @@ snapshots:
|
||||
|
||||
gensync@1.0.0-beta.2: {}
|
||||
|
||||
husky@9.1.7: {}
|
||||
|
||||
immutable@5.1.6: {}
|
||||
|
||||
is-extglob@2.1.1:
|
||||
@ -2186,6 +2265,8 @@ snapshots:
|
||||
is-extglob: 2.1.1
|
||||
optional: true
|
||||
|
||||
iterare@1.2.1: {}
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
jsesc@3.1.0: {}
|
||||
@ -2225,6 +2306,8 @@ snapshots:
|
||||
|
||||
readdirp@5.0.0: {}
|
||||
|
||||
reflect-metadata@0.1.14: {}
|
||||
|
||||
resize-observer-polyfill@1.5.1: {}
|
||||
|
||||
rollup@4.61.0:
|
||||
@ -2258,6 +2341,10 @@ snapshots:
|
||||
'@rollup/rollup-win32-x64-msvc': 4.61.0
|
||||
fsevents: 2.3.3
|
||||
|
||||
rxjs@7.8.2:
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
sass@1.100.0:
|
||||
dependencies:
|
||||
chokidar: 5.0.0
|
||||
@ -2283,15 +2370,25 @@ snapshots:
|
||||
fdir: 6.5.0(picomatch@4.0.4)
|
||||
picomatch: 4.0.4
|
||||
|
||||
tslib@2.5.3: {}
|
||||
|
||||
tslib@2.8.1: {}
|
||||
|
||||
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):
|
||||
dependencies:
|
||||
browserslist: 4.28.2
|
||||
escalade: 3.2.0
|
||||
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:
|
||||
esbuild: 0.27.7
|
||||
fdir: 6.5.0(picomatch@4.0.4)
|
||||
@ -2300,6 +2397,7 @@ snapshots:
|
||||
rollup: 4.61.0
|
||||
tinyglobby: 0.2.17
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.41
|
||||
fsevents: 2.3.3
|
||||
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,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,9 @@
|
||||
{
|
||||
"path": "./packages/knife4j-vue3-ui"
|
||||
},
|
||||
{
|
||||
"path": "./packages/nestjs-knife4j"
|
||||
},
|
||||
{
|
||||
"path": "./apps/playground"
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user