nsgm-cli 2.0.25 → 2.1.1
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/babel.config.js +18 -0
- package/client/components/Button.tsx +8 -0
- package/client/components/__tests__/Button.test.tsx +15 -0
- package/client/redux/store.ts +13 -11
- package/client/utils/fetch.ts +35 -6
- package/eslint.config.js +87 -0
- package/generation/eslintrc.js +16 -0
- package/generation/server/rest.js +2 -2
- package/generation/server/utils/common.js +4 -2
- package/generation/tsconfig.json +2 -1
- package/jest.config.js +29 -0
- package/lib/args.js +8 -9
- package/lib/generate.js +313 -420
- package/lib/index.d.ts +1 -1
- package/lib/index.js +106 -98
- package/lib/server/db.d.ts +6 -1
- package/lib/server/db.js +143 -61
- package/lib/server/graphql.js +147 -84
- package/lib/server/plugins/date.js +10 -10
- package/lib/server/utils/graphql-cache.d.ts +9 -0
- package/lib/server/utils/graphql-cache.js +69 -0
- package/lib/tsconfig.build.tsbuildinfo +1 -1
- package/next-env.d.ts +1 -1
- package/next.config.js +67 -0
- package/package.json +41 -17
- package/pages/template/manage.tsx +3 -3
- package/scripts/performance-check.sh +29 -0
- package/server/modules/template/resolver.js +211 -208
- package/server/rest.js +2 -2
- package/server/utils/common.js +4 -2
- package/server/utils/db-pool-manager.js +64 -0
- package/server/utils/performance.js +70 -0
package/babel.config.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/components/Button.test.js
|
|
2
|
+
import { render, screen, fireEvent } from '@testing-library/react';
|
|
3
|
+
import Button from '../Button';
|
|
4
|
+
|
|
5
|
+
test('renders button with text', () => {
|
|
6
|
+
render(<Button>Click me</Button>);
|
|
7
|
+
expect(screen.getByText('Click me')).toBeInTheDocument();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test('calls onClick when clicked', () => {
|
|
11
|
+
const handleClick = jest.fn();
|
|
12
|
+
render(<Button onClick={handleClick}>Click me</Button>);
|
|
13
|
+
fireEvent.click(screen.getByText('Click me'));
|
|
14
|
+
expect(handleClick).toHaveBeenCalledTimes(1);
|
|
15
|
+
});
|
package/client/redux/store.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { useMemo } from 'react'
|
|
2
2
|
import { combineReducers } from 'redux'
|
|
3
|
-
import { configureStore,
|
|
4
|
-
import { thunk } from 'redux-thunk'
|
|
3
|
+
import { configureStore, type EnhancedStore } from '@reduxjs/toolkit'
|
|
5
4
|
import reducers from './reducers'
|
|
6
|
-
import _ from 'lodash'
|
|
7
5
|
|
|
8
|
-
let store:
|
|
6
|
+
let store: EnhancedStore | undefined
|
|
9
7
|
|
|
10
|
-
const reducersKeysLen =
|
|
8
|
+
const reducersKeysLen = Object.keys(reducers).length
|
|
11
9
|
|
|
12
|
-
let combineReducer: any =
|
|
10
|
+
let combineReducer: any = () => ({})
|
|
13
11
|
|
|
14
12
|
if (reducersKeysLen > 0) {
|
|
15
13
|
combineReducer = combineReducers({ ...reducers })
|
|
@@ -17,17 +15,21 @@ if (reducersKeysLen > 0) {
|
|
|
17
15
|
|
|
18
16
|
export type RootState = ReturnType<typeof combineReducer>
|
|
19
17
|
|
|
20
|
-
function initStore(initialState
|
|
18
|
+
function initStore(initialState?: any): EnhancedStore {
|
|
21
19
|
return configureStore({
|
|
22
20
|
reducer: combineReducer,
|
|
23
21
|
preloadedState: initialState,
|
|
24
|
-
devTools:
|
|
22
|
+
devTools: process.env.NODE_ENV !== 'production',
|
|
25
23
|
middleware: (getDefaultMiddleware) =>
|
|
26
|
-
getDefaultMiddleware(
|
|
24
|
+
getDefaultMiddleware({
|
|
25
|
+
serializableCheck: {
|
|
26
|
+
ignoredActions: ['persist/PERSIST', 'persist/REHYDRATE'],
|
|
27
|
+
},
|
|
28
|
+
}),
|
|
27
29
|
})
|
|
28
30
|
}
|
|
29
31
|
|
|
30
|
-
export const initializeStore = (preloadedState
|
|
32
|
+
export const initializeStore = (preloadedState?: any): EnhancedStore => {
|
|
31
33
|
let _store = store ?? initStore(preloadedState)
|
|
32
34
|
|
|
33
35
|
if (preloadedState && store) {
|
|
@@ -45,7 +47,7 @@ export const initializeStore = (preloadedState: any) => {
|
|
|
45
47
|
return _store
|
|
46
48
|
}
|
|
47
49
|
|
|
48
|
-
export function useStore(initialState
|
|
50
|
+
export function useStore(initialState?: any): EnhancedStore {
|
|
49
51
|
const store = useMemo(() => initializeStore(initialState), [initialState])
|
|
50
52
|
return store
|
|
51
53
|
}
|
package/client/utils/fetch.ts
CHANGED
|
@@ -1,25 +1,54 @@
|
|
|
1
1
|
import axios from 'axios'
|
|
2
2
|
import { getLocalApiPrefix } from './common'
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// 添加缓存机制
|
|
5
|
+
const queryCache = new Map<string, { data: any; timestamp: number }>()
|
|
6
|
+
const CACHE_DURATION = 5 * 60 * 1000 // 5分钟缓存
|
|
7
|
+
|
|
8
|
+
export const getLocalGraphql = (query: string, variables: any = {}, useCache = false) => {
|
|
5
9
|
return new Promise((resolve, reject) => {
|
|
10
|
+
// 生成缓存键
|
|
11
|
+
const cacheKey = `${query}:${JSON.stringify(variables)}`
|
|
12
|
+
|
|
13
|
+
// 检查缓存
|
|
14
|
+
if (useCache && queryCache.has(cacheKey)) {
|
|
15
|
+
const cached = queryCache.get(cacheKey)!
|
|
16
|
+
if (Date.now() - cached.timestamp < CACHE_DURATION) {
|
|
17
|
+
resolve(cached.data)
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
6
22
|
axios
|
|
7
23
|
.post(getLocalApiPrefix() + '/graphql', {
|
|
8
24
|
query,
|
|
9
25
|
variables
|
|
10
26
|
})
|
|
11
27
|
.then((res) => {
|
|
12
|
-
// console.log('axios_res', res)
|
|
13
28
|
if (res) {
|
|
14
29
|
const { data } = res
|
|
30
|
+
|
|
31
|
+
// 缓存结果
|
|
32
|
+
if (useCache && data && !data.errors) {
|
|
33
|
+
queryCache.set(cacheKey, {
|
|
34
|
+
data,
|
|
35
|
+
timestamp: Date.now()
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
15
39
|
resolve(data)
|
|
16
40
|
} else {
|
|
17
|
-
reject()
|
|
41
|
+
reject(new Error('No data received'))
|
|
18
42
|
}
|
|
19
43
|
})
|
|
20
|
-
.catch((
|
|
21
|
-
|
|
22
|
-
reject(
|
|
44
|
+
.catch((error) => {
|
|
45
|
+
console.error('GraphQL query failed:', error)
|
|
46
|
+
reject(error)
|
|
23
47
|
})
|
|
24
48
|
})
|
|
25
49
|
}
|
|
50
|
+
|
|
51
|
+
// 清除缓存
|
|
52
|
+
export const clearGraphqlCache = () => {
|
|
53
|
+
queryCache.clear()
|
|
54
|
+
}
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const js = require('@eslint/js');
|
|
2
|
+
const typescript = require('@typescript-eslint/eslint-plugin');
|
|
3
|
+
const typescriptParser = require('@typescript-eslint/parser');
|
|
4
|
+
const prettier = require('eslint-plugin-prettier');
|
|
5
|
+
|
|
6
|
+
module.exports = [
|
|
7
|
+
js.configs.recommended,
|
|
8
|
+
{
|
|
9
|
+
files: ['src/**/*.{ts,tsx}'],
|
|
10
|
+
languageOptions: {
|
|
11
|
+
parser: typescriptParser,
|
|
12
|
+
parserOptions: {
|
|
13
|
+
ecmaVersion: 2022,
|
|
14
|
+
sourceType: 'module',
|
|
15
|
+
project: './tsconfig.json'
|
|
16
|
+
},
|
|
17
|
+
globals: {
|
|
18
|
+
__dirname: 'readonly',
|
|
19
|
+
__filename: 'readonly',
|
|
20
|
+
process: 'readonly',
|
|
21
|
+
Buffer: 'readonly',
|
|
22
|
+
console: 'readonly',
|
|
23
|
+
setTimeout: 'readonly',
|
|
24
|
+
clearTimeout: 'readonly',
|
|
25
|
+
setInterval: 'readonly',
|
|
26
|
+
clearInterval: 'readonly'
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
plugins: {
|
|
30
|
+
'@typescript-eslint': typescript,
|
|
31
|
+
'prettier': prettier
|
|
32
|
+
},
|
|
33
|
+
rules: {
|
|
34
|
+
'no-console': 'off', // 允许 console 语句用于调试
|
|
35
|
+
'no-eval': 'off',
|
|
36
|
+
'no-undef': 'off',
|
|
37
|
+
'no-unused-vars': 'off', // 关闭原生规则,使用 TypeScript 版本
|
|
38
|
+
'no-case-declarations': 'off',
|
|
39
|
+
'no-fallthrough': 'off',
|
|
40
|
+
'prefer-const': 'warn',
|
|
41
|
+
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'warn',
|
|
42
|
+
'no-unreachable': 'error',
|
|
43
|
+
'no-duplicate-imports': 'error',
|
|
44
|
+
'prefer-template': 'warn',
|
|
45
|
+
'prefer-arrow-callback': 'warn',
|
|
46
|
+
'@typescript-eslint/no-var-requires': 'off', // 允许 require
|
|
47
|
+
'@typescript-eslint/no-explicit-any': 'warn', // 警告但不报错
|
|
48
|
+
'@typescript-eslint/no-unused-vars': ['warn', {
|
|
49
|
+
argsIgnorePattern: '^_',
|
|
50
|
+
varsIgnorePattern: '^_'
|
|
51
|
+
}],
|
|
52
|
+
'@typescript-eslint/no-inferrable-types': 'warn',
|
|
53
|
+
'@typescript-eslint/prefer-optional-chain': 'warn',
|
|
54
|
+
'@typescript-eslint/no-explicit-any': 'off',
|
|
55
|
+
'prettier/prettier': 'error'
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
files: ['**/*.js'],
|
|
60
|
+
languageOptions: {
|
|
61
|
+
ecmaVersion: 2022,
|
|
62
|
+
sourceType: 'commonjs',
|
|
63
|
+
globals: {
|
|
64
|
+
require: 'readonly',
|
|
65
|
+
module: 'readonly',
|
|
66
|
+
exports: 'readonly',
|
|
67
|
+
__dirname: 'readonly',
|
|
68
|
+
__filename: 'readonly',
|
|
69
|
+
process: 'readonly',
|
|
70
|
+
Buffer: 'readonly',
|
|
71
|
+
console: 'readonly'
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
rules: {
|
|
75
|
+
'no-console': 'off'
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
ignores: [
|
|
80
|
+
'node_modules/**',
|
|
81
|
+
'lib/**',
|
|
82
|
+
'.next/**',
|
|
83
|
+
'coverage/**',
|
|
84
|
+
'*.config.js'
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const express = require('express')
|
|
2
|
-
const
|
|
2
|
+
const dayjs = require('dayjs')
|
|
3
3
|
const sso = require('./apis/sso')
|
|
4
4
|
//const template = require('./apis/template')
|
|
5
5
|
|
|
@@ -7,7 +7,7 @@ const router = express.Router()
|
|
|
7
7
|
|
|
8
8
|
router.use((req, res, next) => {
|
|
9
9
|
const fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl
|
|
10
|
-
console.log(
|
|
10
|
+
console.log(dayjs().format('YYYY-MM-DD HH:mm:ss') + ' ' + fullUrl)
|
|
11
11
|
next()
|
|
12
12
|
})
|
|
13
13
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const { serverDB } = require('nsgm-cli')
|
|
2
2
|
|
|
3
|
-
const { getConnection } = serverDB
|
|
3
|
+
const { getConnection, executeQuery, executePaginatedQuery } = serverDB
|
|
4
4
|
|
|
5
5
|
module.exports = {
|
|
6
|
-
getConnection
|
|
6
|
+
getConnection, // 兼容旧版本
|
|
7
|
+
executeQuery, // 新的查询方法
|
|
8
|
+
executePaginatedQuery // 新的分页查询方法
|
|
7
9
|
}
|
package/generation/tsconfig.json
CHANGED
package/jest.config.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
testEnvironment: 'jsdom',
|
|
3
|
+
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
|
|
4
|
+
transform: {
|
|
5
|
+
'^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }]
|
|
6
|
+
},
|
|
7
|
+
testMatch: [
|
|
8
|
+
'**/__tests__/**/*.(ts|tsx|js)',
|
|
9
|
+
'**/*.(test|spec).(ts|tsx|js)'
|
|
10
|
+
],
|
|
11
|
+
collectCoverage: true,
|
|
12
|
+
collectCoverageFrom: [
|
|
13
|
+
'client/**/*.{js,jsx,ts,tsx}',
|
|
14
|
+
'pages/**/*.{js,jsx,ts,tsx}',
|
|
15
|
+
'server/**/*.{js,jsx,ts,tsx}',
|
|
16
|
+
'src/**/*.{js,jsx,ts,tsx}',
|
|
17
|
+
'!**/*.d.ts',
|
|
18
|
+
'!**/node_modules/**',
|
|
19
|
+
'!**/.next/**'
|
|
20
|
+
],
|
|
21
|
+
coverageDirectory: 'coverage',
|
|
22
|
+
coverageReporters: ['json', 'lcov', 'text', 'clover'],
|
|
23
|
+
extensionsToTreatAsEsm: ['.ts', '.tsx'],
|
|
24
|
+
globals: {
|
|
25
|
+
'ts-jest': {
|
|
26
|
+
useESM: true
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
package/lib/args.js
CHANGED
|
@@ -4,24 +4,23 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getProcessArgvs = void 0;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
var result = {
|
|
7
|
+
const lodash_1 = __importDefault(require("lodash"));
|
|
8
|
+
const getProcessArgvs = (removeItems = 2) => {
|
|
9
|
+
const args = process.argv.slice(removeItems);
|
|
10
|
+
const result = {
|
|
12
11
|
command: '', // dev, start, build, export, create, delete, init, help
|
|
13
12
|
dictionary: '', // export/init dictionary=${dictionary}
|
|
14
13
|
controller: '',
|
|
15
14
|
action: '' // create/delete controller=${controller} action=${action}
|
|
16
15
|
};
|
|
17
|
-
lodash_1.default.each(args,
|
|
16
|
+
lodash_1.default.each(args, (item, index) => {
|
|
18
17
|
if (item.indexOf('=') !== -1) {
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const itemArr = item.split('=');
|
|
19
|
+
const key = itemArr[0].toLowerCase();
|
|
21
20
|
result[key] = itemArr[1];
|
|
22
21
|
}
|
|
23
22
|
else {
|
|
24
|
-
|
|
23
|
+
const { command } = result;
|
|
25
24
|
switch (index) {
|
|
26
25
|
case 0:
|
|
27
26
|
result.command = item;
|