nsgm-cli 2.1.41 → 2.1.43

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 CHANGED
@@ -113,7 +113,7 @@ cp .env.example .env
113
113
 
114
114
  ```bash
115
115
  # Generate secure password hash
116
- npm run generate-password yourNewPassword
116
+ pnpm run generate-password yourNewPassword
117
117
 
118
118
  # Edit .env file with generated hash
119
119
  nano .env
@@ -123,10 +123,10 @@ nano .env
123
123
 
124
124
  ```bash
125
125
  # Install dependencies
126
- npm install
126
+ pnpm install
127
127
 
128
128
  # Start development server
129
- npm run dev
129
+ pnpm run dev
130
130
  ```
131
131
 
132
132
  Your application will be available at `http://localhost:3000` with:
@@ -173,9 +173,9 @@ nsgm upgrade # Upgrade project base files
173
173
  nsgm export # Export static pages
174
174
 
175
175
  # Development tools
176
- npm run lint # Code linting
177
- npm run test # Run tests
178
- npm run test:coverage # Test coverage report
176
+ pnpm run lint # Code linting
177
+ pnpm run test # Run tests
178
+ pnpm run test:coverage # Test coverage report
179
179
  ```
180
180
 
181
181
  ## 🎨 Generated Controller Features
@@ -1,14 +1,14 @@
1
- import React, { useEffect, useState } from "react";
1
+ import { useEffect, useState, ReactNode, FC } from "react";
2
2
  import { ThemeProvider } from "styled-components";
3
3
  import { GlobalStyle } from "@/styled/common";
4
4
 
5
5
  interface ClientProvidersProps {
6
- children: React.ReactNode;
6
+ children: ReactNode;
7
7
  theme: any;
8
8
  whiteColor?: boolean;
9
9
  }
10
10
 
11
- const ClientProviders: React.FC<ClientProvidersProps> = ({ children, theme, whiteColor = true }) => {
11
+ const ClientProviders: FC<ClientProvidersProps> = ({ children, theme, whiteColor = true }) => {
12
12
  const [isClient, setIsClient] = useState(false);
13
13
 
14
14
  useEffect(() => {
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from "react";
1
+ import { useEffect, useState, CSSProperties, FC } from "react";
2
2
  import { Select } from "antd";
3
3
  import { useRouter } from "next/router";
4
4
  import { GlobalOutlined } from "@ant-design/icons";
@@ -6,11 +6,11 @@ import { GlobalOutlined } from "@ant-design/icons";
6
6
  const { Option } = Select;
7
7
 
8
8
  interface LanguageSwitcherProps {
9
- style?: React.CSSProperties;
9
+ style?: CSSProperties;
10
10
  size?: "small" | "middle" | "large";
11
11
  }
12
12
 
13
- const LanguageSwitcher: React.FC<LanguageSwitcherProps> = ({ style, size = "middle" }) => {
13
+ const LanguageSwitcher: FC<LanguageSwitcherProps> = ({ style, size = "middle" }) => {
14
14
  const router = useRouter();
15
15
  const [mounted, setMounted] = useState(false);
16
16
  const [currentLocale, setCurrentLocale] = useState("zh-CN");
@@ -1,4 +1,4 @@
1
- import React, { useEffect, useState } from "react";
1
+ import { useEffect, useState, ReactNode } from "react";
2
2
  import { Layout, Menu, Dropdown, Space } from "antd";
3
3
  import {
4
4
  Container,
@@ -28,7 +28,7 @@ interface MenuItem {
28
28
  key: string;
29
29
  text: string;
30
30
  url: string;
31
- icon?: React.ReactNode;
31
+ icon?: ReactNode;
32
32
  subMenus?: SubMenuItem[];
33
33
  }
34
34
 
@@ -171,7 +171,7 @@ export const StyledInput = styled(Input)`
171
171
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
172
172
  `;
173
173
 
174
- export const StyledTable = styled(Table)`
174
+ export const StyledTable: any = styled(Table)`
175
175
  margin-top: 16px;
176
176
  border-radius: 12px;
177
177
  overflow: hidden;
@@ -17,27 +17,27 @@
17
17
 
18
18
  | 命令 | 说明 |
19
19
  | ---------------- | ------------ |
20
- | `npm run dev` | 开发模式 |
21
- | `npm run start` | 生产模式 |
22
- | `npm run build` | 编译项目 |
23
- | `npm run export` | 导出静态页面 |
20
+ | `pnpm run dev` | 开发模式 |
21
+ | `pnpm run start` | 生产模式 |
22
+ | `pnpm run build` | 编译项目 |
23
+ | `pnpm run export` | 导出静态页面 |
24
24
 
25
25
  ### 测试命令
26
26
 
27
27
  | 命令 | 说明 |
28
28
  | ----------------------- | ---------------- |
29
- | `npm test` | 运行所有测试 |
30
- | `npm run test:watch` | 监视模式运行测试 |
31
- | `npm run test:coverage` | 生成覆盖率报告 |
29
+ | `pnpm test` | 运行所有测试 |
30
+ | `pnpm run test:watch` | 监视模式运行测试 |
31
+ | `pnpm run test:coverage` | 生成覆盖率报告 |
32
32
 
33
33
  ### 代码生成命令
34
34
 
35
35
  | 命令 | 说明 |
36
36
  | ----------------------- | --------------------- |
37
- | `npm run create` | 创建模板页面 |
38
- | `npm run delete` | 删除模板页面 |
39
- | `npm run create-config` | 从配置文件批量创建模块 |
40
- | `npm run delete-config` | 从配置文件批量删除模块 |
37
+ | `pnpm run create` | 创建模板页面 |
38
+ | `pnpm run delete` | 删除模板页面 |
39
+ | `pnpm run create-config` | 从配置文件批量创建模块 |
40
+ | `pnpm run delete-config` | 从配置文件批量删除模块 |
41
41
 
42
42
  ### 项目维护命令
43
43
 
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "nsgm-cli-project",
3
3
  "version": "1.0.0",
4
+ "packageManager": "pnpm@10.28.0",
4
5
  "description": "",
5
6
  "main": "app.js",
6
7
  "scripts": {
@@ -27,24 +28,28 @@
27
28
  "nsgm-cli": "^2"
28
29
  },
29
30
  "devDependencies": {
30
- "@types/node": "^20",
31
- "@types/react": "^18",
32
- "@types/lodash": "^4",
33
- "typescript": "^5",
34
- "jest": "^30",
35
- "jest-environment-jsdom": "^30",
36
- "@testing-library/jest-dom": "^6",
37
- "@testing-library/react": "^14",
38
- "@testing-library/user-event": "^14",
39
- "@typescript-eslint/eslint-plugin": "^8",
40
- "@typescript-eslint/parser": "^8",
41
- "@types/jest": "^30",
42
- "eslint": "^9",
43
- "eslint-config-prettier": "^10",
44
- "eslint-plugin-prettier": "^5",
45
- "prettier": "^3",
46
- "next-i18next": "^15",
47
- "react-i18next": "^15",
48
- "i18next": "^24"
31
+ "@types/node": "^24.1.0",
32
+ "@types/react": "^18.3.23",
33
+ "@types/react-dom": "^18.3.7",
34
+ "@types/lodash": "^4.17.20",
35
+ "@types/express-serve-static-core": "^5.1.1",
36
+ "@types/qs": "^6.14.0",
37
+ "@types/hoist-non-react-statics": "^3.3.7",
38
+ "typescript": "^5.8.3",
39
+ "jest": "^30.0.5",
40
+ "jest-environment-jsdom": "^30.0.5",
41
+ "@testing-library/jest-dom": "^6.6.3",
42
+ "@testing-library/react": "^16.3.0",
43
+ "@testing-library/user-event": "^14.6.1",
44
+ "@types/jest": "^30.0.5",
45
+ "@typescript-eslint/eslint-plugin": "^8.38.0",
46
+ "@typescript-eslint/parser": "^8.38.0",
47
+ "eslint": "^9.31.0",
48
+ "eslint-config-prettier": "^10.1.8",
49
+ "eslint-plugin-prettier": "^5.5.3",
50
+ "prettier": "^3.6.2",
51
+ "next-i18next": "^15.3.0",
52
+ "react-i18next": "^15.2.0",
53
+ "i18next": "^24.1.0"
49
54
  }
50
- }
55
+ }
package/lib/generate.js CHANGED
@@ -13,7 +13,7 @@ const generate_init_1 = require("./generate_init");
13
13
  const generate_create_1 = require("./generate_create");
14
14
  const generate_delete_1 = require("./generate_delete");
15
15
  // 常量提取
16
- const NPM_INSTALL_FLAGS = "--legacy-peer-deps";
16
+ const PNPM_INSTALL_FLAGS = "--legacy-peer-deps";
17
17
  // 辅助函数
18
18
  const normalizeDirectory = (dictionary) => {
19
19
  // 禁止绝对路径,强制所有生成目录都在 cwd 下
@@ -26,11 +26,11 @@ const installNpmPackages = (targetDir) => {
26
26
  try {
27
27
  const prefix = targetDir ? `cd ${targetDir} && ` : "";
28
28
  console.log("Installing all dependencies from package.json...");
29
- const installResult = shelljs_1.default.exec(`${prefix}npm install ${NPM_INSTALL_FLAGS}`);
29
+ const installResult = shelljs_1.default.exec(`${prefix}pnpm install ${PNPM_INSTALL_FLAGS}`);
30
30
  return installResult.code === 0;
31
31
  }
32
32
  catch (error) {
33
- console.error("Failed to install npm packages:", error);
33
+ console.error("Failed to install pnpm packages:", error);
34
34
  return false;
35
35
  }
36
36
  };
@@ -4,6 +4,14 @@ import { BaseGenerator } from "./base-generator";
4
4
  * 自动生成对应的 DataLoader JavaScript 文件
5
5
  */
6
6
  export declare class DataLoaderGenerator extends BaseGenerator {
7
+ /**
8
+ * 可能的 JSON 字段名(需要自动解析)
9
+ */
10
+ private jsonFieldNames;
11
+ /**
12
+ * 获取可能是 JSON 的字段
13
+ */
14
+ private getJsonFields;
7
15
  generate(): string;
8
16
  /**
9
17
  * 生成外键 DataLoader
@@ -7,13 +7,66 @@ const base_generator_1 = require("./base-generator");
7
7
  * 自动生成对应的 DataLoader JavaScript 文件
8
8
  */
9
9
  class DataLoaderGenerator extends base_generator_1.BaseGenerator {
10
+ constructor() {
11
+ super(...arguments);
12
+ /**
13
+ * 可能的 JSON 字段名(需要自动解析)
14
+ */
15
+ this.jsonFieldNames = [
16
+ "images",
17
+ "photos",
18
+ "gallery",
19
+ "metadata",
20
+ "attributes",
21
+ "specs",
22
+ "options",
23
+ "settings",
24
+ "config",
25
+ "extra",
26
+ "data",
27
+ "json",
28
+ "params",
29
+ ];
30
+ }
31
+ /**
32
+ * 获取可能是 JSON 的字段
33
+ */
34
+ getJsonFields() {
35
+ return this.fields
36
+ .filter((f) => this.jsonFieldNames.some((jsonName) => f.name.toLowerCase().includes(jsonName)))
37
+ .map((f) => f.name);
38
+ }
10
39
  generate() {
11
40
  const capitalizedController = this.getCapitalizedController();
12
41
  const selectFields = this.fields.map((f) => f.name).join(", ");
42
+ const jsonFields = this.getJsonFields();
43
+ const hasJsonFields = jsonFields.length > 0;
13
44
  return `const DataLoader = require('dataloader');
