@tramvai/module-render 2.21.0 → 2.21.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/package.json +18 -16
- package/tests.d.ts +13 -0
- package/tests.js +267 -0
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tramvai/module-render",
|
|
3
|
-
"version": "2.21.
|
|
3
|
+
"version": "2.21.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"browser": "lib/browser.js",
|
|
6
6
|
"main": "lib/server.js",
|
|
7
7
|
"typings": "lib/server.d.ts",
|
|
8
8
|
"files": [
|
|
9
9
|
"lib",
|
|
10
|
-
"__migrations__"
|
|
10
|
+
"__migrations__",
|
|
11
|
+
"tests.js",
|
|
12
|
+
"tests.d.ts"
|
|
11
13
|
],
|
|
12
14
|
"sideEffects": false,
|
|
13
15
|
"repository": {
|
|
@@ -24,13 +26,13 @@
|
|
|
24
26
|
"@tinkoff/htmlpagebuilder": "0.5.2",
|
|
25
27
|
"@tinkoff/layout-factory": "0.3.2",
|
|
26
28
|
"@tinkoff/url": "0.8.2",
|
|
27
|
-
"@tinkoff/user-agent": "0.4.
|
|
28
|
-
"@tramvai/module-client-hints": "2.21.
|
|
29
|
-
"@tramvai/module-router": "2.21.
|
|
30
|
-
"@tramvai/react": "2.21.
|
|
29
|
+
"@tinkoff/user-agent": "0.4.54",
|
|
30
|
+
"@tramvai/module-client-hints": "2.21.1",
|
|
31
|
+
"@tramvai/module-router": "2.21.1",
|
|
32
|
+
"@tramvai/react": "2.21.1",
|
|
31
33
|
"@tramvai/safe-strings": "0.5.2",
|
|
32
|
-
"@tramvai/tokens-render": "2.21.
|
|
33
|
-
"@tramvai/experiments": "2.21.
|
|
34
|
+
"@tramvai/tokens-render": "2.21.1",
|
|
35
|
+
"@tramvai/experiments": "2.21.1",
|
|
34
36
|
"@types/loadable__server": "^5.12.6",
|
|
35
37
|
"node-fetch": "^2.6.1"
|
|
36
38
|
},
|
|
@@ -38,14 +40,14 @@
|
|
|
38
40
|
"@tinkoff/dippy": "0.8.2",
|
|
39
41
|
"@tinkoff/utils": "^2.1.2",
|
|
40
42
|
"@tinkoff/react-hooks": "0.1.2",
|
|
41
|
-
"@tramvai/cli": "2.21.
|
|
42
|
-
"@tramvai/core": "2.21.
|
|
43
|
-
"@tramvai/module-common": "2.21.
|
|
44
|
-
"@tramvai/state": "2.21.
|
|
45
|
-
"@tramvai/test-helpers": "2.21.
|
|
46
|
-
"@tramvai/tokens-common": "2.21.
|
|
47
|
-
"@tramvai/tokens-router": "2.21.
|
|
48
|
-
"@tramvai/tokens-server-private": "2.21.
|
|
43
|
+
"@tramvai/cli": "2.21.1",
|
|
44
|
+
"@tramvai/core": "2.21.1",
|
|
45
|
+
"@tramvai/module-common": "2.21.1",
|
|
46
|
+
"@tramvai/state": "2.21.1",
|
|
47
|
+
"@tramvai/test-helpers": "2.21.1",
|
|
48
|
+
"@tramvai/tokens-common": "2.21.1",
|
|
49
|
+
"@tramvai/tokens-router": "2.21.1",
|
|
50
|
+
"@tramvai/tokens-server-private": "2.21.1",
|
|
49
51
|
"express": "^4.17.1",
|
|
50
52
|
"prop-types": "^15.6.2",
|
|
51
53
|
"react": ">=16.14.0",
|
package/tests.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { getDiWrapper } from "@tramvai/test-helpers";
|
|
2
|
+
type Options = Parameters<typeof getDiWrapper>[0];
|
|
3
|
+
declare const testPageResources: (options: Options) => {
|
|
4
|
+
render: () => {
|
|
5
|
+
parsed: import("node-html-parser").HTMLElement;
|
|
6
|
+
body: string;
|
|
7
|
+
head: string;
|
|
8
|
+
application: string;
|
|
9
|
+
};
|
|
10
|
+
di: import("@tinkoff/dippy").Container;
|
|
11
|
+
runLine: (line: import("@tinkoff/dippy").MultiTokenInterface<import("@tramvai/core").Command>) => Promise<any[]>;
|
|
12
|
+
};
|
|
13
|
+
export { testPageResources };
|
package/tests.js
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var flatten = require('@tinkoff/utils/array/flatten');
|
|
6
|
+
var testHelpers = require('@tramvai/test-helpers');
|
|
7
|
+
var core = require('@tramvai/core');
|
|
8
|
+
var tokensRender = require('@tramvai/tokens-render');
|
|
9
|
+
var htmlpagebuilder = require('@tinkoff/htmlpagebuilder');
|
|
10
|
+
var toArray = require('@tinkoff/utils/array/toArray');
|
|
11
|
+
require('@tinkoff/utils/is/undefined');
|
|
12
|
+
require('@tinkoff/utils/is/empty');
|
|
13
|
+
require('@tinkoff/url');
|
|
14
|
+
require('node-fetch');
|
|
15
|
+
require('@tinkoff/utils/string/startsWith');
|
|
16
|
+
var dippy = require('@tinkoff/dippy');
|
|
17
|
+
require('@tramvai/safe-strings');
|
|
18
|
+
require('@loadable/server');
|
|
19
|
+
require('@tinkoff/utils/object/has');
|
|
20
|
+
require('@tinkoff/utils/array/last');
|
|
21
|
+
require('@tramvai/experiments');
|
|
22
|
+
require('@tinkoff/utils/array/uniq');
|
|
23
|
+
var path = require('path');
|
|
24
|
+
require('@tinkoff/utils/array/each');
|
|
25
|
+
require('@tinkoff/utils/object/path');
|
|
26
|
+
|
|
27
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
28
|
+
|
|
29
|
+
function _interopNamespace(e) {
|
|
30
|
+
if (e && e.__esModule) return e;
|
|
31
|
+
var n = Object.create(null);
|
|
32
|
+
if (e) {
|
|
33
|
+
Object.keys(e).forEach(function (k) {
|
|
34
|
+
if (k !== 'default') {
|
|
35
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
36
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
37
|
+
enumerable: true,
|
|
38
|
+
get: function () { return e[k]; }
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
n["default"] = e;
|
|
44
|
+
return n;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
var flatten__default = /*#__PURE__*/_interopDefaultLegacy(flatten);
|
|
48
|
+
var toArray__default = /*#__PURE__*/_interopDefaultLegacy(toArray);
|
|
49
|
+
var path__namespace = /*#__PURE__*/_interopNamespace(path);
|
|
50
|
+
|
|
51
|
+
class ResourcesRegistry {
|
|
52
|
+
constructor({ resourceInliner }) {
|
|
53
|
+
this.resources = new Set();
|
|
54
|
+
this.resourceInliner = resourceInliner;
|
|
55
|
+
}
|
|
56
|
+
register(resourceOrResources) {
|
|
57
|
+
toArray__default["default"](resourceOrResources).forEach((resource) => {
|
|
58
|
+
this.resources.add(resource);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
getPageResources() {
|
|
62
|
+
return Array.from(this.resources.values())
|
|
63
|
+
.reduce((acc, resource) => {
|
|
64
|
+
if (this.resourceInliner.shouldInline(resource)) {
|
|
65
|
+
Array.prototype.push.apply(acc, this.resourceInliner.inlineResource(resource));
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
acc.push(resource);
|
|
69
|
+
}
|
|
70
|
+
return acc;
|
|
71
|
+
}, [])
|
|
72
|
+
.filter((resource) => this.resourceInliner.shouldAddResource(resource));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
process.env.NODE_ENV === 'development' &&
|
|
77
|
+
(process.env.ASSETS_PREFIX === 'static' || !process.env.ASSETS_PREFIX)
|
|
78
|
+
? `http://localhost:${process.env.PORT_STATIC}/dist/`
|
|
79
|
+
: process.env.ASSETS_PREFIX;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @description
|
|
83
|
+
* Инлайнер ресурсов - используется на сервере для регистрации файлов, которые должны быть вставлены
|
|
84
|
+
* в итоговую html-страницу в виде ссылки на файл или заинлайнеными полностью
|
|
85
|
+
*/
|
|
86
|
+
const RESOURCE_INLINER = dippy.createToken('resourceInliner');
|
|
87
|
+
/**
|
|
88
|
+
* @description
|
|
89
|
+
* Кэш загруженных ресурсов.
|
|
90
|
+
*/
|
|
91
|
+
dippy.createToken('resourcesRegistryCache');
|
|
92
|
+
|
|
93
|
+
const requireFunc =
|
|
94
|
+
// @ts-ignore
|
|
95
|
+
typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require;
|
|
96
|
+
|
|
97
|
+
let appConfig;
|
|
98
|
+
try {
|
|
99
|
+
appConfig = require('@tramvai/cli/lib/external/config').appConfig;
|
|
100
|
+
}
|
|
101
|
+
catch (e) { }
|
|
102
|
+
if (process.env.NODE_ENV === 'development') ;
|
|
103
|
+
if (process.env.NODE_ENV === 'test') ;
|
|
104
|
+
if (process.env.NODE_ENV === 'production') {
|
|
105
|
+
const SEARCH_PATHS = [process.cwd(), __dirname];
|
|
106
|
+
const webpackStats = (fileName) => {
|
|
107
|
+
let stats;
|
|
108
|
+
for (const dir of SEARCH_PATHS) {
|
|
109
|
+
try {
|
|
110
|
+
const statsPath = path__namespace.resolve(dir, fileName);
|
|
111
|
+
stats = requireFunc(statsPath);
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
catch (e) {
|
|
115
|
+
// ignore errors as this function is used to load stats for several optional destinations
|
|
116
|
+
// and these destinations may not have stats file
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (!stats) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (!process.env.ASSETS_PREFIX) {
|
|
123
|
+
if (process.env.STATIC_PREFIX) {
|
|
124
|
+
throw new Error('Required env variable "ASSETS_PREFIX" is not set. Instead of using "STATIC_PREFIX" env please define "ASSETS_PREFIX: STATIC_PREFIX + /compiled"');
|
|
125
|
+
}
|
|
126
|
+
throw new Error('Required env variable "ASSETS_PREFIX" is not set');
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
...stats,
|
|
130
|
+
publicPath: process.env.ASSETS_PREFIX,
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
const statsLegacy = webpackStats('stats.json');
|
|
134
|
+
webpackStats('stats.modern.json') || statsLegacy;
|
|
135
|
+
if (!statsLegacy) {
|
|
136
|
+
throw new Error(`Cannot find stats.json.
|
|
137
|
+
It should be placed in one of the next places:
|
|
138
|
+
${SEARCH_PATHS.join('\n\t')}
|
|
139
|
+
In case it happens on deployment:
|
|
140
|
+
- In case you are using two independent jobs for building app
|
|
141
|
+
- Either do not split build command by two independent jobs and use one common job with "tramvai build" command without --buildType
|
|
142
|
+
- Or copy stats.json (and stats.modern.json if present) file from client build output to server output by yourself in your CI
|
|
143
|
+
- Otherwise report issue to tramvai team
|
|
144
|
+
In case it happens locally:
|
|
145
|
+
- prefer to use command "tramvai start-prod" to test prod-build locally
|
|
146
|
+
- copy stats.json next to built server.js file
|
|
147
|
+
`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const formatAttributes = (htmlAttrs, target) => {
|
|
152
|
+
if (!htmlAttrs) {
|
|
153
|
+
return '';
|
|
154
|
+
}
|
|
155
|
+
const targetAttrs = htmlAttrs.filter((item) => item.target === target);
|
|
156
|
+
const collectedAttrs = targetAttrs.reduce((acc, item) => ({ ...acc, ...item.attrs }), {});
|
|
157
|
+
const attrsString = Object.keys(collectedAttrs).reduce((acc, name) => {
|
|
158
|
+
if (collectedAttrs[name] === true) {
|
|
159
|
+
return `${acc} ${name}`;
|
|
160
|
+
}
|
|
161
|
+
return `${acc} ${name}="${collectedAttrs[name]}"`;
|
|
162
|
+
}, '');
|
|
163
|
+
return attrsString.trim();
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
/* eslint-disable sort-class-members/sort-class-members */
|
|
167
|
+
const mapResourcesToSlots = (resources) => resources.reduce((acc, resource) => {
|
|
168
|
+
const { slot } = resource;
|
|
169
|
+
acc[slot] = Array.isArray(acc[slot]) ? [...acc[slot], resource] : [resource];
|
|
170
|
+
return acc;
|
|
171
|
+
}, {});
|
|
172
|
+
/* eslint-enable sort-class-members/sort-class-members */
|
|
173
|
+
|
|
174
|
+
const { REACT_RENDER, HEAD_CORE_SCRIPTS, HEAD_DYNAMIC_SCRIPTS, HEAD_META, HEAD_POLYFILLS, HEAD_CORE_STYLES, HEAD_PERFORMANCE, HEAD_ANALYTICS, BODY_START, BODY_END, HEAD_ICONS, BODY_TAIL_ANALYTICS, BODY_TAIL, } = tokensRender.ResourceSlot;
|
|
175
|
+
const htmlPageSchemaFactory = ({ htmlAttrs, }) => {
|
|
176
|
+
return [
|
|
177
|
+
htmlpagebuilder.staticRender('<!DOCTYPE html>'),
|
|
178
|
+
htmlpagebuilder.staticRender(`<html ${formatAttributes(htmlAttrs, 'html')}>`),
|
|
179
|
+
htmlpagebuilder.staticRender('<head>'),
|
|
180
|
+
htmlpagebuilder.staticRender('<meta charset="UTF-8">'),
|
|
181
|
+
htmlpagebuilder.dynamicRender(HEAD_META),
|
|
182
|
+
htmlpagebuilder.dynamicRender(HEAD_PERFORMANCE),
|
|
183
|
+
htmlpagebuilder.dynamicRender(HEAD_CORE_STYLES),
|
|
184
|
+
htmlpagebuilder.dynamicRender(HEAD_POLYFILLS),
|
|
185
|
+
htmlpagebuilder.dynamicRender(HEAD_DYNAMIC_SCRIPTS),
|
|
186
|
+
htmlpagebuilder.dynamicRender(HEAD_CORE_SCRIPTS),
|
|
187
|
+
htmlpagebuilder.dynamicRender(HEAD_ANALYTICS),
|
|
188
|
+
htmlpagebuilder.dynamicRender(HEAD_ICONS),
|
|
189
|
+
htmlpagebuilder.staticRender('</head>'),
|
|
190
|
+
htmlpagebuilder.staticRender(`<body ${formatAttributes(htmlAttrs, 'body')}>`),
|
|
191
|
+
htmlpagebuilder.dynamicRender(BODY_START),
|
|
192
|
+
// react app
|
|
193
|
+
htmlpagebuilder.dynamicRender(REACT_RENDER),
|
|
194
|
+
htmlpagebuilder.dynamicRender(BODY_END),
|
|
195
|
+
htmlpagebuilder.dynamicRender(BODY_TAIL_ANALYTICS),
|
|
196
|
+
htmlpagebuilder.dynamicRender(BODY_TAIL),
|
|
197
|
+
htmlpagebuilder.staticRender('</body>'),
|
|
198
|
+
htmlpagebuilder.staticRender('</html>'),
|
|
199
|
+
];
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
const testPageResources = (options) => {
|
|
203
|
+
var _a;
|
|
204
|
+
const { modules, providers = [] } = options;
|
|
205
|
+
const { di, runLine } = testHelpers.getDiWrapper({
|
|
206
|
+
di: options.di,
|
|
207
|
+
modules,
|
|
208
|
+
providers: [
|
|
209
|
+
{
|
|
210
|
+
provide: 'htmlPageSchema',
|
|
211
|
+
useFactory: htmlPageSchemaFactory,
|
|
212
|
+
deps: {
|
|
213
|
+
htmlAttrs: tokensRender.HTML_ATTRS,
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
provide: tokensRender.HTML_ATTRS,
|
|
218
|
+
useValue: {
|
|
219
|
+
target: 'html',
|
|
220
|
+
attrs: {
|
|
221
|
+
class: 'no-js',
|
|
222
|
+
lang: 'ru',
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
multi: true,
|
|
226
|
+
},
|
|
227
|
+
...providers,
|
|
228
|
+
core.provide({
|
|
229
|
+
provide: tokensRender.RESOURCES_REGISTRY,
|
|
230
|
+
useClass: ResourcesRegistry,
|
|
231
|
+
deps: {
|
|
232
|
+
resourceInliner: RESOURCE_INLINER,
|
|
233
|
+
},
|
|
234
|
+
}),
|
|
235
|
+
core.provide({
|
|
236
|
+
provide: RESOURCE_INLINER,
|
|
237
|
+
useValue: {
|
|
238
|
+
shouldInline() {
|
|
239
|
+
return false;
|
|
240
|
+
},
|
|
241
|
+
shouldAddResource() {
|
|
242
|
+
return true;
|
|
243
|
+
},
|
|
244
|
+
inlineResource() {
|
|
245
|
+
return [];
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
}),
|
|
249
|
+
],
|
|
250
|
+
});
|
|
251
|
+
const renderSlots = flatten__default["default"]((_a = di.get({ token: tokensRender.RENDER_SLOTS, optional: true })) !== null && _a !== void 0 ? _a : []);
|
|
252
|
+
const resourcesRegistry = di.get(tokensRender.RESOURCES_REGISTRY);
|
|
253
|
+
const render = () => {
|
|
254
|
+
const rawHtml = htmlpagebuilder.buildPage({
|
|
255
|
+
slotHandlers: mapResourcesToSlots([...renderSlots, ...resourcesRegistry.getPageResources()]),
|
|
256
|
+
description: di.get('htmlPageSchema'),
|
|
257
|
+
});
|
|
258
|
+
return testHelpers.parseHtml(rawHtml, {});
|
|
259
|
+
};
|
|
260
|
+
return {
|
|
261
|
+
render,
|
|
262
|
+
di,
|
|
263
|
+
runLine,
|
|
264
|
+
};
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
exports.testPageResources = testPageResources;
|