@tryghost/url-utils 4.4.15 → 5.0.0-rc.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/lib/UrlUtils.js
CHANGED
|
@@ -2,6 +2,24 @@
|
|
|
2
2
|
const _ = require('lodash');
|
|
3
3
|
const utils = require('./utils');
|
|
4
4
|
|
|
5
|
+
const STATIC_IMAGE_URL_PREFIX = 'content/images';
|
|
6
|
+
const STATIC_FILES_URL_PREFIX = 'content/files';
|
|
7
|
+
const STATIC_MEDIA_URL_PREFIX = 'content/media';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @typedef {Object} AssetBaseUrls
|
|
11
|
+
* @property {string} [image] Absolute base URL for images
|
|
12
|
+
* @property {string} [files] Absolute base URL for files
|
|
13
|
+
* @property {string} [media] Absolute base URL for media
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
function trimTrailingSlash(url) {
|
|
17
|
+
if (!url) {
|
|
18
|
+
return url;
|
|
19
|
+
}
|
|
20
|
+
return url.replace(/\/+$/, '');
|
|
21
|
+
}
|
|
22
|
+
|
|
5
23
|
// similar to Object.assign but will not override defaults if a source value is undefined
|
|
6
24
|
function assignOptions(target, ...sources) {
|
|
7
25
|
const options = sources.map((x) => {
|
|
@@ -22,24 +40,49 @@ module.exports = class UrlUtils {
|
|
|
22
40
|
* @param {('content' | 'admin')} [options.defaultApiType='content'] default API type to be used
|
|
23
41
|
* @param {Object} [options.slugs] object with 2 properties reserved and protected containing arrays of special case slugs
|
|
24
42
|
* @param {Number} [options.redirectCacheMaxAge]
|
|
25
|
-
* @param {
|
|
26
|
-
|
|
43
|
+
* @param {AssetBaseUrls} [options.assetBaseUrls] asset CDN base URLs
|
|
44
|
+
*/
|
|
27
45
|
constructor(options = {}) {
|
|
28
46
|
const defaultOptions = {
|
|
29
47
|
slugs: null,
|
|
30
48
|
redirectCacheMaxAge: null,
|
|
31
49
|
baseApiPath: '/ghost/api',
|
|
32
|
-
defaultApiType: 'content'
|
|
33
|
-
staticImageUrlPrefix: 'content/images'
|
|
50
|
+
defaultApiType: 'content'
|
|
34
51
|
};
|
|
35
52
|
|
|
36
53
|
this._config = assignOptions({}, defaultOptions, options);
|
|
54
|
+
this._config.staticImageUrlPrefix = STATIC_IMAGE_URL_PREFIX;
|
|
55
|
+
this._config.staticFilesUrlPrefix = STATIC_FILES_URL_PREFIX;
|
|
56
|
+
this._config.staticMediaUrlPrefix = STATIC_MEDIA_URL_PREFIX;
|
|
57
|
+
|
|
58
|
+
const assetBaseUrls = options.assetBaseUrls || {};
|
|
59
|
+
this._assetBaseUrls = {
|
|
60
|
+
image: trimTrailingSlash(assetBaseUrls.image || null),
|
|
61
|
+
files: trimTrailingSlash(assetBaseUrls.files || null),
|
|
62
|
+
media: trimTrailingSlash(assetBaseUrls.media || null)
|
|
63
|
+
};
|
|
37
64
|
|
|
38
65
|
this.getSubdir = options.getSubdir;
|
|
39
66
|
this.getSiteUrl = options.getSiteUrl;
|
|
40
67
|
this.getAdminUrl = options.getAdminUrl;
|
|
41
68
|
}
|
|
42
69
|
|
|
70
|
+
_assetOptionDefaults() {
|
|
71
|
+
const siteBase = trimTrailingSlash(this.getSiteUrl ? this.getSiteUrl() : null);
|
|
72
|
+
return {
|
|
73
|
+
staticImageUrlPrefix: this._config.staticImageUrlPrefix,
|
|
74
|
+
staticFilesUrlPrefix: this._config.staticFilesUrlPrefix,
|
|
75
|
+
staticMediaUrlPrefix: this._config.staticMediaUrlPrefix,
|
|
76
|
+
imageBaseUrl: this._assetBaseUrls.image || siteBase,
|
|
77
|
+
filesBaseUrl: this._assetBaseUrls.files || null,
|
|
78
|
+
mediaBaseUrl: this._assetBaseUrls.media || null
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
_buildAssetOptions(additionalDefaults = {}, options) {
|
|
83
|
+
return assignOptions({}, this._assetOptionDefaults(), additionalDefaults, options || {});
|
|
84
|
+
}
|
|
85
|
+
|
|
43
86
|
getProtectedSlugs() {
|
|
44
87
|
let subDir = this.getSubdir();
|
|
45
88
|
|
|
@@ -234,23 +277,28 @@ module.exports = class UrlUtils {
|
|
|
234
277
|
options = itemPath;
|
|
235
278
|
itemPath = null;
|
|
236
279
|
}
|
|
237
|
-
|
|
280
|
+
const _options = this._buildAssetOptions({}, options);
|
|
281
|
+
return utils.toTransformReady(url, this.getSiteUrl(), itemPath, _options);
|
|
238
282
|
}
|
|
239
283
|
|
|
240
284
|
absoluteToTransformReady(url, options) {
|
|
241
|
-
|
|
285
|
+
const _options = this._buildAssetOptions({}, options);
|
|
286
|
+
return utils.absoluteToTransformReady(url, this.getSiteUrl(), _options);
|
|
242
287
|
}
|
|
243
288
|
|
|
244
289
|
relativeToTransformReady(url, options) {
|
|
245
|
-
|
|
290
|
+
const _options = this._buildAssetOptions({}, options);
|
|
291
|
+
return utils.relativeToTransformReady(url, this.getSiteUrl(), _options);
|
|
246
292
|
}
|
|
247
293
|
|
|
248
294
|
transformReadyToAbsolute(url, options) {
|
|
249
|
-
|
|
295
|
+
const _options = this._buildAssetOptions({}, options);
|
|
296
|
+
return utils.transformReadyToAbsolute(url, this.getSiteUrl(), _options);
|
|
250
297
|
}
|
|
251
298
|
|
|
252
299
|
transformReadyToRelative(url, options) {
|
|
253
|
-
|
|
300
|
+
const _options = this._buildAssetOptions({}, options);
|
|
301
|
+
return utils.transformReadyToRelative(url, this.getSiteUrl(), _options);
|
|
254
302
|
}
|
|
255
303
|
|
|
256
304
|
htmlToTransformReady(html, itemPath, options) {
|
|
@@ -258,7 +306,8 @@ module.exports = class UrlUtils {
|
|
|
258
306
|
options = itemPath;
|
|
259
307
|
itemPath = null;
|
|
260
308
|
}
|
|
261
|
-
|
|
309
|
+
const _options = this._buildAssetOptions({}, options);
|
|
310
|
+
return utils.htmlToTransformReady(html, this.getSiteUrl(), itemPath, _options);
|
|
262
311
|
}
|
|
263
312
|
|
|
264
313
|
/**
|
|
@@ -276,11 +325,9 @@ module.exports = class UrlUtils {
|
|
|
276
325
|
options = itemPath;
|
|
277
326
|
itemPath = null;
|
|
278
327
|
}
|
|
279
|
-
const
|
|
280
|
-
assetsOnly: false
|
|
281
|
-
|
|
282
|
-
};
|
|
283
|
-
const _options = assignOptions({}, defaultOptions, options || {});
|
|
328
|
+
const _options = this._buildAssetOptions({
|
|
329
|
+
assetsOnly: false
|
|
330
|
+
}, options);
|
|
284
331
|
return utils.htmlRelativeToAbsolute(html, this.getSiteUrl(), itemPath, _options);
|
|
285
332
|
}
|
|
286
333
|
|
|
@@ -289,29 +336,23 @@ module.exports = class UrlUtils {
|
|
|
289
336
|
options = itemPath;
|
|
290
337
|
itemPath = null;
|
|
291
338
|
}
|
|
292
|
-
const
|
|
293
|
-
assetsOnly: false
|
|
294
|
-
|
|
295
|
-
};
|
|
296
|
-
const _options = assignOptions({}, defaultOptions, options || {});
|
|
339
|
+
const _options = this._buildAssetOptions({
|
|
340
|
+
assetsOnly: false
|
|
341
|
+
}, options);
|
|
297
342
|
return utils.htmlRelativeToTransformReady(html, this.getSiteUrl(), itemPath, _options);
|
|
298
343
|
}
|
|
299
344
|
|
|
300
345
|
htmlAbsoluteToRelative(html, options = {}) {
|
|
301
|
-
const
|
|
302
|
-
assetsOnly: false
|
|
303
|
-
|
|
304
|
-
};
|
|
305
|
-
const _options = assignOptions({}, defaultOptions, options);
|
|
346
|
+
const _options = this._buildAssetOptions({
|
|
347
|
+
assetsOnly: false
|
|
348
|
+
}, options);
|
|
306
349
|
return utils.htmlAbsoluteToRelative(html, this.getSiteUrl(), _options);
|
|
307
350
|
}
|
|
308
351
|
|
|
309
352
|
htmlAbsoluteToTransformReady(html, options = {}) {
|
|
310
|
-
const
|
|
311
|
-
assetsOnly: false
|
|
312
|
-
|
|
313
|
-
};
|
|
314
|
-
const _options = assignOptions({}, defaultOptions, options);
|
|
353
|
+
const _options = this._buildAssetOptions({
|
|
354
|
+
assetsOnly: false
|
|
355
|
+
}, options);
|
|
315
356
|
return utils.htmlAbsoluteToTransformReady(html, this.getSiteUrl(), _options);
|
|
316
357
|
}
|
|
317
358
|
|
|
@@ -320,7 +361,8 @@ module.exports = class UrlUtils {
|
|
|
320
361
|
options = itemPath;
|
|
321
362
|
itemPath = null;
|
|
322
363
|
}
|
|
323
|
-
|
|
364
|
+
const _options = this._buildAssetOptions({}, options);
|
|
365
|
+
return utils.markdownToTransformReady(markdown, this.getSiteUrl(), itemPath, _options);
|
|
324
366
|
}
|
|
325
367
|
|
|
326
368
|
markdownRelativeToAbsolute(markdown, itemPath, options) {
|
|
@@ -328,11 +370,9 @@ module.exports = class UrlUtils {
|
|
|
328
370
|
options = itemPath;
|
|
329
371
|
itemPath = null;
|
|
330
372
|
}
|
|
331
|
-
const
|
|
332
|
-
assetsOnly: false
|
|
333
|
-
|
|
334
|
-
};
|
|
335
|
-
const _options = assignOptions({}, defaultOptions, options || {});
|
|
373
|
+
const _options = this._buildAssetOptions({
|
|
374
|
+
assetsOnly: false
|
|
375
|
+
}, options);
|
|
336
376
|
return utils.markdownRelativeToAbsolute(markdown, this.getSiteUrl(), itemPath, _options);
|
|
337
377
|
}
|
|
338
378
|
|
|
@@ -341,29 +381,23 @@ module.exports = class UrlUtils {
|
|
|
341
381
|
options = itemPath;
|
|
342
382
|
itemPath = null;
|
|
343
383
|
}
|
|
344
|
-
const
|
|
345
|
-
assetsOnly: false
|
|
346
|
-
|
|
347
|
-
};
|
|
348
|
-
const _options = assignOptions({}, defaultOptions, options || {});
|
|
384
|
+
const _options = this._buildAssetOptions({
|
|
385
|
+
assetsOnly: false
|
|
386
|
+
}, options);
|
|
349
387
|
return utils.markdownRelativeToTransformReady(markdown, this.getSiteUrl(), itemPath, _options);
|
|
350
388
|
}
|
|
351
389
|
|
|
352
390
|
markdownAbsoluteToRelative(markdown, options = {}) {
|
|
353
|
-
const
|
|
354
|
-
assetsOnly: false
|
|
355
|
-
|
|
356
|
-
};
|
|
357
|
-
const _options = assignOptions({}, defaultOptions, options);
|
|
391
|
+
const _options = this._buildAssetOptions({
|
|
392
|
+
assetsOnly: false
|
|
393
|
+
}, options);
|
|
358
394
|
return utils.markdownAbsoluteToRelative(markdown, this.getSiteUrl(), _options);
|
|
359
395
|
}
|
|
360
396
|
|
|
361
397
|
markdownAbsoluteToTransformReady(markdown, options) {
|
|
362
|
-
const
|
|
363
|
-
assetsOnly: false
|
|
364
|
-
|
|
365
|
-
};
|
|
366
|
-
const _options = assignOptions({}, defaultOptions, options);
|
|
398
|
+
const _options = this._buildAssetOptions({
|
|
399
|
+
assetsOnly: false
|
|
400
|
+
}, options);
|
|
367
401
|
return utils.markdownAbsoluteToTransformReady(markdown, this.getSiteUrl(), _options);
|
|
368
402
|
}
|
|
369
403
|
|
|
@@ -372,10 +406,9 @@ module.exports = class UrlUtils {
|
|
|
372
406
|
options = itemPath;
|
|
373
407
|
itemPath = null;
|
|
374
408
|
}
|
|
375
|
-
const
|
|
409
|
+
const _options = this._buildAssetOptions({
|
|
376
410
|
cardTransformers: this._config.cardTransformers
|
|
377
|
-
};
|
|
378
|
-
const _options = assignOptions({}, defaultOptions, options || {});
|
|
411
|
+
}, options);
|
|
379
412
|
return utils.mobiledocToTransformReady(serializedMobiledoc, this.getSiteUrl(), itemPath, _options);
|
|
380
413
|
}
|
|
381
414
|
|
|
@@ -384,12 +417,10 @@ module.exports = class UrlUtils {
|
|
|
384
417
|
options = itemPath;
|
|
385
418
|
itemPath = null;
|
|
386
419
|
}
|
|
387
|
-
const
|
|
420
|
+
const _options = this._buildAssetOptions({
|
|
388
421
|
assetsOnly: false,
|
|
389
|
-
staticImageUrlPrefix: this._config.staticImageUrlPrefix,
|
|
390
422
|
cardTransformers: this._config.cardTransformers
|
|
391
|
-
};
|
|
392
|
-
const _options = assignOptions({}, defaultOptions, options || {});
|
|
423
|
+
}, options);
|
|
393
424
|
return utils.mobiledocRelativeToAbsolute(serializedMobiledoc, this.getSiteUrl(), itemPath, _options);
|
|
394
425
|
}
|
|
395
426
|
|
|
@@ -398,32 +429,26 @@ module.exports = class UrlUtils {
|
|
|
398
429
|
options = itemPath;
|
|
399
430
|
itemPath = null;
|
|
400
431
|
}
|
|
401
|
-
const
|
|
432
|
+
const _options = this._buildAssetOptions({
|
|
402
433
|
assetsOnly: false,
|
|
403
|
-
staticImageUrlPrefix: this._config.staticImageUrlPrefix,
|
|
404
434
|
cardTransformers: this._config.cardTransformers
|
|
405
|
-
};
|
|
406
|
-
const _options = assignOptions({}, defaultOptions, options || {});
|
|
435
|
+
}, options);
|
|
407
436
|
return utils.mobiledocRelativeToTransformReady(serializedMobiledoc, this.getSiteUrl(), itemPath, _options);
|
|
408
437
|
}
|
|
409
438
|
|
|
410
439
|
mobiledocAbsoluteToRelative(serializedMobiledoc, options = {}) {
|
|
411
|
-
const
|
|
440
|
+
const _options = this._buildAssetOptions({
|
|
412
441
|
assetsOnly: false,
|
|
413
|
-
staticImageUrlPrefix: this._config.staticImageUrlPrefix,
|
|
414
442
|
cardTransformers: this._config.cardTransformers
|
|
415
|
-
};
|
|
416
|
-
const _options = assignOptions({}, defaultOptions, options);
|
|
443
|
+
}, options);
|
|
417
444
|
return utils.mobiledocAbsoluteToRelative(serializedMobiledoc, this.getSiteUrl(), _options);
|
|
418
445
|
}
|
|
419
446
|
|
|
420
447
|
mobiledocAbsoluteToTransformReady(serializedMobiledoc, options = {}) {
|
|
421
|
-
const
|
|
448
|
+
const _options = this._buildAssetOptions({
|
|
422
449
|
assetsOnly: false,
|
|
423
|
-
staticImageUrlPrefix: this._config.staticImageUrlPrefix,
|
|
424
450
|
cardTransformers: this._config.cardTransformers
|
|
425
|
-
};
|
|
426
|
-
const _options = assignOptions({}, defaultOptions, options);
|
|
451
|
+
}, options);
|
|
427
452
|
return utils.mobiledocAbsoluteToTransformReady(serializedMobiledoc, this.getSiteUrl(), _options);
|
|
428
453
|
}
|
|
429
454
|
|
|
@@ -432,10 +457,9 @@ module.exports = class UrlUtils {
|
|
|
432
457
|
options = itemPath;
|
|
433
458
|
itemPath = null;
|
|
434
459
|
}
|
|
435
|
-
const
|
|
460
|
+
const _options = this._buildAssetOptions({
|
|
436
461
|
cardTransformers: this._config.cardTransformers
|
|
437
|
-
};
|
|
438
|
-
const _options = assignOptions({}, defaultOptions, options || {});
|
|
462
|
+
}, options);
|
|
439
463
|
return utils.lexicalToTransformReady(serializedLexical, this.getSiteUrl(), itemPath, _options);
|
|
440
464
|
}
|
|
441
465
|
|
|
@@ -444,12 +468,10 @@ module.exports = class UrlUtils {
|
|
|
444
468
|
options = itemPath;
|
|
445
469
|
itemPath = null;
|
|
446
470
|
}
|
|
447
|
-
const
|
|
471
|
+
const _options = this._buildAssetOptions({
|
|
448
472
|
assetsOnly: false,
|
|
449
|
-
staticImageUrlPrefix: this._config.staticImageUrlPrefix,
|
|
450
473
|
cardTransformers: this._config.cardTransformers
|
|
451
|
-
};
|
|
452
|
-
const _options = assignOptions({}, defaultOptions, options || {});
|
|
474
|
+
}, options);
|
|
453
475
|
return utils.lexicalRelativeToAbsolute(serializedLexical, this.getSiteUrl(), itemPath, _options);
|
|
454
476
|
}
|
|
455
477
|
|
|
@@ -458,40 +480,31 @@ module.exports = class UrlUtils {
|
|
|
458
480
|
options = itemPath;
|
|
459
481
|
itemPath = null;
|
|
460
482
|
}
|
|
461
|
-
const
|
|
483
|
+
const _options = this._buildAssetOptions({
|
|
462
484
|
assetsOnly: false,
|
|
463
|
-
staticImageUrlPrefix: this._config.staticImageUrlPrefix,
|
|
464
485
|
cardTransformers: this._config.cardTransformers
|
|
465
|
-
};
|
|
466
|
-
const _options = assignOptions({}, defaultOptions, options || {});
|
|
486
|
+
}, options);
|
|
467
487
|
return utils.lexicalRelativeToTransformReady(serializedLexical, this.getSiteUrl(), itemPath, _options);
|
|
468
488
|
}
|
|
469
489
|
|
|
470
490
|
lexicalAbsoluteToRelative(serializedLexical, options = {}) {
|
|
471
|
-
const
|
|
491
|
+
const _options = this._buildAssetOptions({
|
|
472
492
|
assetsOnly: false,
|
|
473
|
-
staticImageUrlPrefix: this._config.staticImageUrlPrefix,
|
|
474
493
|
cardTransformers: this._config.cardTransformers
|
|
475
|
-
};
|
|
476
|
-
const _options = assignOptions({}, defaultOptions, options);
|
|
494
|
+
}, options);
|
|
477
495
|
return utils.lexicalAbsoluteToRelative(serializedLexical, this.getSiteUrl(), _options);
|
|
478
496
|
}
|
|
479
497
|
|
|
480
498
|
lexicalAbsoluteToTransformReady(serializedLexical, options = {}) {
|
|
481
|
-
const
|
|
499
|
+
const _options = this._buildAssetOptions({
|
|
482
500
|
assetsOnly: false,
|
|
483
|
-
staticImageUrlPrefix: this._config.staticImageUrlPrefix,
|
|
484
501
|
cardTransformers: this._config.cardTransformers
|
|
485
|
-
};
|
|
486
|
-
const _options = assignOptions({}, defaultOptions, options);
|
|
502
|
+
}, options);
|
|
487
503
|
return utils.lexicalAbsoluteToTransformReady(serializedLexical, this.getSiteUrl(), _options);
|
|
488
504
|
}
|
|
489
505
|
|
|
490
506
|
plaintextToTransformReady(plaintext, options = {}) {
|
|
491
|
-
const
|
|
492
|
-
staticImageUrlPrefix: this._config.staticImageUrlPrefix
|
|
493
|
-
};
|
|
494
|
-
const _options = assignOptions({}, defaultOptions, options);
|
|
507
|
+
const _options = this._buildAssetOptions({}, options);
|
|
495
508
|
return utils.plaintextToTransformReady(plaintext, this.getSiteUrl(), _options);
|
|
496
509
|
}
|
|
497
510
|
|
|
@@ -534,7 +547,15 @@ module.exports = class UrlUtils {
|
|
|
534
547
|
* my-content/another-dir/images/2017/01/02/author.png
|
|
535
548
|
*/
|
|
536
549
|
get STATIC_IMAGE_URL_PREFIX() {
|
|
537
|
-
return
|
|
550
|
+
return STATIC_IMAGE_URL_PREFIX;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
get STATIC_FILES_URL_PREFIX() {
|
|
554
|
+
return STATIC_FILES_URL_PREFIX;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
get STATIC_MEDIA_URL_PREFIX() {
|
|
558
|
+
return STATIC_MEDIA_URL_PREFIX;
|
|
538
559
|
}
|
|
539
560
|
|
|
540
561
|
// expose underlying functions to ease testing
|
|
@@ -1,23 +1,101 @@
|
|
|
1
|
+
const {URL} = require('url');
|
|
1
2
|
const absoluteToRelative = require('./absolute-to-relative');
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
function normalizeBaseUrl(baseUrl) {
|
|
5
|
+
if (!baseUrl) {
|
|
6
|
+
return null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const parsed = new URL(baseUrl);
|
|
11
|
+
let pathname = parsed.pathname.replace(/\/$/, '');
|
|
12
|
+
if (pathname === '/') {
|
|
13
|
+
pathname = '';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
origin: parsed.origin,
|
|
18
|
+
pathname
|
|
19
|
+
};
|
|
20
|
+
} catch (e) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function matchCdnBase(parsedUrl, options) {
|
|
26
|
+
const candidates = [
|
|
27
|
+
{base: normalizeBaseUrl(options.mediaBaseUrl), prefix: options.staticMediaUrlPrefix},
|
|
28
|
+
{base: normalizeBaseUrl(options.filesBaseUrl), prefix: options.staticFilesUrlPrefix}
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
for (const candidate of candidates) {
|
|
32
|
+
if (!candidate.base || !candidate.prefix) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (parsedUrl.origin !== candidate.base.origin) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let pathname = parsedUrl.pathname;
|
|
41
|
+
const basePath = candidate.base.pathname;
|
|
42
|
+
|
|
43
|
+
if (basePath) {
|
|
44
|
+
if (pathname === basePath) {
|
|
45
|
+
pathname = '/';
|
|
46
|
+
} else if (pathname.startsWith(basePath + '/')) {
|
|
47
|
+
pathname = pathname.slice(basePath.length);
|
|
48
|
+
} else {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!pathname.startsWith('/')) {
|
|
54
|
+
pathname = `/${pathname}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!pathname.startsWith(`/${candidate.prefix}`)) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return `${pathname}${parsedUrl.search}${parsedUrl.hash}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const absoluteToTransformReady = function (url, root, _options = {}) {
|
|
4
68
|
const defaultOptions = {
|
|
5
69
|
replacementStr: '__GHOST_URL__',
|
|
6
|
-
withoutSubdirectory: true
|
|
70
|
+
withoutSubdirectory: true,
|
|
71
|
+
staticImageUrlPrefix: 'content/images',
|
|
72
|
+
staticFilesUrlPrefix: 'content/files',
|
|
73
|
+
staticMediaUrlPrefix: 'content/media',
|
|
74
|
+
imageBaseUrl: null,
|
|
75
|
+
filesBaseUrl: null,
|
|
76
|
+
mediaBaseUrl: null
|
|
7
77
|
};
|
|
8
78
|
const options = Object.assign({}, defaultOptions, _options);
|
|
9
79
|
|
|
10
|
-
|
|
80
|
+
let parsedInput;
|
|
11
81
|
try {
|
|
12
|
-
|
|
13
|
-
if (parsedURL.origin === 'http://relative') {
|
|
14
|
-
return url;
|
|
15
|
-
}
|
|
82
|
+
parsedInput = new URL(url, 'http://relative');
|
|
16
83
|
} catch (e) {
|
|
17
84
|
// url was unparseable
|
|
18
85
|
return url;
|
|
19
86
|
}
|
|
20
87
|
|
|
88
|
+
if (parsedInput.origin !== 'http://relative') {
|
|
89
|
+
const cdnMatch = matchCdnBase(parsedInput, options);
|
|
90
|
+
if (cdnMatch) {
|
|
91
|
+
return `${options.replacementStr}${cdnMatch}`;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (parsedInput.origin === 'http://relative') {
|
|
96
|
+
return url;
|
|
97
|
+
}
|
|
98
|
+
|
|
21
99
|
// convert to relative with stripped subdir
|
|
22
100
|
// always returns root-relative starting with forward slash
|
|
23
101
|
const relativeUrl = absoluteToRelative(url, root, options);
|
|
@@ -1,13 +1,37 @@
|
|
|
1
1
|
const htmlTransform = require('./html-transform');
|
|
2
2
|
const absoluteToTransformReady = require('./absolute-to-transform-ready');
|
|
3
3
|
|
|
4
|
+
function escapeRegExp(string) {
|
|
5
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function buildEarlyExitMatch(siteUrl, options) {
|
|
9
|
+
const candidates = [siteUrl, options.imageBaseUrl, options.filesBaseUrl, options.mediaBaseUrl]
|
|
10
|
+
.filter(Boolean)
|
|
11
|
+
.map((value) => {
|
|
12
|
+
let normalized = options.ignoreProtocol ? value.replace(/http:|https:/, '') : value;
|
|
13
|
+
return normalized.replace(/\/$/, '');
|
|
14
|
+
})
|
|
15
|
+
.filter(Boolean)
|
|
16
|
+
.map(escapeRegExp);
|
|
17
|
+
|
|
18
|
+
if (!candidates.length) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (candidates.length === 1) {
|
|
23
|
+
return candidates[0];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return `(?:${candidates.join('|')})`;
|
|
27
|
+
}
|
|
28
|
+
|
|
4
29
|
const htmlAbsoluteToTransformReady = function (html = '', siteUrl, _options) {
|
|
5
30
|
const defaultOptions = {assetsOnly: false, ignoreProtocol: true};
|
|
6
31
|
const options = Object.assign({}, defaultOptions, _options || {});
|
|
7
32
|
|
|
8
|
-
// exit early and avoid parsing if the content does not contain the siteUrl
|
|
9
|
-
options.earlyExitMatchStr =
|
|
10
|
-
options.earlyExitMatchStr = options.earlyExitMatchStr.replace(/\/$/, '');
|
|
33
|
+
// exit early and avoid parsing if the content does not contain the siteUrl or configured asset bases
|
|
34
|
+
options.earlyExitMatchStr = buildEarlyExitMatch(siteUrl, options);
|
|
11
35
|
|
|
12
36
|
// need to ignore itemPath because absoluteToRelative doesn't take that option
|
|
13
37
|
const transformFunction = function (_url, _siteUrl, _itemPath, __options) {
|
|
@@ -2,9 +2,22 @@ function escapeRegExp(string) {
|
|
|
2
2
|
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
function trimTrailingSlash(url) {
|
|
6
|
+
if (!url) {
|
|
7
|
+
return url;
|
|
8
|
+
}
|
|
9
|
+
return url.replace(/\/+$/, '');
|
|
10
|
+
}
|
|
11
|
+
|
|
5
12
|
const transformReadyToAbsolute = function (str = '', root, _options = {}) {
|
|
6
13
|
const defaultOptions = {
|
|
7
|
-
replacementStr: '__GHOST_URL__'
|
|
14
|
+
replacementStr: '__GHOST_URL__',
|
|
15
|
+
staticImageUrlPrefix: 'content/images',
|
|
16
|
+
staticFilesUrlPrefix: 'content/files',
|
|
17
|
+
staticMediaUrlPrefix: 'content/media',
|
|
18
|
+
imageBaseUrl: null,
|
|
19
|
+
filesBaseUrl: null,
|
|
20
|
+
mediaBaseUrl: null
|
|
8
21
|
};
|
|
9
22
|
const options = Object.assign({}, defaultOptions, _options);
|
|
10
23
|
|
|
@@ -13,8 +26,28 @@ const transformReadyToAbsolute = function (str = '', root, _options = {}) {
|
|
|
13
26
|
}
|
|
14
27
|
|
|
15
28
|
const replacementRegex = new RegExp(escapeRegExp(options.replacementStr), 'g');
|
|
29
|
+
const fallbackBase = trimTrailingSlash(options.imageBaseUrl || root);
|
|
30
|
+
const mediaBase = trimTrailingSlash(options.mediaBaseUrl);
|
|
31
|
+
const filesBase = trimTrailingSlash(options.filesBaseUrl);
|
|
32
|
+
const imageBase = trimTrailingSlash(options.imageBaseUrl) || fallbackBase;
|
|
33
|
+
|
|
34
|
+
return str.replace(replacementRegex, (match, offset) => {
|
|
35
|
+
const remainder = str.slice(offset + match.length);
|
|
36
|
+
|
|
37
|
+
if (options.staticMediaUrlPrefix && remainder.startsWith(`/${options.staticMediaUrlPrefix}`) && mediaBase) {
|
|
38
|
+
return mediaBase;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (options.staticFilesUrlPrefix && remainder.startsWith(`/${options.staticFilesUrlPrefix}`) && filesBase) {
|
|
42
|
+
return filesBase;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (options.staticImageUrlPrefix && remainder.startsWith(`/${options.staticImageUrlPrefix}`) && imageBase) {
|
|
46
|
+
return imageBase;
|
|
47
|
+
}
|
|
16
48
|
|
|
17
|
-
|
|
49
|
+
return fallbackBase;
|
|
50
|
+
});
|
|
18
51
|
};
|
|
19
52
|
|
|
20
53
|
module.exports = transformReadyToAbsolute;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tryghost/url-utils",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0-rc.1",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/TryGhost/SDK.git",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@tryghost/config-url-helpers": "^1.0.17",
|
|
27
27
|
"c8": "10.1.3",
|
|
28
|
-
"mocha": "11.
|
|
29
|
-
"rewire": "9.0.
|
|
28
|
+
"mocha": "11.7.4",
|
|
29
|
+
"rewire": "9.0.1",
|
|
30
30
|
"should": "13.2.3",
|
|
31
31
|
"sinon": "21.0.0"
|
|
32
32
|
},
|
|
@@ -38,6 +38,5 @@
|
|
|
38
38
|
"remark": "^11.0.2",
|
|
39
39
|
"remark-footnotes": "^1.0.0",
|
|
40
40
|
"unist-util-visit": "^2.0.0"
|
|
41
|
-
}
|
|
42
|
-
"gitHead": "860707f032864400017b0f176cbabf181f99b764"
|
|
41
|
+
}
|
|
43
42
|
}
|