@tramvai/module-render 2.7.0 → 2.7.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.
@@ -6,8 +6,10 @@ export interface ResourcesInlinerType {
6
6
  }
7
7
  export declare class ResourcesInliner implements ResourcesInlinerType {
8
8
  private resourceInlineThreshold?;
9
+ private internalFilesCache;
9
10
  private resourcesRegistryCache;
10
11
  private log;
12
+ private runningRequests;
11
13
  private scheduleFileLoad;
12
14
  private scheduleFileSizeLoad;
13
15
  constructor({ resourcesRegistryCache, resourceInlineThreshold, logger }: {
@@ -15,6 +17,7 @@ export declare class ResourcesInliner implements ResourcesInlinerType {
15
17
  resourceInlineThreshold: any;
16
18
  logger: any;
17
19
  });
20
+ private getFilesCache;
18
21
  shouldAddResource(resource: PageResource): boolean;
19
22
  shouldInline(resource: PageResource): boolean;
20
23
  inlineResource(resource: PageResource): PageResource[];
@@ -9,8 +9,7 @@ export declare const RESOURCE_INLINER: import("@tinkoff/dippy/lib/createToken/cr
9
9
  export declare type ResourcesRegistryCache = {
10
10
  filesCache: Cache;
11
11
  sizeCache: Cache;
12
- requestsCache: Cache;
13
- disabledUrlsCache: any;
12
+ disabledUrlsCache: Cache;
14
13
  };
15
14
  /**
16
15
  * @description
package/lib/server.es.js CHANGED
@@ -2,7 +2,7 @@ import { __decorate } from 'tslib';
2
2
  import { PureComponent, useMemo, createElement } from 'react';
3
3
  import { renderToString } from 'react-dom/server';
4
4
  import { Module, provide, commandLineListTokens, DI_TOKEN } from '@tramvai/core';
5
- import { CREATE_CACHE_TOKEN, LOGGER_TOKEN, REQUEST_MANAGER_TOKEN, RESPONSE_MANAGER_TOKEN, CONTEXT_TOKEN } from '@tramvai/module-common';
5
+ import { COMBINE_REDUCERS, CREATE_CACHE_TOKEN, LOGGER_TOKEN, REQUEST_MANAGER_TOKEN, RESPONSE_MANAGER_TOKEN, CONTEXT_TOKEN } from '@tramvai/tokens-common';
6
6
  import { PAGE_SERVICE_TOKEN } from '@tramvai/tokens-router';
7
7
  import { ClientHintsModule, USER_AGENT_TOKEN } from '@tramvai/module-client-hints';
8
8
  import { ResourceType, ResourceSlot, DEFAULT_LAYOUT_COMPONENT, LAYOUT_OPTIONS, DEFAULT_FOOTER_COMPONENT, DEFAULT_HEADER_COMPONENT, TRAMVAI_RENDER_MODE, RESOURCES_REGISTRY, RESOURCE_INLINE_OPTIONS, RENDER_SLOTS, POLYFILL_CONDITION, HTML_ATTRS, CUSTOM_RENDER, EXTEND_RENDER } from '@tramvai/tokens-render';
@@ -33,7 +33,6 @@ import { jsx } from 'react/jsx-runtime';
33
33
  import { createEvent, createReducer, useStore, Provider } from '@tramvai/state';
34
34
  import { useRoute, useUrl } from '@tramvai/module-router';
35
35
  import { composeLayoutOptions, createLayout } from '@tinkoff/layout-factory';
36
- import { COMBINE_REDUCERS } from '@tramvai/tokens-common';
37
36
 
38
37
  const thirtySeconds = 1000 * 30;
39
38
  const getFileContentLength = async (url) => {
@@ -75,6 +74,11 @@ const processFile = (resource, file) => {
75
74
  return file;
76
75
  };
77
76
 
77
+ const INTERNAL_CACHE_SIZE = 50;
78
+ const ASSETS_PREFIX = process.env.NODE_ENV === 'development' &&
79
+ (process.env.ASSETS_PREFIX === 'static' || !process.env.ASSETS_PREFIX)
80
+ ? `http://localhost:${process.env.PORT_STATIC}/dist/`
81
+ : process.env.ASSETS_PREFIX;
78
82
  const getInlineType = (type) => {
79
83
  switch (type) {
80
84
  case ResourceType.style:
@@ -95,51 +99,53 @@ const getResourceUrl = (resource) => {
95
99
  };
96
100
  class ResourcesInliner {
97
101
  constructor({ resourcesRegistryCache, resourceInlineThreshold, logger }) {
98
- this.scheduleFileLoad = (resource, resourceInlineThreshold) => {
102
+ this.internalFilesCache = new Map();
103
+ this.runningRequests = new Set();
104
+ this.scheduleFileLoad = async (resource, resourceInlineThreshold) => {
99
105
  const url = getResourceUrl(resource);
100
106
  const requestKey = `file${url}`;
101
- const urlIsDisabled = this.resourcesRegistryCache.disabledUrlsCache.get(url);
102
- const requestIsInProgress = this.resourcesRegistryCache.requestsCache.get(requestKey);
103
- if (!requestIsInProgress && !urlIsDisabled) {
104
- const result = this.resourcesRegistryCache.filesCache.get(url);
105
- if (result) {
106
- return result;
107
- }
108
- const getFilePromise = getFile(url)
109
- .then((file) => {
107
+ const filesCache = this.getFilesCache(url);
108
+ const result = filesCache.get(url);
109
+ if (result) {
110
+ return result;
111
+ }
112
+ if (!this.runningRequests.has(requestKey)) {
113
+ this.runningRequests.add(url);
114
+ try {
115
+ const file = await getFile(url);
110
116
  if (file === undefined) {
111
117
  this.resourcesRegistryCache.disabledUrlsCache.set(url, true);
112
118
  return;
113
119
  }
114
120
  const size = file.length;
115
121
  if (size < resourceInlineThreshold) {
116
- this.resourcesRegistryCache.filesCache.set(url, processFile(resource, file));
122
+ filesCache.set(url, processFile(resource, file));
117
123
  }
118
124
  this.resourcesRegistryCache.sizeCache.set(url, size);
119
- })
120
- .catch((error) => {
125
+ }
126
+ catch (error) {
121
127
  this.log.warn({
122
128
  event: 'file-load-failed',
123
129
  url,
124
130
  error,
125
131
  });
126
- })
127
- .finally(() => {
128
- this.resourcesRegistryCache.requestsCache.set(requestKey, undefined);
129
- });
130
- this.resourcesRegistryCache.requestsCache.set(requestKey, getFilePromise);
132
+ }
133
+ finally {
134
+ this.runningRequests.delete(requestKey);
135
+ }
131
136
  }
132
137
  };
133
- this.scheduleFileSizeLoad = (resource, resourceInlineThreshold) => {
138
+ this.scheduleFileSizeLoad = async (resource, resourceInlineThreshold) => {
134
139
  const url = getResourceUrl(resource);
135
140
  const requestKey = `size${url}`;
136
- if (!this.resourcesRegistryCache.requestsCache.has(requestKey)) {
137
- const result = this.resourcesRegistryCache.sizeCache.get(url);
138
- if (result) {
139
- return result;
140
- }
141
- const getFileSizePromise = getFileContentLength(url)
142
- .then((contentLength) => {
141
+ const result = this.resourcesRegistryCache.sizeCache.get(url);
142
+ if (result) {
143
+ return result;
144
+ }
145
+ if (!this.runningRequests.has(requestKey)) {
146
+ this.runningRequests.add(requestKey);
147
+ try {
148
+ const contentLength = await getFileContentLength(url);
143
149
  const size = isUndefined(contentLength) ? 0 : +contentLength;
144
150
  if (size) {
145
151
  this.resourcesRegistryCache.sizeCache.set(url, size);
@@ -147,41 +153,48 @@ class ResourcesInliner {
147
153
  if (size < resourceInlineThreshold) {
148
154
  this.scheduleFileLoad(resource, resourceInlineThreshold);
149
155
  }
150
- })
151
- .catch((error) => {
156
+ }
157
+ catch (error) {
152
158
  this.log.warn({
153
159
  event: 'file-content-length-load-failed',
154
160
  url,
155
161
  error,
156
162
  });
157
- })
158
- .finally(() => {
159
- this.resourcesRegistryCache.requestsCache.set(requestKey, undefined);
160
- });
161
- this.resourcesRegistryCache.requestsCache.set(requestKey, getFileSizePromise);
163
+ }
164
+ finally {
165
+ this.runningRequests.delete(requestKey);
166
+ }
162
167
  }
163
168
  };
164
169
  this.resourcesRegistryCache = resourcesRegistryCache;
165
170
  this.resourceInlineThreshold = resourceInlineThreshold;
166
171
  this.log = logger('resources-inliner');
167
172
  }
168
- // Метод проверки, стоит ли добавлять preload-ресурс
173
+ getFilesCache(url) {
174
+ if (url.startsWith(ASSETS_PREFIX)) {
175
+ // internal resources are resources generated by the current app itself
176
+ // these kind of resources are pretty static and won't be changed while app is running
177
+ // so we can cache it with bare Map and do not care about how to cleanup cache from outdated entries
178
+ return this.internalFilesCache;
179
+ }
180
+ return this.resourcesRegistryCache.filesCache;
181
+ }
182
+ // check that resource's preload-link should be added to render
169
183
  shouldAddResource(resource) {
170
184
  if (resource.type !== ResourceType.preloadLink) {
171
- // Мы фильтруем только preloadLink, если это ресурс другого типа он должен
172
- // попасть в итоговую выборку.
185
+ // only checking preload-links
173
186
  return true;
174
187
  }
175
188
  const url = getResourceUrl(resource);
176
189
  if (isUndefined(url)) {
177
- // Если у ресурса нет URL'а, в кеше этого файла точно нет
190
+ // if url is undefined that file is not in cache
178
191
  return true;
179
192
  }
180
- // Если файл лежит в кеше, значит он прошёл все проверки и будет инлайниться =>
181
- // нам не нужно добавлять для него preload.
182
- return !this.resourcesRegistryCache.filesCache.has(url);
193
+ // if file is residing in cache that means it will be inlined in page render
194
+ // therefore no need to have preload-link for the inlined resource
195
+ return !this.getFilesCache(url).has(url);
183
196
  }
184
- // Метод проверки, должен ли быть заинлайнен в HTML-страницу ресурс.
197
+ // method for check is passed resource should be inlined in HTML-page
185
198
  shouldInline(resource) {
186
199
  var _a;
187
200
  if (!(((_a = this.resourceInlineThreshold) === null || _a === void 0 ? void 0 : _a.types) || []).includes(resource.type)) {
@@ -192,7 +205,16 @@ class ResourcesInliner {
192
205
  return false;
193
206
  }
194
207
  const url = getResourceUrl(resource);
195
- if (isUndefined(url)) {
208
+ const filesCache = this.getFilesCache(url);
209
+ if (isUndefined(url) || this.resourcesRegistryCache.disabledUrlsCache.has(url)) {
210
+ return false;
211
+ }
212
+ if (filesCache.has(url)) {
213
+ return true;
214
+ }
215
+ if (filesCache === this.internalFilesCache &&
216
+ this.internalFilesCache.size >= INTERNAL_CACHE_SIZE) {
217
+ // if we've exceeded limits for the internal resources cache ignore any new entries
196
218
  return false;
197
219
  }
198
220
  if (!this.resourcesRegistryCache.sizeCache.has(url)) {
@@ -203,25 +225,22 @@ class ResourcesInliner {
203
225
  if (size > resourceInlineThreshold) {
204
226
  return false;
205
227
  }
206
- if (!this.resourcesRegistryCache.filesCache.has(url)) {
207
- this.scheduleFileLoad(resource, resourceInlineThreshold);
208
- return false;
209
- }
210
- return true;
228
+ this.scheduleFileLoad(resource, resourceInlineThreshold);
229
+ return false;
211
230
  }
212
231
  inlineResource(resource) {
213
232
  const url = getResourceUrl(resource);
214
233
  if (isUndefined(url)) {
215
- // В теории, такого быть не должно, но добавим проверку на всякий случай
234
+ // usually, it should not happen but anyway check it for safety
216
235
  return [resource];
217
236
  }
218
- const text = this.resourcesRegistryCache.filesCache.get(url);
237
+ const text = this.getFilesCache(url).get(url);
219
238
  if (isEmpty(text)) {
220
239
  return [resource];
221
240
  }
222
241
  const result = [];
223
242
  if (process.env.NODE_ENV === 'development') {
224
- // Добавляем html комментарии для упрощения отладки инлайнинга в dev режиме
243
+ // html comment for debugging inlining in dev mode
225
244
  result.push({
226
245
  slot: resource.slot,
227
246
  type: ResourceType.asIs,
@@ -234,11 +253,10 @@ class ResourcesInliner {
234
253
  payload: text,
235
254
  });
236
255
  if (resource.type === ResourceType.style) {
237
- // Если не добавить data-href, extract-css-chunks-webpack-plugin
238
- // добавит (https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/blob/master/src/index.js#L346)
239
- // в head ссылку на файл и инлайнинг будет бесполезен, т.к. браузер всё равно пойдёт скачивать этот файл.
240
- // При этом в случае css-файлов он ищет тэг link, а при инлайнинге мы вставляем style => мы не можем
241
- // использовать тэг выше, а приходится генерировать новый.
256
+ // If we don't add data-href then extract-css-chunks-webpack-plugin
257
+ // will add link to resources to the html head (https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/blob/master/src/index.js#L346)
258
+ // wherein link in case of css files plugin will look for a link tag, but we add a style tag
259
+ // so we can't use tag from above and have to generate new one
242
260
  result.push({
243
261
  slot: resource.slot,
244
262
  type: ResourceType.style,
@@ -736,6 +754,7 @@ const providers = [
736
754
  ];
737
755
 
738
756
  var RenderModule_1;
757
+ const REQUEST_TTL = 5 * 60 * 1000;
739
758
  const DEFAULT_POLYFILL_CONDITION = '!window.Promise.prototype.finally || !window.URL || !window.URLSearchParams || !window.AbortController || !window.IntersectionObserver || !Object.fromEntries || !window.ResizeObserver';
740
759
  let RenderModule = RenderModule_1 = class RenderModule {
741
760
  static forRoot({ polyfillCondition }) {
@@ -768,12 +787,10 @@ RenderModule = RenderModule_1 = __decorate([
768
787
  provide: RESOURCES_REGISTRY_CACHE,
769
788
  scope: Scope.SINGLETON,
770
789
  useFactory: ({ createCache }) => {
771
- const thirtyMinutes = 1000 * 60 * 30;
772
790
  return {
773
- filesCache: createCache('memory', { max: 50, ttl: thirtyMinutes }),
774
- sizeCache: createCache('memory', { max: 100, ttl: thirtyMinutes }),
775
- requestsCache: createCache('memory', { max: 150, ttl: 1000 * 60 * 5 }),
776
- disabledUrlsCache: createCache('memory', { max: 150, ttl: 1000 * 60 * 5 }),
791
+ filesCache: createCache('memory', { max: 50 }),
792
+ sizeCache: createCache('memory', { max: 100 }),
793
+ disabledUrlsCache: createCache('memory', { max: 150, ttl: REQUEST_TTL }),
777
794
  };
778
795
  },
779
796
  deps: {
@@ -782,6 +799,7 @@ RenderModule = RenderModule_1 = __decorate([
782
799
  }),
783
800
  provide({
784
801
  provide: RESOURCE_INLINER,
802
+ scope: Scope.SINGLETON,
785
803
  useClass: ResourcesInliner,
786
804
  deps: {
787
805
  resourcesRegistryCache: RESOURCES_REGISTRY_CACHE,
@@ -890,7 +908,7 @@ RenderModule = RenderModule_1 = __decorate([
890
908
  useValue: DEFAULT_POLYFILL_CONDITION,
891
909
  }),
892
910
  provide({
893
- // Включаем инлайнинг CSS-файлов размером до 40кб (до gzip) по умолчанию.
911
+ // by default, enable inlining for css files with size below 40kb before gzip
894
912
  provide: RESOURCE_INLINE_OPTIONS,
895
913
  useValue: {
896
914
  threshold: 40960,
package/lib/server.js CHANGED
@@ -6,7 +6,7 @@ var tslib = require('tslib');
6
6
  var react = require('react');
7
7
  var server$1 = require('react-dom/server');
8
8
  var core = require('@tramvai/core');
9
- var moduleCommon = require('@tramvai/module-common');
9
+ var tokensCommon = require('@tramvai/tokens-common');
10
10
  var tokensRouter = require('@tramvai/tokens-router');
11
11
  var moduleClientHints = require('@tramvai/module-client-hints');
12
12
  var tokensRender = require('@tramvai/tokens-render');
@@ -36,7 +36,6 @@ var jsxRuntime = require('react/jsx-runtime');
36
36
  var state = require('@tramvai/state');
37
37
  var moduleRouter = require('@tramvai/module-router');
38
38
  var layoutFactory = require('@tinkoff/layout-factory');
39
- var tokensCommon = require('@tramvai/tokens-common');
40
39
 
41
40
  function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
42
41
 
@@ -111,6 +110,11 @@ const processFile = (resource, file) => {
111
110
  return file;
112
111
  };
113
112
 
113
+ const INTERNAL_CACHE_SIZE = 50;
114
+ const ASSETS_PREFIX = process.env.NODE_ENV === 'development' &&
115
+ (process.env.ASSETS_PREFIX === 'static' || !process.env.ASSETS_PREFIX)
116
+ ? `http://localhost:${process.env.PORT_STATIC}/dist/`
117
+ : process.env.ASSETS_PREFIX;
114
118
  const getInlineType = (type) => {
115
119
  switch (type) {
116
120
  case tokensRender.ResourceType.style:
@@ -131,51 +135,53 @@ const getResourceUrl = (resource) => {
131
135
  };
132
136
  class ResourcesInliner {
133
137
  constructor({ resourcesRegistryCache, resourceInlineThreshold, logger }) {
134
- this.scheduleFileLoad = (resource, resourceInlineThreshold) => {
138
+ this.internalFilesCache = new Map();
139
+ this.runningRequests = new Set();
140
+ this.scheduleFileLoad = async (resource, resourceInlineThreshold) => {
135
141
  const url = getResourceUrl(resource);
136
142
  const requestKey = `file${url}`;
137
- const urlIsDisabled = this.resourcesRegistryCache.disabledUrlsCache.get(url);
138
- const requestIsInProgress = this.resourcesRegistryCache.requestsCache.get(requestKey);
139
- if (!requestIsInProgress && !urlIsDisabled) {
140
- const result = this.resourcesRegistryCache.filesCache.get(url);
141
- if (result) {
142
- return result;
143
- }
144
- const getFilePromise = getFile(url)
145
- .then((file) => {
143
+ const filesCache = this.getFilesCache(url);
144
+ const result = filesCache.get(url);
145
+ if (result) {
146
+ return result;
147
+ }
148
+ if (!this.runningRequests.has(requestKey)) {
149
+ this.runningRequests.add(url);
150
+ try {
151
+ const file = await getFile(url);
146
152
  if (file === undefined) {
147
153
  this.resourcesRegistryCache.disabledUrlsCache.set(url, true);
148
154
  return;
149
155
  }
150
156
  const size = file.length;
151
157
  if (size < resourceInlineThreshold) {
152
- this.resourcesRegistryCache.filesCache.set(url, processFile(resource, file));
158
+ filesCache.set(url, processFile(resource, file));
153
159
  }
154
160
  this.resourcesRegistryCache.sizeCache.set(url, size);
155
- })
156
- .catch((error) => {
161
+ }
162
+ catch (error) {
157
163
  this.log.warn({
158
164
  event: 'file-load-failed',
159
165
  url,
160
166
  error,
161
167
  });
162
- })
163
- .finally(() => {
164
- this.resourcesRegistryCache.requestsCache.set(requestKey, undefined);
165
- });
166
- this.resourcesRegistryCache.requestsCache.set(requestKey, getFilePromise);
168
+ }
169
+ finally {
170
+ this.runningRequests.delete(requestKey);
171
+ }
167
172
  }
168
173
  };
169
- this.scheduleFileSizeLoad = (resource, resourceInlineThreshold) => {
174
+ this.scheduleFileSizeLoad = async (resource, resourceInlineThreshold) => {
170
175
  const url = getResourceUrl(resource);
171
176
  const requestKey = `size${url}`;
172
- if (!this.resourcesRegistryCache.requestsCache.has(requestKey)) {
173
- const result = this.resourcesRegistryCache.sizeCache.get(url);
174
- if (result) {
175
- return result;
176
- }
177
- const getFileSizePromise = getFileContentLength(url)
178
- .then((contentLength) => {
177
+ const result = this.resourcesRegistryCache.sizeCache.get(url);
178
+ if (result) {
179
+ return result;
180
+ }
181
+ if (!this.runningRequests.has(requestKey)) {
182
+ this.runningRequests.add(requestKey);
183
+ try {
184
+ const contentLength = await getFileContentLength(url);
179
185
  const size = isUndefined__default["default"](contentLength) ? 0 : +contentLength;
180
186
  if (size) {
181
187
  this.resourcesRegistryCache.sizeCache.set(url, size);
@@ -183,41 +189,48 @@ class ResourcesInliner {
183
189
  if (size < resourceInlineThreshold) {
184
190
  this.scheduleFileLoad(resource, resourceInlineThreshold);
185
191
  }
186
- })
187
- .catch((error) => {
192
+ }
193
+ catch (error) {
188
194
  this.log.warn({
189
195
  event: 'file-content-length-load-failed',
190
196
  url,
191
197
  error,
192
198
  });
193
- })
194
- .finally(() => {
195
- this.resourcesRegistryCache.requestsCache.set(requestKey, undefined);
196
- });
197
- this.resourcesRegistryCache.requestsCache.set(requestKey, getFileSizePromise);
199
+ }
200
+ finally {
201
+ this.runningRequests.delete(requestKey);
202
+ }
198
203
  }
199
204
  };
200
205
  this.resourcesRegistryCache = resourcesRegistryCache;
201
206
  this.resourceInlineThreshold = resourceInlineThreshold;
202
207
  this.log = logger('resources-inliner');
203
208
  }
204
- // Метод проверки, стоит ли добавлять preload-ресурс
209
+ getFilesCache(url) {
210
+ if (url.startsWith(ASSETS_PREFIX)) {
211
+ // internal resources are resources generated by the current app itself
212
+ // these kind of resources are pretty static and won't be changed while app is running
213
+ // so we can cache it with bare Map and do not care about how to cleanup cache from outdated entries
214
+ return this.internalFilesCache;
215
+ }
216
+ return this.resourcesRegistryCache.filesCache;
217
+ }
218
+ // check that resource's preload-link should be added to render
205
219
  shouldAddResource(resource) {
206
220
  if (resource.type !== tokensRender.ResourceType.preloadLink) {
207
- // Мы фильтруем только preloadLink, если это ресурс другого типа он должен
208
- // попасть в итоговую выборку.
221
+ // only checking preload-links
209
222
  return true;
210
223
  }
211
224
  const url = getResourceUrl(resource);
212
225
  if (isUndefined__default["default"](url)) {
213
- // Если у ресурса нет URL'а, в кеше этого файла точно нет
226
+ // if url is undefined that file is not in cache
214
227
  return true;
215
228
  }
216
- // Если файл лежит в кеше, значит он прошёл все проверки и будет инлайниться =>
217
- // нам не нужно добавлять для него preload.
218
- return !this.resourcesRegistryCache.filesCache.has(url);
229
+ // if file is residing in cache that means it will be inlined in page render
230
+ // therefore no need to have preload-link for the inlined resource
231
+ return !this.getFilesCache(url).has(url);
219
232
  }
220
- // Метод проверки, должен ли быть заинлайнен в HTML-страницу ресурс.
233
+ // method for check is passed resource should be inlined in HTML-page
221
234
  shouldInline(resource) {
222
235
  var _a;
223
236
  if (!(((_a = this.resourceInlineThreshold) === null || _a === void 0 ? void 0 : _a.types) || []).includes(resource.type)) {
@@ -228,7 +241,16 @@ class ResourcesInliner {
228
241
  return false;
229
242
  }
230
243
  const url = getResourceUrl(resource);
231
- if (isUndefined__default["default"](url)) {
244
+ const filesCache = this.getFilesCache(url);
245
+ if (isUndefined__default["default"](url) || this.resourcesRegistryCache.disabledUrlsCache.has(url)) {
246
+ return false;
247
+ }
248
+ if (filesCache.has(url)) {
249
+ return true;
250
+ }
251
+ if (filesCache === this.internalFilesCache &&
252
+ this.internalFilesCache.size >= INTERNAL_CACHE_SIZE) {
253
+ // if we've exceeded limits for the internal resources cache ignore any new entries
232
254
  return false;
233
255
  }
234
256
  if (!this.resourcesRegistryCache.sizeCache.has(url)) {
@@ -239,25 +261,22 @@ class ResourcesInliner {
239
261
  if (size > resourceInlineThreshold) {
240
262
  return false;
241
263
  }
242
- if (!this.resourcesRegistryCache.filesCache.has(url)) {
243
- this.scheduleFileLoad(resource, resourceInlineThreshold);
244
- return false;
245
- }
246
- return true;
264
+ this.scheduleFileLoad(resource, resourceInlineThreshold);
265
+ return false;
247
266
  }
248
267
  inlineResource(resource) {
249
268
  const url = getResourceUrl(resource);
250
269
  if (isUndefined__default["default"](url)) {
251
- // В теории, такого быть не должно, но добавим проверку на всякий случай
270
+ // usually, it should not happen but anyway check it for safety
252
271
  return [resource];
253
272
  }
254
- const text = this.resourcesRegistryCache.filesCache.get(url);
273
+ const text = this.getFilesCache(url).get(url);
255
274
  if (isEmpty__default["default"](text)) {
256
275
  return [resource];
257
276
  }
258
277
  const result = [];
259
278
  if (process.env.NODE_ENV === 'development') {
260
- // Добавляем html комментарии для упрощения отладки инлайнинга в dev режиме
279
+ // html comment for debugging inlining in dev mode
261
280
  result.push({
262
281
  slot: resource.slot,
263
282
  type: tokensRender.ResourceType.asIs,
@@ -270,11 +289,10 @@ class ResourcesInliner {
270
289
  payload: text,
271
290
  });
272
291
  if (resource.type === tokensRender.ResourceType.style) {
273
- // Если не добавить data-href, extract-css-chunks-webpack-plugin
274
- // добавит (https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/blob/master/src/index.js#L346)
275
- // в head ссылку на файл и инлайнинг будет бесполезен, т.к. браузер всё равно пойдёт скачивать этот файл.
276
- // При этом в случае css-файлов он ищет тэг link, а при инлайнинге мы вставляем style => мы не можем
277
- // использовать тэг выше, а приходится генерировать новый.
292
+ // If we don't add data-href then extract-css-chunks-webpack-plugin
293
+ // will add link to resources to the html head (https://github.com/faceyspacey/extract-css-chunks-webpack-plugin/blob/master/src/index.js#L346)
294
+ // wherein link in case of css files plugin will look for a link tag, but we add a style tag
295
+ // so we can't use tag from above and have to generate new one
278
296
  result.push({
279
297
  slot: resource.slot,
280
298
  type: tokensRender.ResourceType.style,
@@ -772,6 +790,7 @@ const providers = [
772
790
  ];
773
791
 
774
792
  var RenderModule_1;
793
+ const REQUEST_TTL = 5 * 60 * 1000;
775
794
  const DEFAULT_POLYFILL_CONDITION = '!window.Promise.prototype.finally || !window.URL || !window.URLSearchParams || !window.AbortController || !window.IntersectionObserver || !Object.fromEntries || !window.ResizeObserver';
776
795
  exports.RenderModule = RenderModule_1 = class RenderModule {
777
796
  static forRoot({ polyfillCondition }) {
@@ -804,25 +823,24 @@ exports.RenderModule = RenderModule_1 = tslib.__decorate([
804
823
  provide: RESOURCES_REGISTRY_CACHE,
805
824
  scope: dippy.Scope.SINGLETON,
806
825
  useFactory: ({ createCache }) => {
807
- const thirtyMinutes = 1000 * 60 * 30;
808
826
  return {
809
- filesCache: createCache('memory', { max: 50, ttl: thirtyMinutes }),
810
- sizeCache: createCache('memory', { max: 100, ttl: thirtyMinutes }),
811
- requestsCache: createCache('memory', { max: 150, ttl: 1000 * 60 * 5 }),
812
- disabledUrlsCache: createCache('memory', { max: 150, ttl: 1000 * 60 * 5 }),
827
+ filesCache: createCache('memory', { max: 50 }),
828
+ sizeCache: createCache('memory', { max: 100 }),
829
+ disabledUrlsCache: createCache('memory', { max: 150, ttl: REQUEST_TTL }),
813
830
  };
814
831
  },
815
832
  deps: {
816
- createCache: moduleCommon.CREATE_CACHE_TOKEN,
833
+ createCache: tokensCommon.CREATE_CACHE_TOKEN,
817
834
  },
818
835
  }),
819
836
  core.provide({
820
837
  provide: RESOURCE_INLINER,
838
+ scope: dippy.Scope.SINGLETON,
821
839
  useClass: ResourcesInliner,
822
840
  deps: {
823
841
  resourcesRegistryCache: RESOURCES_REGISTRY_CACHE,
824
842
  resourceInlineThreshold: { token: tokensRender.RESOURCE_INLINE_OPTIONS, optional: true },
825
- logger: moduleCommon.LOGGER_TOKEN,
843
+ logger: tokensCommon.LOGGER_TOKEN,
826
844
  },
827
845
  }),
828
846
  core.provide({
@@ -859,11 +877,11 @@ exports.RenderModule = RenderModule_1 = tslib.__decorate([
859
877
  };
860
878
  },
861
879
  deps: {
862
- logger: moduleCommon.LOGGER_TOKEN,
863
- requestManager: moduleCommon.REQUEST_MANAGER_TOKEN,
864
- responseManager: moduleCommon.RESPONSE_MANAGER_TOKEN,
880
+ logger: tokensCommon.LOGGER_TOKEN,
881
+ requestManager: tokensCommon.REQUEST_MANAGER_TOKEN,
882
+ responseManager: tokensCommon.RESPONSE_MANAGER_TOKEN,
865
883
  htmlBuilder: 'htmlBuilder',
866
- context: moduleCommon.CONTEXT_TOKEN,
884
+ context: tokensCommon.CONTEXT_TOKEN,
867
885
  },
868
886
  multi: true,
869
887
  }),
@@ -874,7 +892,7 @@ exports.RenderModule = RenderModule_1 = tslib.__decorate([
874
892
  reactRender: 'reactRender',
875
893
  pageService: tokensRouter.PAGE_SERVICE_TOKEN,
876
894
  resourcesRegistry: tokensRender.RESOURCES_REGISTRY,
877
- context: moduleCommon.CONTEXT_TOKEN,
895
+ context: tokensCommon.CONTEXT_TOKEN,
878
896
  htmlPageSchema: 'htmlPageSchema',
879
897
  renderSlots: { token: tokensRender.RENDER_SLOTS, optional: true },
880
898
  polyfillCondition: tokensRender.POLYFILL_CONDITION,
@@ -886,7 +904,7 @@ exports.RenderModule = RenderModule_1 = tslib.__decorate([
886
904
  provide: 'reactRender',
887
905
  useClass: ReactRenderServer,
888
906
  deps: {
889
- context: moduleCommon.CONTEXT_TOKEN,
907
+ context: tokensCommon.CONTEXT_TOKEN,
890
908
  pageService: tokensRouter.PAGE_SERVICE_TOKEN,
891
909
  customRender: { token: tokensRender.CUSTOM_RENDER, optional: true },
892
910
  extendRender: { token: tokensRender.EXTEND_RENDER, optional: true },
@@ -926,7 +944,7 @@ exports.RenderModule = RenderModule_1 = tslib.__decorate([
926
944
  useValue: DEFAULT_POLYFILL_CONDITION,
927
945
  }),
928
946
  core.provide({
929
- // Включаем инлайнинг CSS-файлов размером до 40кб (до gzip) по умолчанию.
947
+ // by default, enable inlining for css files with size below 40kb before gzip
930
948
  provide: tokensRender.RESOURCE_INLINE_OPTIONS,
931
949
  useValue: {
932
950
  threshold: 40960,
@@ -959,7 +977,7 @@ exports.RenderModule = RenderModule_1 = tslib.__decorate([
959
977
  },
960
978
  deps: {
961
979
  RootErrorBoundary: { token: react$1.ROOT_ERROR_BOUNDARY_COMPONENT_TOKEN, optional: true },
962
- logger: moduleCommon.LOGGER_TOKEN,
980
+ logger: tokensCommon.LOGGER_TOKEN,
963
981
  },
964
982
  }),
965
983
  core.provide({
@@ -974,7 +992,7 @@ exports.RenderModule = RenderModule_1 = tslib.__decorate([
974
992
  return result;
975
993
  },
976
994
  deps: {
977
- requestManager: moduleCommon.REQUEST_MANAGER_TOKEN,
995
+ requestManager: tokensCommon.REQUEST_MANAGER_TOKEN,
978
996
  userAgent: moduleClientHints.USER_AGENT_TOKEN,
979
997
  cache: 'modernSatisfiesLruCache',
980
998
  },
@@ -986,7 +1004,7 @@ exports.RenderModule = RenderModule_1 = tslib.__decorate([
986
1004
  return createCache('memory', { max: 50 });
987
1005
  },
988
1006
  deps: {
989
- createCache: moduleCommon.CREATE_CACHE_TOKEN,
1007
+ createCache: tokensCommon.CREATE_CACHE_TOKEN,
990
1008
  },
991
1009
  }),
992
1010
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tramvai/module-render",
3
- "version": "2.7.0",
3
+ "version": "2.7.1",
4
4
  "description": "",
5
5
  "browser": "lib/browser.js",
6
6
  "main": "lib/server.js",
@@ -24,13 +24,13 @@
24
24
  "@tinkoff/htmlpagebuilder": "0.4.24",
25
25
  "@tinkoff/layout-factory": "0.2.31",
26
26
  "@tinkoff/url": "0.7.39",
27
- "@tinkoff/user-agent": "0.4.23",
28
- "@tramvai/module-client-hints": "2.7.0",
29
- "@tramvai/module-router": "2.7.0",
30
- "@tramvai/react": "2.7.0",
27
+ "@tinkoff/user-agent": "0.4.24",
28
+ "@tramvai/module-client-hints": "2.7.1",
29
+ "@tramvai/module-router": "2.7.1",
30
+ "@tramvai/react": "2.7.1",
31
31
  "@tramvai/safe-strings": "0.4.5",
32
- "@tramvai/tokens-render": "2.7.0",
33
- "@tramvai/experiments": "2.7.0",
32
+ "@tramvai/tokens-render": "2.7.1",
33
+ "@tramvai/experiments": "2.7.1",
34
34
  "@types/loadable__server": "^5.12.6",
35
35
  "node-fetch": "^2.6.1"
36
36
  },
@@ -38,14 +38,14 @@
38
38
  "@tinkoff/dippy": "0.7.44",
39
39
  "@tinkoff/utils": "^2.1.2",
40
40
  "@tinkoff/react-hooks": "0.0.27",
41
- "@tramvai/cli": "2.7.0",
42
- "@tramvai/core": "2.7.0",
43
- "@tramvai/module-common": "2.7.0",
44
- "@tramvai/state": "2.7.0",
45
- "@tramvai/test-helpers": "2.7.0",
46
- "@tramvai/tokens-common": "2.7.0",
47
- "@tramvai/tokens-router": "2.7.0",
48
- "@tramvai/tokens-server-private": "2.7.0",
41
+ "@tramvai/cli": "2.7.1",
42
+ "@tramvai/core": "2.7.1",
43
+ "@tramvai/module-common": "2.7.1",
44
+ "@tramvai/state": "2.7.1",
45
+ "@tramvai/test-helpers": "2.7.1",
46
+ "@tramvai/tokens-common": "2.7.1",
47
+ "@tramvai/tokens-router": "2.7.1",
48
+ "@tramvai/tokens-server-private": "2.7.1",
49
49
  "express": "^4.17.1",
50
50
  "prop-types": "^15.6.2",
51
51
  "react": ">=16.14.0",