14
45
  const { executeQuery } = require('../utils/common');
15
46
 
16
- /**
47
+ ${hasJsonFields
48
+ ? `/**
49
+ * 处理 ${this.controller} 行数据,将 JSON 字符串解析为对象
50
+ */
51
+ function process${capitalizedController}Row(row) {
52
+ if (!row) return row;
53
+
54
+ ${jsonFields
55
+ .map((field) => ` // 处理 ${field} 字段(JSON 字符串转对象)
56
+ if (row.${field} && typeof row.${field} === 'string') {
57
+ try {
58
+ row.${field} = JSON.parse(row.${field});
59
+ } catch (e) {
60
+ row.${field} = ${field === "images" || field === "photos" || field === "gallery" ? "[]" : "{}"};
61
+ }
62
+ }`)
63
+ .join(",\n ")}
64
+
65
+ return row;
66
+ }
67
+
68
+ `
69
+ : ""}/**
17
70
  * ${capitalizedController} DataLoader
18
71
  * 针对 ${this.controller} 表的批量数据加载器,解决 N+1 查询问题
19
72
  */
@@ -31,9 +84,7 @@ class ${capitalizedController}DataLoader {
31
84
  const results = await executeQuery(sql, [...ids]);
32
85
 
33
86
  // 确保返回顺序与输入 keys 一致,未找到的返回 null
34
- return ids.map(id =>
35
- results.find((row) => row.id === id) || null
36
- );
87
+ return ids.map(id => ${hasJsonFields ? `process${capitalizedController}Row(results.find((row) => row.id === id) || null)` : `results.find((row) => row.id === id) || null`});
37
88
  } catch (error) {
38
89
  console.error('DataLoader byId 批量加载失败:', error);
39
90
  throw error;
@@ -58,9 +109,7 @@ class ${capitalizedController}DataLoader {
58
109
  const results = await executeQuery(sql, [...names]);
59
110
 
60
111
  // 确保返回顺序与输入 keys 一致
61
- return names.map(name =>
62
- results.find((row) => row.name === name) || null
63
- );
112
+ return names.map(name => ${hasJsonFields ? `process${capitalizedController}Row(results.find((row) => row.name === name) || null)` : `results.find((row) => row.name === name) || null`});
64
113
  } catch (error) {
65
114
  console.error('DataLoader byName 批量加载失败:', error);
66
115
  throw error;
@@ -83,7 +132,8 @@ class ${capitalizedController}DataLoader {
83
132
  const results = await Promise.all(
84
133
  searchTerms.map(async (term) => {
85
134
  const sql = 'SELECT ${selectFields} FROM ${this.controller} WHERE name LIKE ?';
86
- return executeQuery(sql, [\`%\${term}%\`]);
135
+ const rows = await executeQuery(sql, [\`%\${term}%\`]);
136
+ ${hasJsonFields ? `return rows.map(process${capitalizedController}Row);` : "return rows;"}
87
137
  })
88
138
  );
89
139
 
@@ -100,7 +150,7 @@ class ${capitalizedController}DataLoader {
100
150
  }
101
151
  );
102
152
 
103
- ${this.generateForeignKeyLoaders()}
153
+ ${this.generateForeignKeyLoaders(hasJsonFields, capitalizedController)}
104
154
  }
105
155
 
106
156
  /**
@@ -170,7 +220,7 @@ module.exports = { ${capitalizedController}DataLoader, create${capitalizedContro
170
220
  /**
171
221
  * 生成外键 DataLoader
172
222
  */
173
- generateForeignKeyLoaders() {
223
+ generateForeignKeyLoaders(hasJsonFields, capitalizedController) {
174
224
  const foreignKeys = this.fields.filter((f) => f.name.endsWith("_id") && f.name !== "id");
175
225
  if (foreignKeys.length === 0) {
176
226
  return "";
@@ -193,7 +243,9 @@ module.exports = { ${capitalizedController}DataLoader, create${capitalizedContro
193
243
 
194
244
  // 按外键分组
195
245
  return ${fk.name}s.map(${fk.name} =>
196
- results.filter((row) => row.${fk.name} === ${fk.name})
246
+ results
247
+ .filter((row) => row.${fk.name} === ${fk.name})
248
+ ${hasJsonFields ? `.map(process${capitalizedController}Row)` : ""}
197
249
  );
198
250
  } catch (error) {
199
251
  console.error('DataLoader by${capitalizedRelated}Id 批量加载失败:', error);
@@ -180,7 +180,7 @@ export const ModalContainer = styled.div\`
180
180
  }
181
181
  \`
182
182
 
183
- export const StyledButton = styled(Button)<{ $primary?: boolean; $export?: boolean; $import?: boolean; $danger?: boolean }>\`
183
+ export const StyledButton: any = styled(Button)\`
184
184
  display: flex;
185
185
  align-items: center;
186
186
  border-radius: 6px;
@@ -216,13 +216,13 @@ export const StyledButton = styled(Button)<{ $primary?: boolean; $export?: boole
216
216
  \`}
217
217
  \`
218
218
 
219
- export const StyledInput = styled(Input)\`
219
+ export const StyledInput: any = styled(Input)\`
220
220
  width: 200px;
221
221
  border-radius: 6px;
222
222
  box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
223
223
  \`
224
224
 
225
- export const StyledTable = styled(Table)\`
225
+ export const StyledTable: any = styled(Table)\`
226
226
  margin-top: 16px;
227
227
  border-radius: 8px;
228
228
  overflow: hidden;
@@ -254,7 +254,7 @@ export const IconWrapper = styled.i\`
254
254
  margin-right: 5px;
255
255
  \`
256
256
 
257
- export const RoundedButton = styled(Button)\`
257
+ export const RoundedButton: any = styled(Button)\`
258
258
  border-radius: 4px;
259
259
  \`
260
260
 
package/lib/index.js CHANGED
File without changes
@@ -12,6 +12,6 @@ declare module "express-serve-static-core" {
12
12
  export declare const csrfProtection: (req: Request, res: Response, next: NextFunction) => unknown;
13
13
  export declare const getCSRFToken: (req: Request, res: Response) => void;
14
14
  export declare const securityMiddleware: {
15
- basicHeaders: import("express").RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
15
+ basicHeaders: any;
16
16
  };
17
- export declare const createCSPMiddleware: () => import("express").RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
17
+ export declare const createCSPMiddleware: () => any;