@qse/edu-scripts 1.13.8 → 1.13.9

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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # 更新日志
2
2
 
3
+ ## 1.13.9 (2023-08-10)
4
+
5
+ - feat: 增加 mock 功能
6
+
3
7
  ## 1.13.8 (2023-07-25)
4
8
 
5
9
  - feat: 支持 markdown 导入
package/docs/feat.md CHANGED
@@ -81,3 +81,87 @@ export default () => (
81
81
  为所有 `overflow: auto/scroll` 自动增加 `-webkit-overflow-scrolling: touch`
82
82
 
83
83
  为所有 `env(safe-area-inset-*)` 增加 fallback
84
+
85
+ ## Mock
86
+
87
+ 项目内建 mock 服务器,支持热更新,可以在 development 环境下使用
88
+
89
+ mock 功能会根据 mock 文件夹自动判断是否开启
90
+
91
+ 可以通过 override.config.js 文件配置 mock 参数关闭该功能
92
+
93
+ ### 使用步骤
94
+
95
+ 根目录创建 mock 文件夹,在该文件夹下创建的所有 js、ts 文件都会被自动加载,可以创建多层级文件夹
96
+
97
+ ```js
98
+ // path: /mock/index.js
99
+ // path: /mock/depth/folder/index.js 支持深层文件
100
+
101
+ import { defineMock } from '@qse/edu-scripts'
102
+
103
+ export default defineMock({
104
+ 'GET /api/test/test': { hello: 'world' },
105
+ 'GET /api/test/test2': [1, 2, 3],
106
+ 'POST /api/test2/test': (req, res) => {
107
+ console.log(req.body, req.cookies)
108
+ res.json({ foo: 'bar' })
109
+ },
110
+ })
111
+ ```
112
+
113
+ ### mock 规则
114
+
115
+ ```ts
116
+ import type { RequestHandler } from 'express'
117
+ type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE'
118
+ type API = string
119
+
120
+ export type MockConfig = Record<
121
+ `${Method} ${API}`,
122
+ string | number | null | undefined | boolean | Record<string, any> | RequestHandler
123
+ >
124
+
125
+ export function defineMock(config: MockConfig) {
126
+ return config
127
+ }
128
+ ```
129
+
130
+ mock 名称是由 method 和 apiUrl 组成的,按这种规则去匹配
131
+
132
+ 所有 mock 文件最终会被合并到一起,形成一个大的对象。**所以当出现同名的 mock,前面的会被后面的覆盖**
133
+
134
+ 可以通过下面的方式去获取参数
135
+
136
+ - `req.body` 获取 body 参数
137
+ - `req.query` 获取链接上的参数
138
+ - `req.cookies` 获取 cookies
139
+
140
+ <Alert>mock 规则比 proxy 具有更高的优先级,如果匹配中了 mock,就会不再去 proxy 了</Alert>
141
+
142
+ ```ts
143
+ // path: /mock/index.ts
144
+ import Mock from 'mockjs'
145
+
146
+ export default {
147
+ // method 必须是大写。可以直接返回数据
148
+ 'GET /api/test': { hello: 'world' },
149
+
150
+ // 可以通过 express.requestHandler 处理请求。能实现自定义内容,发送文件等
151
+ 'POST /api/test2': (req, res) => {
152
+ // 例如:将 body 内容返回
153
+ res.json(req.body)
154
+ },
155
+
156
+ // 可以通过第三方库生成 mock 数据
157
+ 'POST /api/test3': Mock.mock({
158
+ // 属性 list 的值是一个数组,其中含有 1 到 10 个元素
159
+ 'list|1-10': [
160
+ {
161
+ // 属性 id 是一个自增数,起始值为 1,每次增 1
162
+ 'id|+1': 1,
163
+ },
164
+ ],
165
+ }),
166
+ }
167
+ ```
package/docs/override.md CHANGED
@@ -46,6 +46,13 @@ export type Config = {
46
46
  */
47
47
  proxy?: ProxyConfigArray
48
48
  extraPostCSSPlugins?: any[]
49
+ /**
50
+ * 开启 mock 功能,会自动加载 mock 文件夹下的文件
51
+ *
52
+ * 默认情况下自动判断根目录是否存在 mock 文件夹,如果存在则开启 mock 功能。
53
+ * 如果设置 false,会关闭 mock 功能
54
+ */
55
+ mock?: boolean
49
56
  }
50
57
  ```
51
58
 
@@ -30,6 +30,7 @@ var paths = {
30
30
  src: resolveApp("src"),
31
31
  public: resolveApp("public"),
32
32
  static: resolveApp("public", "static"),
33
- theme: getExistPath(resolveApp("theme.json"), resolveApp("theme.js"))
33
+ theme: getExistPath(resolveApp("theme.json"), resolveApp("theme.js")),
34
+ mock: resolveApp("mock")
34
35
  };
35
36
  module.exports = paths;
@@ -0,0 +1,6 @@
1
+ import type { RequestHandler } from 'express';
2
+ type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE';
3
+ type API = string;
4
+ export type MockConfig = Record<`${Method} ${API}`, string | number | null | undefined | boolean | Record<string, any> | RequestHandler>;
5
+ export declare function defineMock(config: MockConfig): MockConfig;
6
+ export {};
@@ -0,0 +1,31 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/config/plugins/mock-server/defineMock.ts
20
+ var defineMock_exports = {};
21
+ __export(defineMock_exports, {
22
+ defineMock: () => defineMock
23
+ });
24
+ module.exports = __toCommonJS(defineMock_exports);
25
+ function defineMock(config) {
26
+ return config;
27
+ }
28
+ // Annotate the CommonJS export names for ESM import in node:
29
+ 0 && (module.exports = {
30
+ defineMock
31
+ });
@@ -0,0 +1,69 @@
1
+ // src/config/plugins/mock-server/index.js
2
+ var paths = require("../../paths");
3
+ var globby = require("globby");
4
+ var chokidar = require("chokidar");
5
+ var debounce = require("lodash/debounce");
6
+ var chalk = require("chalk");
7
+ var fs = require("fs-extra");
8
+ var express = require("express");
9
+ var cookieParser = require("cookie-parser");
10
+ var mockCache = {};
11
+ var isSetup = false;
12
+ var setupMock = debounce(function setupMock2() {
13
+ mockCache = {};
14
+ const files = globby.sync(paths.mock);
15
+ if (isSetup) {
16
+ console.log(chalk.green("Mock files changed, reloaded"));
17
+ } else {
18
+ isSetup = true;
19
+ }
20
+ for (const file of files) {
21
+ delete require.cache[require.resolve(file)];
22
+ try {
23
+ let mock = require(file);
24
+ mock = mock.default || mock;
25
+ mockCache = { ...mockCache, ...mock };
26
+ } catch (e) {
27
+ console.error(chalk.red(`Mock file ${file} error: ${e.message}`));
28
+ }
29
+ }
30
+ console.log("🚀 ~ file: index.js:47 ~ setupMock ~ mockCache:", mockCache);
31
+ }, 100);
32
+ function mockMiddlewave(req, res, next) {
33
+ const { method, path } = req;
34
+ const key = `${method.toUpperCase()} ${path}`;
35
+ const mock = mockCache[key];
36
+ console.log(chalk.green(`Mock: ${key}`));
37
+ if (mock) {
38
+ if (typeof mock === "function") {
39
+ mock(req, res, next);
40
+ } else {
41
+ res.json(mock);
42
+ }
43
+ } else {
44
+ next();
45
+ }
46
+ }
47
+ function setupMockServer(middelwaves, devServer) {
48
+ if (!fs.existsSync(paths.mock))
49
+ return;
50
+ require("@babel/register")({
51
+ presets: [["@babel/preset-env", { modules: "cjs" }], "@babel/preset-typescript"],
52
+ babelrc: false,
53
+ only: [/\/mock\//, /\/src\//],
54
+ extensions: [".js", ".ts"]
55
+ });
56
+ devServer.app.use(express.urlencoded({ extended: false }));
57
+ devServer.app.use(express.json());
58
+ devServer.app.use(cookieParser());
59
+ middelwaves.unshift({
60
+ name: "edu-scripts-mock-middelwave",
61
+ middleware: mockMiddlewave
62
+ });
63
+ console.log(
64
+ `<i> ${chalk.green.bold("[edu-scripts] Mock server created:")} ${chalk.cyan.bold(paths.mock)}`
65
+ );
66
+ chokidar.watch(paths.mock).on("all", setupMock);
67
+ return middelwaves;
68
+ }
69
+ module.exports = setupMockServer;
@@ -1,5 +1,6 @@
1
1
  // src/config/webpackDevServerConfig.js
2
2
  var WebpackDevServer = require("webpack-dev-server");
3
+ var setupMockServer = require("./plugins/mock-server");
3
4
  module.exports = function getWebpackDevServerConfig(args, override) {
4
5
  const host = process.env.HOST || "0.0.0.0";
5
6
  const sockHost = process.env.WDS_SOCKET_HOST;
@@ -24,6 +25,12 @@ module.exports = function getWebpackDevServerConfig(args, override) {
24
25
  "Access-Control-Allow-Methods": "*",
25
26
  "Access-Control-Allow-Headers": "*"
26
27
  },
28
+ setupMiddlewares: (middlewares, devServer2) => {
29
+ if (override.mock !== false) {
30
+ setupMockServer(middlewares, devServer2);
31
+ }
32
+ return middlewares;
33
+ },
27
34
  proxy: [
28
35
  ...override.proxy,
29
36
  {
package/lib/index.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { defineConfig } from './utils/defineConfig';
2
+ export { defineMock } from './config/plugins/mock-server/defineMock';
package/lib/index.js CHANGED
@@ -19,11 +19,14 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  // src/index.ts
20
20
  var src_exports = {};
21
21
  __export(src_exports, {
22
- defineConfig: () => import_defineConfig.defineConfig
22
+ defineConfig: () => import_defineConfig.defineConfig,
23
+ defineMock: () => import_defineMock.defineMock
23
24
  });
24
25
  module.exports = __toCommonJS(src_exports);
25
26
  var import_defineConfig = require("./utils/defineConfig");
27
+ var import_defineMock = require("./config/plugins/mock-server/defineMock");
26
28
  // Annotate the CommonJS export names for ESM import in node:
27
29
  0 && (module.exports = {
28
- defineConfig
30
+ defineConfig,
31
+ defineMock
29
32
  });
@@ -55,5 +55,12 @@ export type Config = {
55
55
  */
56
56
  proxy?: ProxyConfigArray;
57
57
  extraPostCSSPlugins?: any[];
58
+ /**
59
+ * 开启 mock 功能,会自动加载 mock 文件夹下的文件
60
+ *
61
+ * 默认情况下自动判断根目录是否存在 mock 文件夹,如果存在则开启 mock 功能。
62
+ * 如果设置 false,会关闭 mock 功能
63
+ */
64
+ mock?: boolean;
58
65
  };
59
66
  export declare function defineConfig(config: Config): Config;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qse/edu-scripts",
3
- "version": "1.13.8",
3
+ "version": "1.13.9",
4
4
  "author": "Kinoko",
5
5
  "license": "MIT",
6
6
  "description": "教育工程化基础框架",
@@ -40,6 +40,7 @@
40
40
  "@babel/preset-env": "~7.18.10",
41
41
  "@babel/preset-react": "~7.18.6",
42
42
  "@babel/preset-typescript": "~7.18.6",
43
+ "@babel/register": "~7.18.0",
43
44
  "@babel/runtime": "~7.18.9",
44
45
  "@pmmmwh/react-refresh-webpack-plugin": "^0.5.10",
45
46
  "@qse/ssh-sftp": "^1.0.0",
@@ -48,9 +49,12 @@
48
49
  "babel-plugin-import": "^1.13.6",
49
50
  "case-sensitive-paths-webpack-plugin": "^2.4.0",
50
51
  "chalk": "^4.1.2",
52
+ "chokidar": "^3.5.3",
53
+ "cookie-parser": "^1.4.6",
51
54
  "css-loader": "^6.8.1",
52
55
  "cssnano": "^6.0.1",
53
56
  "esbuild-loader": "^2.21.0",
57
+ "express": "^4.18.2",
54
58
  "filesize": "^8.0.7",
55
59
  "fs-extra": "^10.1.0",
56
60
  "globby": "^11.1.0",
@@ -33,5 +33,6 @@ const paths = {
33
33
  public: resolveApp('public'),
34
34
  static: resolveApp('public', 'static'),
35
35
  theme: getExistPath(resolveApp('theme.json'), resolveApp('theme.js')),
36
+ mock: resolveApp('mock'),
36
37
  }
37
38
  module.exports = paths
@@ -0,0 +1,12 @@
1
+ import type { RequestHandler } from 'express'
2
+ type Method = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'HEAD' | 'OPTIONS' | 'CONNECT' | 'TRACE'
3
+ type API = string
4
+
5
+ export type MockConfig = Record<
6
+ `${Method} ${API}`,
7
+ string | number | null | undefined | boolean | Record<string, any> | RequestHandler
8
+ >
9
+
10
+ export function defineMock(config: MockConfig) {
11
+ return config
12
+ }
@@ -0,0 +1,92 @@
1
+ const paths = require('../../paths')
2
+ const globby = require('globby')
3
+ const chokidar = require('chokidar')
4
+ const debounce = require('lodash/debounce')
5
+ const chalk = require('chalk')
6
+ const fs = require('fs-extra')
7
+ const express = require('express')
8
+ const cookieParser = require('cookie-parser')
9
+ let mockCache = {}
10
+ let isSetup = false
11
+
12
+ /**
13
+ * @param {import('webpack-dev-server').Middleware[]} middelwaves
14
+ */
15
+ const setupMock = debounce(function setupMock() {
16
+ // clean
17
+ mockCache = {}
18
+
19
+ const files = globby.sync(paths.mock)
20
+
21
+ if (isSetup) {
22
+ console.log(chalk.green('Mock files changed, reloaded'))
23
+ } else {
24
+ isSetup = true
25
+ }
26
+
27
+ // setup
28
+ for (const file of files) {
29
+ delete require.cache[require.resolve(file)]
30
+ try {
31
+ let mock = require(file)
32
+ mock = mock.default || mock
33
+ mockCache = { ...mockCache, ...mock }
34
+ } catch (e) {
35
+ console.error(chalk.red(`Mock file ${file} error: ${e.message}`))
36
+ }
37
+ }
38
+ console.log('🚀 ~ file: index.js:47 ~ setupMock ~ mockCache:', mockCache)
39
+ }, 100)
40
+
41
+ /**
42
+ * @type {import('express').RequestHandler}
43
+ */
44
+ function mockMiddlewave(req, res, next) {
45
+ const { method, path } = req
46
+ const key = `${method.toUpperCase()} ${path}`
47
+ const mock = mockCache[key]
48
+ console.log(chalk.green(`Mock: ${key}`))
49
+ if (mock) {
50
+ if (typeof mock === 'function') {
51
+ mock(req, res, next)
52
+ } else {
53
+ res.json(mock)
54
+ }
55
+ } else {
56
+ next()
57
+ }
58
+ }
59
+
60
+ /**
61
+ * @param {import('webpack-dev-server').Middleware[]} middelwaves
62
+ * @param {import('webpack-dev-server')} devServer
63
+ */
64
+ function setupMockServer(middelwaves, devServer) {
65
+ if (!fs.existsSync(paths.mock)) return
66
+
67
+ require('@babel/register')({
68
+ presets: [['@babel/preset-env', { modules: 'cjs' }], '@babel/preset-typescript'],
69
+ babelrc: false,
70
+ only: [/\/mock\//, /\/src\//],
71
+ extensions: ['.js', '.ts'],
72
+ })
73
+
74
+ devServer.app.use(express.urlencoded({ extended: false }))
75
+ devServer.app.use(express.json())
76
+ devServer.app.use(cookieParser())
77
+
78
+ middelwaves.unshift({
79
+ name: 'edu-scripts-mock-middelwave',
80
+ middleware: mockMiddlewave,
81
+ })
82
+
83
+ console.log(
84
+ `<i> ${chalk.green.bold('[edu-scripts] Mock server created:')} ${chalk.cyan.bold(paths.mock)}`
85
+ )
86
+
87
+ chokidar.watch(paths.mock).on('all', setupMock)
88
+
89
+ return middelwaves
90
+ }
91
+
92
+ module.exports = setupMockServer
@@ -1,4 +1,5 @@
1
1
  const WebpackDevServer = require('webpack-dev-server')
2
+ const setupMockServer = require('./plugins/mock-server')
2
3
 
3
4
  /**
4
5
  * @param {*} args
@@ -30,6 +31,13 @@ module.exports = function getWebpackDevServerConfig(args, override) {
30
31
  'Access-Control-Allow-Methods': '*',
31
32
  'Access-Control-Allow-Headers': '*',
32
33
  },
34
+ setupMiddlewares: (middlewares, devServer) => {
35
+ if (override.mock !== false) {
36
+ setupMockServer(middlewares, devServer)
37
+ }
38
+
39
+ return middlewares
40
+ },
33
41
  proxy: [
34
42
  ...override.proxy,
35
43
  {
package/src/index.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { defineConfig } from './utils/defineConfig'
2
+ export { defineMock } from './config/plugins/mock-server/defineMock'
@@ -57,6 +57,13 @@ export type Config = {
57
57
  */
58
58
  proxy?: ProxyConfigArray
59
59
  extraPostCSSPlugins?: any[]
60
+ /**
61
+ * 开启 mock 功能,会自动加载 mock 文件夹下的文件
62
+ *
63
+ * 默认情况下自动判断根目录是否存在 mock 文件夹,如果存在则开启 mock 功能。
64
+ * 如果设置 false,会关闭 mock 功能
65
+ */
66
+ mock?: boolean
60
67
  }
61
68
  export function defineConfig(config: Config) {
62
69
  return config