ssr-plugin-react 6.1.5 → 6.1.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 +264 -223
- package/LICENSE +1 -1
- package/README.md +37 -29
- package/cjs/config/base.d.ts +1 -1
- package/cjs/entry/create-context.d.ts +1 -3
- package/cjs/entry/create-context.js +14 -3
- package/cjs/entry/server-entry.js +4 -2
- package/esm/config/base.d.ts +1 -1
- package/esm/entry/create-context.d.ts +1 -3
- package/esm/entry/create-context.js +14 -3
- package/esm/entry/server-entry.js +4 -2
- package/package.json +10 -10
- package/src/entry/create-context.ts +12 -3
- package/src/entry/server-entry.tsx +8 -5
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -13,18 +13,18 @@
|
|
|
13
13
|
</div>
|
|
14
14
|
<br />
|
|
15
15
|
<div align="center">
|
|
16
|
-
<a href="https://github.com/
|
|
16
|
+
<a href="https://github.com/zhangyuang/ssr/actions" target="_blank"><img src="https://github.com/zhangyuang/ssr/workflows/CI/badge.svg" alt="githubActions"></a>
|
|
17
17
|
<a href="https://www.cypress.io/" target="_blank"><img src="https://img.shields.io/badge/cypress-dashboard-brightgreen.svg" alt="cypress"></a>
|
|
18
18
|
<a href="https://npmcharts.com/compare/ssr" target="_blank"><img src="https://img.shields.io/npm/dm/ssr" alt="download"></a>
|
|
19
19
|
<a href="https://standardjs.com" target="_blank"><img src="https://img.shields.io/badge/code_style-standard-brightgreen.svg" alt="standardjs"></a>
|
|
20
|
-
<a href="https://github.com/
|
|
21
|
-
<a href="https://github.com/
|
|
20
|
+
<a href="https://github.com/zhangyuang/ssr" target="_blank"><img src="https://img.shields.io/npm/l/vue.svg" alt="License"></a>
|
|
21
|
+
<a href="https://github.com/zhangyuang/ssr" target="_blank"><img src="https://img.shields.io/badge/node-%3E=12-green.svg" alt="Node"></a>
|
|
22
22
|
</div>
|
|
23
23
|
<br />
|
|
24
24
|
|
|
25
25
|
`ssr` 框架是为前端框架在服务端渲染的场景下所打造的开箱即用的服务端渲染框架。了解什么是服务端渲染请查看[文档](http://doc.ssr-fc.com/docs/features$ssr)
|
|
26
26
|
|
|
27
|
-
此框架脱胎于 [egg-react-ssr](https://github.com/
|
|
27
|
+
此框架脱胎于 [egg-react-ssr](https://github.com/zhangyuang/egg-react-ssr) 项目和`ssr` v4.3版本(midway-faas + react ssr),在之前的基础上做了诸多演进,通过插件化的代码组织形式,支持任意服务端框架与任意前端框架的组合使用。开发者可以选择通过 Serverless 方式部署或是以传统 Node.js 的应用形式部署,并且我们专注于提升 Serverless 场景下服务端渲染应用的开发体验,打造了一站式的开发,发布应用服务的功能。最大程度提升开发者的开发体验,将应用的开发,部署成本降到最低。
|
|
28
28
|
|
|
29
29
|
在最新的 v5.0 版本中,同时支持 `React` 和 `Vue2/Vue3` 作为服务端渲染框架且构建工具我们同样支持了最流行的 `Vite` 来提升应用的启动速度和 HMR 速度,且提供一键以 Serverless 的形式发布上云的功能。我们可以非常有自信说它是地球上最先进的 `ssr` 框架。如果你希望获得开箱即用的体验且能够一键部署上云,请选择 `ssr` 框架。
|
|
30
30
|
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
|
|
45
45
|
## 哪些应用在使用
|
|
46
46
|
|
|
47
|
-
正在使用这个项目的公司(应用), 如果您正在使用但名单中没有列出来的话请提 [issue](https://github.com/
|
|
47
|
+
正在使用这个项目的公司(应用), 如果您正在使用但名单中没有列出来的话请提 [issue](https://github.com/zhangyuang/ssr/issues),欢迎推广分享,我们将随时提供技术支持
|
|
48
48
|
|
|
49
49
|
<table>
|
|
50
50
|
<tr>
|
|
@@ -106,6 +106,14 @@
|
|
|
106
106
|
<td align="center" style="height: 100px"><a href="https://jiawu.dewu.com" target="_blank"><img src="https://cdn.poizon.com/node-common/31116a45953ad8b468c6c832036aac88.png" width="200px;margin: 20px 0" alt="Dream2qBlog"/><br />
|
|
107
107
|
<a href="https://jiawu.dewu.com" target="_bvlank"><b>得物
|
|
108
108
|
</b></a></td>
|
|
109
|
+
<td align="center" style="height: 100px"><a href="https://www.habisummercamp.com/" target="_blank"><img src="https://res.wx.qq.com/op_res/XXUpF56bDue53vZadoCSPSnqKF3bZqPOQnqsPa_ngEARa0OUIW22pX7eKsftPb6pc5ny62cR_wXM4g5xJ7G3DQ
|
|
110
|
+
" width="200px;margin: 20px 0" alt="Dream2qBlog"/><br />
|
|
111
|
+
<a href="https://www.habisummercamp.com/" target="_bvlank"><b>HappyPC
|
|
112
|
+
</b></a></td>
|
|
113
|
+
<td align="center" style="height: 100px"><a href="https://m.habisummercamp.com/" target="_blank"><img src="https://res.wx.qq.com/op_res/XXUpF56bDue53vZadoCSPSnqKF3bZqPOQnqsPa_ngEARa0OUIW22pX7eKsftPb6pc5ny62cR_wXM4g5xJ7G3DQ
|
|
114
|
+
" width="200px;margin: 20px 0" alt="Dream2qBlog"/><br />
|
|
115
|
+
<a href="https://m.habisummercamp.com/" target="_bvlank"><b>HappyMobile
|
|
116
|
+
</b></a></td>
|
|
109
117
|
</tr>
|
|
110
118
|
</table>
|
|
111
119
|
|
|
@@ -156,12 +164,12 @@
|
|
|
156
164
|
|
|
157
165
|
注意: 在 `Midway.js` `Nest.js` 场景下我们都已实现了一键部署到 `Serverless` 平台的能力。但底层实现略有差异。更加详细的介绍可以阅读 [Serverless](http://doc.ssr-fc.com/docs/features$serverless) 章节。如需要大量使用 `Serverless` 平台提供的能力,我们建议创建 `Midway.js` 类型的应用。强烈建议阅读我们的 [官方文档](http://doc.ssr-fc.com/) 来获得更加详细的了解
|
|
158
166
|
|
|
159
|
-
- [midway-react-ssr](https://github.com/
|
|
160
|
-
- [midway-vue-ssr](https://github.com/
|
|
161
|
-
- [midway-vue3-ssr](https://github.com/
|
|
162
|
-
- [nestjs-react-ssr](https://github.com/
|
|
163
|
-
- [nestjs-vue-ssr](https://github.com/
|
|
164
|
-
- [nestjs-vue3-ssr](https://github.com/
|
|
167
|
+
- [midway-react-ssr](https://github.com/zhangyuang/ssr/tree/dev/example/midway-react-ssr)
|
|
168
|
+
- [midway-vue-ssr](https://github.com/zhangyuang/ssr/tree/dev/example/midway-vue-ssr)
|
|
169
|
+
- [midway-vue3-ssr](https://github.com/zhangyuang/ssr/tree/dev/example/midway-vue3-ssr)
|
|
170
|
+
- [nestjs-react-ssr](https://github.com/zhangyuang/ssr/tree/dev/example/nestjs-react-ssr)
|
|
171
|
+
- [nestjs-vue-ssr](https://github.com/zhangyuang/ssr/tree/dev/example/nestjs-vue-ssr)
|
|
172
|
+
- [nestjs-vue3-ssr](https://github.com/zhangyuang/ssr/tree/dev/example/nestjs-vue3-ssr)
|
|
165
173
|
|
|
166
174
|
开发者可根据实际技术栈需要创建不同类型的应用快速开始
|
|
167
175
|
|
|
@@ -232,21 +240,21 @@ $ npm run start:vite # 以 vite 模式启动,等价于 npx ssr start --vite
|
|
|
232
240
|
[ssr-types-react-status]: https://img.shields.io/npm/v/ssr-types-react.svg
|
|
233
241
|
[ssr-webpack-status]: https://img.shields.io/npm/v/ssr-webpack.svg
|
|
234
242
|
|
|
235
|
-
[ssr]: https://github.com/
|
|
236
|
-
[ssr-client-utils]: https://github.com/
|
|
237
|
-
[ssr-core-react]: https://github.com/
|
|
238
|
-
[ssr-core-vue]: https://github.com/
|
|
239
|
-
[ssr-hoc-react]: https://github.com/
|
|
240
|
-
[ssr-hoc-vue3]: https://github.com/
|
|
241
|
-
[ssr-plugin-midway]: https://github.com/
|
|
242
|
-
[ssr-plugin-nestjs]: https://github.com/
|
|
243
|
-
[ssr-plugin-react]: https://github.com/
|
|
244
|
-
[ssr-plugin-vue]: https://github.com/
|
|
245
|
-
[ssr-plugin-vue3]: https://github.com/
|
|
246
|
-
[ssr-server-utils]: https://github.com/
|
|
247
|
-
[ssr-types]: https://github.com/
|
|
248
|
-
[ssr-types-react]: https://github.com/
|
|
249
|
-
[ssr-webpack]: https://github.com/
|
|
243
|
+
[ssr]: https://github.com/zhangyuang/ssr/tree/dev/packages/cli
|
|
244
|
+
[ssr-client-utils]: https://github.com/zhangyuang/ssr/tree/dev/packages/client-utils
|
|
245
|
+
[ssr-core-react]: https://github.com/zhangyuang/ssr/tree/dev/packages/core-react
|
|
246
|
+
[ssr-core-vue]: https://github.com/zhangyuang/ssr/tree/dev/packages/core-vue
|
|
247
|
+
[ssr-hoc-react]: https://github.com/zhangyuang/ssr/tree/dev/packages/hoc-react
|
|
248
|
+
[ssr-hoc-vue3]: https://github.com/zhangyuang/ssr/tree/dev/packages/hoc-vue3
|
|
249
|
+
[ssr-plugin-midway]: https://github.com/zhangyuang/ssr/tree/dev/packages/plugin-midway
|
|
250
|
+
[ssr-plugin-nestjs]: https://github.com/zhangyuang/ssr/tree/dev/packages/plugin-nestjs
|
|
251
|
+
[ssr-plugin-react]: https://github.com/zhangyuang/ssr/tree/dev/packages/plugin-react
|
|
252
|
+
[ssr-plugin-vue]: https://github.com/zhangyuang/ssr/tree/dev/packages/plugin-vue
|
|
253
|
+
[ssr-plugin-vue3]: https://github.com/zhangyuang/ssr/tree/dev/packages/plugin-vue3
|
|
254
|
+
[ssr-server-utils]: https://github.com/zhangyuang/ssr/tree/dev/packages/server-utils
|
|
255
|
+
[ssr-types]: https://github.com/zhangyuang/ssr/tree/dev/packages/types
|
|
256
|
+
[ssr-types-react]: https://github.com/zhangyuang/ssr/tree/dev/packages/types-react
|
|
257
|
+
[ssr-webpack]: https://github.com/zhangyuang/ssr/tree/dev/packages/webpack
|
|
250
258
|
## CONTRIBUTING
|
|
251
259
|
|
|
252
260
|
如果你想为本应用贡献代码,请阅读[贡献文档](./CONTRIBUTING.md),我们为你准备了丰富的脚本用于 bootstrap
|
|
@@ -257,12 +265,12 @@ $ npm run start:vite # 以 vite 模式启动,等价于 npx ssr start --vite
|
|
|
257
265
|
|
|
258
266
|
## 答疑群
|
|
259
267
|
|
|
260
|
-
虽然我们已经尽力检查了一遍应用,但仍有可能有疏漏的地方,如果你在使用过程中发现任何问题或者建议,欢迎提[issue](https://github.com/
|
|
268
|
+
虽然我们已经尽力检查了一遍应用,但仍有可能有疏漏的地方,如果你在使用过程中发现任何问题或者建议,欢迎提[issue](https://github.com/zhangyuang/ssr/issues)或者[PR](https://github.com/zhangyuang/ssr/pulls)
|
|
261
269
|
欢迎直接扫码加入钉钉群或者微信群
|
|
262
270
|
|
|
263
271
|
<div style="display:flex">
|
|
264
272
|
<img src="https://res.wx.qq.com/op_res/7F1t4Z8yCHWilehbcFGjAj0yVn0URMiWBGVJa-TVu_eqw5IwUXA2kPYBnfX6YRHy0FVBB-yC6l0IEL02QTJkLg" width="300">
|
|
265
|
-
<img src="https://res.wx.qq.com/op_res/
|
|
273
|
+
<img src="https://res.wx.qq.com/op_res/ECwdFhkCwm6NE1c_jP-DOYaVaR8K_ORJeEzFlDW9eBJj8igF3yeut9KnoChxyqJ4ReJogCG9bL_qZ3OYdZBI0w" width="300">
|
|
266
274
|
</div>
|
|
267
275
|
|
|
268
276
|
## 前端开发手册
|
|
@@ -271,4 +279,4 @@ $ npm run start:vite # 以 vite 模式启动,等价于 npx ssr start --vite
|
|
|
271
279
|
|
|
272
280
|
## 项目 Star 数增长趋势
|
|
273
281
|
|
|
274
|
-
[](https://starchart.cc/zhangyuang/ssr)
|
package/cjs/config/base.d.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// @ts-nocheck
|
|
2
3
|
// The file is provisional,don't depend on it
|
|
4
|
+
var _a;
|
|
3
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
6
|
exports.STORE_CONTEXT = void 0;
|
|
5
7
|
const react_1 = require("react");
|
|
6
|
-
|
|
7
|
-
state: {}
|
|
8
|
-
});
|
|
8
|
+
let STORE_CONTEXT;
|
|
9
9
|
exports.STORE_CONTEXT = STORE_CONTEXT;
|
|
10
|
+
if (__isBrowser__) {
|
|
11
|
+
exports.STORE_CONTEXT = STORE_CONTEXT = (_a = window.STORE_CONTEXT) !== null && _a !== void 0 ? _a : (0, react_1.createContext)({
|
|
12
|
+
state: {}
|
|
13
|
+
});
|
|
14
|
+
window.STORE_CONTEXT = STORE_CONTEXT;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
exports.STORE_CONTEXT = STORE_CONTEXT = (0, react_1.createContext)({
|
|
18
|
+
state: {}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.serverRender = void 0;
|
|
4
4
|
const React = require("react");
|
|
5
|
+
const react_router_dom_1 = require("react-router-dom");
|
|
5
6
|
const ssr_server_utils_1 = require("ssr-server-utils");
|
|
6
7
|
const serialize = require("serialize-javascript");
|
|
7
8
|
// @ts-expect-error
|
|
@@ -96,7 +97,8 @@ const serverRender = async (ctx, config) => {
|
|
|
96
97
|
const injectState = isCsr ? null : React.createElement("script", { dangerouslySetInnerHTML: {
|
|
97
98
|
__html: `window.__USE_SSR__=true; window.__INITIAL_DATA__ =${serialize(combineData)}`
|
|
98
99
|
} });
|
|
99
|
-
return (React.createElement(
|
|
100
|
-
React.createElement(
|
|
100
|
+
return (React.createElement(react_router_dom_1.StaticRouter, { location: ctx.request.url },
|
|
101
|
+
React.createElement(create_context_1.STORE_CONTEXT.Provider, { value: { state: combineData } },
|
|
102
|
+
React.createElement(index_tsx_1.default, { ctx: ctx, config: config, staticList: staticList, injectState: injectState }, isCsr ? React.createElement(React.Fragment, null) : React.createElement(Component, null)))));
|
|
101
103
|
};
|
|
102
104
|
exports.serverRender = serverRender;
|
package/esm/config/base.d.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
// The file is provisional,don't depend on it
|
|
3
|
+
var _a;
|
|
2
4
|
import { createContext } from 'react';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
let STORE_CONTEXT;
|
|
6
|
+
if (__isBrowser__) {
|
|
7
|
+
STORE_CONTEXT = (_a = window.STORE_CONTEXT) !== null && _a !== void 0 ? _a : createContext({
|
|
8
|
+
state: {}
|
|
9
|
+
});
|
|
10
|
+
window.STORE_CONTEXT = STORE_CONTEXT;
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
STORE_CONTEXT = createContext({
|
|
14
|
+
state: {}
|
|
15
|
+
});
|
|
16
|
+
}
|
|
6
17
|
export { STORE_CONTEXT };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
+
import { StaticRouter } from 'react-router-dom';
|
|
2
3
|
import { findRoute, getManifest, logGreen, normalizePath, addAsyncChunk } from 'ssr-server-utils';
|
|
3
4
|
import * as serialize from 'serialize-javascript';
|
|
4
5
|
// @ts-expect-error
|
|
@@ -93,7 +94,8 @@ const serverRender = async (ctx, config) => {
|
|
|
93
94
|
const injectState = isCsr ? null : React.createElement("script", { dangerouslySetInnerHTML: {
|
|
94
95
|
__html: `window.__USE_SSR__=true; window.__INITIAL_DATA__ =${serialize(combineData)}`
|
|
95
96
|
} });
|
|
96
|
-
return (React.createElement(
|
|
97
|
-
React.createElement(
|
|
97
|
+
return (React.createElement(StaticRouter, { location: ctx.request.url },
|
|
98
|
+
React.createElement(Context.Provider, { value: { state: combineData } },
|
|
99
|
+
React.createElement(Layout, { ctx: ctx, config: config, staticList: staticList, injectState: injectState }, isCsr ? React.createElement(React.Fragment, null) : React.createElement(Component, null)))));
|
|
98
100
|
};
|
|
99
101
|
export { serverRender };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ssr-plugin-react",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.9",
|
|
4
4
|
"description": "plugin-react for ssr",
|
|
5
5
|
"main": "./cjs/index.js",
|
|
6
6
|
"module": "./esm/index.js",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
16
|
-
"url": "git+https://github.com/
|
|
16
|
+
"url": "git+https://github.com/zhangyuang/ssr.git"
|
|
17
17
|
},
|
|
18
18
|
"keywords": [
|
|
19
19
|
"cli",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"author": "zhangyuang",
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"bugs": {
|
|
28
|
-
"url": "https://github.com/
|
|
28
|
+
"url": "https://github.com/zhangyuang/ssr/issues"
|
|
29
29
|
},
|
|
30
|
-
"homepage": "https://github.com/
|
|
30
|
+
"homepage": "https://github.com/zhangyuang/ssr#readme",
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@babel/core": "^7.0.0",
|
|
33
33
|
"@babel/plugin-transform-runtime": "^7.9.6",
|
|
@@ -53,10 +53,10 @@
|
|
|
53
53
|
"postcss-safe-parser": "4.0.1",
|
|
54
54
|
"react-dev-utils": "^11.0.4",
|
|
55
55
|
"serialize-javascript": "^3.0.0",
|
|
56
|
-
"ssr-client-utils": "^5.6.
|
|
57
|
-
"ssr-hoc-react": "^6.0.
|
|
58
|
-
"ssr-server-utils": "^5.7.
|
|
59
|
-
"ssr-webpack": "^5.7.
|
|
56
|
+
"ssr-client-utils": "^5.6.40",
|
|
57
|
+
"ssr-hoc-react": "^6.0.7",
|
|
58
|
+
"ssr-server-utils": "^5.7.10",
|
|
59
|
+
"ssr-webpack": "^5.7.9",
|
|
60
60
|
"terser-webpack-plugin": "^2.3.5",
|
|
61
61
|
"url-loader": "^4.1.1",
|
|
62
62
|
"webpack-bundle-analyzer": "^3.6.1",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"@types/react-router-dom": "^5.1.3",
|
|
71
71
|
"@types/serialize-javascript": "^1.5.0",
|
|
72
72
|
"@types/webpack": "^4.41.10",
|
|
73
|
-
"ssr-types-react": "^6.0.
|
|
73
|
+
"ssr-types-react": "^6.0.6"
|
|
74
74
|
},
|
|
75
|
-
"gitHead": "
|
|
75
|
+
"gitHead": "d13f3be99541ee9bcc9694aed1f642531f2680e4"
|
|
76
76
|
}
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
1
2
|
// The file is provisional,don't depend on it
|
|
2
3
|
|
|
3
4
|
import { createContext } from 'react'
|
|
4
5
|
import { IContext } from 'ssr-types-react'
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
let STORE_CONTEXT
|
|
8
|
+
if (__isBrowser__) {
|
|
9
|
+
STORE_CONTEXT = window.STORE_CONTEXT ?? createContext<IContext>({
|
|
10
|
+
state: {}
|
|
11
|
+
})
|
|
12
|
+
window.STORE_CONTEXT = STORE_CONTEXT
|
|
13
|
+
} else {
|
|
14
|
+
STORE_CONTEXT = createContext<IContext>({
|
|
15
|
+
state: {}
|
|
16
|
+
})
|
|
17
|
+
}
|
|
9
18
|
|
|
10
19
|
export {
|
|
11
20
|
STORE_CONTEXT
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
|
+
import { StaticRouter } from 'react-router-dom'
|
|
2
3
|
import { findRoute, getManifest, logGreen, normalizePath, addAsyncChunk } from 'ssr-server-utils'
|
|
3
4
|
import { ISSRContext, IGlobal, IConfig, ReactRoutesType, ReactESMFeRouteItem } from 'ssr-types-react'
|
|
4
5
|
import * as serialize from 'serialize-javascript'
|
|
@@ -109,11 +110,13 @@ const serverRender = async (ctx: ISSRContext, config: IConfig): Promise<React.Re
|
|
|
109
110
|
}} />
|
|
110
111
|
|
|
111
112
|
return (
|
|
112
|
-
<
|
|
113
|
-
<
|
|
114
|
-
{
|
|
115
|
-
|
|
116
|
-
|
|
113
|
+
<StaticRouter location={ctx.request.url}>
|
|
114
|
+
<Context.Provider value={{ state: combineData }}>
|
|
115
|
+
<Layout ctx={ctx} config={config} staticList={staticList} injectState={injectState}>
|
|
116
|
+
{isCsr ? <></> : <Component />}
|
|
117
|
+
</Layout>
|
|
118
|
+
</Context.Provider>
|
|
119
|
+
</StaticRouter>
|
|
117
120
|
)
|
|
118
121
|
}
|
|
119
122
|
|