@enonic/nextjs-adapter 1.0.1 → 1.0.2

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/UrlProcessor.d.ts CHANGED
@@ -18,6 +18,7 @@ export declare class UrlProcessor {
18
18
  static processSrcSet(srcset: string, meta: MetaData): string;
19
19
  private static stripApiUrl;
20
20
  private static isPageUrl;
21
+ private static startsWithHash;
21
22
  private static getApiUrl;
22
23
  }
23
24
  export declare const getUrl: (url: string, meta: MetaData) => string;
package/UrlProcessor.js CHANGED
@@ -3,8 +3,9 @@ import { useRouter } from 'next/router';
3
3
  class UrlProcessor {
4
4
  static process(url, meta) {
5
5
  let result;
6
- if (!meta || !this.isPageUrl(url) && meta.renderMode === RENDER_MODE.NEXT) {
7
- // keep content urls absolute in next mode for everything but page urls
6
+ if (this.startsWithHash(url) || !meta || !this.isPageUrl(url) && meta.renderMode === RENDER_MODE.NEXT) {
7
+ // keep page-relative urls starting with hash
8
+ // keep urls absolute in next mode for everything but page urls
8
9
  result = url;
9
10
  }
10
11
  else {
@@ -60,6 +61,9 @@ class UrlProcessor {
60
61
  static isPageUrl(url) {
61
62
  return !this.IMG_ATMT_REGEXP.test(url);
62
63
  }
64
+ static startsWithHash(url) {
65
+ return (url === null || url === void 0 ? void 0 : url.charAt(0)) == '#';
66
+ }
63
67
  static getApiUrl(meta) {
64
68
  var _a, _b;
65
69
  let combinedUrl = ((_a = meta.apiUrl) === null || _a === void 0 ? void 0 : _a.replace(this.endSlashPattern, '')) || '';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enonic/nextjs-adapter",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "license": "Apache-2.0",
5
5
  "main": "index.js",
6
6
  "publishConfig": {
@@ -13,22 +13,22 @@
13
13
  "lint:fix": "eslint --fix server && eslint --fix src"
14
14
  },
15
15
  "dependencies": {
16
- "html-react-parser": "^3.0.15",
17
- "lru-cache": "^8.0.4",
18
- "next": "^13.2.4",
16
+ "html-react-parser": "^3.0.16",
17
+ "lru-cache": "^9.1.1",
18
+ "next": "^13.3.1",
19
19
  "react": "^18.2.0",
20
20
  "react-dom": "^18.2.0",
21
21
  "unescape": "^1.0.1"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@enonic/eslint-config": "^1.1.0",
25
- "@types/node": "^18.15.10",
26
- "@types/react": "^18.0.29",
25
+ "@types/node": "^18.16.0",
26
+ "@types/react": "^18.0.38",
27
27
  "@types/react-dom": "^18.0.11",
28
- "@typescript-eslint/eslint-plugin": "^5.56.0",
29
- "@typescript-eslint/parser": "^5.56.0",
30
- "eslint": "^8.36.0",
28
+ "@typescript-eslint/eslint-plugin": "^5.59.0",
29
+ "@typescript-eslint/parser": "^5.59.0",
30
+ "eslint": "^8.39.0",
31
31
  "eslint-plugin-react": "^7.32.2",
32
- "typescript": "^5.0.2"
32
+ "typescript": "^5.0.4"
33
33
  }
34
34
  }
@@ -9,9 +9,13 @@ export default class ClearableFileCache implements CacheHandler {
9
9
  private flushToDisk?;
10
10
  private serverDistDir;
11
11
  private appDir;
12
+ private tagsManifestPath?;
12
13
  constructor(ctx: FileSystemCacheContext);
13
14
  clear(): void;
14
15
  delete(path: string): boolean | undefined;
16
+ private loadTagsManifest;
17
+ setTags(key: string, tags: string[]): Promise<void>;
18
+ revalidateTag(tag: string): Promise<void>;
15
19
  get(key: string, fetchCache?: boolean): Promise<CacheHandlerValue | null>;
16
20
  set(key: string, data: CacheHandlerValue['value']): Promise<void>;
17
21
  getFsPath({ pathname, appDir, fetchCache, }: {
@@ -40,8 +40,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
40
40
  };
41
41
  Object.defineProperty(exports, "__esModule", { value: true });
42
42
  var path_1 = __importDefault(require("path"));
43
- var lru_cache_1 = __importDefault(require("lru-cache"));
43
+ var lru_cache_1 = require("lru-cache");
44
+ var constants_1 = require("next/dist/lib/constants");
44
45
  var memoryCache;
46
+ var tagsManifest;
45
47
  var ClearableFileCache = /** @class */ (function () {
46
48
  function ClearableFileCache(ctx) {
47
49
  this.fs = ctx.fs;
@@ -49,7 +51,7 @@ var ClearableFileCache = /** @class */ (function () {
49
51
  this.serverDistDir = ctx.serverDistDir;
50
52
  this.appDir = !!ctx._appDir;
51
53
  if (ctx.maxMemoryCacheSize && !memoryCache) {
52
- var config = {
54
+ memoryCache = new lru_cache_1.LRUCache({
53
55
  max: ctx.maxMemoryCacheSize,
54
56
  maxSize: 10 * ctx.maxMemoryCacheSize,
55
57
  sizeCalculation: function (_a) {
@@ -73,8 +75,11 @@ var ClearableFileCache = /** @class */ (function () {
73
75
  // rough estimate of size of cache value
74
76
  return (value.html.length + (((_b = JSON.stringify(value.pageData)) === null || _b === void 0 ? void 0 : _b.length) || 0));
75
77
  },
76
- };
77
- memoryCache = new lru_cache_1.default(config);
78
+ });
79
+ }
80
+ if (this.serverDistDir && this.fs) {
81
+ this.tagsManifestPath = path_1.default.join(this.serverDistDir, '..', 'cache', 'fetch-cache', 'tags-manifest.json');
82
+ this.loadTagsManifest();
78
83
  }
79
84
  }
80
85
  ClearableFileCache.prototype.clear = function () {
@@ -83,93 +88,175 @@ var ClearableFileCache = /** @class */ (function () {
83
88
  ClearableFileCache.prototype.delete = function (path) {
84
89
  return memoryCache === null || memoryCache === void 0 ? void 0 : memoryCache.delete(path);
85
90
  };
91
+ ClearableFileCache.prototype.loadTagsManifest = function () {
92
+ if (!this.tagsManifestPath || !this.fs)
93
+ return;
94
+ try {
95
+ tagsManifest = JSON.parse(this.fs.readFileSync(this.tagsManifestPath).toString('utf8'));
96
+ }
97
+ catch (err) {
98
+ tagsManifest = { version: 1, items: {} };
99
+ }
100
+ };
101
+ ClearableFileCache.prototype.setTags = function (key, tags) {
102
+ return __awaiter(this, void 0, void 0, function () {
103
+ var _i, tags_1, tag, data, err_1;
104
+ return __generator(this, function (_a) {
105
+ switch (_a.label) {
106
+ case 0:
107
+ this.loadTagsManifest();
108
+ if (!tagsManifest || !this.tagsManifestPath) {
109
+ return [2 /*return*/];
110
+ }
111
+ for (_i = 0, tags_1 = tags; _i < tags_1.length; _i++) {
112
+ tag = tags_1[_i];
113
+ data = tagsManifest.items[tag] || { keys: [] };
114
+ if (!data.keys.includes(key)) {
115
+ data.keys.push(key);
116
+ }
117
+ tagsManifest.items[tag] = data;
118
+ }
119
+ _a.label = 1;
120
+ case 1:
121
+ _a.trys.push([1, 4, , 5]);
122
+ return [4 /*yield*/, this.fs.mkdir(path_1.default.dirname(this.tagsManifestPath))];
123
+ case 2:
124
+ _a.sent();
125
+ return [4 /*yield*/, this.fs.writeFile(this.tagsManifestPath, JSON.stringify(tagsManifest || {}))];
126
+ case 3:
127
+ _a.sent();
128
+ return [3 /*break*/, 5];
129
+ case 4:
130
+ err_1 = _a.sent();
131
+ console.warn('Failed to update tags manifest.', err_1);
132
+ return [3 /*break*/, 5];
133
+ case 5: return [2 /*return*/];
134
+ }
135
+ });
136
+ });
137
+ };
138
+ ClearableFileCache.prototype.revalidateTag = function (tag) {
139
+ return __awaiter(this, void 0, void 0, function () {
140
+ var data, err_2;
141
+ return __generator(this, function (_a) {
142
+ switch (_a.label) {
143
+ case 0:
144
+ // we need to ensure the tagsManifest is refreshed
145
+ // since separate workers can be updating it at the same
146
+ // time and we can't flush out of sync data
147
+ this.loadTagsManifest();
148
+ if (!tagsManifest || !this.tagsManifestPath) {
149
+ return [2 /*return*/];
150
+ }
151
+ data = tagsManifest.items[tag] || { keys: [] };
152
+ data.revalidatedAt = Date.now();
153
+ tagsManifest.items[tag] = data;
154
+ _a.label = 1;
155
+ case 1:
156
+ _a.trys.push([1, 4, , 5]);
157
+ return [4 /*yield*/, this.fs.mkdir(path_1.default.dirname(this.tagsManifestPath))];
158
+ case 2:
159
+ _a.sent();
160
+ return [4 /*yield*/, this.fs.writeFile(this.tagsManifestPath, JSON.stringify(tagsManifest || {}))];
161
+ case 3:
162
+ _a.sent();
163
+ return [3 /*break*/, 5];
164
+ case 4:
165
+ err_2 = _a.sent();
166
+ console.warn('Failed to update tags manifest.', err_2);
167
+ return [3 /*break*/, 5];
168
+ case 5: return [2 /*return*/];
169
+ }
170
+ });
171
+ });
172
+ };
86
173
  ClearableFileCache.prototype.get = function (key, fetchCache) {
174
+ var _a, _b;
87
175
  return __awaiter(this, void 0, void 0, function () {
88
- var data, filePath, fileData, mtime, meta, _a, _b, cacheEntry, _1, _c, filePath, isAppPath, fileData, mtime, lastModified, pageData, _d, _e, _f, _g, _h, _j, _k, _2;
89
- return __generator(this, function (_l) {
90
- switch (_l.label) {
176
+ var data, filePath, fileData, mtime, meta, _c, _d, cacheEntry, _1, _e, filePath, isAppPath, fileData, mtime, lastModified, parsedData, pageData, _f, _g, _h, _j, _k, _l, _m, _2, innerData, isStale;
177
+ return __generator(this, function (_o) {
178
+ switch (_o.label) {
91
179
  case 0:
92
180
  data = memoryCache === null || memoryCache === void 0 ? void 0 : memoryCache.get(key);
93
- if (!(!data && process.env.NEXT_RUNTIME !== 'edge')) return [3 /*break*/, 21];
94
- _l.label = 1;
181
+ if (!(!data && process.env.NEXT_RUNTIME !== 'edge')) return [3 /*break*/, 20];
182
+ _o.label = 1;
95
183
  case 1:
96
- _l.trys.push([1, 6, , 7]);
184
+ _o.trys.push([1, 6, , 7]);
97
185
  return [4 /*yield*/, this.getFsPath({
98
186
  pathname: "".concat(key, ".body"),
99
187
  appDir: true,
100
188
  })];
101
189
  case 2:
102
- filePath = (_l.sent()).filePath;
190
+ filePath = (_o.sent()).filePath;
103
191
  return [4 /*yield*/, this.fs.readFile(filePath)];
104
192
  case 3:
105
- fileData = _l.sent();
193
+ fileData = _o.sent();
106
194
  return [4 /*yield*/, this.fs.stat(filePath)];
107
195
  case 4:
108
- mtime = (_l.sent()).mtime;
109
- _b = (_a = JSON).parse;
196
+ mtime = (_o.sent()).mtime;
197
+ _d = (_c = JSON).parse;
110
198
  return [4 /*yield*/, this.fs.readFile(filePath.replace(/\.body$/, '.meta'))];
111
199
  case 5:
112
- meta = _b.apply(_a, [_l.sent()]);
200
+ meta = _d.apply(_c, [(_o.sent()).toString('utf8')]);
113
201
  cacheEntry = {
114
202
  lastModified: mtime.getTime(),
115
203
  value: {
116
204
  kind: 'ROUTE',
117
- body: Buffer.from(fileData),
205
+ body: fileData,
118
206
  headers: meta.headers,
119
207
  status: meta.status,
120
208
  },
121
209
  };
122
210
  return [2 /*return*/, cacheEntry];
123
211
  case 6:
124
- _1 = _l.sent();
212
+ _1 = _o.sent();
125
213
  return [3 /*break*/, 7];
126
214
  case 7:
127
- _l.trys.push([7, 20, , 21]);
215
+ _o.trys.push([7, 19, , 20]);
128
216
  return [4 /*yield*/, this.getFsPath({
129
217
  pathname: fetchCache ? key : "".concat(key, ".html"),
130
218
  fetchCache: fetchCache,
131
219
  })];
132
220
  case 8:
133
- _c = _l.sent(), filePath = _c.filePath, isAppPath = _c.isAppPath;
221
+ _e = _o.sent(), filePath = _e.filePath, isAppPath = _e.isAppPath;
134
222
  return [4 /*yield*/, this.fs.readFile(filePath)];
135
223
  case 9:
136
- fileData = _l.sent();
224
+ fileData = (_o.sent()).toString('utf-8');
137
225
  return [4 /*yield*/, this.fs.stat(filePath)];
138
226
  case 10:
139
- mtime = (_l.sent()).mtime;
227
+ mtime = (_o.sent()).mtime;
140
228
  if (!fetchCache) return [3 /*break*/, 11];
141
229
  lastModified = mtime.getTime();
230
+ parsedData = JSON.parse(fileData);
142
231
  data = {
143
232
  lastModified: lastModified,
144
- value: JSON.parse(fileData),
233
+ value: parsedData,
145
234
  };
146
- return [3 /*break*/, 19];
235
+ return [3 /*break*/, 18];
147
236
  case 11:
148
237
  if (!isAppPath) return [3 /*break*/, 14];
149
- _f = (_e = this.fs).readFile;
150
- return [4 /*yield*/, this.getFsPath({ pathname: "".concat(key, ".rsc"), appDir: true })];
151
- case 12: return [4 /*yield*/, _f.apply(_e, [(_l.sent()).filePath])];
238
+ _h = (_g = this.fs).readFile;
239
+ return [4 /*yield*/, this.getFsPath({
240
+ pathname: "".concat(key, ".rsc"),
241
+ appDir: true,
242
+ })];
243
+ case 12: return [4 /*yield*/, _h.apply(_g, [(_o.sent()).filePath])];
152
244
  case 13:
153
- _d = _l.sent();
154
- return [3 /*break*/, 18];
245
+ _f = (_o.sent()).toString('utf8');
246
+ return [3 /*break*/, 17];
155
247
  case 14:
156
- _h = (_g = JSON).parse;
157
- _k = (_j = this.fs).readFile;
248
+ _k = (_j = JSON).parse;
249
+ _m = (_l = this.fs).readFile;
158
250
  return [4 /*yield*/, this.getFsPath({
159
251
  pathname: "".concat(key, ".json"),
160
252
  appDir: false,
161
253
  })];
162
- case 15:
163
- // eslint-disable-next-line @typescript-eslint/await-thenable
164
- return [4 /*yield*/, (_l.sent()).filePath];
165
- case 16: return [4 /*yield*/, _k.apply(_j, [
166
- // eslint-disable-next-line @typescript-eslint/await-thenable
167
- _l.sent()])];
254
+ case 15: return [4 /*yield*/, _m.apply(_l, [(_o.sent()).filePath])];
255
+ case 16:
256
+ _f = _k.apply(_j, [(_o.sent()).toString('utf8')]);
257
+ _o.label = 17;
168
258
  case 17:
169
- _d = _h.apply(_g, [_l.sent()]);
170
- _l.label = 18;
171
- case 18:
172
- pageData = _d;
259
+ pageData = _f;
173
260
  data = {
174
261
  lastModified: mtime.getTime(),
175
262
  value: {
@@ -178,16 +265,30 @@ var ClearableFileCache = /** @class */ (function () {
178
265
  pageData: pageData,
179
266
  },
180
267
  };
181
- _l.label = 19;
182
- case 19:
268
+ _o.label = 18;
269
+ case 18:
183
270
  if (data) {
184
271
  memoryCache === null || memoryCache === void 0 ? void 0 : memoryCache.set(key, data);
185
272
  }
186
- return [3 /*break*/, 21];
273
+ return [3 /*break*/, 20];
274
+ case 19:
275
+ _2 = _o.sent();
276
+ return [3 /*break*/, 20];
187
277
  case 20:
188
- _2 = _l.sent();
189
- return [3 /*break*/, 21];
190
- case 21: return [2 /*return*/, data || null];
278
+ if (data && ((_a = data === null || data === void 0 ? void 0 : data.value) === null || _a === void 0 ? void 0 : _a.kind) === 'FETCH') {
279
+ this.loadTagsManifest();
280
+ innerData = data.value.data;
281
+ isStale = (_b = innerData.tags) === null || _b === void 0 ? void 0 : _b.some(function (tag) {
282
+ var _a;
283
+ return (((_a = tagsManifest === null || tagsManifest === void 0 ? void 0 : tagsManifest.items[tag]) === null || _a === void 0 ? void 0 : _a.revalidatedAt) &&
284
+ (tagsManifest === null || tagsManifest === void 0 ? void 0 : tagsManifest.items[tag].revalidatedAt) >=
285
+ ((data === null || data === void 0 ? void 0 : data.lastModified) || Date.now()));
286
+ });
287
+ if (isStale) {
288
+ data.lastModified = Date.now() - constants_1.CACHE_ONE_YEAR;
289
+ }
290
+ }
291
+ return [2 /*return*/, data || null];
191
292
  }
192
293
  });
193
294
  });
@@ -245,9 +346,9 @@ var ClearableFileCache = /** @class */ (function () {
245
346
  isAppPath ? data.pageData : JSON.stringify(data.pageData)])];
246
347
  case 10:
247
348
  _c.sent();
248
- return [3 /*break*/, 15];
349
+ return [3 /*break*/, 16];
249
350
  case 11:
250
- if (!((data === null || data === void 0 ? void 0 : data.kind) === 'FETCH')) return [3 /*break*/, 15];
351
+ if (!((data === null || data === void 0 ? void 0 : data.kind) === 'FETCH')) return [3 /*break*/, 16];
251
352
  return [4 /*yield*/, this.getFsPath({
252
353
  pathname: key,
253
354
  fetchCache: true,
@@ -260,8 +361,11 @@ var ClearableFileCache = /** @class */ (function () {
260
361
  return [4 /*yield*/, this.fs.writeFile(filePath, JSON.stringify(data))];
261
362
  case 14:
262
363
  _c.sent();
263
- _c.label = 15;
264
- case 15: return [2 /*return*/];
364
+ return [4 /*yield*/, this.setTags(key, data.data.tags || [])];
365
+ case 15:
366
+ _c.sent();
367
+ _c.label = 16;
368
+ case 16: return [2 /*return*/];
265
369
  }
266
370
  });
267
371
  });
@@ -269,7 +373,7 @@ var ClearableFileCache = /** @class */ (function () {
269
373
  ClearableFileCache.prototype.getFsPath = function (_a) {
270
374
  var pathname = _a.pathname, appDir = _a.appDir, fetchCache = _a.fetchCache;
271
375
  return __awaiter(this, void 0, void 0, function () {
272
- var isAppPath, filePath, err_1;
376
+ var isAppPath, filePath, err_3;
273
377
  return __generator(this, function (_b) {
274
378
  switch (_b.label) {
275
379
  case 0:
@@ -299,7 +403,7 @@ var ClearableFileCache = /** @class */ (function () {
299
403
  isAppPath: isAppPath,
300
404
  }];
301
405
  case 3:
302
- err_1 = _b.sent();
406
+ err_3 = _b.sent();
303
407
  return [2 /*return*/, {
304
408
  filePath: path_1.default.join(this.serverDistDir, 'app', pathname),
305
409
  isAppPath: true,
package/server/cache.js CHANGED
@@ -77,24 +77,24 @@ var purgeCache = function (cache, pathname) { return __awaiter(void 0, void 0, v
77
77
  }); };
78
78
  exports.purgeCache = purgeCache;
79
79
  var purgePath = function (cache, pathname) { return __awaiter(void 0, void 0, void 0, function () {
80
- var handler, fullPathname;
80
+ var handler, fsPathObj;
81
81
  return __generator(this, function (_a) {
82
82
  switch (_a.label) {
83
83
  case 0:
84
84
  handler = getCacheHandler(cache);
85
85
  return [4 /*yield*/, handler.getFsPath({ pathname: pathname })];
86
86
  case 1:
87
- fullPathname = _a.sent();
87
+ fsPathObj = _a.sent();
88
88
  try {
89
89
  /* Delete entry in cache */
90
90
  handler.delete(pathname);
91
91
  /* Delete files from disc */
92
- fs.unlinkSync("".concat(fullPathname, ".html"));
93
- fs.unlinkSync("".concat(fullPathname, ".json"));
94
- console.info("\nCache deleted for '".concat(fullPathname, "'\n"));
92
+ fs.unlinkSync("".concat(fsPathObj.filePath, ".html"));
93
+ fs.unlinkSync("".concat(fsPathObj.filePath, ".json"));
94
+ console.info("\nCache deleted for '".concat(fsPathObj.filePath, "'\n"));
95
95
  }
96
96
  catch (err) {
97
- console.error("Could not delete cache of '".concat(fullPathname, "':"), err);
97
+ console.error("Could not delete cache of '".concat(fsPathObj.filePath, "':"), err);
98
98
  }
99
99
  return [2 /*return*/];
100
100
  }