From 7ff7e86285a7c949cf62c9d20890dff0b6f65552 Mon Sep 17 00:00:00 2001 From: Fabien O'Carroll Date: Tue, 21 Oct 2025 15:15:13 +0700 Subject: [PATCH 1/3] Initial TS migration --- packages/url-utils/.eslintignore | 2 + packages/url-utils/index.js | 1 - packages/url-utils/package.json | 19 ++++- .../{lib/UrlUtils.js => src/UrlUtils.ts} | 84 ++++++++++--------- packages/url-utils/src/index.ts | 2 + .../utils/absolute-to-relative.ts} | 1 + .../utils/absolute-to-transform-ready.ts} | 1 + .../utils/deduplicate-double-slashes.ts} | 1 + .../utils/deduplicate-subdirectory.ts} | 1 + .../utils/html-absolute-to-relative.ts} | 1 + .../html-absolute-to-transform-ready.ts} | 1 + .../utils/html-relative-to-absolute.ts} | 1 + .../html-relative-to-transform-ready.ts} | 1 + .../utils/html-to-transform-ready.ts} | 1 + .../utils/html-transform.ts} | 11 ++- .../utils/index.js => src/utils/index.ts} | 1 + .../utils/is-ssl.js => src/utils/is-ssl.ts} | 1 + .../utils/lexical-absolute-to-relative.ts} | 1 + .../lexical-absolute-to-transform-ready.ts} | 1 + .../utils/lexical-relative-to-absolute.ts} | 1 + .../lexical-relative-to-transform-ready.ts} | 1 + .../utils/lexical-to-transform-ready.ts} | 1 + .../utils/lexical-transform.ts} | 3 +- .../utils/markdown-absolute-to-relative.ts} | 3 +- .../markdown-absolute-to-transform-ready.ts} | 3 +- .../utils/markdown-relative-to-absolute.ts} | 3 +- .../markdown-relative-to-transform-ready.ts} | 3 +- .../utils/markdown-to-transform-ready.ts} | 1 + .../utils/markdown-transform.ts} | 3 +- .../utils/mobiledoc-absolute-to-relative.ts} | 1 + .../mobiledoc-absolute-to-transform-ready.ts} | 1 + .../utils/mobiledoc-relative-to-absolute.ts} | 1 + .../mobiledoc-relative-to-transform-ready.ts} | 1 + .../utils/mobiledoc-to-transform-ready.ts} | 1 + .../utils/mobiledoc-transform.ts} | 5 +- .../plaintext-absolute-to-transform-ready.ts} | 1 + .../plaintext-relative-to-transform-ready.ts} | 1 + .../utils/plaintext-to-transform-ready.ts} | 1 + .../utils/relative-to-absolute.ts} | 1 + .../utils/relative-to-transform-ready.ts} | 1 + .../utils/replace-permalink.ts} | 1 + .../utils/strip-subdirectory-from-path.ts} | 1 + .../utils/to-transform-ready.ts} | 1 + .../utils/transform-ready-to-absolute.ts} | 1 + .../utils/transform-ready-to-relative.ts} | 1 + .../url-join.js => src/utils/url-join.ts} | 1 + packages/url-utils/tsconfig.json | 19 +++++ packages/url-utils/tsconfig.test.json | 11 +++ 48 files changed, 149 insertions(+), 56 deletions(-) create mode 100644 packages/url-utils/.eslintignore delete mode 100644 packages/url-utils/index.js rename packages/url-utils/{lib/UrlUtils.js => src/UrlUtils.ts} (87%) create mode 100644 packages/url-utils/src/index.ts rename packages/url-utils/{lib/utils/absolute-to-relative.js => src/utils/absolute-to-relative.ts} (99%) rename packages/url-utils/{lib/utils/absolute-to-transform-ready.js => src/utils/absolute-to-transform-ready.ts} (99%) rename packages/url-utils/{lib/utils/deduplicate-double-slashes.js => src/utils/deduplicate-double-slashes.ts} (91%) rename packages/url-utils/{lib/utils/deduplicate-subdirectory.js => src/utils/deduplicate-subdirectory.ts} (99%) rename packages/url-utils/{lib/utils/html-absolute-to-relative.js => src/utils/html-absolute-to-relative.ts} (98%) rename packages/url-utils/{lib/utils/html-absolute-to-transform-ready.js => src/utils/html-absolute-to-transform-ready.ts} (98%) rename packages/url-utils/{lib/utils/html-relative-to-absolute.js => src/utils/html-relative-to-absolute.ts} (98%) rename packages/url-utils/{lib/utils/html-relative-to-transform-ready.js => src/utils/html-relative-to-transform-ready.ts} (98%) rename packages/url-utils/{lib/utils/html-to-transform-ready.js => src/utils/html-to-transform-ready.ts} (97%) rename packages/url-utils/{lib/utils/html-transform.js => src/utils/html-transform.ts} (94%) rename packages/url-utils/{lib/utils/index.js => src/utils/index.ts} (99%) rename packages/url-utils/{lib/utils/is-ssl.js => src/utils/is-ssl.ts} (95%) rename packages/url-utils/{lib/utils/lexical-absolute-to-relative.js => src/utils/lexical-absolute-to-relative.ts} (98%) rename packages/url-utils/{lib/utils/lexical-absolute-to-transform-ready.js => src/utils/lexical-absolute-to-transform-ready.ts} (98%) rename packages/url-utils/{lib/utils/lexical-relative-to-absolute.js => src/utils/lexical-relative-to-absolute.ts} (98%) rename packages/url-utils/{lib/utils/lexical-relative-to-transform-ready.js => src/utils/lexical-relative-to-transform-ready.ts} (98%) rename packages/url-utils/{lib/utils/lexical-to-transform-ready.js => src/utils/lexical-to-transform-ready.ts} (98%) rename packages/url-utils/{lib/utils/lexical-transform.js => src/utils/lexical-transform.ts} (96%) rename packages/url-utils/{lib/utils/markdown-absolute-to-relative.js => src/utils/markdown-absolute-to-relative.ts} (92%) rename packages/url-utils/{lib/utils/markdown-absolute-to-transform-ready.js => src/utils/markdown-absolute-to-transform-ready.ts} (93%) rename packages/url-utils/{lib/utils/markdown-relative-to-absolute.js => src/utils/markdown-relative-to-absolute.ts} (90%) rename packages/url-utils/{lib/utils/markdown-relative-to-transform-ready.js => src/utils/markdown-relative-to-transform-ready.ts} (90%) rename packages/url-utils/{lib/utils/markdown-to-transform-ready.js => src/utils/markdown-to-transform-ready.ts} (98%) rename packages/url-utils/{lib/utils/markdown-transform.js => src/utils/markdown-transform.ts} (97%) rename packages/url-utils/{lib/utils/mobiledoc-absolute-to-relative.js => src/utils/mobiledoc-absolute-to-relative.ts} (98%) rename packages/url-utils/{lib/utils/mobiledoc-absolute-to-transform-ready.js => src/utils/mobiledoc-absolute-to-transform-ready.ts} (98%) rename packages/url-utils/{lib/utils/mobiledoc-relative-to-absolute.js => src/utils/mobiledoc-relative-to-absolute.ts} (98%) rename packages/url-utils/{lib/utils/mobiledoc-relative-to-transform-ready.js => src/utils/mobiledoc-relative-to-transform-ready.ts} (98%) rename packages/url-utils/{lib/utils/mobiledoc-to-transform-ready.js => src/utils/mobiledoc-to-transform-ready.ts} (98%) rename packages/url-utils/{lib/utils/mobiledoc-transform.js => src/utils/mobiledoc-transform.ts} (93%) rename packages/url-utils/{lib/utils/plaintext-absolute-to-transform-ready.js => src/utils/plaintext-absolute-to-transform-ready.ts} (98%) rename packages/url-utils/{lib/utils/plaintext-relative-to-transform-ready.js => src/utils/plaintext-relative-to-transform-ready.ts} (98%) rename packages/url-utils/{lib/utils/plaintext-to-transform-ready.js => src/utils/plaintext-to-transform-ready.ts} (98%) rename packages/url-utils/{lib/utils/relative-to-absolute.js => src/utils/relative-to-absolute.ts} (99%) rename packages/url-utils/{lib/utils/relative-to-transform-ready.js => src/utils/relative-to-transform-ready.ts} (99%) rename packages/url-utils/{lib/utils/replace-permalink.js => src/utils/replace-permalink.ts} (99%) rename packages/url-utils/{lib/utils/strip-subdirectory-from-path.js => src/utils/strip-subdirectory-from-path.ts} (98%) rename packages/url-utils/{lib/utils/to-transform-ready.js => src/utils/to-transform-ready.ts} (97%) rename packages/url-utils/{lib/utils/transform-ready-to-absolute.js => src/utils/transform-ready-to-absolute.ts} (98%) rename packages/url-utils/{lib/utils/transform-ready-to-relative.js => src/utils/transform-ready-to-relative.ts} (98%) rename packages/url-utils/{lib/utils/url-join.js => src/utils/url-join.ts} (98%) create mode 100644 packages/url-utils/tsconfig.json create mode 100644 packages/url-utils/tsconfig.test.json diff --git a/packages/url-utils/.eslintignore b/packages/url-utils/.eslintignore new file mode 100644 index 000000000..2e6b92a0c --- /dev/null +++ b/packages/url-utils/.eslintignore @@ -0,0 +1,2 @@ +lib/ +coverage/ diff --git a/packages/url-utils/index.js b/packages/url-utils/index.js deleted file mode 100644 index b435fc186..000000000 --- a/packages/url-utils/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('./lib/UrlUtils'); diff --git a/packages/url-utils/package.json b/packages/url-utils/package.json index 17e004dc8..086498225 100644 --- a/packages/url-utils/package.json +++ b/packages/url-utils/package.json @@ -8,27 +8,38 @@ }, "author": "Ghost Foundation", "license": "MIT", - "main": "index.js", + "main": "lib/index.js", + "types": "lib/index.d.ts", "scripts": { "dev": "echo \"Implement me!\"", + "build": "tsc -p tsconfig.json", + "pretest": "yarn build", "test": "NODE_ENV=testing c8 --all --reporter text --reporter cobertura --reporter html mocha './test/**/*.test.js'", "lint": "eslint . --ext .js --cache", "posttest": "yarn lint" }, "files": [ - "lib/", - "index.js" + "lib/" ], "publishConfig": { "access": "public" }, "devDependencies": { "@tryghost/config-url-helpers": "^1.0.17", + "@types/cheerio": "^0.22.35", + "@types/lodash": "^4.17.15", + "@types/moment": "^2.13.0", + "@types/moment-timezone": "^0.5.30", + "@types/node": "^20.16.3", + "@types/remark": "^14.0.4", + "@types/unist": "^2.0.10", + "@types/unist-util-visit": "^4.1.0", "c8": "10.1.3", "mocha": "11.7.4", "rewire": "9.0.1", "should": "13.2.3", - "sinon": "21.0.0" + "sinon": "21.0.0", + "typescript": "^5.5.4" }, "dependencies": { "cheerio": "^0.22.0", diff --git a/packages/url-utils/lib/UrlUtils.js b/packages/url-utils/src/UrlUtils.ts similarity index 87% rename from packages/url-utils/lib/UrlUtils.js rename to packages/url-utils/src/UrlUtils.ts index aaab3ba75..b48009bd8 100644 --- a/packages/url-utils/lib/UrlUtils.js +++ b/packages/url-utils/src/UrlUtils.ts @@ -1,3 +1,4 @@ +export {}; // Contains all path information to be used throughout the codebase. const _ = require('lodash'); const utils = require('./utils'); @@ -12,6 +13,11 @@ function assignOptions(target, ...sources) { return Object.assign(target, ...options); } module.exports = class UrlUtils { + _config: any; + getSubdir: () => string; + getSiteUrl: () => string; + getAdminUrl: () => string; + /** * Initialization method to pass in URL configurations * @param {Object} options @@ -24,7 +30,7 @@ module.exports = class UrlUtils { * @param {Number} [options.redirectCacheMaxAge] * @param {String} [options.staticImageUrlPrefix='content/images'] static prefix for serving images. Should not be passed in, unless customizing ghost instance image storage */ - constructor(options = {}) { + constructor(options: any = {}) { const defaultOptions = { slugs: null, redirectCacheMaxAge: null, @@ -35,15 +41,15 @@ module.exports = class UrlUtils { this._config = assignOptions({}, defaultOptions, options); - this.getSubdir = options.getSubdir; - this.getSiteUrl = options.getSiteUrl; - this.getAdminUrl = options.getAdminUrl; + this.getSubdir = options.getSubdir || (() => ''); + this.getSiteUrl = options.getSiteUrl || (() => ''); + this.getAdminUrl = options.getAdminUrl || (() => ''); } getProtectedSlugs() { let subDir = this.getSubdir(); - if (!_.isEmpty(subDir)) { + if (this._config.slugs && !_.isEmpty(subDir)) { return this._config.slugs.concat([subDir.split('/').pop()]); } else { return this._config.slugs; @@ -55,7 +61,7 @@ module.exports = class UrlUtils { * @param {string} arguments takes arguments and concats those to a valid path/URL. * @return {string} URL concatinated URL/path of arguments. */ - urlJoin(...parts) { + urlJoin(...parts: any[]) { return utils.urlJoin(parts, {rootUrl: this.getSiteUrl()}); } @@ -72,7 +78,7 @@ module.exports = class UrlUtils { // - absolute (optional, default:false) - boolean whether or not the url should be absolute // Returns: // - a URL which always ends with a slash - createUrl(urlPath = '/', absolute = false, trailingSlash) { + createUrl(urlPath = '/', absolute = false, trailingSlash?: boolean) { let base; // create base of url, always ends without a slash @@ -106,7 +112,7 @@ module.exports = class UrlUtils { // - absolute (optional, default:false) - boolean whether or not the url should be absolute // This is probably not the right place for this, but it's the best place for now // @TODO: rewrite, very hard to read, create private functions! - urlFor(context, data, absolute) { + urlFor(context: any, data?: any, absolute?: boolean) { let urlPath = '/'; let imagePathRe; let knownObjects = ['image', 'nav']; @@ -207,12 +213,12 @@ module.exports = class UrlUtils { return this.createUrl(urlPath, absolute); } - redirect301(res, redirectUrl) { + redirect301(res: any, redirectUrl: string) { res.set({'Cache-Control': 'public, max-age=' + this._config.redirectCacheMaxAge}); return res.redirect(301, redirectUrl); } - redirectToAdmin(status, res, adminPath) { + redirectToAdmin(status: number, res: any, adminPath: string) { let redirectUrl = this.urlJoin(this.urlFor('admin', true), adminPath, '/'); if (status === 301) { @@ -221,15 +227,15 @@ module.exports = class UrlUtils { return res.redirect(redirectUrl); } - absoluteToRelative(url, options) { + absoluteToRelative(url: string, options?: any) { return utils.absoluteToRelative(url, this.getSiteUrl(), options); } - relativeToAbsolute(url, options) { + relativeToAbsolute(url: string, options?: any) { return utils.relativeToAbsolute(url, this.getSiteUrl(), options); } - toTransformReady(url, itemPath, options) { + toTransformReady(url: string, itemPath?: any, options?: any) { if (typeof itemPath === 'object' && !options) { options = itemPath; itemPath = null; @@ -237,23 +243,23 @@ module.exports = class UrlUtils { return utils.toTransformReady(url, this.getSiteUrl(), itemPath, options); } - absoluteToTransformReady(url, options) { + absoluteToTransformReady(url: string, options?: any) { return utils.absoluteToTransformReady(url, this.getSiteUrl(), options); } - relativeToTransformReady(url, options) { + relativeToTransformReady(url: string, options?: any) { return utils.relativeToTransformReady(url, this.getSiteUrl(), options); } - transformReadyToAbsolute(url, options) { + transformReadyToAbsolute(url: string, options?: any) { return utils.transformReadyToAbsolute(url, this.getSiteUrl(), options); } - transformReadyToRelative(url, options) { + transformReadyToRelative(url: string, options?: any) { return utils.transformReadyToRelative(url, this.getSiteUrl(), options); } - htmlToTransformReady(html, itemPath, options) { + htmlToTransformReady(html: string, itemPath?: any, options?: any) { if (typeof itemPath === 'object' && !options) { options = itemPath; itemPath = null; @@ -271,7 +277,7 @@ module.exports = class UrlUtils { * absolute urls. Returns an object. The html string can be accessed by calling `html()` on * the variable that takes the result of this function */ - htmlRelativeToAbsolute(html, itemPath, options) { + htmlRelativeToAbsolute(html: string, itemPath?: any, options?: any) { if (typeof itemPath === 'object' && !options) { options = itemPath; itemPath = null; @@ -284,7 +290,7 @@ module.exports = class UrlUtils { return utils.htmlRelativeToAbsolute(html, this.getSiteUrl(), itemPath, _options); } - htmlRelativeToTransformReady(html, itemPath, options) { + htmlRelativeToTransformReady(html: string, itemPath?: any, options?: any) { if (typeof itemPath === 'object' && !options) { options = itemPath; itemPath = null; @@ -297,7 +303,7 @@ module.exports = class UrlUtils { return utils.htmlRelativeToTransformReady(html, this.getSiteUrl(), itemPath, _options); } - htmlAbsoluteToRelative(html, options = {}) { + htmlAbsoluteToRelative(html: string, options: any = {}) { const defaultOptions = { assetsOnly: false, staticImageUrlPrefix: this._config.staticImageUrlPrefix @@ -306,7 +312,7 @@ module.exports = class UrlUtils { return utils.htmlAbsoluteToRelative(html, this.getSiteUrl(), _options); } - htmlAbsoluteToTransformReady(html, options = {}) { + htmlAbsoluteToTransformReady(html: string, options: any = {}) { const defaultOptions = { assetsOnly: false, staticImageUrlPrefix: this._config.staticImageUrlPrefix @@ -315,7 +321,7 @@ module.exports = class UrlUtils { return utils.htmlAbsoluteToTransformReady(html, this.getSiteUrl(), _options); } - markdownToTransformReady(markdown, itemPath, options) { + markdownToTransformReady(markdown: string, itemPath?: any, options?: any) { if (typeof itemPath === 'object' && !options) { options = itemPath; itemPath = null; @@ -323,7 +329,7 @@ module.exports = class UrlUtils { return utils.markdownToTransformReady(markdown, this.getSiteUrl(), itemPath, options); } - markdownRelativeToAbsolute(markdown, itemPath, options) { + markdownRelativeToAbsolute(markdown: string, itemPath?: any, options?: any) { if (typeof itemPath === 'object' && !options) { options = itemPath; itemPath = null; @@ -336,7 +342,7 @@ module.exports = class UrlUtils { return utils.markdownRelativeToAbsolute(markdown, this.getSiteUrl(), itemPath, _options); } - markdownRelativeToTransformReady(markdown, itemPath, options) { + markdownRelativeToTransformReady(markdown: string, itemPath?: any, options?: any) { if (typeof itemPath === 'object' && !options) { options = itemPath; itemPath = null; @@ -349,7 +355,7 @@ module.exports = class UrlUtils { return utils.markdownRelativeToTransformReady(markdown, this.getSiteUrl(), itemPath, _options); } - markdownAbsoluteToRelative(markdown, options = {}) { + markdownAbsoluteToRelative(markdown: string, options: any = {}) { const defaultOptions = { assetsOnly: false, staticImageUrlPrefix: this._config.staticImageUrlPrefix @@ -358,7 +364,7 @@ module.exports = class UrlUtils { return utils.markdownAbsoluteToRelative(markdown, this.getSiteUrl(), _options); } - markdownAbsoluteToTransformReady(markdown, options) { + markdownAbsoluteToTransformReady(markdown: string, options: any) { const defaultOptions = { assetsOnly: false, staticImageUrlPrefix: this._config.staticImageUrlPrefix @@ -367,7 +373,7 @@ module.exports = class UrlUtils { return utils.markdownAbsoluteToTransformReady(markdown, this.getSiteUrl(), _options); } - mobiledocToTransformReady(serializedMobiledoc, itemPath, options) { + mobiledocToTransformReady(serializedMobiledoc: string, itemPath?: any, options?: any) { if (typeof itemPath === 'object' && !options) { options = itemPath; itemPath = null; @@ -379,7 +385,7 @@ module.exports = class UrlUtils { return utils.mobiledocToTransformReady(serializedMobiledoc, this.getSiteUrl(), itemPath, _options); } - mobiledocRelativeToAbsolute(serializedMobiledoc, itemPath, options) { + mobiledocRelativeToAbsolute(serializedMobiledoc: string, itemPath?: any, options?: any) { if (typeof itemPath === 'object' && !options) { options = itemPath; itemPath = null; @@ -393,7 +399,7 @@ module.exports = class UrlUtils { return utils.mobiledocRelativeToAbsolute(serializedMobiledoc, this.getSiteUrl(), itemPath, _options); } - mobiledocRelativeToTransformReady(serializedMobiledoc, itemPath, options) { + mobiledocRelativeToTransformReady(serializedMobiledoc: string, itemPath?: any, options?: any) { if (typeof itemPath === 'object' && !options) { options = itemPath; itemPath = null; @@ -407,7 +413,7 @@ module.exports = class UrlUtils { return utils.mobiledocRelativeToTransformReady(serializedMobiledoc, this.getSiteUrl(), itemPath, _options); } - mobiledocAbsoluteToRelative(serializedMobiledoc, options = {}) { + mobiledocAbsoluteToRelative(serializedMobiledoc: string, options: any = {}) { const defaultOptions = { assetsOnly: false, staticImageUrlPrefix: this._config.staticImageUrlPrefix, @@ -417,7 +423,7 @@ module.exports = class UrlUtils { return utils.mobiledocAbsoluteToRelative(serializedMobiledoc, this.getSiteUrl(), _options); } - mobiledocAbsoluteToTransformReady(serializedMobiledoc, options = {}) { + mobiledocAbsoluteToTransformReady(serializedMobiledoc: string, options: any = {}) { const defaultOptions = { assetsOnly: false, staticImageUrlPrefix: this._config.staticImageUrlPrefix, @@ -427,7 +433,7 @@ module.exports = class UrlUtils { return utils.mobiledocAbsoluteToTransformReady(serializedMobiledoc, this.getSiteUrl(), _options); } - lexicalToTransformReady(serializedLexical, itemPath, options) { + lexicalToTransformReady(serializedLexical: string, itemPath?: any, options?: any) { if (typeof itemPath === 'object' && !options) { options = itemPath; itemPath = null; @@ -439,7 +445,7 @@ module.exports = class UrlUtils { return utils.lexicalToTransformReady(serializedLexical, this.getSiteUrl(), itemPath, _options); } - lexicalRelativeToAbsolute(serializedLexical, itemPath, options) { + lexicalRelativeToAbsolute(serializedLexical: string, itemPath?: any, options?: any) { if (typeof itemPath === 'object' && !options) { options = itemPath; itemPath = null; @@ -453,7 +459,7 @@ module.exports = class UrlUtils { return utils.lexicalRelativeToAbsolute(serializedLexical, this.getSiteUrl(), itemPath, _options); } - lexicalRelativeToTransformReady(serializedLexical, itemPath, options) { + lexicalRelativeToTransformReady(serializedLexical: string, itemPath?: any, options?: any) { if (typeof itemPath === 'object' && !options) { options = itemPath; itemPath = null; @@ -467,7 +473,7 @@ module.exports = class UrlUtils { return utils.lexicalRelativeToTransformReady(serializedLexical, this.getSiteUrl(), itemPath, _options); } - lexicalAbsoluteToRelative(serializedLexical, options = {}) { + lexicalAbsoluteToRelative(serializedLexical: string, options: any = {}) { const defaultOptions = { assetsOnly: false, staticImageUrlPrefix: this._config.staticImageUrlPrefix, @@ -477,7 +483,7 @@ module.exports = class UrlUtils { return utils.lexicalAbsoluteToRelative(serializedLexical, this.getSiteUrl(), _options); } - lexicalAbsoluteToTransformReady(serializedLexical, options = {}) { + lexicalAbsoluteToTransformReady(serializedLexical: string, options: any = {}) { const defaultOptions = { assetsOnly: false, staticImageUrlPrefix: this._config.staticImageUrlPrefix, @@ -487,7 +493,7 @@ module.exports = class UrlUtils { return utils.lexicalAbsoluteToTransformReady(serializedLexical, this.getSiteUrl(), _options); } - plaintextToTransformReady(plaintext, options = {}) { + plaintextToTransformReady(plaintext: string, options: any = {}) { const defaultOptions = { staticImageUrlPrefix: this._config.staticImageUrlPrefix }; @@ -501,7 +507,7 @@ module.exports = class UrlUtils { * @param {string} [context] describing the context for which you need to check a url * @returns {boolean} */ - isSiteUrl(url, context = 'home') { + isSiteUrl(url: URL, context: string = 'home') { const siteUrl = new URL(this.urlFor(context, true)); if (siteUrl.host === url.host) { if (url.pathname.startsWith(siteUrl.pathname)) { diff --git a/packages/url-utils/src/index.ts b/packages/url-utils/src/index.ts new file mode 100644 index 000000000..a4cf14494 --- /dev/null +++ b/packages/url-utils/src/index.ts @@ -0,0 +1,2 @@ +export {}; +module.exports = require('./UrlUtils'); diff --git a/packages/url-utils/lib/utils/absolute-to-relative.js b/packages/url-utils/src/utils/absolute-to-relative.ts similarity index 99% rename from packages/url-utils/lib/utils/absolute-to-relative.js rename to packages/url-utils/src/utils/absolute-to-relative.ts index 2433fe308..39dee3418 100644 --- a/packages/url-utils/lib/utils/absolute-to-relative.js +++ b/packages/url-utils/src/utils/absolute-to-relative.ts @@ -1,3 +1,4 @@ +export {}; // require the whatwg compatible URL library (same behaviour in node and browser) const {URL} = require('url'); const stripSubdirectoryFromPath = require('./strip-subdirectory-from-path'); diff --git a/packages/url-utils/lib/utils/absolute-to-transform-ready.js b/packages/url-utils/src/utils/absolute-to-transform-ready.ts similarity index 99% rename from packages/url-utils/lib/utils/absolute-to-transform-ready.js rename to packages/url-utils/src/utils/absolute-to-transform-ready.ts index 857961b49..fa7fe95e8 100644 --- a/packages/url-utils/lib/utils/absolute-to-transform-ready.js +++ b/packages/url-utils/src/utils/absolute-to-transform-ready.ts @@ -1,3 +1,4 @@ +export {}; const absoluteToRelative = require('./absolute-to-relative'); const absoluteToTransformReady = function (url, root, _options) { diff --git a/packages/url-utils/lib/utils/deduplicate-double-slashes.js b/packages/url-utils/src/utils/deduplicate-double-slashes.ts similarity index 91% rename from packages/url-utils/lib/utils/deduplicate-double-slashes.js rename to packages/url-utils/src/utils/deduplicate-double-slashes.ts index beda744d3..da7bf97dc 100644 --- a/packages/url-utils/lib/utils/deduplicate-double-slashes.js +++ b/packages/url-utils/src/utils/deduplicate-double-slashes.ts @@ -1,3 +1,4 @@ +export {}; function deduplicateDoubleSlashes(url) { return url.replace(/\/\//g, '/'); } diff --git a/packages/url-utils/lib/utils/deduplicate-subdirectory.js b/packages/url-utils/src/utils/deduplicate-subdirectory.ts similarity index 99% rename from packages/url-utils/lib/utils/deduplicate-subdirectory.js rename to packages/url-utils/src/utils/deduplicate-subdirectory.ts index d86ee4a74..01b25181a 100644 --- a/packages/url-utils/lib/utils/deduplicate-subdirectory.js +++ b/packages/url-utils/src/utils/deduplicate-subdirectory.ts @@ -1,3 +1,4 @@ +export {}; const {URL} = require('url'); /** diff --git a/packages/url-utils/lib/utils/html-absolute-to-relative.js b/packages/url-utils/src/utils/html-absolute-to-relative.ts similarity index 98% rename from packages/url-utils/lib/utils/html-absolute-to-relative.js rename to packages/url-utils/src/utils/html-absolute-to-relative.ts index 357d693cb..b78965d98 100644 --- a/packages/url-utils/lib/utils/html-absolute-to-relative.js +++ b/packages/url-utils/src/utils/html-absolute-to-relative.ts @@ -1,3 +1,4 @@ +export {}; const htmlTransform = require('./html-transform'); const absoluteToRelative = require('./absolute-to-relative'); diff --git a/packages/url-utils/lib/utils/html-absolute-to-transform-ready.js b/packages/url-utils/src/utils/html-absolute-to-transform-ready.ts similarity index 98% rename from packages/url-utils/lib/utils/html-absolute-to-transform-ready.js rename to packages/url-utils/src/utils/html-absolute-to-transform-ready.ts index 2eb7f4243..0c66dbed5 100644 --- a/packages/url-utils/lib/utils/html-absolute-to-transform-ready.js +++ b/packages/url-utils/src/utils/html-absolute-to-transform-ready.ts @@ -1,3 +1,4 @@ +export {}; const htmlTransform = require('./html-transform'); const absoluteToTransformReady = require('./absolute-to-transform-ready'); diff --git a/packages/url-utils/lib/utils/html-relative-to-absolute.js b/packages/url-utils/src/utils/html-relative-to-absolute.ts similarity index 98% rename from packages/url-utils/lib/utils/html-relative-to-absolute.js rename to packages/url-utils/src/utils/html-relative-to-absolute.ts index d0789cf1d..53bc4508b 100644 --- a/packages/url-utils/lib/utils/html-relative-to-absolute.js +++ b/packages/url-utils/src/utils/html-relative-to-absolute.ts @@ -1,3 +1,4 @@ +export {}; const htmlTransform = require('./html-transform'); const relativeToAbsolute = require('./relative-to-absolute'); diff --git a/packages/url-utils/lib/utils/html-relative-to-transform-ready.js b/packages/url-utils/src/utils/html-relative-to-transform-ready.ts similarity index 98% rename from packages/url-utils/lib/utils/html-relative-to-transform-ready.js rename to packages/url-utils/src/utils/html-relative-to-transform-ready.ts index 7eac11dba..75847e21d 100644 --- a/packages/url-utils/lib/utils/html-relative-to-transform-ready.js +++ b/packages/url-utils/src/utils/html-relative-to-transform-ready.ts @@ -1,3 +1,4 @@ +export {}; const htmlTransform = require('./html-transform'); const relativeToTransformReady = require('./relative-to-transform-ready'); diff --git a/packages/url-utils/lib/utils/html-to-transform-ready.js b/packages/url-utils/src/utils/html-to-transform-ready.ts similarity index 97% rename from packages/url-utils/lib/utils/html-to-transform-ready.js rename to packages/url-utils/src/utils/html-to-transform-ready.ts index 8606db730..536884b22 100644 --- a/packages/url-utils/lib/utils/html-to-transform-ready.js +++ b/packages/url-utils/src/utils/html-to-transform-ready.ts @@ -1,3 +1,4 @@ +export {}; const htmlRelativeToAbsolute = require('./html-relative-to-absolute'); const htmlAbsoluteToTransformReady = require('./html-absolute-to-transform-ready'); diff --git a/packages/url-utils/lib/utils/html-transform.js b/packages/url-utils/src/utils/html-transform.ts similarity index 94% rename from packages/url-utils/lib/utils/html-transform.js rename to packages/url-utils/src/utils/html-transform.ts index 2271174cb..2fb131770 100644 --- a/packages/url-utils/lib/utils/html-transform.js +++ b/packages/url-utils/src/utils/html-transform.ts @@ -1,3 +1,6 @@ +export {}; +let cheerio = require('cheerio'); + function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } @@ -22,13 +25,12 @@ function extractStyleUrls(style = '') { function htmlTransform(html = '', siteUrl, transformFunction, itemPath, _options) { const defaultOptions = {assetsOnly: false, secure: false}; - const options = Object.assign({}, defaultOptions, _options || {}); + const options: any = Object.assign({}, defaultOptions, _options || {}); if (!html || (options.earlyExitMatchStr && !html.match(new RegExp(options.earlyExitMatchStr)))) { return html; } - const cheerio = require('cheerio'); const htmlContent = cheerio.load(html, {decodeEntities: false}); // replacements is keyed with the attr name + original relative value so @@ -41,7 +43,7 @@ function htmlTransform(html = '', siteUrl, transformFunction, itemPath, _options // {name: 'href', originalValue: '/test', absoluteValue: '.../test'}, // ] // } - const replacements = {}; + const replacements: Record = {}; function addReplacement(replacement) { const key = `${replacement.name}="${replacement.originalValue}"`; @@ -110,7 +112,8 @@ function htmlTransform(html = '', siteUrl, transformFunction, itemPath, _options // Loop over all replacements and use a regex to replace urls in the original html string. // Allows indentation and formatting to be kept compared to using DOM manipulation and render - for (const [, attrs] of Object.entries(replacements)) { + const replacementEntries = Object.entries(replacements) as Array<[string, any[]]>; + for (const [, attrs] of replacementEntries) { let skipCount = 0; attrs.forEach(({skip, name, originalValue, transformedValue}) => { diff --git a/packages/url-utils/lib/utils/index.js b/packages/url-utils/src/utils/index.ts similarity index 99% rename from packages/url-utils/lib/utils/index.js rename to packages/url-utils/src/utils/index.ts index ce7073b9b..15d3905d6 100644 --- a/packages/url-utils/lib/utils/index.js +++ b/packages/url-utils/src/utils/index.ts @@ -1,3 +1,4 @@ +export {}; module.exports = { absoluteToRelative: require('./absolute-to-relative'), absoluteToTransformReady: require('./absolute-to-transform-ready'), diff --git a/packages/url-utils/lib/utils/is-ssl.js b/packages/url-utils/src/utils/is-ssl.ts similarity index 95% rename from packages/url-utils/lib/utils/is-ssl.js rename to packages/url-utils/src/utils/is-ssl.ts index f88bcd8a1..43f6ddde0 100644 --- a/packages/url-utils/lib/utils/is-ssl.js +++ b/packages/url-utils/src/utils/is-ssl.ts @@ -1,3 +1,4 @@ +export {}; // require the whatwg compatible URL library (same behaviour in node and browser) const {URL} = require('url'); diff --git a/packages/url-utils/lib/utils/lexical-absolute-to-relative.js b/packages/url-utils/src/utils/lexical-absolute-to-relative.ts similarity index 98% rename from packages/url-utils/lib/utils/lexical-absolute-to-relative.js rename to packages/url-utils/src/utils/lexical-absolute-to-relative.ts index 05e8c0c5e..1e00e70c3 100644 --- a/packages/url-utils/lib/utils/lexical-absolute-to-relative.js +++ b/packages/url-utils/src/utils/lexical-absolute-to-relative.ts @@ -1,3 +1,4 @@ +export {}; const absoluteToRelative = require('./absolute-to-relative'); const lexicalTransform = require('./lexical-transform'); diff --git a/packages/url-utils/lib/utils/lexical-absolute-to-transform-ready.js b/packages/url-utils/src/utils/lexical-absolute-to-transform-ready.ts similarity index 98% rename from packages/url-utils/lib/utils/lexical-absolute-to-transform-ready.js rename to packages/url-utils/src/utils/lexical-absolute-to-transform-ready.ts index a9485a97f..14f9dc494 100644 --- a/packages/url-utils/lib/utils/lexical-absolute-to-transform-ready.js +++ b/packages/url-utils/src/utils/lexical-absolute-to-transform-ready.ts @@ -1,3 +1,4 @@ +export {}; const absoluteToTransformReady = require('./absolute-to-transform-ready'); const lexicalTransform = require('./lexical-transform'); diff --git a/packages/url-utils/lib/utils/lexical-relative-to-absolute.js b/packages/url-utils/src/utils/lexical-relative-to-absolute.ts similarity index 98% rename from packages/url-utils/lib/utils/lexical-relative-to-absolute.js rename to packages/url-utils/src/utils/lexical-relative-to-absolute.ts index b0c0a7a9d..f51d18ead 100644 --- a/packages/url-utils/lib/utils/lexical-relative-to-absolute.js +++ b/packages/url-utils/src/utils/lexical-relative-to-absolute.ts @@ -1,3 +1,4 @@ +export {}; const relativeToAbsolute = require('./relative-to-absolute'); const lexicalTransform = require('./lexical-transform'); diff --git a/packages/url-utils/lib/utils/lexical-relative-to-transform-ready.js b/packages/url-utils/src/utils/lexical-relative-to-transform-ready.ts similarity index 98% rename from packages/url-utils/lib/utils/lexical-relative-to-transform-ready.js rename to packages/url-utils/src/utils/lexical-relative-to-transform-ready.ts index 02d16310b..29a5fdaf0 100644 --- a/packages/url-utils/lib/utils/lexical-relative-to-transform-ready.js +++ b/packages/url-utils/src/utils/lexical-relative-to-transform-ready.ts @@ -1,3 +1,4 @@ +export {}; const relativeToTransformReady = require('./relative-to-transform-ready'); const lexicalTransform = require('./lexical-transform'); diff --git a/packages/url-utils/lib/utils/lexical-to-transform-ready.js b/packages/url-utils/src/utils/lexical-to-transform-ready.ts similarity index 98% rename from packages/url-utils/lib/utils/lexical-to-transform-ready.js rename to packages/url-utils/src/utils/lexical-to-transform-ready.ts index 2fff3474d..994440700 100644 --- a/packages/url-utils/lib/utils/lexical-to-transform-ready.js +++ b/packages/url-utils/src/utils/lexical-to-transform-ready.ts @@ -1,3 +1,4 @@ +export {}; const lexicalRelativeToAbsolute = require('./lexical-relative-to-absolute'); const lexicalAbsoluteToTransformReady = require('./lexical-absolute-to-transform-ready'); diff --git a/packages/url-utils/lib/utils/lexical-transform.js b/packages/url-utils/src/utils/lexical-transform.ts similarity index 96% rename from packages/url-utils/lib/utils/lexical-transform.js rename to packages/url-utils/src/utils/lexical-transform.ts index 288152fa2..ab69d1245 100644 --- a/packages/url-utils/lib/utils/lexical-transform.js +++ b/packages/url-utils/src/utils/lexical-transform.ts @@ -1,3 +1,4 @@ +export {}; const _ = require('lodash'); // options.transformMap = { @@ -10,7 +11,7 @@ const _ = require('lodash'); function lexicalTransform(serializedLexical, siteUrl, transformFunction, itemPath, _options = {}) { const defaultOptions = {assetsOnly: false, secure: false, nodes: [], transformMap: {}}; - const options = Object.assign({}, defaultOptions, _options, {siteUrl, itemPath}); + const options: any = Object.assign({}, defaultOptions, _options, {siteUrl, itemPath}); if (!serializedLexical) { return serializedLexical; diff --git a/packages/url-utils/lib/utils/markdown-absolute-to-relative.js b/packages/url-utils/src/utils/markdown-absolute-to-relative.ts similarity index 92% rename from packages/url-utils/lib/utils/markdown-absolute-to-relative.js rename to packages/url-utils/src/utils/markdown-absolute-to-relative.ts index 0bd871090..7ac29a919 100644 --- a/packages/url-utils/lib/utils/markdown-absolute-to-relative.js +++ b/packages/url-utils/src/utils/markdown-absolute-to-relative.ts @@ -1,10 +1,11 @@ +export {}; const markdownTransform = require('./markdown-transform'); const absoluteToRelative = require('./absolute-to-relative'); const htmlAbsoluteToRelative = require('./html-absolute-to-relative'); function markdownAbsoluteToRelative(markdown = '', siteUrl, _options = {}) { const defaultOptions = {assetsOnly: false, ignoreProtocol: true}; - const options = Object.assign({}, defaultOptions, _options); + const options: any = Object.assign({}, defaultOptions, _options); options.earlyExitMatchStr = options.ignoreProtocol ? siteUrl.replace(/http:|https:/, '') : siteUrl; options.earlyExitMatchStr = options.earlyExitMatchStr.replace(/\/$/, ''); diff --git a/packages/url-utils/lib/utils/markdown-absolute-to-transform-ready.js b/packages/url-utils/src/utils/markdown-absolute-to-transform-ready.ts similarity index 93% rename from packages/url-utils/lib/utils/markdown-absolute-to-transform-ready.js rename to packages/url-utils/src/utils/markdown-absolute-to-transform-ready.ts index e5133d957..7905f15c9 100644 --- a/packages/url-utils/lib/utils/markdown-absolute-to-transform-ready.js +++ b/packages/url-utils/src/utils/markdown-absolute-to-transform-ready.ts @@ -1,10 +1,11 @@ +export {}; const markdownTransform = require('./markdown-transform'); const absoluteToTransformReady = require('./absolute-to-transform-ready'); const htmlAbsoluteToTransformReady = require('./html-absolute-to-transform-ready'); function markdownAbsoluteToTransformReady(markdown = '', siteUrl, _options = {}) { const defaultOptions = {assetsOnly: false, ignoreProtocol: true}; - const options = Object.assign({}, defaultOptions, _options); + const options: any = Object.assign({}, defaultOptions, _options); options.earlyExitMatchStr = options.ignoreProtocol ? siteUrl.replace(/http:|https:/, '') : siteUrl; options.earlyExitMatchStr = options.earlyExitMatchStr.replace(/\/$/, ''); diff --git a/packages/url-utils/lib/utils/markdown-relative-to-absolute.js b/packages/url-utils/src/utils/markdown-relative-to-absolute.ts similarity index 90% rename from packages/url-utils/lib/utils/markdown-relative-to-absolute.js rename to packages/url-utils/src/utils/markdown-relative-to-absolute.ts index 9f9439e7a..a31d9770c 100644 --- a/packages/url-utils/lib/utils/markdown-relative-to-absolute.js +++ b/packages/url-utils/src/utils/markdown-relative-to-absolute.ts @@ -1,10 +1,11 @@ +export {}; const markdownTransform = require('./markdown-transform'); const htmlRelativeToAbsolute = require('./html-relative-to-absolute'); const relativeToAbsolute = require('./relative-to-absolute'); function markdownRelativeToAbsolute(markdown = '', siteUrl, itemPath, _options = {}) { const defaultOptions = {assetsOnly: false}; - const options = Object.assign({}, defaultOptions, _options); + const options: any = Object.assign({}, defaultOptions, _options); options.earlyExitMatchStr = '\\]\\([^\\s\\)]|href=|src=|srcset='; if (options.assetsOnly) { diff --git a/packages/url-utils/lib/utils/markdown-relative-to-transform-ready.js b/packages/url-utils/src/utils/markdown-relative-to-transform-ready.ts similarity index 90% rename from packages/url-utils/lib/utils/markdown-relative-to-transform-ready.js rename to packages/url-utils/src/utils/markdown-relative-to-transform-ready.ts index 2ec4fd59e..d023a26da 100644 --- a/packages/url-utils/lib/utils/markdown-relative-to-transform-ready.js +++ b/packages/url-utils/src/utils/markdown-relative-to-transform-ready.ts @@ -1,10 +1,11 @@ +export {}; const markdownTransform = require('./markdown-transform'); const htmlRelativeToTransformReady = require('./html-relative-to-transform-ready'); const relativeToTransformReady = require('./relative-to-transform-ready'); function markdownRelativeToTransformReady(markdown = '', siteUrl, itemPath, _options = {}) { const defaultOptions = {assetsOnly: false}; - const options = Object.assign({}, defaultOptions, _options); + const options: any = Object.assign({}, defaultOptions, _options); options.earlyExitMatchStr = '\\]\\([^\\s\\)]|href=|src=|srcset='; if (options.assetsOnly) { diff --git a/packages/url-utils/lib/utils/markdown-to-transform-ready.js b/packages/url-utils/src/utils/markdown-to-transform-ready.ts similarity index 98% rename from packages/url-utils/lib/utils/markdown-to-transform-ready.js rename to packages/url-utils/src/utils/markdown-to-transform-ready.ts index 4ad494ebf..f24ba395d 100644 --- a/packages/url-utils/lib/utils/markdown-to-transform-ready.js +++ b/packages/url-utils/src/utils/markdown-to-transform-ready.ts @@ -1,3 +1,4 @@ +export {}; const markdownRelativeToAbsolute = require('./markdown-relative-to-absolute'); const markdownAbsoluteToTransformReady = require('./markdown-absolute-to-transform-ready'); diff --git a/packages/url-utils/lib/utils/markdown-transform.js b/packages/url-utils/src/utils/markdown-transform.ts similarity index 97% rename from packages/url-utils/lib/utils/markdown-transform.js rename to packages/url-utils/src/utils/markdown-transform.ts index 9b6a7e861..bca482246 100644 --- a/packages/url-utils/lib/utils/markdown-transform.js +++ b/packages/url-utils/src/utils/markdown-transform.ts @@ -1,3 +1,4 @@ +export {}; let remark; const footnotes = require('remark-footnotes'); const visit = require('unist-util-visit'); @@ -17,7 +18,7 @@ function replaceLast(find, replace, str) { function markdownTransform(markdown = '', siteUrl, transformFunctions, itemPath, _options = {}) { const defaultOptions = {assetsOnly: false, ignoreProtocol: true}; - const options = Object.assign({}, defaultOptions, _options); + const options: any = Object.assign({}, defaultOptions, _options); if (!markdown || (options.earlyExitMatchStr && !markdown.match(new RegExp(options.earlyExitMatchStr)))) { return markdown; diff --git a/packages/url-utils/lib/utils/mobiledoc-absolute-to-relative.js b/packages/url-utils/src/utils/mobiledoc-absolute-to-relative.ts similarity index 98% rename from packages/url-utils/lib/utils/mobiledoc-absolute-to-relative.js rename to packages/url-utils/src/utils/mobiledoc-absolute-to-relative.ts index dfc2c7d4e..29b8a29d2 100644 --- a/packages/url-utils/lib/utils/mobiledoc-absolute-to-relative.js +++ b/packages/url-utils/src/utils/mobiledoc-absolute-to-relative.ts @@ -1,3 +1,4 @@ +export {}; const absoluteToRelative = require('./absolute-to-relative'); const mobiledocTransform = require('./mobiledoc-transform'); diff --git a/packages/url-utils/lib/utils/mobiledoc-absolute-to-transform-ready.js b/packages/url-utils/src/utils/mobiledoc-absolute-to-transform-ready.ts similarity index 98% rename from packages/url-utils/lib/utils/mobiledoc-absolute-to-transform-ready.js rename to packages/url-utils/src/utils/mobiledoc-absolute-to-transform-ready.ts index a10aa53bd..2a9f5668e 100644 --- a/packages/url-utils/lib/utils/mobiledoc-absolute-to-transform-ready.js +++ b/packages/url-utils/src/utils/mobiledoc-absolute-to-transform-ready.ts @@ -1,3 +1,4 @@ +export {}; const absoluteToTransformReady = require('./absolute-to-transform-ready'); const mobiledocTransform = require('./mobiledoc-transform'); diff --git a/packages/url-utils/lib/utils/mobiledoc-relative-to-absolute.js b/packages/url-utils/src/utils/mobiledoc-relative-to-absolute.ts similarity index 98% rename from packages/url-utils/lib/utils/mobiledoc-relative-to-absolute.js rename to packages/url-utils/src/utils/mobiledoc-relative-to-absolute.ts index 1d44be9a3..b9957ee5d 100644 --- a/packages/url-utils/lib/utils/mobiledoc-relative-to-absolute.js +++ b/packages/url-utils/src/utils/mobiledoc-relative-to-absolute.ts @@ -1,3 +1,4 @@ +export {}; const relativeToAbsolute = require('./relative-to-absolute'); const mobiledocTransform = require('./mobiledoc-transform'); diff --git a/packages/url-utils/lib/utils/mobiledoc-relative-to-transform-ready.js b/packages/url-utils/src/utils/mobiledoc-relative-to-transform-ready.ts similarity index 98% rename from packages/url-utils/lib/utils/mobiledoc-relative-to-transform-ready.js rename to packages/url-utils/src/utils/mobiledoc-relative-to-transform-ready.ts index d92c9e733..215aab227 100644 --- a/packages/url-utils/lib/utils/mobiledoc-relative-to-transform-ready.js +++ b/packages/url-utils/src/utils/mobiledoc-relative-to-transform-ready.ts @@ -1,3 +1,4 @@ +export {}; const relativeToTransformReady = require('./relative-to-transform-ready'); const mobiledocTransform = require('./mobiledoc-transform'); diff --git a/packages/url-utils/lib/utils/mobiledoc-to-transform-ready.js b/packages/url-utils/src/utils/mobiledoc-to-transform-ready.ts similarity index 98% rename from packages/url-utils/lib/utils/mobiledoc-to-transform-ready.js rename to packages/url-utils/src/utils/mobiledoc-to-transform-ready.ts index 998c2e966..2df4ec257 100644 --- a/packages/url-utils/lib/utils/mobiledoc-to-transform-ready.js +++ b/packages/url-utils/src/utils/mobiledoc-to-transform-ready.ts @@ -1,3 +1,4 @@ +export {}; const mobiledocRelativeToAbsolute = require('./mobiledoc-relative-to-absolute'); const mobiledocAbsoluteToTransformReady = require('./mobiledoc-absolute-to-transform-ready'); diff --git a/packages/url-utils/lib/utils/mobiledoc-transform.js b/packages/url-utils/src/utils/mobiledoc-transform.ts similarity index 93% rename from packages/url-utils/lib/utils/mobiledoc-transform.js rename to packages/url-utils/src/utils/mobiledoc-transform.ts index 5253a880d..abf84f8ac 100644 --- a/packages/url-utils/lib/utils/mobiledoc-transform.js +++ b/packages/url-utils/src/utils/mobiledoc-transform.ts @@ -1,11 +1,12 @@ +export {}; function mobiledocTransform(serializedMobiledoc, siteUrl, transformFunction, itemPath, _options = {}) { const defaultOptions = {assetsOnly: false, secure: false, cardTransformers: []}; - const options = Object.assign({}, defaultOptions, _options, {siteUrl, itemPath}); + const options: any = Object.assign({}, defaultOptions, _options, {siteUrl, itemPath}); // options.cardTransformers has an object for each card that has a name and multiple // transformer functions. By collecting the functions we need into a named object it // reduces the need to loop through and find the transformer for each card later on - const cardTransformers = {}; + const cardTransformers: Record = {}; options.cardTransformers.forEach((cardTransformer) => { cardTransformers[cardTransformer.name] = cardTransformer[options.transformType]; }); diff --git a/packages/url-utils/lib/utils/plaintext-absolute-to-transform-ready.js b/packages/url-utils/src/utils/plaintext-absolute-to-transform-ready.ts similarity index 98% rename from packages/url-utils/lib/utils/plaintext-absolute-to-transform-ready.js rename to packages/url-utils/src/utils/plaintext-absolute-to-transform-ready.ts index a3dbb57fb..8ae0638e7 100644 --- a/packages/url-utils/lib/utils/plaintext-absolute-to-transform-ready.js +++ b/packages/url-utils/src/utils/plaintext-absolute-to-transform-ready.ts @@ -1,3 +1,4 @@ +export {}; const absoluteToTransformReady = require('./absolute-to-transform-ready'); function escapeRegExp(string) { diff --git a/packages/url-utils/lib/utils/plaintext-relative-to-transform-ready.js b/packages/url-utils/src/utils/plaintext-relative-to-transform-ready.ts similarity index 98% rename from packages/url-utils/lib/utils/plaintext-relative-to-transform-ready.js rename to packages/url-utils/src/utils/plaintext-relative-to-transform-ready.ts index a4c207c9e..ce3ce160b 100644 --- a/packages/url-utils/lib/utils/plaintext-relative-to-transform-ready.js +++ b/packages/url-utils/src/utils/plaintext-relative-to-transform-ready.ts @@ -1,3 +1,4 @@ +export {}; const relativeToTransformReady = require('./relative-to-transform-ready'); const plaintextRelativeToTransformReady = function plaintextRelativeToTransformReady(plaintext, rootUrl, itemPath, options) { diff --git a/packages/url-utils/lib/utils/plaintext-to-transform-ready.js b/packages/url-utils/src/utils/plaintext-to-transform-ready.ts similarity index 98% rename from packages/url-utils/lib/utils/plaintext-to-transform-ready.js rename to packages/url-utils/src/utils/plaintext-to-transform-ready.ts index 6c2b8d8fe..c16cfc23f 100644 --- a/packages/url-utils/lib/utils/plaintext-to-transform-ready.js +++ b/packages/url-utils/src/utils/plaintext-to-transform-ready.ts @@ -1,3 +1,4 @@ +export {}; const plaintextRelativeToTransformReady = require('./plaintext-relative-to-transform-ready'); const plaintextAbsoluteToTransformReady = require('./plaintext-absolute-to-transform-ready'); diff --git a/packages/url-utils/lib/utils/relative-to-absolute.js b/packages/url-utils/src/utils/relative-to-absolute.ts similarity index 99% rename from packages/url-utils/lib/utils/relative-to-absolute.js rename to packages/url-utils/src/utils/relative-to-absolute.ts index 7f5e3ce33..abb3282b5 100644 --- a/packages/url-utils/lib/utils/relative-to-absolute.js +++ b/packages/url-utils/src/utils/relative-to-absolute.ts @@ -1,3 +1,4 @@ +export {}; // require the whatwg compatible URL library (same behaviour in node and browser) const {URL} = require('url'); const urlJoin = require('./url-join'); diff --git a/packages/url-utils/lib/utils/relative-to-transform-ready.js b/packages/url-utils/src/utils/relative-to-transform-ready.ts similarity index 99% rename from packages/url-utils/lib/utils/relative-to-transform-ready.js rename to packages/url-utils/src/utils/relative-to-transform-ready.ts index 13b85474f..0bf54a579 100644 --- a/packages/url-utils/lib/utils/relative-to-transform-ready.js +++ b/packages/url-utils/src/utils/relative-to-transform-ready.ts @@ -1,3 +1,4 @@ +export {}; const relativeToAbsolute = require('./relative-to-absolute'); const relativeToTransformReady = function (url, root, itemPath, _options) { diff --git a/packages/url-utils/lib/utils/replace-permalink.js b/packages/url-utils/src/utils/replace-permalink.ts similarity index 99% rename from packages/url-utils/lib/utils/replace-permalink.js rename to packages/url-utils/src/utils/replace-permalink.ts index 20846ca14..0852a44d3 100644 --- a/packages/url-utils/lib/utils/replace-permalink.js +++ b/packages/url-utils/src/utils/replace-permalink.ts @@ -1,3 +1,4 @@ +export {}; const moment = require('moment-timezone'); /** diff --git a/packages/url-utils/lib/utils/strip-subdirectory-from-path.js b/packages/url-utils/src/utils/strip-subdirectory-from-path.ts similarity index 98% rename from packages/url-utils/lib/utils/strip-subdirectory-from-path.js rename to packages/url-utils/src/utils/strip-subdirectory-from-path.ts index c649d681d..d2b6318ce 100644 --- a/packages/url-utils/lib/utils/strip-subdirectory-from-path.js +++ b/packages/url-utils/src/utils/strip-subdirectory-from-path.ts @@ -1,3 +1,4 @@ +export {}; const {URL} = require('url'); /** diff --git a/packages/url-utils/lib/utils/to-transform-ready.js b/packages/url-utils/src/utils/to-transform-ready.ts similarity index 97% rename from packages/url-utils/lib/utils/to-transform-ready.js rename to packages/url-utils/src/utils/to-transform-ready.ts index 3f25f6789..ea87c29ce 100644 --- a/packages/url-utils/lib/utils/to-transform-ready.js +++ b/packages/url-utils/src/utils/to-transform-ready.ts @@ -1,3 +1,4 @@ +export {}; const relativeToAbsolute = require('./relative-to-absolute'); const absoluteToTransformReady = require('./absolute-to-transform-ready'); diff --git a/packages/url-utils/lib/utils/transform-ready-to-absolute.js b/packages/url-utils/src/utils/transform-ready-to-absolute.ts similarity index 98% rename from packages/url-utils/lib/utils/transform-ready-to-absolute.js rename to packages/url-utils/src/utils/transform-ready-to-absolute.ts index a5f8d10cf..c51e15f90 100644 --- a/packages/url-utils/lib/utils/transform-ready-to-absolute.js +++ b/packages/url-utils/src/utils/transform-ready-to-absolute.ts @@ -1,3 +1,4 @@ +export {}; function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } diff --git a/packages/url-utils/lib/utils/transform-ready-to-relative.js b/packages/url-utils/src/utils/transform-ready-to-relative.ts similarity index 98% rename from packages/url-utils/lib/utils/transform-ready-to-relative.js rename to packages/url-utils/src/utils/transform-ready-to-relative.ts index 4a73f4d5a..fbe7bf179 100644 --- a/packages/url-utils/lib/utils/transform-ready-to-relative.js +++ b/packages/url-utils/src/utils/transform-ready-to-relative.ts @@ -1,3 +1,4 @@ +export {}; function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } diff --git a/packages/url-utils/lib/utils/url-join.js b/packages/url-utils/src/utils/url-join.ts similarity index 98% rename from packages/url-utils/lib/utils/url-join.js rename to packages/url-utils/src/utils/url-join.ts index 2cf5c0730..75817024b 100644 --- a/packages/url-utils/lib/utils/url-join.js +++ b/packages/url-utils/src/utils/url-join.ts @@ -1,3 +1,4 @@ +export {}; const deduplicateSubdirectory = require('./deduplicate-subdirectory'); /** urlJoin diff --git a/packages/url-utils/tsconfig.json b/packages/url-utils/tsconfig.json new file mode 100644 index 000000000..d0d0c01ca --- /dev/null +++ b/packages/url-utils/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2019", + "module": "CommonJS", + "rootDir": "src", + "outDir": "lib", + "declaration": true, + "sourceMap": true, + "inlineSources": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "moduleResolution": "Node", + "skipLibCheck": true, + "strict": false + }, + "include": [ + "src/**/*" + ] +} diff --git a/packages/url-utils/tsconfig.test.json b/packages/url-utils/tsconfig.test.json new file mode 100644 index 000000000..19bade918 --- /dev/null +++ b/packages/url-utils/tsconfig.test.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "rootDir": "test", + "outDir": "tmp/test", + "declaration": false + }, + "include": [ + "test/**/*.ts" + ] +} From 9daf3e7aa8ab0bdc484cf3a5638280d2e6d0610a Mon Sep 17 00:00:00 2001 From: Fabien O'Carroll Date: Tue, 21 Oct 2025 18:36:51 +0700 Subject: [PATCH 2/3] Adding types --- packages/url-utils/src/UrlUtils.ts | 675 ++++++++++-------- packages/url-utils/src/index.ts | 6 +- .../src/utils/absolute-to-relative.ts | 30 +- .../src/utils/absolute-to-transform-ready.ts | 36 +- .../src/utils/deduplicate-double-slashes.ts | 5 +- .../src/utils/deduplicate-subdirectory.ts | 7 +- .../src/utils/html-absolute-to-relative.ts | 25 +- .../utils/html-absolute-to-transform-ready.ts | 27 +- .../src/utils/html-relative-to-absolute.ts | 36 +- .../utils/html-relative-to-transform-ready.ts | 47 +- .../src/utils/html-to-transform-ready.ts | 31 +- .../url-utils/src/utils/html-transform.ts | 60 +- packages/url-utils/src/utils/index.ts | 153 +++- packages/url-utils/src/utils/is-ssl.ts | 8 +- .../src/utils/lexical-absolute-to-relative.ts | 35 +- .../lexical-absolute-to-transform-ready.ts | 35 +- .../src/utils/lexical-relative-to-absolute.ts | 33 +- .../lexical-relative-to-transform-ready.ts | 32 +- .../src/utils/lexical-to-transform-ready.ts | 31 +- .../url-utils/src/utils/lexical-transform.ts | 106 ++- .../utils/markdown-absolute-to-relative.ts | 30 +- .../markdown-absolute-to-transform-ready.ts | 30 +- .../utils/markdown-relative-to-absolute.ts | 41 +- .../markdown-relative-to-transform-ready.ts | 41 +- .../src/utils/markdown-to-transform-ready.ts | 31 +- .../url-utils/src/utils/markdown-transform.ts | 81 ++- .../utils/mobiledoc-absolute-to-relative.ts | 32 +- .../mobiledoc-absolute-to-transform-ready.ts | 32 +- .../utils/mobiledoc-relative-to-absolute.ts | 32 +- .../mobiledoc-relative-to-transform-ready.ts | 31 +- .../src/utils/mobiledoc-to-transform-ready.ts | 31 +- .../src/utils/mobiledoc-transform.ts | 73 +- .../plaintext-absolute-to-transform-ready.ts | 18 +- .../plaintext-relative-to-transform-ready.ts | 26 +- .../src/utils/plaintext-to-transform-ready.ts | 32 +- .../src/utils/relative-to-absolute.ts | 39 +- .../src/utils/relative-to-transform-ready.ts | 43 +- .../url-utils/src/utils/replace-permalink.ts | 48 +- .../src/utils/strip-subdirectory-from-path.ts | 7 +- .../url-utils/src/utils/to-transform-ready.ts | 31 +- .../src/utils/transform-ready-to-absolute.ts | 18 +- .../src/utils/transform-ready-to-relative.ts | 18 +- packages/url-utils/src/utils/types.ts | 88 +++ packages/url-utils/src/utils/url-join.ts | 15 +- .../url-utils/test/unit/url-utils.test.js | 3 +- .../unit/utils/absolute-to-relative.test.js | 2 +- .../utils/absolute-to-transform-ready.test.js | 2 +- .../utils/deduplicate-subdirectory.test.js | 2 +- .../utils/html-absolute-to-relative.test.js | 2 +- .../html-absolute-to-transform-ready.test.js | 6 +- .../utils/html-relative-to-absolute.test.js | 6 +- .../html-relative-to-transform-ready.test.js | 2 +- .../url-utils/test/unit/utils/is-ssl.test.js | 2 +- .../lexical-absolute-to-relative.test.js | 5 +- ...exical-absolute-to-transform-ready.test.js | 5 +- .../lexical-relative-to-absolute.test.js | 5 +- ...exical-relative-to-transform-ready.test.js | 5 +- .../markdown-absolute-to-relative.test.js | 10 +- ...rkdown-absolute-to-transform-ready.test.js | 10 +- .../markdown-relative-to-absolute.test.js | 10 +- ...rkdown-relative-to-transform-ready.test.js | 10 +- .../mobiledoc-absolute-to-relative.test.js | 2 +- ...iledoc-absolute-to-transform-ready.test.js | 2 +- .../mobiledoc-relative-to-absolute.test.js | 2 +- ...iledoc-relative-to-transform-ready.test.js | 2 +- ...intext-absolute-to-transform-ready.test.js | 2 +- ...intext-relative-to-transform-ready.test.js | 2 +- .../plaintext-to-transform-ready.test.js | 2 +- .../unit/utils/relative-to-absolute.test.js | 2 +- .../utils/relative-to-transform-ready.test.js | 2 +- .../test/unit/utils/replace-permalink.test.js | 2 +- .../strip-subdirectory-from-path.test.js | 2 +- .../utils/transform-ready-to-absolute.test.js | 2 +- .../utils/transform-ready-to-relative.test.js | 2 +- .../test/unit/utils/url-join.test.js | 2 +- 75 files changed, 1577 insertions(+), 824 deletions(-) create mode 100644 packages/url-utils/src/utils/types.ts diff --git a/packages/url-utils/src/UrlUtils.ts b/packages/url-utils/src/UrlUtils.ts index b48009bd8..3616e169d 100644 --- a/packages/url-utils/src/UrlUtils.ts +++ b/packages/url-utils/src/UrlUtils.ts @@ -1,37 +1,102 @@ -export {}; -// Contains all path information to be used throughout the codebase. -const _ = require('lodash'); -const utils = require('./utils'); - -// similar to Object.assign but will not override defaults if a source value is undefined -function assignOptions(target, ...sources) { - const options = sources.map((x) => { - return Object.entries(x) - .filter(([, value]) => value !== undefined) - .reduce((obj, [key, value]) => (obj[key] = value, obj), {}); - }); - return Object.assign(target, ...options); +import _ from 'lodash'; +import utils from './utils'; +import type {AbsoluteToRelativeOptionsInput} from './utils/absolute-to-relative'; +import type {AbsoluteToTransformReadyOptionsInput} from './utils/absolute-to-transform-ready'; +import type {RelativeToAbsoluteOptionsInput} from './utils/relative-to-absolute'; +import type {RelativeToTransformReadyOptionsInput} from './utils/relative-to-transform-ready'; +import type {HtmlRelativeToAbsoluteOptionsInput} from './utils/html-relative-to-absolute'; +import type {HtmlRelativeToTransformReadyOptionsInput} from './utils/html-relative-to-transform-ready'; +import type {HtmlAbsoluteToRelativeOptionsInput} from './utils/html-absolute-to-relative'; +import type {HtmlAbsoluteToTransformReadyOptionsInput} from './utils/html-absolute-to-transform-ready'; +import type {MarkdownRelativeToAbsoluteOptionsInput} from './utils/markdown-relative-to-absolute'; +import type {MarkdownRelativeToTransformReadyOptionsInput} from './utils/markdown-relative-to-transform-ready'; +import type {MarkdownAbsoluteToRelativeOptionsInput} from './utils/markdown-absolute-to-relative'; +import type {MarkdownAbsoluteToTransformReadyOptionsInput} from './utils/markdown-absolute-to-transform-ready'; +import type {MarkdownToTransformReadyOptions} from './utils/markdown-to-transform-ready'; +import type {MobiledocRelativeToAbsoluteOptions} from './utils/mobiledoc-relative-to-absolute'; +import type {MobiledocRelativeToTransformReadyOptions} from './utils/mobiledoc-relative-to-transform-ready'; +import type {MobiledocAbsoluteToRelativeOptions} from './utils/mobiledoc-absolute-to-relative'; +import type {MobiledocAbsoluteToTransformReadyOptions} from './utils/mobiledoc-absolute-to-transform-ready'; +import type {MobiledocToTransformReadyOptions} from './utils/mobiledoc-to-transform-ready'; +import type {LexicalRelativeToAbsoluteOptions} from './utils/lexical-relative-to-absolute'; +import type {LexicalRelativeToTransformReadyOptions} from './utils/lexical-relative-to-transform-ready'; +import type {LexicalAbsoluteToRelativeOptions} from './utils/lexical-absolute-to-relative'; +import type {LexicalAbsoluteToTransformReadyOptions} from './utils/lexical-absolute-to-transform-ready'; +import type {LexicalToTransformReadyOptions} from './utils/lexical-to-transform-ready'; +import type {PlaintextToTransformReadyOptions} from './utils/plaintext-to-transform-ready'; +import type {TransformReadyReplacementOptionsInput, MobiledocCardTransformer} from './utils/types'; +import type {ToTransformReadyOptions} from './utils/to-transform-ready'; + +const KNOWN_PATHS: Record = { + home: '/', + sitemap_xsl: '/sitemap.xsl' +}; + +type UrlGetter = () => string; + +interface UrlUtilsConfig extends Record { + slugs: string[] | null; + redirectCacheMaxAge: number | null; + baseApiPath: string; + defaultApiType: 'content' | 'admin'; + staticImageUrlPrefix: string; + cardTransformers?: MobiledocCardTransformer[]; } -module.exports = class UrlUtils { - _config: any; - getSubdir: () => string; - getSiteUrl: () => string; - getAdminUrl: () => string; - - /** - * Initialization method to pass in URL configurations - * @param {Object} options - * @param {Function} options.getSubdir - * @param {Function} options.getSiteUrl - * @param {Function} options.getAdminUrl Ghost instance admin URL - * @param {String} [options.baseApiPath='/ghost/api'] static prefix for serving API. Should not te passed in, unless the API is being run under custom URL - * @param {('content' | 'admin')} [options.defaultApiType='content'] default API type to be used - * @param {Object} [options.slugs] object with 2 properties reserved and protected containing arrays of special case slugs - * @param {Number} [options.redirectCacheMaxAge] - * @param {String} [options.staticImageUrlPrefix='content/images'] static prefix for serving images. Should not be passed in, unless customizing ghost instance image storage - */ - constructor(options: any = {}) { - const defaultOptions = { + +interface UrlUtilsOptions extends Partial { + getSubdir?: UrlGetter; + getSiteUrl?: UrlGetter; + getAdminUrl?: UrlGetter; +} + +interface RedirectResponse { + set(headers: Record): unknown; + redirect(status: number, url: string): unknown; + redirect(url: string): unknown; +} + +type UrlForContext = string | Record; + +interface UrlForNav { + url: string; +} + +interface UrlForData { + image?: string; + nav?: UrlForNav; + type?: 'admin' | 'content'; + trailingSlash?: boolean; + [key: string]: unknown; +} + +function assignOptions>(target: T, ...sources: Array | undefined | null>): T { + for (const source of sources) { + if (!source) { + continue; + } + + for (const [key, value] of Object.entries(source) as Array<[keyof T, T[keyof T]]>) { + if (value !== undefined) { + target[key] = value; + } + } + } + + return target; +} + +function sanitizedMerge>(defaults: T, overrides?: Partial): T { + return assignOptions({...defaults}, overrides); +} + +export default class UrlUtils { + private _config: UrlUtilsConfig; + public getSubdir: UrlGetter; + public getSiteUrl: UrlGetter; + public getAdminUrl: UrlGetter; + + constructor(options: UrlUtilsOptions = {}) { + const defaultOptions: UrlUtilsConfig = { slugs: null, redirectCacheMaxAge: null, baseApiPath: '/ghost/api', @@ -39,246 +104,219 @@ module.exports = class UrlUtils { staticImageUrlPrefix: 'content/images' }; - this._config = assignOptions({}, defaultOptions, options); + const configOverrides: Partial = { + slugs: options.slugs, + redirectCacheMaxAge: options.redirectCacheMaxAge, + baseApiPath: options.baseApiPath, + defaultApiType: options.defaultApiType, + staticImageUrlPrefix: options.staticImageUrlPrefix, + cardTransformers: options.cardTransformers + }; + + this._config = sanitizedMerge(defaultOptions, configOverrides); - this.getSubdir = options.getSubdir || (() => ''); - this.getSiteUrl = options.getSiteUrl || (() => ''); - this.getAdminUrl = options.getAdminUrl || (() => ''); + this.getSubdir = options.getSubdir ?? (() => ''); + this.getSiteUrl = options.getSiteUrl ?? (() => ''); + this.getAdminUrl = options.getAdminUrl ?? (() => ''); } - getProtectedSlugs() { - let subDir = this.getSubdir(); + getProtectedSlugs(): string[] | null { + const subDir = this.getSubdir(); if (this._config.slugs && !_.isEmpty(subDir)) { - return this._config.slugs.concat([subDir.split('/').pop()]); - } else { - return this._config.slugs; + const parts = subDir.split('/'); + const lastPart = parts[parts.length - 1]; + return this._config.slugs.concat(lastPart ? [lastPart] : []); } + + return this._config.slugs; } - /** urlJoin - * Returns a URL/path for internal use in Ghost. - * @param {string} arguments takes arguments and concats those to a valid path/URL. - * @return {string} URL concatinated URL/path of arguments. - */ - urlJoin(...parts: any[]) { + urlJoin(...parts: string[]): string { return utils.urlJoin(parts, {rootUrl: this.getSiteUrl()}); } - // ## createUrl - // Simple url creation from a given path - // Ensures that our urls contain the subdirectory if there is one - // And are correctly formatted as either relative or absolute - // Usage: - // createUrl('/', true) -> http://my-ghost-blog.com/ - // E.g. /blog/ subdir - // createUrl('/welcome-to-ghost/') -> /blog/welcome-to-ghost/ - // Parameters: - // - urlPath - string which must start and end with a slash - // - absolute (optional, default:false) - boolean whether or not the url should be absolute - // Returns: - // - a URL which always ends with a slash - createUrl(urlPath = '/', absolute = false, trailingSlash?: boolean) { - let base; - - // create base of url, always ends without a slash + createUrl(urlPath: string = '/', absolute = false, trailingSlash?: boolean): string { + let base: string; + if (absolute) { base = this.getSiteUrl(); } else { base = this.getSubdir(); } + let finalPath = urlPath; if (trailingSlash) { - if (!urlPath.match(/\/$/)) { - urlPath += '/'; + if (!finalPath.endsWith('/')) { + finalPath += '/'; } } - return this.urlJoin(base, urlPath); - } - - // ## urlFor - // Synchronous url creation for a given context - // Can generate a url for a named path and given path. - // Determines what sort of context it has been given, and delegates to the correct generation method, - // Finally passing to createUrl, to ensure any subdirectory is honoured, and the url is absolute if needed - // Usage: - // urlFor('home', true) -> http://my-ghost-blog.com/ - // E.g. /blog/ subdir - // urlFor({relativeUrl: '/my-static-page/'}) -> /blog/my-static-page/ - // Parameters: - // - context - a string, or json object describing the context for which you need a url - // - data (optional) - a json object containing data needed to generate a url - // - absolute (optional, default:false) - boolean whether or not the url should be absolute - // This is probably not the right place for this, but it's the best place for now - // @TODO: rewrite, very hard to read, create private functions! - urlFor(context: any, data?: any, absolute?: boolean) { - let urlPath = '/'; - let imagePathRe; - let knownObjects = ['image', 'nav']; - let baseUrl; - let hostname; - - // this will become really big - let knownPaths = { - home: '/', - sitemap_xsl: '/sitemap.xsl' - }; + return this.urlJoin(base, finalPath); + } - // Make data properly optional - if (_.isBoolean(data)) { - absolute = data; - data = null; + urlFor(context: UrlForContext, data?: UrlForData | boolean, absolute?: boolean): string { + let urlPath = '/'; + const knownObjects = ['image', 'nav']; + let absoluteFlag = Boolean(absolute); + let dataObj: UrlForData | undefined; + + if (typeof data === 'boolean') { + absoluteFlag = data; + dataObj = undefined; + } else if (data && typeof data === 'object') { + dataObj = data; } - if (_.isObject(context) && context.relativeUrl) { - urlPath = context.relativeUrl; - } else if (_.isString(context) && _.indexOf(knownObjects, context) !== -1) { - if (context === 'image' && data.image) { - urlPath = data.image; - imagePathRe = new RegExp('^' + this.getSubdir() + '/' + this._config.staticImageUrlPrefix); - absolute = imagePathRe.test(data.image) ? absolute : false; + if (typeof context === 'object' && context !== null) { + const relativeUrl = (context as {relativeUrl?: unknown}).relativeUrl; + if (typeof relativeUrl === 'string' && relativeUrl) { + urlPath = relativeUrl; + } + } else if (typeof context === 'string' && knownObjects.includes(context)) { + if (context === 'image' && dataObj?.image) { + urlPath = dataObj.image; + const imagePathRe = new RegExp('^' + this.getSubdir() + '/' + this._config.staticImageUrlPrefix); + absoluteFlag = imagePathRe.test(dataObj.image) ? absoluteFlag : false; - if (absolute) { - // Remove the sub-directory from the URL because ghostConfig will add it back. + if (absoluteFlag) { urlPath = urlPath.replace(new RegExp('^' + this.getSubdir()), ''); - baseUrl = this.getSiteUrl().replace(/\/$/, ''); + const baseUrl = this.getSiteUrl().replace(/\/$/, ''); urlPath = baseUrl + urlPath; } return urlPath; - } else if (context === 'nav' && data.nav) { - urlPath = data.nav.url; - baseUrl = this.getSiteUrl(); - hostname = baseUrl.split('//')[1]; - - // If the hostname is present in the url - if (urlPath.indexOf(hostname) > -1 - // do no not apply, if there is a subdomain, or a mailto link - && !urlPath.split(hostname)[0].match(/\.|mailto:/) - // do not apply, if there is a port after the hostname - && urlPath.split(hostname)[1].substring(0, 1) !== ':') { - // make link relative to account for possible mismatch in http/https etc, force absolute - urlPath = urlPath.split(hostname)[1]; - urlPath = this.urlJoin('/', urlPath); - absolute = true; + } + + if (context === 'nav' && dataObj?.nav) { + urlPath = dataObj.nav.url; + const baseUrl = this.getSiteUrl(); + const hostname = baseUrl.split('//')[1]; + + if (hostname) { + const prefix = urlPath.split(hostname)[0]; + const suffix = urlPath.split(hostname)[1] ?? ''; + const isSubdomain = prefix.match(/\.|mailto:/); + const hasPort = suffix.startsWith(':'); + + if (urlPath.includes(hostname) && !isSubdomain && !hasPort) { + const stripped = urlPath.split(hostname)[1]; + urlPath = this.urlJoin('/', stripped); + absoluteFlag = true; + } } } - } else if (context === 'home' && absolute) { + } else if (context === 'home' && absoluteFlag) { urlPath = this.getSiteUrl(); - // CASE: there are cases where urlFor('home') needs to be returned without trailing - // slash e. g. the `{{@site.url}}` helper. See https://github.com/TryGhost/Ghost/issues/8569 - if (data && data.trailingSlash === false) { + if (dataObj && dataObj.trailingSlash === false) { urlPath = urlPath.replace(/\/$/, ''); } } else if (context === 'admin') { - let adminUrl = this.getAdminUrl() || this.getSiteUrl(); - let adminPath = '/ghost/'; + const adminUrl = this.getAdminUrl() || this.getSiteUrl(); + const adminPath = '/ghost/'; - if (absolute) { + if (absoluteFlag) { urlPath = this.urlJoin(adminUrl, adminPath); } else { urlPath = adminPath; } } else if (context === 'api') { - let adminUrl = this.getAdminUrl() || this.getSiteUrl(); - let apiPath = this._config.baseApiPath + '/'; + const adminUrl = this.getAdminUrl() || this.getSiteUrl(); + let apiPath = `${this._config.baseApiPath}/`; - if (data.type && ['admin', 'content'].includes(data.type)) { - apiPath += data.type; + if (dataObj?.type && ['admin', 'content'].includes(dataObj.type)) { + apiPath += dataObj.type; } else { apiPath += this._config.defaultApiType; } - // Ensure we end with a trailing slash apiPath += '/'; - if (absolute) { + if (absoluteFlag) { urlPath = this.urlJoin(adminUrl, apiPath); } else { urlPath = apiPath; } - } else if (_.isString(context) && _.indexOf(_.keys(knownPaths), context) !== -1) { - // trying to create a url for a named path - urlPath = knownPaths[context]; + } else if (typeof context === 'string' && Object.prototype.hasOwnProperty.call(KNOWN_PATHS, context)) { + urlPath = KNOWN_PATHS[context]; } - // This url already has a protocol so is likely an external url to be returned - // or it is an alternative scheme, protocol-less, or an anchor-only path - if (urlPath && (urlPath.indexOf('://') !== -1 || urlPath.match(/^(\/\/|#|[a-zA-Z0-9-]+:)/))) { + const hasProtocol = urlPath.includes('://'); + const isSpecial = /^(\/\/|#|[a-zA-Z0-9-]+:)/.test(urlPath); + if (urlPath && (hasProtocol || isSpecial)) { return urlPath; } - return this.createUrl(urlPath, absolute); + return this.createUrl(urlPath, absoluteFlag); } - redirect301(res: any, redirectUrl: string) { - res.set({'Cache-Control': 'public, max-age=' + this._config.redirectCacheMaxAge}); + redirect301(res: RedirectResponse, redirectUrl: string) { + if (this._config.redirectCacheMaxAge !== null) { + res.set({ + 'Cache-Control': `public, max-age=${this._config.redirectCacheMaxAge}` + }); + } + return res.redirect(301, redirectUrl); } - redirectToAdmin(status: number, res: any, adminPath: string) { - let redirectUrl = this.urlJoin(this.urlFor('admin', true), adminPath, '/'); + redirectToAdmin(status: number, res: RedirectResponse, adminPath: string) { + const redirectUrl = this.urlJoin(this.urlFor('admin', true), adminPath, '/'); if (status === 301) { return this.redirect301(res, redirectUrl); } - return res.redirect(redirectUrl); + + return typeof res.redirect === 'function' ? res.redirect(redirectUrl) : undefined; } - absoluteToRelative(url: string, options?: any) { + absoluteToRelative(url: string, options?: AbsoluteToRelativeOptionsInput): string { return utils.absoluteToRelative(url, this.getSiteUrl(), options); } - relativeToAbsolute(url: string, options?: any) { + relativeToAbsolute(url: string, options?: RelativeToAbsoluteOptionsInput): string { return utils.relativeToAbsolute(url, this.getSiteUrl(), options); } - toTransformReady(url: string, itemPath?: any, options?: any) { - if (typeof itemPath === 'object' && !options) { + toTransformReady(url: string, itemPath?: string | ToTransformReadyOptions, options?: ToTransformReadyOptions): string { + if (typeof itemPath === 'object' && itemPath !== null && !options) { options = itemPath; itemPath = null; } - return utils.toTransformReady(url, this.getSiteUrl(), itemPath, options); + return utils.toTransformReady(url, this.getSiteUrl(), itemPath ?? null, options); } - absoluteToTransformReady(url: string, options?: any) { + absoluteToTransformReady(url: string, options?: AbsoluteToTransformReadyOptionsInput): string { return utils.absoluteToTransformReady(url, this.getSiteUrl(), options); } - relativeToTransformReady(url: string, options?: any) { - return utils.relativeToTransformReady(url, this.getSiteUrl(), options); + relativeToTransformReady(url: string, options?: RelativeToTransformReadyOptionsInput): string { + const defaultOptions: RelativeToTransformReadyOptionsInput = { + staticImageUrlPrefix: this._config.staticImageUrlPrefix + }; + const mergedOptions = sanitizedMerge(defaultOptions, options ?? undefined); + return utils.relativeToTransformReady(url, this.getSiteUrl(), mergedOptions); } - transformReadyToAbsolute(url: string, options?: any) { + transformReadyToAbsolute(url: string, options?: TransformReadyReplacementOptionsInput): string { return utils.transformReadyToAbsolute(url, this.getSiteUrl(), options); } - transformReadyToRelative(url: string, options?: any) { + transformReadyToRelative(url: string, options?: TransformReadyReplacementOptionsInput): string { return utils.transformReadyToRelative(url, this.getSiteUrl(), options); } - htmlToTransformReady(html: string, itemPath?: any, options?: any) { - if (typeof itemPath === 'object' && !options) { + htmlToTransformReady(html: string, itemPath?: string | HtmlRelativeToTransformReadyOptionsInput, options?: HtmlRelativeToTransformReadyOptionsInput): string { + if (typeof itemPath === 'object' && itemPath !== null && !options) { options = itemPath; itemPath = null; } - return utils.htmlToTransformReady(html, this.getSiteUrl(), itemPath, options); - } - - /** - * Convert relative URLs in html into absolute URLs - * @param {string} html - * @param {string} itemPath (path of current context) - * @param {Object} options - * @returns {object} htmlContent - * @description Takes html, blog url and item path and converts relative url into - * absolute urls. Returns an object. The html string can be accessed by calling `html()` on - * the variable that takes the result of this function - */ - htmlRelativeToAbsolute(html: string, itemPath?: any, options?: any) { - if (typeof itemPath === 'object' && !options) { + return utils.htmlToTransformReady(html, this.getSiteUrl(), itemPath ?? null, options); + } + + htmlRelativeToAbsolute(html: string, itemPath?: string | HtmlRelativeToAbsoluteOptionsInput, options?: HtmlRelativeToAbsoluteOptionsInput): string { + if (typeof itemPath === 'object' && itemPath !== null && !options) { options = itemPath; itemPath = null; } @@ -286,12 +324,12 @@ module.exports = class UrlUtils { assetsOnly: false, staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options || {}); - return utils.htmlRelativeToAbsolute(html, this.getSiteUrl(), itemPath, _options); + const mergedOptions = sanitizedMerge(defaultOptions, options ?? undefined); + return utils.htmlRelativeToAbsolute(html, this.getSiteUrl(), itemPath ?? null, mergedOptions); } - htmlRelativeToTransformReady(html: string, itemPath?: any, options?: any) { - if (typeof itemPath === 'object' && !options) { + htmlRelativeToTransformReady(html: string, itemPath?: string | HtmlRelativeToTransformReadyOptionsInput, options?: HtmlRelativeToTransformReadyOptionsInput): string { + if (typeof itemPath === 'object' && itemPath !== null && !options) { options = itemPath; itemPath = null; } @@ -299,38 +337,38 @@ module.exports = class UrlUtils { assetsOnly: false, staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options || {}); - return utils.htmlRelativeToTransformReady(html, this.getSiteUrl(), itemPath, _options); + const mergedOptions = sanitizedMerge(defaultOptions, options ?? undefined); + return utils.htmlRelativeToTransformReady(html, this.getSiteUrl(), itemPath ?? null, mergedOptions); } - htmlAbsoluteToRelative(html: string, options: any = {}) { + htmlAbsoluteToRelative(html: string, options: HtmlAbsoluteToRelativeOptionsInput = {}): string { const defaultOptions = { assetsOnly: false, staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options); - return utils.htmlAbsoluteToRelative(html, this.getSiteUrl(), _options); + const mergedOptions = sanitizedMerge(defaultOptions, options); + return utils.htmlAbsoluteToRelative(html, this.getSiteUrl(), mergedOptions); } - htmlAbsoluteToTransformReady(html: string, options: any = {}) { + htmlAbsoluteToTransformReady(html: string, options: HtmlAbsoluteToTransformReadyOptionsInput = {}): string { const defaultOptions = { assetsOnly: false, staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options); - return utils.htmlAbsoluteToTransformReady(html, this.getSiteUrl(), _options); + const mergedOptions = sanitizedMerge(defaultOptions, options); + return utils.htmlAbsoluteToTransformReady(html, this.getSiteUrl(), mergedOptions); } - markdownToTransformReady(markdown: string, itemPath?: any, options?: any) { - if (typeof itemPath === 'object' && !options) { + markdownToTransformReady(markdown: string, itemPath?: string | MarkdownToTransformReadyOptions, options?: MarkdownToTransformReadyOptions): string { + if (typeof itemPath === 'object' && itemPath !== null && !options) { options = itemPath; itemPath = null; } - return utils.markdownToTransformReady(markdown, this.getSiteUrl(), itemPath, options); + return utils.markdownToTransformReady(markdown, this.getSiteUrl(), itemPath ?? null, options); } - markdownRelativeToAbsolute(markdown: string, itemPath?: any, options?: any) { - if (typeof itemPath === 'object' && !options) { + markdownRelativeToAbsolute(markdown: string, itemPath?: string | MarkdownRelativeToAbsoluteOptionsInput, options?: MarkdownRelativeToAbsoluteOptionsInput): string { + if (typeof itemPath === 'object' && itemPath !== null && !options) { options = itemPath; itemPath = null; } @@ -338,12 +376,12 @@ module.exports = class UrlUtils { assetsOnly: false, staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options || {}); - return utils.markdownRelativeToAbsolute(markdown, this.getSiteUrl(), itemPath, _options); + const mergedOptions = sanitizedMerge(defaultOptions, options ?? undefined); + return utils.markdownRelativeToAbsolute(markdown, this.getSiteUrl(), itemPath ?? null, mergedOptions); } - markdownRelativeToTransformReady(markdown: string, itemPath?: any, options?: any) { - if (typeof itemPath === 'object' && !options) { + markdownRelativeToTransformReady(markdown: string, itemPath?: string | MarkdownRelativeToTransformReadyOptionsInput, options?: MarkdownRelativeToTransformReadyOptionsInput): string { + if (typeof itemPath === 'object' && itemPath !== null && !options) { options = itemPath; itemPath = null; } @@ -351,169 +389,190 @@ module.exports = class UrlUtils { assetsOnly: false, staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options || {}); - return utils.markdownRelativeToTransformReady(markdown, this.getSiteUrl(), itemPath, _options); + const mergedOptions = sanitizedMerge(defaultOptions, options ?? undefined); + return utils.markdownRelativeToTransformReady(markdown, this.getSiteUrl(), itemPath ?? null, mergedOptions); } - markdownAbsoluteToRelative(markdown: string, options: any = {}) { + markdownAbsoluteToRelative(markdown: string, options: MarkdownAbsoluteToRelativeOptionsInput = {}): string { const defaultOptions = { assetsOnly: false, staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options); - return utils.markdownAbsoluteToRelative(markdown, this.getSiteUrl(), _options); + const mergedOptions = sanitizedMerge(defaultOptions, options); + return utils.markdownAbsoluteToRelative(markdown, this.getSiteUrl(), mergedOptions); } - markdownAbsoluteToTransformReady(markdown: string, options: any) { + markdownAbsoluteToTransformReady(markdown: string, options: MarkdownAbsoluteToTransformReadyOptionsInput = {}): string { const defaultOptions = { assetsOnly: false, staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options); - return utils.markdownAbsoluteToTransformReady(markdown, this.getSiteUrl(), _options); + const mergedOptions = sanitizedMerge(defaultOptions, options); + return utils.markdownAbsoluteToTransformReady(markdown, this.getSiteUrl(), mergedOptions); } - mobiledocToTransformReady(serializedMobiledoc: string, itemPath?: any, options?: any) { - if (typeof itemPath === 'object' && !options) { - options = itemPath; - itemPath = null; + mobiledocToTransformReady(serializedMobiledoc: string, itemPath?: string | MobiledocToTransformReadyOptions, options?: MobiledocToTransformReadyOptions): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions = options; + + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; } - const defaultOptions = { - cardTransformers: this._config.cardTransformers - }; - const _options = assignOptions({}, defaultOptions, options || {}); - return utils.mobiledocToTransformReady(serializedMobiledoc, this.getSiteUrl(), itemPath, _options); + const defaultOptions: MobiledocToTransformReadyOptions = {}; + if (this._config.cardTransformers) { + defaultOptions.cardTransformers = this._config.cardTransformers; + } + const mergedOptions = sanitizedMerge(defaultOptions, resolvedOptions ?? undefined); + return utils.mobiledocToTransformReady(serializedMobiledoc, this.getSiteUrl(), resolvedItemPath, mergedOptions); } - mobiledocRelativeToAbsolute(serializedMobiledoc: string, itemPath?: any, options?: any) { - if (typeof itemPath === 'object' && !options) { - options = itemPath; - itemPath = null; + mobiledocRelativeToAbsolute(serializedMobiledoc: string, itemPath?: string | MobiledocRelativeToAbsoluteOptions, options?: MobiledocRelativeToAbsoluteOptions): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions = options; + + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; } - const defaultOptions = { + const defaultOptions: MobiledocRelativeToAbsoluteOptions = { assetsOnly: false, - staticImageUrlPrefix: this._config.staticImageUrlPrefix, - cardTransformers: this._config.cardTransformers + staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options || {}); - return utils.mobiledocRelativeToAbsolute(serializedMobiledoc, this.getSiteUrl(), itemPath, _options); + if (this._config.cardTransformers) { + defaultOptions.cardTransformers = this._config.cardTransformers; + } + const mergedOptions = sanitizedMerge(defaultOptions, resolvedOptions ?? undefined); + return utils.mobiledocRelativeToAbsolute(serializedMobiledoc, this.getSiteUrl(), resolvedItemPath, mergedOptions); } - mobiledocRelativeToTransformReady(serializedMobiledoc: string, itemPath?: any, options?: any) { - if (typeof itemPath === 'object' && !options) { - options = itemPath; - itemPath = null; + mobiledocRelativeToTransformReady(serializedMobiledoc: string, itemPath?: string | MobiledocRelativeToTransformReadyOptions, options?: MobiledocRelativeToTransformReadyOptions): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions = options; + + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; } - const defaultOptions = { + const defaultOptions: MobiledocRelativeToTransformReadyOptions = { assetsOnly: false, - staticImageUrlPrefix: this._config.staticImageUrlPrefix, - cardTransformers: this._config.cardTransformers + staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options || {}); - return utils.mobiledocRelativeToTransformReady(serializedMobiledoc, this.getSiteUrl(), itemPath, _options); + if (this._config.cardTransformers) { + defaultOptions.cardTransformers = this._config.cardTransformers; + } + const mergedOptions = sanitizedMerge(defaultOptions, resolvedOptions ?? undefined); + return utils.mobiledocRelativeToTransformReady(serializedMobiledoc, this.getSiteUrl(), resolvedItemPath, mergedOptions); } - mobiledocAbsoluteToRelative(serializedMobiledoc: string, options: any = {}) { - const defaultOptions = { + mobiledocAbsoluteToRelative(serializedMobiledoc: string, options: MobiledocAbsoluteToRelativeOptions = {}): string { + const defaultOptions: MobiledocAbsoluteToRelativeOptions = { assetsOnly: false, - staticImageUrlPrefix: this._config.staticImageUrlPrefix, - cardTransformers: this._config.cardTransformers + staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options); - return utils.mobiledocAbsoluteToRelative(serializedMobiledoc, this.getSiteUrl(), _options); + if (this._config.cardTransformers) { + defaultOptions.cardTransformers = this._config.cardTransformers; + } + const mergedOptions = sanitizedMerge(defaultOptions, options); + return utils.mobiledocAbsoluteToRelative(serializedMobiledoc, this.getSiteUrl(), mergedOptions); } - mobiledocAbsoluteToTransformReady(serializedMobiledoc: string, options: any = {}) { - const defaultOptions = { + mobiledocAbsoluteToTransformReady(serializedMobiledoc: string, options: MobiledocAbsoluteToTransformReadyOptions = {}): string { + const defaultOptions: MobiledocAbsoluteToTransformReadyOptions = { assetsOnly: false, - staticImageUrlPrefix: this._config.staticImageUrlPrefix, - cardTransformers: this._config.cardTransformers + staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options); - return utils.mobiledocAbsoluteToTransformReady(serializedMobiledoc, this.getSiteUrl(), _options); + if (this._config.cardTransformers) { + defaultOptions.cardTransformers = this._config.cardTransformers; + } + const mergedOptions = sanitizedMerge(defaultOptions, options); + return utils.mobiledocAbsoluteToTransformReady(serializedMobiledoc, this.getSiteUrl(), mergedOptions); } - lexicalToTransformReady(serializedLexical: string, itemPath?: any, options?: any) { - if (typeof itemPath === 'object' && !options) { - options = itemPath; - itemPath = null; + lexicalToTransformReady(serializedLexical: string, itemPath?: string | LexicalToTransformReadyOptions, options?: LexicalToTransformReadyOptions): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions = options; + + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; } - const defaultOptions = { - cardTransformers: this._config.cardTransformers - }; - const _options = assignOptions({}, defaultOptions, options || {}); - return utils.lexicalToTransformReady(serializedLexical, this.getSiteUrl(), itemPath, _options); + const defaultOptions: LexicalToTransformReadyOptions = {}; + if (this._config.cardTransformers) { + defaultOptions.cardTransformers = this._config.cardTransformers; + } + const mergedOptions = sanitizedMerge(defaultOptions, resolvedOptions ?? undefined); + return utils.lexicalToTransformReady(serializedLexical, this.getSiteUrl(), resolvedItemPath, mergedOptions); } - lexicalRelativeToAbsolute(serializedLexical: string, itemPath?: any, options?: any) { - if (typeof itemPath === 'object' && !options) { - options = itemPath; - itemPath = null; + lexicalRelativeToAbsolute(serializedLexical: string, itemPath?: string | LexicalRelativeToAbsoluteOptions, options?: LexicalRelativeToAbsoluteOptions): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions = options; + + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; } - const defaultOptions = { + const defaultOptions: LexicalRelativeToAbsoluteOptions = { assetsOnly: false, - staticImageUrlPrefix: this._config.staticImageUrlPrefix, - cardTransformers: this._config.cardTransformers + staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options || {}); - return utils.lexicalRelativeToAbsolute(serializedLexical, this.getSiteUrl(), itemPath, _options); + if (this._config.cardTransformers) { + defaultOptions.cardTransformers = this._config.cardTransformers; + } + const mergedOptions = sanitizedMerge(defaultOptions, resolvedOptions ?? undefined); + return utils.lexicalRelativeToAbsolute(serializedLexical, this.getSiteUrl(), resolvedItemPath, mergedOptions); } - lexicalRelativeToTransformReady(serializedLexical: string, itemPath?: any, options?: any) { - if (typeof itemPath === 'object' && !options) { - options = itemPath; - itemPath = null; + lexicalRelativeToTransformReady(serializedLexical: string, itemPath?: string | LexicalRelativeToTransformReadyOptions, options?: LexicalRelativeToTransformReadyOptions): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions = options; + + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; } - const defaultOptions = { + const defaultOptions: LexicalRelativeToTransformReadyOptions = { assetsOnly: false, - staticImageUrlPrefix: this._config.staticImageUrlPrefix, - cardTransformers: this._config.cardTransformers + staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options || {}); - return utils.lexicalRelativeToTransformReady(serializedLexical, this.getSiteUrl(), itemPath, _options); + if (this._config.cardTransformers) { + defaultOptions.cardTransformers = this._config.cardTransformers; + } + const mergedOptions = sanitizedMerge(defaultOptions, resolvedOptions ?? undefined); + return utils.lexicalRelativeToTransformReady(serializedLexical, this.getSiteUrl(), resolvedItemPath, mergedOptions); } - lexicalAbsoluteToRelative(serializedLexical: string, options: any = {}) { - const defaultOptions = { + lexicalAbsoluteToRelative(serializedLexical: string, options: LexicalAbsoluteToRelativeOptions = {}): string { + const defaultOptions: LexicalAbsoluteToRelativeOptions = { assetsOnly: false, - staticImageUrlPrefix: this._config.staticImageUrlPrefix, - cardTransformers: this._config.cardTransformers + staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options); - return utils.lexicalAbsoluteToRelative(serializedLexical, this.getSiteUrl(), _options); + if (this._config.cardTransformers) { + defaultOptions.cardTransformers = this._config.cardTransformers; + } + const mergedOptions = sanitizedMerge(defaultOptions, options); + return utils.lexicalAbsoluteToRelative(serializedLexical, this.getSiteUrl(), mergedOptions); } - lexicalAbsoluteToTransformReady(serializedLexical: string, options: any = {}) { - const defaultOptions = { + lexicalAbsoluteToTransformReady(serializedLexical: string, options: LexicalAbsoluteToTransformReadyOptions = {}): string { + const defaultOptions: LexicalAbsoluteToTransformReadyOptions = { assetsOnly: false, - staticImageUrlPrefix: this._config.staticImageUrlPrefix, - cardTransformers: this._config.cardTransformers + staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options); - return utils.lexicalAbsoluteToTransformReady(serializedLexical, this.getSiteUrl(), _options); + if (this._config.cardTransformers) { + defaultOptions.cardTransformers = this._config.cardTransformers; + } + const mergedOptions = sanitizedMerge(defaultOptions, options); + return utils.lexicalAbsoluteToTransformReady(serializedLexical, this.getSiteUrl(), mergedOptions); } - plaintextToTransformReady(plaintext: string, options: any = {}) { - const defaultOptions = { + plaintextToTransformReady(plaintext: string, options: PlaintextToTransformReadyOptions = {}): string { + const defaultOptions: PlaintextToTransformReadyOptions = { staticImageUrlPrefix: this._config.staticImageUrlPrefix }; - const _options = assignOptions({}, defaultOptions, options); - return utils.plaintextToTransformReady(plaintext, this.getSiteUrl(), _options); + const mergedOptions = sanitizedMerge(defaultOptions, options); + return utils.plaintextToTransformReady(plaintext, this.getSiteUrl(), mergedOptions); } - /** - * Return whether the provided URL is part of the site (checks if same domain and within subdirectory) - * @param {URL} url - * @param {string} [context] describing the context for which you need to check a url - * @returns {boolean} - */ - isSiteUrl(url: URL, context: string = 'home') { + isSiteUrl(url: URL, context: string = 'home'): boolean { const siteUrl = new URL(this.urlFor(context, true)); if (siteUrl.host === url.host) { - if (url.pathname.startsWith(siteUrl.pathname)) { - return true; - } - return false; + return url.pathname.startsWith(siteUrl.pathname); } return false; } @@ -530,21 +589,13 @@ module.exports = class UrlUtils { return utils.deduplicateDoubleSlashes; } - /** - * If you request **any** image in Ghost, it get's served via - * http://your-blog.com/content/images/2017/01/02/author.png - * - * /content/images/ is a static prefix for serving images! - * - * But internally the image is located for example in your custom content path: - * my-content/another-dir/images/2017/01/02/author.png - */ - get STATIC_IMAGE_URL_PREFIX() { + get STATIC_IMAGE_URL_PREFIX(): string { return this._config.staticImageUrlPrefix; } - // expose underlying functions to ease testing get _utils() { return utils; } -}; +} + +export type {UrlUtilsOptions, UrlUtilsConfig}; diff --git a/packages/url-utils/src/index.ts b/packages/url-utils/src/index.ts index a4cf14494..15c7184fd 100644 --- a/packages/url-utils/src/index.ts +++ b/packages/url-utils/src/index.ts @@ -1,2 +1,6 @@ export {}; -module.exports = require('./UrlUtils'); +import UrlUtils from './UrlUtils'; + +export default UrlUtils; +export {UrlUtils}; +export * from './UrlUtils'; diff --git a/packages/url-utils/src/utils/absolute-to-relative.ts b/packages/url-utils/src/utils/absolute-to-relative.ts index 39dee3418..320af4968 100644 --- a/packages/url-utils/src/utils/absolute-to-relative.ts +++ b/packages/url-utils/src/utils/absolute-to-relative.ts @@ -1,7 +1,14 @@ -export {}; -// require the whatwg compatible URL library (same behaviour in node and browser) -const {URL} = require('url'); -const stripSubdirectoryFromPath = require('./strip-subdirectory-from-path'); +import {URL} from 'url'; +import stripSubdirectoryFromPath from './strip-subdirectory-from-path'; + +export interface AbsoluteToRelativeOptions { + ignoreProtocol: boolean; + withoutSubdirectory: boolean; + assetsOnly: boolean; + staticImageUrlPrefix: string; +} + +export type AbsoluteToRelativeOptionsInput = Partial; /** * Convert an absolute URL to a root-relative path if it matches the supplied root domain. @@ -13,14 +20,17 @@ const stripSubdirectoryFromPath = require('./strip-subdirectory-from-path'); * @param {boolean} [options.withoutSubdirectory=false] Strip the root subdirectory from the returned path * @returns {string} The passed-in url or a relative path */ -const absoluteToRelative = function absoluteToRelative(url, rootUrl, _options = {}) { - const defaultOptions = { +const absoluteToRelative = function absoluteToRelative(url: string, rootUrl?: string, _options: AbsoluteToRelativeOptionsInput = {}): string { + const defaultOptions: AbsoluteToRelativeOptions = { ignoreProtocol: true, withoutSubdirectory: false, assetsOnly: false, staticImageUrlPrefix: 'content/images' }; - const options = Object.assign({}, defaultOptions, _options); + const options: AbsoluteToRelativeOptions = { + ...defaultOptions, + ..._options + }; if (options.assetsOnly) { const staticImageUrlPrefixRegex = new RegExp(options.staticImageUrlPrefix); @@ -44,6 +54,10 @@ const absoluteToRelative = function absoluteToRelative(url, rootUrl, _options = return url; } + if (!parsedRoot) { + return url; + } + const matchesHost = parsedUrl.host === parsedRoot.host; const matchesProtocol = parsedUrl.protocol === parsedRoot.protocol; const matchesPath = parsedUrl.pathname.indexOf(parsedRoot.pathname) === 0; @@ -61,4 +75,4 @@ const absoluteToRelative = function absoluteToRelative(url, rootUrl, _options = return url; }; -module.exports = absoluteToRelative; +export default absoluteToRelative; diff --git a/packages/url-utils/src/utils/absolute-to-transform-ready.ts b/packages/url-utils/src/utils/absolute-to-transform-ready.ts index fa7fe95e8..ad55416f2 100644 --- a/packages/url-utils/src/utils/absolute-to-transform-ready.ts +++ b/packages/url-utils/src/utils/absolute-to-transform-ready.ts @@ -1,12 +1,27 @@ -export {}; -const absoluteToRelative = require('./absolute-to-relative'); +import absoluteToRelative, {type AbsoluteToRelativeOptionsInput} from './absolute-to-relative'; +import type {TransformReadyReplacementOptions, TransformReadyReplacementOptionsInput} from './types'; -const absoluteToTransformReady = function (url, root, _options) { - const defaultOptions = { +export interface AbsoluteToTransformReadyOptions extends TransformReadyReplacementOptions { + withoutSubdirectory: boolean; + ignoreProtocol: boolean; + assetsOnly: boolean; + staticImageUrlPrefix: string; +} + +export type AbsoluteToTransformReadyOptionsInput = Partial; + +const absoluteToTransformReady = function (url: string, root: string, _options?: AbsoluteToTransformReadyOptionsInput): string { + const defaultOptions: AbsoluteToTransformReadyOptions = { replacementStr: '__GHOST_URL__', - withoutSubdirectory: true + withoutSubdirectory: true, + ignoreProtocol: true, + assetsOnly: false, + staticImageUrlPrefix: 'content/images' + }; + const options: AbsoluteToTransformReadyOptions = { + ...defaultOptions, + ..._options }; - const options = Object.assign({}, defaultOptions, _options); // return relative urls as-is try { @@ -21,7 +36,12 @@ const absoluteToTransformReady = function (url, root, _options) { // convert to relative with stripped subdir // always returns root-relative starting with forward slash - const relativeUrl = absoluteToRelative(url, root, options); + const relativeUrl = absoluteToRelative(url, root, { + ignoreProtocol: options.ignoreProtocol, + withoutSubdirectory: options.withoutSubdirectory, + assetsOnly: options.assetsOnly, + staticImageUrlPrefix: options.staticImageUrlPrefix + } satisfies AbsoluteToRelativeOptionsInput); // return still absolute urls as-is (eg. external site, mailto, etc) try { @@ -37,4 +57,4 @@ const absoluteToTransformReady = function (url, root, _options) { return `${options.replacementStr}${relativeUrl}`; }; -module.exports = absoluteToTransformReady; +export default absoluteToTransformReady; diff --git a/packages/url-utils/src/utils/deduplicate-double-slashes.ts b/packages/url-utils/src/utils/deduplicate-double-slashes.ts index da7bf97dc..afbcc7fdd 100644 --- a/packages/url-utils/src/utils/deduplicate-double-slashes.ts +++ b/packages/url-utils/src/utils/deduplicate-double-slashes.ts @@ -1,6 +1,5 @@ -export {}; -function deduplicateDoubleSlashes(url) { +function deduplicateDoubleSlashes(url: string): string { return url.replace(/\/\//g, '/'); } -module.exports = deduplicateDoubleSlashes; +export default deduplicateDoubleSlashes; diff --git a/packages/url-utils/src/utils/deduplicate-subdirectory.ts b/packages/url-utils/src/utils/deduplicate-subdirectory.ts index 01b25181a..c6b9c6e84 100644 --- a/packages/url-utils/src/utils/deduplicate-subdirectory.ts +++ b/packages/url-utils/src/utils/deduplicate-subdirectory.ts @@ -1,5 +1,4 @@ -export {}; -const {URL} = require('url'); +import {URL} from 'url'; /** * Remove duplicated directories from the start of a path or url's path @@ -8,7 +7,7 @@ const {URL} = require('url'); * @param {string} rootUrl Root URL with an optional subdirectory * @returns {string} URL or pathname with any duplicated subdirectory removed */ -const deduplicateSubdirectory = function deduplicateSubdirectory(url, rootUrl) { +const deduplicateSubdirectory = function deduplicateSubdirectory(url: string, rootUrl: string): string { // force root url to always have a trailing-slash for consistent behaviour if (!rootUrl.endsWith('/')) { rootUrl = `${rootUrl}/`; @@ -29,4 +28,4 @@ const deduplicateSubdirectory = function deduplicateSubdirectory(url, rootUrl) { return url.replace(subdirRegex, `$1${subdir}/`); }; -module.exports = deduplicateSubdirectory; +export default deduplicateSubdirectory; diff --git a/packages/url-utils/src/utils/html-absolute-to-relative.ts b/packages/url-utils/src/utils/html-absolute-to-relative.ts index b78965d98..835e19e97 100644 --- a/packages/url-utils/src/utils/html-absolute-to-relative.ts +++ b/packages/url-utils/src/utils/html-absolute-to-relative.ts @@ -1,21 +1,30 @@ -export {}; -const htmlTransform = require('./html-transform'); -const absoluteToRelative = require('./absolute-to-relative'); +import absoluteToRelative, {type AbsoluteToRelativeOptionsInput} from './absolute-to-relative'; +import htmlTransform from './html-transform'; +import type {HtmlTransformOptions, HtmlTransformOptionsInput} from './types'; -function htmlAbsoluteToRelative(html = '', siteUrl, _options) { - const defaultOptions = {assetsOnly: false, ignoreProtocol: true}; - const options = Object.assign({}, defaultOptions, _options || {}); +export interface HtmlAbsoluteToRelativeOptions extends HtmlTransformOptions { + ignoreProtocol: boolean; +} + +export type HtmlAbsoluteToRelativeOptionsInput = HtmlTransformOptionsInput & AbsoluteToRelativeOptionsInput & Partial>; + +function htmlAbsoluteToRelative(html: string = '', siteUrl: string, _options?: HtmlAbsoluteToRelativeOptionsInput): string { + const defaultOptions: HtmlAbsoluteToRelativeOptions = {assetsOnly: false, secure: false, ignoreProtocol: true}; + const options: HtmlAbsoluteToRelativeOptions = { + ...defaultOptions, + ..._options + }; // exit early and avoid parsing if the content does not contain the siteUrl options.earlyExitMatchStr = options.ignoreProtocol ? siteUrl.replace(/http:|https:/, '') : siteUrl; options.earlyExitMatchStr = options.earlyExitMatchStr.replace(/\/$/, ''); // need to ignore itemPath because absoluteToRelative doesn't take that option - const transformFunction = function (_url, _siteUrl, _itemPath, __options) { + const transformFunction = function (_url: string, _siteUrl: string, _itemPath: string | null, __options?: AbsoluteToRelativeOptionsInput): string { return absoluteToRelative(_url, _siteUrl, __options); }; return htmlTransform(html, siteUrl, transformFunction, '', options); } -module.exports = htmlAbsoluteToRelative; +export default htmlAbsoluteToRelative; diff --git a/packages/url-utils/src/utils/html-absolute-to-transform-ready.ts b/packages/url-utils/src/utils/html-absolute-to-transform-ready.ts index 0c66dbed5..895eb81d1 100644 --- a/packages/url-utils/src/utils/html-absolute-to-transform-ready.ts +++ b/packages/url-utils/src/utils/html-absolute-to-transform-ready.ts @@ -1,21 +1,30 @@ -export {}; -const htmlTransform = require('./html-transform'); -const absoluteToTransformReady = require('./absolute-to-transform-ready'); +import absoluteToTransformReady, {type AbsoluteToTransformReadyOptionsInput} from './absolute-to-transform-ready'; +import htmlTransform from './html-transform'; +import type {HtmlTransformOptions, HtmlTransformOptionsInput} from './types'; -const htmlAbsoluteToTransformReady = function (html = '', siteUrl, _options) { - const defaultOptions = {assetsOnly: false, ignoreProtocol: true}; - const options = Object.assign({}, defaultOptions, _options || {}); +export interface HtmlAbsoluteToTransformReadyOptions extends HtmlTransformOptions { + ignoreProtocol: boolean; +} + +export type HtmlAbsoluteToTransformReadyOptionsInput = HtmlTransformOptionsInput & AbsoluteToTransformReadyOptionsInput & Partial>; + +const htmlAbsoluteToTransformReady = function (html: string = '', siteUrl: string, _options?: HtmlAbsoluteToTransformReadyOptionsInput): string { + const defaultOptions: HtmlAbsoluteToTransformReadyOptions = {assetsOnly: false, secure: false, ignoreProtocol: true}; + const options: HtmlAbsoluteToTransformReadyOptions = { + ...defaultOptions, + ..._options + }; // exit early and avoid parsing if the content does not contain the siteUrl options.earlyExitMatchStr = options.ignoreProtocol ? siteUrl.replace(/http:|https:/, '') : siteUrl; options.earlyExitMatchStr = options.earlyExitMatchStr.replace(/\/$/, ''); - // need to ignore itemPath because absoluteToRelative doesn't take that option - const transformFunction = function (_url, _siteUrl, _itemPath, __options) { + // need to ignore itemPath because absoluteToTransformReady doesn't take that option + const transformFunction = function (_url: string, _siteUrl: string, _itemPath: string | null, __options?: AbsoluteToTransformReadyOptionsInput): string { return absoluteToTransformReady(_url, _siteUrl, __options); }; return htmlTransform(html, siteUrl, transformFunction, '', options); }; -module.exports = htmlAbsoluteToTransformReady; +export default htmlAbsoluteToTransformReady; diff --git a/packages/url-utils/src/utils/html-relative-to-absolute.ts b/packages/url-utils/src/utils/html-relative-to-absolute.ts index 53bc4508b..1c4ea1c1a 100644 --- a/packages/url-utils/src/utils/html-relative-to-absolute.ts +++ b/packages/url-utils/src/utils/html-relative-to-absolute.ts @@ -1,18 +1,36 @@ -export {}; -const htmlTransform = require('./html-transform'); -const relativeToAbsolute = require('./relative-to-absolute'); +import htmlTransform from './html-transform'; +import type {HtmlTransformOptions, HtmlTransformOptionsInput} from './types'; +import relativeToAbsolute, {type RelativeToAbsoluteOptionsInput} from './relative-to-absolute'; -function htmlRelativeToAbsolute(html = '', siteUrl, itemPath, _options) { - const defaultOptions = {assetsOnly: false, secure: false}; - const options = Object.assign({}, defaultOptions, _options || {}); +export type HtmlRelativeToAbsoluteOptions = HtmlTransformOptions; +export type HtmlRelativeToAbsoluteOptionsInput = HtmlTransformOptionsInput & RelativeToAbsoluteOptionsInput; + +function htmlRelativeToAbsolute( + html: string = '', + siteUrl: string, + itemPath?: string | HtmlRelativeToAbsoluteOptionsInput | null, + _options?: HtmlRelativeToAbsoluteOptionsInput +): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions: HtmlRelativeToAbsoluteOptionsInput | undefined = _options; + + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; + } + + const defaultOptions: HtmlRelativeToAbsoluteOptions = {assetsOnly: false, secure: false}; + const options: HtmlRelativeToAbsoluteOptions = { + ...defaultOptions, + ...resolvedOptions + }; // exit early and avoid parsing if the content does not contain an attribute we might transform options.earlyExitMatchStr = 'href=|src=|srcset='; if (options.assetsOnly) { - options.earlyExitMatchStr = options.staticImageUrlPrefix; + options.earlyExitMatchStr = options.staticImageUrlPrefix ?? 'content/images'; } - return htmlTransform(html, siteUrl, relativeToAbsolute, itemPath, options); + return htmlTransform(html, siteUrl, relativeToAbsolute, resolvedItemPath, options); } -module.exports = htmlRelativeToAbsolute; +export default htmlRelativeToAbsolute; diff --git a/packages/url-utils/src/utils/html-relative-to-transform-ready.ts b/packages/url-utils/src/utils/html-relative-to-transform-ready.ts index 75847e21d..dd25f9aa4 100644 --- a/packages/url-utils/src/utils/html-relative-to-transform-ready.ts +++ b/packages/url-utils/src/utils/html-relative-to-transform-ready.ts @@ -1,29 +1,40 @@ -export {}; -const htmlTransform = require('./html-transform'); -const relativeToTransformReady = require('./relative-to-transform-ready'); - -const htmlRelativeToTransformReady = function (html = '', root, itemPath, _options) { - // itemPath is optional, if it's an object may be the options param instead - if (typeof itemPath === 'object' && !_options) { - _options = itemPath; - itemPath = null; +import htmlTransform from './html-transform'; +import type {HtmlTransformOptions} from './types'; +import relativeToTransformReady, {type RelativeToTransformReadyOptionsInput} from './relative-to-transform-ready'; + +export interface HtmlRelativeToTransformReadyOptions extends HtmlTransformOptions { + replacementStr: string; +} + +export type HtmlRelativeToTransformReadyOptionsInput = RelativeToTransformReadyOptionsInput & Partial; + +const htmlRelativeToTransformReady = function ( + html: string = '', + root: string, + itemPath?: string | HtmlRelativeToTransformReadyOptionsInput | null, + _options?: HtmlRelativeToTransformReadyOptionsInput +): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions: HtmlRelativeToTransformReadyOptionsInput | undefined = _options; + + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; } - const defaultOptions = { - replacementStr: '__GHOST_URL__' - }; - const overrideOptions = { - secure: false + const options: HtmlRelativeToTransformReadyOptions = { + assetsOnly: false, + secure: false, + replacementStr: '__GHOST_URL__', + ...resolvedOptions }; - const options = Object.assign({}, defaultOptions, _options, overrideOptions); // exit early and avoid parsing if the content does not contain an attribute we might transform options.earlyExitMatchStr = 'href=|src=|srcset='; if (options.assetsOnly) { - options.earlyExitMatchStr = options.staticImageUrlPrefix; + options.earlyExitMatchStr = options.staticImageUrlPrefix ?? 'content/images'; } - return htmlTransform(html, root, relativeToTransformReady, itemPath, options); + return htmlTransform(html, root, relativeToTransformReady, resolvedItemPath, options); }; -module.exports = htmlRelativeToTransformReady; +export default htmlRelativeToTransformReady; diff --git a/packages/url-utils/src/utils/html-to-transform-ready.ts b/packages/url-utils/src/utils/html-to-transform-ready.ts index 536884b22..771a3e5e0 100644 --- a/packages/url-utils/src/utils/html-to-transform-ready.ts +++ b/packages/url-utils/src/utils/html-to-transform-ready.ts @@ -1,14 +1,23 @@ -export {}; -const htmlRelativeToAbsolute = require('./html-relative-to-absolute'); -const htmlAbsoluteToTransformReady = require('./html-absolute-to-transform-ready'); - -function htmlToTransformReady(html, siteUrl, itemPath, options) { - if (typeof itemPath === 'object' && !options) { - options = itemPath; - itemPath = null; +import htmlAbsoluteToTransformReady, {type HtmlAbsoluteToTransformReadyOptionsInput} from './html-absolute-to-transform-ready'; +import htmlRelativeToAbsolute, {type HtmlRelativeToAbsoluteOptionsInput} from './html-relative-to-absolute'; + +export type HtmlToTransformReadyOptions = HtmlRelativeToAbsoluteOptionsInput & HtmlAbsoluteToTransformReadyOptionsInput; + +function htmlToTransformReady( + html: string, + siteUrl: string, + itemPath?: string | HtmlToTransformReadyOptions | null, + options?: HtmlToTransformReadyOptions +): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions: HtmlToTransformReadyOptions | undefined = options; + + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; } - const absolute = htmlRelativeToAbsolute(html, siteUrl, itemPath, options); - return htmlAbsoluteToTransformReady(absolute, siteUrl, options); + + const absolute = htmlRelativeToAbsolute(html, siteUrl, resolvedItemPath, resolvedOptions); + return htmlAbsoluteToTransformReady(absolute, siteUrl, resolvedOptions); } -module.exports = htmlToTransformReady; +export default htmlToTransformReady; diff --git a/packages/url-utils/src/utils/html-transform.ts b/packages/url-utils/src/utils/html-transform.ts index 2fb131770..86235b42b 100644 --- a/packages/url-utils/src/utils/html-transform.ts +++ b/packages/url-utils/src/utils/html-transform.ts @@ -1,20 +1,31 @@ -export {}; -let cheerio = require('cheerio'); +import * as cheerio from 'cheerio'; +import type {HtmlTransformOptions, HtmlTransformOptionsInput, UnknownRecord} from './types'; -function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +type HtmlTransformFunction = (value: string, siteUrl: string, itemPath: string | null, options: HtmlTransformOptions) => string; + +interface AttributeReplacement { + name: string; + originalValue: string; + transformedValue?: string; + skip?: boolean; +} + +type ReplacementMap = Record; + +function escapeRegExp(value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } -function extractSrcsetUrls(srcset = '') { +function extractSrcsetUrls(srcset: string = ''): string[] { return srcset.split(',').map((part) => { return part.trim().split(/\s+/)[0]; }); } -function extractStyleUrls(style = '') { - const urls = []; +function extractStyleUrls(style: string = ''): string[] { + const urls: string[] = []; const regex = /url\(['|"]([^)]+)['|"]\)/g; - let match; + let match: RegExpExecArray | null; while ((match = regex.exec(style)) !== null) { urls.push(match[1]); @@ -23,9 +34,18 @@ function extractStyleUrls(style = '') { return urls; } -function htmlTransform(html = '', siteUrl, transformFunction, itemPath, _options) { - const defaultOptions = {assetsOnly: false, secure: false}; - const options: any = Object.assign({}, defaultOptions, _options || {}); +function htmlTransform( + html: string = '', + siteUrl: string, + transformFunction: HtmlTransformFunction, + itemPath: string | null = null, + _options?: HtmlTransformOptionsInput | UnknownRecord +): string { + const defaultOptions: HtmlTransformOptions = {assetsOnly: false, secure: false}; + const options: HtmlTransformOptions = { + ...defaultOptions, + ...(_options as HtmlTransformOptionsInput | undefined) + }; if (!html || (options.earlyExitMatchStr && !html.match(new RegExp(options.earlyExitMatchStr)))) { return html; @@ -43,9 +63,9 @@ function htmlTransform(html = '', siteUrl, transformFunction, itemPath, _options // {name: 'href', originalValue: '/test', absoluteValue: '.../test'}, // ] // } - const replacements: Record = {}; + const replacements: ReplacementMap = {}; - function addReplacement(replacement) { + function addReplacement(replacement: AttributeReplacement): void { const key = `${replacement.name}="${replacement.originalValue}"`; if (!replacements[key]) { @@ -62,14 +82,18 @@ function htmlTransform(html = '', siteUrl, transformFunction, itemPath, _options if (el.name === 'stream' || htmlContent(el).closest('code').length) { addReplacement({ name: attributeName, - originalValue: htmlContent(el).attr(attributeName), + originalValue: htmlContent(el).attr(attributeName) ?? '', skip: true }); return; } - el = htmlContent(el); - const originalValue = el.attr(attributeName); + const element = htmlContent(el); + const originalValue = element.attr(attributeName); + + if (!originalValue) { + return; + } if (attributeName === 'srcset' || attributeName === 'style') { let urls; @@ -112,7 +136,7 @@ function htmlTransform(html = '', siteUrl, transformFunction, itemPath, _options // Loop over all replacements and use a regex to replace urls in the original html string. // Allows indentation and formatting to be kept compared to using DOM manipulation and render - const replacementEntries = Object.entries(replacements) as Array<[string, any[]]>; + const replacementEntries = Object.entries(replacements) as Array<[string, AttributeReplacement[]]>; for (const [, attrs] of replacementEntries) { let skipCount = 0; @@ -143,4 +167,4 @@ function htmlTransform(html = '', siteUrl, transformFunction, itemPath, _options return html; } -module.exports = htmlTransform; +export default htmlTransform; diff --git a/packages/url-utils/src/utils/index.ts b/packages/url-utils/src/utils/index.ts index 15d3905d6..593fef163 100644 --- a/packages/url-utils/src/utils/index.ts +++ b/packages/url-utils/src/utils/index.ts @@ -1,39 +1,116 @@ -export {}; -module.exports = { - absoluteToRelative: require('./absolute-to-relative'), - absoluteToTransformReady: require('./absolute-to-transform-ready'), - deduplicateDoubleSlashes: require('./deduplicate-double-slashes'), - deduplicateSubdirectory: require('./deduplicate-subdirectory'), - htmlAbsoluteToRelative: require('./html-absolute-to-relative'), - htmlRelativeToAbsolute: require('./html-relative-to-absolute'), - htmlAbsoluteToTransformReady: require('./html-absolute-to-transform-ready'), - htmlRelativeToTransformReady: require('./html-relative-to-transform-ready'), - htmlToTransformReady: require('./html-to-transform-ready'), - isSSL: require('./is-ssl'), - markdownAbsoluteToRelative: require('./markdown-absolute-to-relative'), - markdownRelativeToAbsolute: require('./markdown-relative-to-absolute'), - markdownAbsoluteToTransformReady: require('./markdown-absolute-to-transform-ready'), - markdownRelativeToTransformReady: require('./markdown-relative-to-transform-ready'), - markdownToTransformReady: require('./markdown-to-transform-ready'), - mobiledocAbsoluteToRelative: require('./mobiledoc-absolute-to-relative'), - mobiledocRelativeToAbsolute: require('./mobiledoc-relative-to-absolute'), - mobiledocAbsoluteToTransformReady: require('./mobiledoc-absolute-to-transform-ready'), - mobiledocRelativeToTransformReady: require('./mobiledoc-relative-to-transform-ready'), - mobiledocToTransformReady: require('./mobiledoc-to-transform-ready'), - lexicalAbsoluteToRelative: require('./lexical-absolute-to-relative'), - lexicalRelativeToAbsolute: require('./lexical-relative-to-absolute'), - lexicalAbsoluteToTransformReady: require('./lexical-absolute-to-transform-ready'), - lexicalRelativeToTransformReady: require('./lexical-relative-to-transform-ready'), - lexicalToTransformReady: require('./lexical-to-transform-ready'), - plaintextAbsoluteToTransformReady: require('./plaintext-absolute-to-transform-ready'), - plaintextRelativeToTransformReady: require('./plaintext-relative-to-transform-ready'), - plaintextToTransformReady: require('./plaintext-to-transform-ready'), - relativeToAbsolute: require('./relative-to-absolute'), - relativeToTransformReady: require('./relative-to-transform-ready'), - replacePermalink: require('./replace-permalink'), - stripSubdirectoryFromPath: require('./strip-subdirectory-from-path'), - toTransformReady: require('./to-transform-ready'), - transformReadyToAbsolute: require('./transform-ready-to-absolute'), - transformReadyToRelative: require('./transform-ready-to-relative'), - urlJoin: require('./url-join') +import absoluteToRelative from './absolute-to-relative'; +import absoluteToTransformReady from './absolute-to-transform-ready'; +import deduplicateDoubleSlashes from './deduplicate-double-slashes'; +import deduplicateSubdirectory from './deduplicate-subdirectory'; +import htmlAbsoluteToRelative from './html-absolute-to-relative'; +import htmlRelativeToAbsolute from './html-relative-to-absolute'; +import htmlAbsoluteToTransformReady from './html-absolute-to-transform-ready'; +import htmlRelativeToTransformReady from './html-relative-to-transform-ready'; +import htmlToTransformReady from './html-to-transform-ready'; +import isSSL from './is-ssl'; +import lexicalAbsoluteToRelative from './lexical-absolute-to-relative'; +import lexicalAbsoluteToTransformReady from './lexical-absolute-to-transform-ready'; +import lexicalRelativeToAbsolute from './lexical-relative-to-absolute'; +import lexicalRelativeToTransformReady from './lexical-relative-to-transform-ready'; +import lexicalToTransformReady from './lexical-to-transform-ready'; +import markdownAbsoluteToRelative from './markdown-absolute-to-relative'; +import markdownAbsoluteToTransformReady from './markdown-absolute-to-transform-ready'; +import markdownRelativeToAbsolute from './markdown-relative-to-absolute'; +import markdownRelativeToTransformReady from './markdown-relative-to-transform-ready'; +import markdownToTransformReady from './markdown-to-transform-ready'; +import mobiledocAbsoluteToRelative from './mobiledoc-absolute-to-relative'; +import mobiledocAbsoluteToTransformReady from './mobiledoc-absolute-to-transform-ready'; +import mobiledocRelativeToAbsolute from './mobiledoc-relative-to-absolute'; +import mobiledocRelativeToTransformReady from './mobiledoc-relative-to-transform-ready'; +import mobiledocToTransformReady from './mobiledoc-to-transform-ready'; +import plaintextAbsoluteToTransformReady from './plaintext-absolute-to-transform-ready'; +import plaintextRelativeToTransformReady from './plaintext-relative-to-transform-ready'; +import plaintextToTransformReady from './plaintext-to-transform-ready'; +import relativeToAbsolute from './relative-to-absolute'; +import relativeToTransformReady from './relative-to-transform-ready'; +import replacePermalink from './replace-permalink'; +import stripSubdirectoryFromPath from './strip-subdirectory-from-path'; +import toTransformReady from './to-transform-ready'; +import transformReadyToAbsolute from './transform-ready-to-absolute'; +import transformReadyToRelative from './transform-ready-to-relative'; +import urlJoin from './url-join'; + +export { + absoluteToRelative, + absoluteToTransformReady, + deduplicateDoubleSlashes, + deduplicateSubdirectory, + htmlAbsoluteToRelative, + htmlRelativeToAbsolute, + htmlAbsoluteToTransformReady, + htmlRelativeToTransformReady, + htmlToTransformReady, + isSSL, + lexicalAbsoluteToRelative, + lexicalAbsoluteToTransformReady, + lexicalRelativeToAbsolute, + lexicalRelativeToTransformReady, + lexicalToTransformReady, + markdownAbsoluteToRelative, + markdownAbsoluteToTransformReady, + markdownRelativeToAbsolute, + markdownRelativeToTransformReady, + markdownToTransformReady, + mobiledocAbsoluteToRelative, + mobiledocAbsoluteToTransformReady, + mobiledocRelativeToAbsolute, + mobiledocRelativeToTransformReady, + mobiledocToTransformReady, + plaintextAbsoluteToTransformReady, + plaintextRelativeToTransformReady, + plaintextToTransformReady, + relativeToAbsolute, + relativeToTransformReady, + replacePermalink, + stripSubdirectoryFromPath, + toTransformReady, + transformReadyToAbsolute, + transformReadyToRelative, + urlJoin }; + +const utils = { + absoluteToRelative, + absoluteToTransformReady, + deduplicateDoubleSlashes, + deduplicateSubdirectory, + htmlAbsoluteToRelative, + htmlRelativeToAbsolute, + htmlAbsoluteToTransformReady, + htmlRelativeToTransformReady, + htmlToTransformReady, + isSSL, + lexicalAbsoluteToRelative, + lexicalAbsoluteToTransformReady, + lexicalRelativeToAbsolute, + lexicalRelativeToTransformReady, + lexicalToTransformReady, + markdownAbsoluteToRelative, + markdownAbsoluteToTransformReady, + markdownRelativeToAbsolute, + markdownRelativeToTransformReady, + markdownToTransformReady, + mobiledocAbsoluteToRelative, + mobiledocAbsoluteToTransformReady, + mobiledocRelativeToAbsolute, + mobiledocRelativeToTransformReady, + mobiledocToTransformReady, + plaintextAbsoluteToTransformReady, + plaintextRelativeToTransformReady, + plaintextToTransformReady, + relativeToAbsolute, + relativeToTransformReady, + replacePermalink, + stripSubdirectoryFromPath, + toTransformReady, + transformReadyToAbsolute, + transformReadyToRelative, + urlJoin +}; + +export default utils; diff --git a/packages/url-utils/src/utils/is-ssl.ts b/packages/url-utils/src/utils/is-ssl.ts index 43f6ddde0..5feda8251 100644 --- a/packages/url-utils/src/utils/is-ssl.ts +++ b/packages/url-utils/src/utils/is-ssl.ts @@ -1,10 +1,8 @@ -export {}; -// require the whatwg compatible URL library (same behaviour in node and browser) -const {URL} = require('url'); +import {URL} from 'url'; -function isSSL(urlToParse) { +function isSSL(urlToParse: string): boolean { const {protocol} = new URL(urlToParse); return protocol === 'https:'; } -module.exports = isSSL; +export default isSSL; diff --git a/packages/url-utils/src/utils/lexical-absolute-to-relative.ts b/packages/url-utils/src/utils/lexical-absolute-to-relative.ts index 1e00e70c3..dbd83bac5 100644 --- a/packages/url-utils/src/utils/lexical-absolute-to-relative.ts +++ b/packages/url-utils/src/utils/lexical-absolute-to-relative.ts @@ -1,17 +1,32 @@ -export {}; -const absoluteToRelative = require('./absolute-to-relative'); -const lexicalTransform = require('./lexical-transform'); +import absoluteToRelative, {type AbsoluteToRelativeOptionsInput} from './absolute-to-relative'; +import lexicalTransform from './lexical-transform'; +import type {LexicalTransformOptionsInput} from './types'; -function lexicalAbsoluteToRelative(serializedLexical, siteUrl, _options = {}) { - const defaultOptions = {assetsOnly: false, secure: false, nodes: [], transformMap: {}}; - const overrideOptions = {siteUrl, transformType: 'absoluteToRelative'}; - const options = Object.assign({}, defaultOptions, _options, overrideOptions); +export type LexicalAbsoluteToRelativeOptions = LexicalTransformOptionsInput & AbsoluteToRelativeOptionsInput; - const transformFunction = function (_url, _siteUrl, _itemPath, __options) { +function lexicalAbsoluteToRelative( + serializedLexical: string, + siteUrl: string, + _options: LexicalAbsoluteToRelativeOptions = {} +): string { + const overrideOptions: LexicalAbsoluteToRelativeOptions = { + siteUrl, + transformType: 'absoluteToRelative' + }; + const options: LexicalAbsoluteToRelativeOptions = { + assetsOnly: false, + secure: false, + nodes: [], + transformMap: {}, + ..._options, + ...overrideOptions + }; + + const transformFunction = function (_url: string, _siteUrl: string, _itemPath: string | null, __options?: AbsoluteToRelativeOptionsInput): string { return absoluteToRelative(_url, _siteUrl, __options); }; - return lexicalTransform(serializedLexical, siteUrl, transformFunction, '', options); + return lexicalTransform(serializedLexical, siteUrl, transformFunction, null, options); } -module.exports = lexicalAbsoluteToRelative; +export default lexicalAbsoluteToRelative; diff --git a/packages/url-utils/src/utils/lexical-absolute-to-transform-ready.ts b/packages/url-utils/src/utils/lexical-absolute-to-transform-ready.ts index 14f9dc494..3fcb76411 100644 --- a/packages/url-utils/src/utils/lexical-absolute-to-transform-ready.ts +++ b/packages/url-utils/src/utils/lexical-absolute-to-transform-ready.ts @@ -1,17 +1,32 @@ -export {}; -const absoluteToTransformReady = require('./absolute-to-transform-ready'); -const lexicalTransform = require('./lexical-transform'); +import absoluteToTransformReady, {type AbsoluteToTransformReadyOptionsInput} from './absolute-to-transform-ready'; +import lexicalTransform from './lexical-transform'; +import type {LexicalTransformOptionsInput} from './types'; -function lexicalAbsoluteToRelative(serializedLexical, siteUrl, _options = {}) { - const defaultOptions = {assetsOnly: false, secure: false, nodes: [], transformMap: {}}; - const overrideOptions = {siteUrl, transformType: 'toTransformReady'}; - const options = Object.assign({}, defaultOptions, _options, overrideOptions); +export type LexicalAbsoluteToTransformReadyOptions = LexicalTransformOptionsInput & AbsoluteToTransformReadyOptionsInput; - const transformFunction = function (_url, _siteUrl, _itemPath, __options) { +function lexicalAbsoluteToTransformReady( + serializedLexical: string, + siteUrl: string, + _options: LexicalAbsoluteToTransformReadyOptions = {} +): string { + const overrideOptions: LexicalAbsoluteToTransformReadyOptions = { + siteUrl, + transformType: 'toTransformReady' + }; + const options: LexicalAbsoluteToTransformReadyOptions = { + assetsOnly: false, + secure: false, + nodes: [], + transformMap: {}, + ..._options, + ...overrideOptions + }; + + const transformFunction = function (_url: string, _siteUrl: string, _itemPath: string | null, __options?: AbsoluteToTransformReadyOptionsInput): string { return absoluteToTransformReady(_url, _siteUrl, __options); }; - return lexicalTransform(serializedLexical, siteUrl, transformFunction, '', options); + return lexicalTransform(serializedLexical, siteUrl, transformFunction, null, options); } -module.exports = lexicalAbsoluteToRelative; +export default lexicalAbsoluteToTransformReady; diff --git a/packages/url-utils/src/utils/lexical-relative-to-absolute.ts b/packages/url-utils/src/utils/lexical-relative-to-absolute.ts index f51d18ead..933f7799f 100644 --- a/packages/url-utils/src/utils/lexical-relative-to-absolute.ts +++ b/packages/url-utils/src/utils/lexical-relative-to-absolute.ts @@ -1,13 +1,30 @@ -export {}; -const relativeToAbsolute = require('./relative-to-absolute'); -const lexicalTransform = require('./lexical-transform'); +import lexicalTransform from './lexical-transform'; +import relativeToAbsolute, {type RelativeToAbsoluteOptionsInput} from './relative-to-absolute'; +import type {LexicalTransformOptionsInput} from './types'; -function lexicalRelativeToAbsolute(serializedLexical, siteUrl, itemPath, _options = {}) { - const defaultOptions = {assetsOnly: false, secure: false, nodes: [], transformMap: {}}; - const overrideOptions = {siteUrl, itemPath, transformType: 'relativeToAbsolute'}; - const options = Object.assign({}, defaultOptions, _options, overrideOptions); +export type LexicalRelativeToAbsoluteOptions = LexicalTransformOptionsInput & RelativeToAbsoluteOptionsInput; + +function lexicalRelativeToAbsolute( + serializedLexical: string, + siteUrl: string, + itemPath: string | null, + _options: LexicalRelativeToAbsoluteOptions = {} +): string { + const overrideOptions: LexicalRelativeToAbsoluteOptions = { + siteUrl, + itemPath, + transformType: 'relativeToAbsolute' + }; + const options: LexicalRelativeToAbsoluteOptions = { + assetsOnly: false, + secure: false, + nodes: [], + transformMap: {}, + ..._options, + ...overrideOptions + }; return lexicalTransform(serializedLexical, siteUrl, relativeToAbsolute, itemPath, options); } -module.exports = lexicalRelativeToAbsolute; +export default lexicalRelativeToAbsolute; diff --git a/packages/url-utils/src/utils/lexical-relative-to-transform-ready.ts b/packages/url-utils/src/utils/lexical-relative-to-transform-ready.ts index 29a5fdaf0..a27e28211 100644 --- a/packages/url-utils/src/utils/lexical-relative-to-transform-ready.ts +++ b/packages/url-utils/src/utils/lexical-relative-to-transform-ready.ts @@ -1,13 +1,29 @@ -export {}; -const relativeToTransformReady = require('./relative-to-transform-ready'); -const lexicalTransform = require('./lexical-transform'); +import lexicalTransform from './lexical-transform'; +import relativeToTransformReady, {type RelativeToTransformReadyOptionsInput} from './relative-to-transform-ready'; +import type {LexicalTransformOptionsInput} from './types'; -function lexicalRelativeToTransformReady(serializedLexical, siteUrl, itemPath, _options = {}) { - const defaultOptions = {assetsOnly: false, secure: false, nodes: [], transformMap: {}}; - const overrideOptions = {siteUrl, transformType: 'toTransformReady'}; - const options = Object.assign({}, defaultOptions, _options, overrideOptions); +export type LexicalRelativeToTransformReadyOptions = LexicalTransformOptionsInput & RelativeToTransformReadyOptionsInput; + +function lexicalRelativeToTransformReady( + serializedLexical: string, + siteUrl: string, + itemPath: string | null, + _options: LexicalRelativeToTransformReadyOptions = {} +): string { + const overrideOptions: LexicalRelativeToTransformReadyOptions = { + siteUrl, + transformType: 'toTransformReady' + }; + const options: LexicalRelativeToTransformReadyOptions = { + assetsOnly: false, + secure: false, + nodes: [], + transformMap: {}, + ..._options, + ...overrideOptions + }; return lexicalTransform(serializedLexical, siteUrl, relativeToTransformReady, itemPath, options); } -module.exports = lexicalRelativeToTransformReady; +export default lexicalRelativeToTransformReady; diff --git a/packages/url-utils/src/utils/lexical-to-transform-ready.ts b/packages/url-utils/src/utils/lexical-to-transform-ready.ts index 994440700..3d6790222 100644 --- a/packages/url-utils/src/utils/lexical-to-transform-ready.ts +++ b/packages/url-utils/src/utils/lexical-to-transform-ready.ts @@ -1,14 +1,23 @@ -export {}; -const lexicalRelativeToAbsolute = require('./lexical-relative-to-absolute'); -const lexicalAbsoluteToTransformReady = require('./lexical-absolute-to-transform-ready'); - -function lexicalToTransformReady(lexical, siteUrl, itemPath, options) { - if (typeof itemPath === 'object' && !options) { - options = itemPath; - itemPath = null; +import lexicalAbsoluteToTransformReady, {type LexicalAbsoluteToTransformReadyOptions} from './lexical-absolute-to-transform-ready'; +import lexicalRelativeToAbsolute, {type LexicalRelativeToAbsoluteOptions} from './lexical-relative-to-absolute'; + +export type LexicalToTransformReadyOptions = LexicalRelativeToAbsoluteOptions & LexicalAbsoluteToTransformReadyOptions; + +function lexicalToTransformReady( + lexical: string, + siteUrl: string, + itemPath?: string | LexicalToTransformReadyOptions | null, + options?: LexicalToTransformReadyOptions +): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions: LexicalToTransformReadyOptions | undefined = options; + + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; } - const absolute = lexicalRelativeToAbsolute(lexical, siteUrl, itemPath, options); - return lexicalAbsoluteToTransformReady(absolute, siteUrl, options); + + const absolute = lexicalRelativeToAbsolute(lexical, siteUrl, resolvedItemPath, resolvedOptions); + return lexicalAbsoluteToTransformReady(absolute, siteUrl, resolvedOptions); } -module.exports = lexicalToTransformReady; +export default lexicalToTransformReady; diff --git a/packages/url-utils/src/utils/lexical-transform.ts b/packages/url-utils/src/utils/lexical-transform.ts index ab69d1245..b8381cdf7 100644 --- a/packages/url-utils/src/utils/lexical-transform.ts +++ b/packages/url-utils/src/utils/lexical-transform.ts @@ -1,5 +1,27 @@ -export {}; -const _ = require('lodash'); +import _ from 'lodash'; +import type { + LexicalNodeConfig, + LexicalTransformOptions, + LexicalTransformOptionsInput, + LexicalTransformRegistry, + LexicalTransformType, + LexicalUrlTransformMap +} from './types'; + +interface LexicalNode { + type?: string; + url?: string; + children?: LexicalNode[]; + [key: string]: unknown; +} + +interface LexicalDocument { + root?: { + children?: LexicalNode[]; + }; +} + +type LexicalTransformFunction = (value: string, siteUrl: string, itemPath: string | null, options: LexicalTransformOptions) => string; // options.transformMap = { // relativeToAbsolute: { @@ -9,9 +31,28 @@ const _ = require('lodash'); // } // options.transformType = 'relativeToAbsolute' -function lexicalTransform(serializedLexical, siteUrl, transformFunction, itemPath, _options = {}) { - const defaultOptions = {assetsOnly: false, secure: false, nodes: [], transformMap: {}}; - const options: any = Object.assign({}, defaultOptions, _options, {siteUrl, itemPath}); +function lexicalTransform( + serializedLexical: string, + siteUrl: string, + transformFunction: LexicalTransformFunction, + itemPath: string | null, + _options: LexicalTransformOptionsInput = {} +): string { + const defaultOptions: LexicalTransformOptions = { + assetsOnly: false, + secure: false, + nodes: [], + transformMap: {}, + transformType: 'relativeToAbsolute', + siteUrl, + itemPath + }; + const options: LexicalTransformOptions = { + ...defaultOptions, + ..._options, + siteUrl, + itemPath + }; if (!serializedLexical) { return serializedLexical; @@ -19,7 +60,7 @@ function lexicalTransform(serializedLexical, siteUrl, transformFunction, itemPat // function only accepts serialized lexical so there's no chance of accidentally // modifying pass-by-reference objects - const lexical = JSON.parse(serializedLexical); + const lexical = JSON.parse(serializedLexical) as LexicalDocument; if (!lexical?.root?.children) { return serializedLexical; @@ -27,41 +68,58 @@ function lexicalTransform(serializedLexical, siteUrl, transformFunction, itemPat // create a map of node types to urlTransformMap objects // e.g. {'image': {src: 'url', caption: 'html'} - const nodeMap = new Map(); - options.nodes.forEach(node => node.urlTransformMap && nodeMap.set(node.getType(), node.urlTransformMap)); + const nodeMap = new Map(); + options.nodes.forEach((node: LexicalNodeConfig) => { + if (node.urlTransformMap) { + nodeMap.set(node.getType(), node.urlTransformMap); + } + }); + + const getTransformRegistry = (transformType: LexicalTransformType): Record string> | undefined => { + return options.transformMap?.[transformType]; + }; - const transformProperty = function (obj, propertyPath, transform) { + const transformProperty = function (obj: LexicalNode, propertyPath: string, transform: string | LexicalUrlTransformMap) { const propertyValue = _.get(obj, propertyPath); if (Array.isArray(propertyValue)) { propertyValue.forEach((item) => { // arrays of objects need to be defined as a nested object in the urlTransformMap // so the `transform` value is that nested object - Object.entries(transform).forEach(([itemPropertyPath, itemTransform]) => { - transformProperty(item, itemPropertyPath, itemTransform); - }); + if (typeof transform === 'object') { + Object.entries(transform).forEach(([itemPropertyPath, itemTransform]) => { + transformProperty(item as LexicalNode, itemPropertyPath, itemTransform); + }); + } }); return; } - if (propertyValue) { - _.set(obj, propertyPath, options.transformMap[options.transformType][transform](propertyValue)); + if (propertyValue && typeof transform === 'string') { + const registry = getTransformRegistry(options.transformType); + const transformer = registry?.[transform]; + if (transformer && typeof propertyValue === 'string') { + _.set(obj, propertyPath, transformer(propertyValue)); + } } }; // recursively walk the Lexical node tree transforming any card data properties and links - const transformChildren = function (children) { + const transformChildren = function (children: LexicalNode[]) { for (const child of children) { - const isCard = child.type && nodeMap.has(child.type); - const isLink = !!child.url; + const isCard = child.type ? nodeMap.has(child.type) : false; + const isLink = typeof child.url === 'string'; if (isCard) { - Object.entries(nodeMap.get(child.type)).forEach(([propertyPath, transform]) => { - transformProperty(child, propertyPath, transform); - }); + const map = child.type ? nodeMap.get(child.type) : undefined; + if (map) { + Object.entries(map).forEach(([propertyPath, transform]) => { + transformProperty(child, propertyPath, transform); + }); + } } else if (isLink) { - child.url = transformFunction(child.url, siteUrl, itemPath, options); + child.url = transformFunction(child.url as string, siteUrl, itemPath, options); } if (child.children) { @@ -70,9 +128,11 @@ function lexicalTransform(serializedLexical, siteUrl, transformFunction, itemPat } }; - transformChildren(lexical.root.children); + if (lexical.root?.children) { + transformChildren(lexical.root.children); + } return JSON.stringify(lexical); } -module.exports = lexicalTransform; +export default lexicalTransform; diff --git a/packages/url-utils/src/utils/markdown-absolute-to-relative.ts b/packages/url-utils/src/utils/markdown-absolute-to-relative.ts index 7ac29a919..74fffebc9 100644 --- a/packages/url-utils/src/utils/markdown-absolute-to-relative.ts +++ b/packages/url-utils/src/utils/markdown-absolute-to-relative.ts @@ -1,21 +1,29 @@ -export {}; -const markdownTransform = require('./markdown-transform'); -const absoluteToRelative = require('./absolute-to-relative'); -const htmlAbsoluteToRelative = require('./html-absolute-to-relative'); +import absoluteToRelative, {type AbsoluteToRelativeOptionsInput} from './absolute-to-relative'; +import htmlAbsoluteToRelative from './html-absolute-to-relative'; +import markdownTransformDefault from './markdown-transform'; +import type {MarkdownTransformOptions, MarkdownTransformOptionsInput} from './types'; -function markdownAbsoluteToRelative(markdown = '', siteUrl, _options = {}) { - const defaultOptions = {assetsOnly: false, ignoreProtocol: true}; - const options: any = Object.assign({}, defaultOptions, _options); +const markdownTransform = markdownTransformDefault; + +export type MarkdownAbsoluteToRelativeOptions = MarkdownTransformOptions; +export type MarkdownAbsoluteToRelativeOptionsInput = MarkdownTransformOptionsInput & AbsoluteToRelativeOptionsInput; + +function markdownAbsoluteToRelative(markdown: string = '', siteUrl: string, _options: MarkdownAbsoluteToRelativeOptionsInput = {}): string { + const options: MarkdownAbsoluteToRelativeOptions = { + assetsOnly: false, + ignoreProtocol: true, + ..._options + }; options.earlyExitMatchStr = options.ignoreProtocol ? siteUrl.replace(/http:|https:/, '') : siteUrl; options.earlyExitMatchStr = options.earlyExitMatchStr.replace(/\/$/, ''); - // need to ignore itemPath because absoluteToRelative functions doen't take that option + // need to ignore itemPath because absoluteToRelative functions don't take that option const transformFunctions = { - html(_url, _siteUrl, _itemPath, __options) { + html(_url: string, _siteUrl: string, _itemPath: string | null, __options?: AbsoluteToRelativeOptionsInput) { return htmlAbsoluteToRelative(_url, _siteUrl, __options); }, - url(_url, _siteUrl, _itemPath, __options) { + url(_url: string, _siteUrl: string, _itemPath: string | null, __options?: AbsoluteToRelativeOptionsInput) { return absoluteToRelative(_url, _siteUrl, __options); } }; @@ -23,4 +31,4 @@ function markdownAbsoluteToRelative(markdown = '', siteUrl, _options = {}) { return markdownTransform(markdown, siteUrl, transformFunctions, '', options); } -module.exports = markdownAbsoluteToRelative; +export default markdownAbsoluteToRelative; diff --git a/packages/url-utils/src/utils/markdown-absolute-to-transform-ready.ts b/packages/url-utils/src/utils/markdown-absolute-to-transform-ready.ts index 7905f15c9..749450f7f 100644 --- a/packages/url-utils/src/utils/markdown-absolute-to-transform-ready.ts +++ b/packages/url-utils/src/utils/markdown-absolute-to-transform-ready.ts @@ -1,21 +1,29 @@ -export {}; -const markdownTransform = require('./markdown-transform'); -const absoluteToTransformReady = require('./absolute-to-transform-ready'); -const htmlAbsoluteToTransformReady = require('./html-absolute-to-transform-ready'); +import absoluteToTransformReady, {type AbsoluteToTransformReadyOptionsInput} from './absolute-to-transform-ready'; +import htmlAbsoluteToTransformReady from './html-absolute-to-transform-ready'; +import markdownTransformDefault from './markdown-transform'; +import type {MarkdownTransformOptions, MarkdownTransformOptionsInput} from './types'; -function markdownAbsoluteToTransformReady(markdown = '', siteUrl, _options = {}) { - const defaultOptions = {assetsOnly: false, ignoreProtocol: true}; - const options: any = Object.assign({}, defaultOptions, _options); +const markdownTransform = markdownTransformDefault; + +export type MarkdownAbsoluteToTransformReadyOptions = MarkdownTransformOptions; +export type MarkdownAbsoluteToTransformReadyOptionsInput = MarkdownTransformOptionsInput & AbsoluteToTransformReadyOptionsInput; + +function markdownAbsoluteToTransformReady(markdown: string = '', siteUrl: string, _options: MarkdownAbsoluteToTransformReadyOptionsInput = {}): string { + const options: MarkdownAbsoluteToTransformReadyOptions = { + assetsOnly: false, + ignoreProtocol: true, + ..._options + }; options.earlyExitMatchStr = options.ignoreProtocol ? siteUrl.replace(/http:|https:/, '') : siteUrl; options.earlyExitMatchStr = options.earlyExitMatchStr.replace(/\/$/, ''); - // need to ignore itemPath because absoluteToTransformReady functions doen't take that option + // need to ignore itemPath because absoluteToTransformReady functions don't take that option const transformFunctions = { - html(_url, _siteUrl, _itemPath, __options) { + html(_url: string, _siteUrl: string, _itemPath: string | null, __options?: AbsoluteToTransformReadyOptionsInput) { return htmlAbsoluteToTransformReady(_url, _siteUrl, __options); }, - url(_url, _siteUrl, _itemPath, __options) { + url(_url: string, _siteUrl: string, _itemPath: string | null, __options?: AbsoluteToTransformReadyOptionsInput) { return absoluteToTransformReady(_url, _siteUrl, __options); } }; @@ -23,4 +31,4 @@ function markdownAbsoluteToTransformReady(markdown = '', siteUrl, _options = {}) return markdownTransform(markdown, siteUrl, transformFunctions, '', options); } -module.exports = markdownAbsoluteToTransformReady; +export default markdownAbsoluteToTransformReady; diff --git a/packages/url-utils/src/utils/markdown-relative-to-absolute.ts b/packages/url-utils/src/utils/markdown-relative-to-absolute.ts index a31d9770c..04164fb19 100644 --- a/packages/url-utils/src/utils/markdown-relative-to-absolute.ts +++ b/packages/url-utils/src/utils/markdown-relative-to-absolute.ts @@ -1,15 +1,36 @@ -export {}; -const markdownTransform = require('./markdown-transform'); -const htmlRelativeToAbsolute = require('./html-relative-to-absolute'); -const relativeToAbsolute = require('./relative-to-absolute'); +import htmlRelativeToAbsolute from './html-relative-to-absolute'; +import markdownTransformDefault from './markdown-transform'; +import relativeToAbsolute from './relative-to-absolute'; +import type {MarkdownTransformOptions, MarkdownTransformOptionsInput} from './types'; -function markdownRelativeToAbsolute(markdown = '', siteUrl, itemPath, _options = {}) { - const defaultOptions = {assetsOnly: false}; - const options: any = Object.assign({}, defaultOptions, _options); +const markdownTransform = markdownTransformDefault; + +export type MarkdownRelativeToAbsoluteOptions = MarkdownTransformOptions; +export type MarkdownRelativeToAbsoluteOptionsInput = MarkdownTransformOptionsInput; + +function markdownRelativeToAbsolute( + markdown: string = '', + siteUrl: string, + itemPath?: string | MarkdownRelativeToAbsoluteOptionsInput | null, + _options: MarkdownRelativeToAbsoluteOptionsInput = {} +): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions: MarkdownRelativeToAbsoluteOptionsInput = _options; + + if (typeof itemPath === 'object' && itemPath !== null) { + resolvedOptions = itemPath; + resolvedItemPath = null; + } + + const options: MarkdownRelativeToAbsoluteOptions = { + assetsOnly: false, + ignoreProtocol: true, + ...resolvedOptions + }; options.earlyExitMatchStr = '\\]\\([^\\s\\)]|href=|src=|srcset='; if (options.assetsOnly) { - options.earlyExitMatchStr = options.staticImageUrlPrefix; + options.earlyExitMatchStr = options.staticImageUrlPrefix ?? 'content/images'; } const transformFunctions = { @@ -17,7 +38,7 @@ function markdownRelativeToAbsolute(markdown = '', siteUrl, itemPath, _options = url: relativeToAbsolute }; - return markdownTransform(markdown, siteUrl, transformFunctions, itemPath, options); + return markdownTransform(markdown, siteUrl, transformFunctions, resolvedItemPath, options); } -module.exports = markdownRelativeToAbsolute; +export default markdownRelativeToAbsolute; diff --git a/packages/url-utils/src/utils/markdown-relative-to-transform-ready.ts b/packages/url-utils/src/utils/markdown-relative-to-transform-ready.ts index d023a26da..a1cff2cb9 100644 --- a/packages/url-utils/src/utils/markdown-relative-to-transform-ready.ts +++ b/packages/url-utils/src/utils/markdown-relative-to-transform-ready.ts @@ -1,15 +1,36 @@ -export {}; -const markdownTransform = require('./markdown-transform'); -const htmlRelativeToTransformReady = require('./html-relative-to-transform-ready'); -const relativeToTransformReady = require('./relative-to-transform-ready'); +import htmlRelativeToTransformReady from './html-relative-to-transform-ready'; +import markdownTransformDefault from './markdown-transform'; +import relativeToTransformReady from './relative-to-transform-ready'; +import type {MarkdownTransformOptions, MarkdownTransformOptionsInput} from './types'; -function markdownRelativeToTransformReady(markdown = '', siteUrl, itemPath, _options = {}) { - const defaultOptions = {assetsOnly: false}; - const options: any = Object.assign({}, defaultOptions, _options); +const markdownTransform = markdownTransformDefault; + +export type MarkdownRelativeToTransformReadyOptions = MarkdownTransformOptions; +export type MarkdownRelativeToTransformReadyOptionsInput = MarkdownTransformOptionsInput; + +function markdownRelativeToTransformReady( + markdown: string = '', + siteUrl: string, + itemPath?: string | MarkdownRelativeToTransformReadyOptionsInput | null, + _options: MarkdownRelativeToTransformReadyOptionsInput = {} +): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions: MarkdownRelativeToTransformReadyOptionsInput = _options; + + if (typeof itemPath === 'object' && itemPath !== null) { + resolvedOptions = itemPath; + resolvedItemPath = null; + } + + const options: MarkdownRelativeToTransformReadyOptions = { + assetsOnly: false, + ignoreProtocol: true, + ...resolvedOptions + }; options.earlyExitMatchStr = '\\]\\([^\\s\\)]|href=|src=|srcset='; if (options.assetsOnly) { - options.earlyExitMatchStr = options.staticImageUrlPrefix; + options.earlyExitMatchStr = options.staticImageUrlPrefix ?? 'content/images'; } const transformFunctions = { @@ -17,7 +38,7 @@ function markdownRelativeToTransformReady(markdown = '', siteUrl, itemPath, _opt url: relativeToTransformReady }; - return markdownTransform(markdown, siteUrl, transformFunctions, itemPath, options); + return markdownTransform(markdown, siteUrl, transformFunctions, resolvedItemPath, options); } -module.exports = markdownRelativeToTransformReady; +export default markdownRelativeToTransformReady; diff --git a/packages/url-utils/src/utils/markdown-to-transform-ready.ts b/packages/url-utils/src/utils/markdown-to-transform-ready.ts index f24ba395d..614f00c57 100644 --- a/packages/url-utils/src/utils/markdown-to-transform-ready.ts +++ b/packages/url-utils/src/utils/markdown-to-transform-ready.ts @@ -1,14 +1,23 @@ -export {}; -const markdownRelativeToAbsolute = require('./markdown-relative-to-absolute'); -const markdownAbsoluteToTransformReady = require('./markdown-absolute-to-transform-ready'); - -function markdownToTransformReady(markdown, siteUrl, itemPath, options) { - if (typeof itemPath === 'object' && !options) { - options = itemPath; - itemPath = null; +import markdownAbsoluteToTransformReady, {type MarkdownAbsoluteToTransformReadyOptionsInput} from './markdown-absolute-to-transform-ready'; +import markdownRelativeToAbsolute, {type MarkdownRelativeToAbsoluteOptionsInput} from './markdown-relative-to-absolute'; + +export type MarkdownToTransformReadyOptions = MarkdownRelativeToAbsoluteOptionsInput & MarkdownAbsoluteToTransformReadyOptionsInput; + +function markdownToTransformReady( + markdown: string, + siteUrl: string, + itemPath?: string | MarkdownToTransformReadyOptions | null, + options?: MarkdownToTransformReadyOptions +): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions: MarkdownToTransformReadyOptions | undefined = options; + + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; } - const absolute = markdownRelativeToAbsolute(markdown, siteUrl, itemPath, options); - return markdownAbsoluteToTransformReady(absolute, siteUrl, options); + + const absolute = markdownRelativeToAbsolute(markdown, siteUrl, resolvedItemPath, resolvedOptions); + return markdownAbsoluteToTransformReady(absolute, siteUrl, resolvedOptions); } -module.exports = markdownToTransformReady; +export default markdownToTransformReady; diff --git a/packages/url-utils/src/utils/markdown-transform.ts b/packages/url-utils/src/utils/markdown-transform.ts index bca482246..f4f68675b 100644 --- a/packages/url-utils/src/utils/markdown-transform.ts +++ b/packages/url-utils/src/utils/markdown-transform.ts @@ -1,9 +1,33 @@ -export {}; -let remark; -const footnotes = require('remark-footnotes'); -const visit = require('unist-util-visit'); +import remarkDefault from 'remark'; +import footnotes from 'remark-footnotes'; +import visit from 'unist-util-visit'; +import type {Node} from 'unist'; +import type {MarkdownTransformOptions, MarkdownTransformOptionsInput} from './types'; -function replaceLast(find, replace, str) { +const remark = remarkDefault; + +interface MarkdownTransformFunctions { + html: (value: string, siteUrl: string, itemPath: string | null, options: MarkdownTransformOptions) => string; + url: (value: string, siteUrl: string, itemPath: string | null, options: MarkdownTransformOptions) => string; +} + +interface MarkdownReplacement { + old: string; + new: string; + start: number; + end: number; +} + +type RemarkNode = Node & { + value?: string; + url?: string; + position?: { + start?: {offset?: number} & Record; + end?: {offset?: number} & Record; + }; +}; + +function replaceLast(find: string, replace: string, str: string): string { const lastIndex = str.lastIndexOf(find); if (lastIndex === -1) { @@ -16,19 +40,24 @@ function replaceLast(find, replace, str) { return begin + replace + end; } -function markdownTransform(markdown = '', siteUrl, transformFunctions, itemPath, _options = {}) { - const defaultOptions = {assetsOnly: false, ignoreProtocol: true}; - const options: any = Object.assign({}, defaultOptions, _options); +function markdownTransform( + markdown: string = '', + siteUrl: string, + transformFunctions: MarkdownTransformFunctions, + itemPath: string | null = null, + _options: MarkdownTransformOptionsInput = {} +): string { + const defaultOptions: MarkdownTransformOptions = {assetsOnly: false, ignoreProtocol: true}; + const options: MarkdownTransformOptions = { + ...defaultOptions, + ..._options + }; if (!markdown || (options.earlyExitMatchStr && !markdown.match(new RegExp(options.earlyExitMatchStr)))) { return markdown; } - const replacements = []; - - if (!remark) { - remark = require('remark'); - } + const replacements: MarkdownReplacement[] = []; const tree = remark() .use({settings: {commonmark: true}}) @@ -36,30 +65,34 @@ function markdownTransform(markdown = '', siteUrl, transformFunctions, itemPath, .parse(markdown); visit(tree, ['link', 'image', 'html'], (node) => { - if (node.type === 'html' && node.value.match(/src|srcset|href/)) { - const oldValue = node.value; - const newValue = transformFunctions.html(node.value, siteUrl, itemPath, options); + const remarkNode = node as RemarkNode; + const startOffset = remarkNode.position?.start?.offset; + const endOffset = remarkNode.position?.end?.offset; + + if (remarkNode.type === 'html' && typeof remarkNode.value === 'string' && /src|srcset|href/.test(remarkNode.value) && typeof startOffset === 'number' && typeof endOffset === 'number') { + const oldValue = remarkNode.value; + const newValue = transformFunctions.html(oldValue, siteUrl, itemPath, options); if (newValue !== oldValue) { replacements.push({ old: oldValue, new: newValue, - start: node.position.start.offset, - end: node.position.end.offset + start: startOffset, + end: endOffset }); } } - if (node.type === 'link' || node.type === 'image') { - const oldValue = node.url; - const newValue = transformFunctions.url(node.url, siteUrl, itemPath, options); + if ((remarkNode.type === 'link' || remarkNode.type === 'image') && typeof remarkNode.url === 'string' && typeof startOffset === 'number' && typeof endOffset === 'number') { + const oldValue = remarkNode.url; + const newValue = transformFunctions.url(oldValue, siteUrl, itemPath, options); if (newValue !== oldValue) { replacements.push({ old: oldValue, new: newValue, - start: node.position.start.offset, - end: node.position.end.offset + start: startOffset, + end: endOffset }); } } @@ -96,4 +129,4 @@ function markdownTransform(markdown = '', siteUrl, transformFunctions, itemPath, return result; } -module.exports = markdownTransform; +export default markdownTransform; diff --git a/packages/url-utils/src/utils/mobiledoc-absolute-to-relative.ts b/packages/url-utils/src/utils/mobiledoc-absolute-to-relative.ts index 29b8a29d2..a9bb7f2a9 100644 --- a/packages/url-utils/src/utils/mobiledoc-absolute-to-relative.ts +++ b/packages/url-utils/src/utils/mobiledoc-absolute-to-relative.ts @@ -1,17 +1,31 @@ -export {}; -const absoluteToRelative = require('./absolute-to-relative'); -const mobiledocTransform = require('./mobiledoc-transform'); +import absoluteToRelative, {type AbsoluteToRelativeOptionsInput} from './absolute-to-relative'; +import mobiledocTransform from './mobiledoc-transform'; +import type {MobiledocTransformOptionsInput} from './types'; -function mobiledocAbsoluteToRelative(serializedMobiledoc, siteUrl, _options = {}) { - const defaultOptions = {assetsOnly: false, secure: false, cardTransformers: []}; - const overrideOptions = {siteUrl, transformType: 'absoluteToRelative'}; - const options = Object.assign({}, defaultOptions, _options, overrideOptions); +export type MobiledocAbsoluteToRelativeOptions = MobiledocTransformOptionsInput & AbsoluteToRelativeOptionsInput; - const transformFunction = function (_url, _siteUrl, _itemPath, __options) { +function mobiledocAbsoluteToRelative( + serializedMobiledoc: string, + siteUrl: string, + _options: MobiledocAbsoluteToRelativeOptions = {} +): string { + const overrideOptions: MobiledocAbsoluteToRelativeOptions = { + siteUrl, + transformType: 'absoluteToRelative' + }; + const options: MobiledocAbsoluteToRelativeOptions = { + assetsOnly: false, + secure: false, + cardTransformers: [], + ..._options, + ...overrideOptions + }; + + const transformFunction = function (_url: string, _siteUrl: string, _itemPath: string | null, __options?: AbsoluteToRelativeOptionsInput): string { return absoluteToRelative(_url, _siteUrl, __options); }; return mobiledocTransform(serializedMobiledoc, siteUrl, transformFunction, '', options); } -module.exports = mobiledocAbsoluteToRelative; +export default mobiledocAbsoluteToRelative; diff --git a/packages/url-utils/src/utils/mobiledoc-absolute-to-transform-ready.ts b/packages/url-utils/src/utils/mobiledoc-absolute-to-transform-ready.ts index 2a9f5668e..afb6290a2 100644 --- a/packages/url-utils/src/utils/mobiledoc-absolute-to-transform-ready.ts +++ b/packages/url-utils/src/utils/mobiledoc-absolute-to-transform-ready.ts @@ -1,17 +1,31 @@ -export {}; -const absoluteToTransformReady = require('./absolute-to-transform-ready'); -const mobiledocTransform = require('./mobiledoc-transform'); +import absoluteToTransformReady, {type AbsoluteToTransformReadyOptionsInput} from './absolute-to-transform-ready'; +import mobiledocTransform from './mobiledoc-transform'; +import type {MobiledocTransformOptionsInput} from './types'; -function mobiledocAbsoluteToRelative(serializedMobiledoc, siteUrl, _options = {}) { - const defaultOptions = {assetsOnly: false, secure: false, cardTransformers: []}; - const overrideOptions = {siteUrl, transformType: 'toTransformReady'}; - const options = Object.assign({}, defaultOptions, _options, overrideOptions); +export type MobiledocAbsoluteToTransformReadyOptions = MobiledocTransformOptionsInput & AbsoluteToTransformReadyOptionsInput; - const transformFunction = function (_url, _siteUrl, _itemPath, __options) { +function mobiledocAbsoluteToTransformReady( + serializedMobiledoc: string, + siteUrl: string, + _options: MobiledocAbsoluteToTransformReadyOptions = {} +): string { + const overrideOptions: MobiledocAbsoluteToTransformReadyOptions = { + siteUrl, + transformType: 'toTransformReady' + }; + const options: MobiledocAbsoluteToTransformReadyOptions = { + assetsOnly: false, + secure: false, + cardTransformers: [], + ..._options, + ...overrideOptions + }; + + const transformFunction = function (_url: string, _siteUrl: string, _itemPath: string | null, __options?: AbsoluteToTransformReadyOptionsInput): string { return absoluteToTransformReady(_url, _siteUrl, __options); }; return mobiledocTransform(serializedMobiledoc, siteUrl, transformFunction, '', options); } -module.exports = mobiledocAbsoluteToRelative; +export default mobiledocAbsoluteToTransformReady; diff --git a/packages/url-utils/src/utils/mobiledoc-relative-to-absolute.ts b/packages/url-utils/src/utils/mobiledoc-relative-to-absolute.ts index b9957ee5d..bbc10d237 100644 --- a/packages/url-utils/src/utils/mobiledoc-relative-to-absolute.ts +++ b/packages/url-utils/src/utils/mobiledoc-relative-to-absolute.ts @@ -1,13 +1,29 @@ -export {}; -const relativeToAbsolute = require('./relative-to-absolute'); -const mobiledocTransform = require('./mobiledoc-transform'); +import relativeToAbsolute, {type RelativeToAbsoluteOptionsInput} from './relative-to-absolute'; +import mobiledocTransform from './mobiledoc-transform'; +import type {MobiledocTransformOptionsInput} from './types'; -function mobiledocRelativeToAbsolute(serializedMobiledoc, siteUrl, itemPath, _options = {}) { - const defaultOptions = {assetsOnly: false, secure: false, cardTransformers: []}; - const overrideOptions = {siteUrl, itemPath, transformType: 'relativeToAbsolute'}; - const options = Object.assign({}, defaultOptions, _options, overrideOptions); +export type MobiledocRelativeToAbsoluteOptions = MobiledocTransformOptionsInput & RelativeToAbsoluteOptionsInput; + +function mobiledocRelativeToAbsolute( + serializedMobiledoc: string, + siteUrl: string, + itemPath: string | null, + _options: MobiledocRelativeToAbsoluteOptions = {} +): string { + const overrideOptions: MobiledocRelativeToAbsoluteOptions = { + siteUrl, + itemPath, + transformType: 'relativeToAbsolute' + }; + const options: MobiledocRelativeToAbsoluteOptions = { + assetsOnly: false, + secure: false, + cardTransformers: [], + ..._options, + ...overrideOptions + }; return mobiledocTransform(serializedMobiledoc, siteUrl, relativeToAbsolute, itemPath, options); } -module.exports = mobiledocRelativeToAbsolute; +export default mobiledocRelativeToAbsolute; diff --git a/packages/url-utils/src/utils/mobiledoc-relative-to-transform-ready.ts b/packages/url-utils/src/utils/mobiledoc-relative-to-transform-ready.ts index 215aab227..a0dc28f8a 100644 --- a/packages/url-utils/src/utils/mobiledoc-relative-to-transform-ready.ts +++ b/packages/url-utils/src/utils/mobiledoc-relative-to-transform-ready.ts @@ -1,13 +1,28 @@ -export {}; -const relativeToTransformReady = require('./relative-to-transform-ready'); -const mobiledocTransform = require('./mobiledoc-transform'); +import mobiledocTransform from './mobiledoc-transform'; +import relativeToTransformReady, {type RelativeToTransformReadyOptionsInput} from './relative-to-transform-ready'; +import type {MobiledocTransformOptionsInput} from './types'; -function mobiledocRelativeToTransformReady(serializedMobiledoc, siteUrl, itemPath, _options = {}) { - const defaultOptions = {assetsOnly: false, secure: false, cardTransformers: []}; - const overrideOptions = {siteUrl, transformType: 'toTransformReady'}; - const options = Object.assign({}, defaultOptions, _options, overrideOptions); +export type MobiledocRelativeToTransformReadyOptions = MobiledocTransformOptionsInput & RelativeToTransformReadyOptionsInput; + +function mobiledocRelativeToTransformReady( + serializedMobiledoc: string, + siteUrl: string, + itemPath: string | null, + _options: MobiledocRelativeToTransformReadyOptions = {} +): string { + const overrideOptions: MobiledocRelativeToTransformReadyOptions = { + siteUrl, + transformType: 'toTransformReady' + }; + const options: MobiledocRelativeToTransformReadyOptions = { + assetsOnly: false, + secure: false, + cardTransformers: [], + ..._options, + ...overrideOptions + }; return mobiledocTransform(serializedMobiledoc, siteUrl, relativeToTransformReady, itemPath, options); } -module.exports = mobiledocRelativeToTransformReady; +export default mobiledocRelativeToTransformReady; diff --git a/packages/url-utils/src/utils/mobiledoc-to-transform-ready.ts b/packages/url-utils/src/utils/mobiledoc-to-transform-ready.ts index 2df4ec257..4871c2eb8 100644 --- a/packages/url-utils/src/utils/mobiledoc-to-transform-ready.ts +++ b/packages/url-utils/src/utils/mobiledoc-to-transform-ready.ts @@ -1,14 +1,23 @@ -export {}; -const mobiledocRelativeToAbsolute = require('./mobiledoc-relative-to-absolute'); -const mobiledocAbsoluteToTransformReady = require('./mobiledoc-absolute-to-transform-ready'); - -function mobiledocToTransformReady(mobiledoc, siteUrl, itemPath, options) { - if (typeof itemPath === 'object' && !options) { - options = itemPath; - itemPath = null; +import mobiledocAbsoluteToTransformReady, {type MobiledocAbsoluteToTransformReadyOptions} from './mobiledoc-absolute-to-transform-ready'; +import mobiledocRelativeToAbsolute, {type MobiledocRelativeToAbsoluteOptions} from './mobiledoc-relative-to-absolute'; + +export type MobiledocToTransformReadyOptions = MobiledocRelativeToAbsoluteOptions & MobiledocAbsoluteToTransformReadyOptions; + +function mobiledocToTransformReady( + mobiledoc: string, + siteUrl: string, + itemPath?: string | MobiledocToTransformReadyOptions | null, + options?: MobiledocToTransformReadyOptions +): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions: MobiledocToTransformReadyOptions | undefined = options; + + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; } - const absolute = mobiledocRelativeToAbsolute(mobiledoc, siteUrl, itemPath, options); - return mobiledocAbsoluteToTransformReady(absolute, siteUrl, options); + + const absolute = mobiledocRelativeToAbsolute(mobiledoc, siteUrl, resolvedItemPath, resolvedOptions); + return mobiledocAbsoluteToTransformReady(absolute, siteUrl, resolvedOptions); } -module.exports = mobiledocToTransformReady; +export default mobiledocToTransformReady; diff --git a/packages/url-utils/src/utils/mobiledoc-transform.ts b/packages/url-utils/src/utils/mobiledoc-transform.ts index abf84f8ac..bdab223a7 100644 --- a/packages/url-utils/src/utils/mobiledoc-transform.ts +++ b/packages/url-utils/src/utils/mobiledoc-transform.ts @@ -1,20 +1,60 @@ -export {}; -function mobiledocTransform(serializedMobiledoc, siteUrl, transformFunction, itemPath, _options = {}) { - const defaultOptions = {assetsOnly: false, secure: false, cardTransformers: []}; - const options: any = Object.assign({}, defaultOptions, _options, {siteUrl, itemPath}); - - // options.cardTransformers has an object for each card that has a name and multiple - // transformer functions. By collecting the functions we need into a named object it - // reduces the need to loop through and find the transformer for each card later on - const cardTransformers: Record = {}; - options.cardTransformers.forEach((cardTransformer) => { - cardTransformers[cardTransformer.name] = cardTransformer[options.transformType]; +import type { + CardTransformer, + MobiledocCardTransformer, + MobiledocTransformOptions, + MobiledocTransformOptionsInput +} from './types'; + +type MobiledocMarkup = [string, string[] | undefined]; +type MobiledocCard = [string, unknown]; + +interface MobiledocDocument { + markups?: MobiledocMarkup[]; + cards?: MobiledocCard[]; +} + +type MobiledocTransformFunction = ( + value: string, + siteUrl: string, + itemPath: string | null, + options: MobiledocTransformOptions +) => string | undefined; + +function mobiledocTransform( + serializedMobiledoc: string, + siteUrl: string, + transformFunction: MobiledocTransformFunction, + itemPath: string | null, + _options: MobiledocTransformOptionsInput = {} +): string { + const defaultOptions: MobiledocTransformOptions = { + assetsOnly: false, + secure: false, + cardTransformers: [], + siteUrl, + itemPath, + transformType: 'relativeToAbsolute' + }; + + const options: MobiledocTransformOptions = { + ...defaultOptions, + ..._options, + siteUrl, + itemPath + }; + + const transformerMap = new Map(); + options.cardTransformers.forEach((cardTransformer: MobiledocCardTransformer) => { + const transformer = cardTransformer[options.transformType]; + if (transformer) { + transformerMap.set(cardTransformer.name, transformer); + } }); - delete options.cardTransformers; + delete (options as unknown as Record).cardTransformers; // function only accepts serialized mobiledoc so there's no chance of accidentally // modifying pass-by-reference objects - const mobiledoc = JSON.parse(serializedMobiledoc); + const mobiledoc = JSON.parse(serializedMobiledoc) as MobiledocDocument; // any mobiledoc links will have an 'a' markup with an 'href' attribute (mobiledoc.markups || []).forEach((markup) => { @@ -43,9 +83,10 @@ function mobiledocTransform(serializedMobiledoc, siteUrl, transformFunction, ite // are passed in as options from the consuming application. (mobiledoc.cards || []).forEach((card) => { const [name, payload] = card; - if (cardTransformers[name]) { + const transformer = transformerMap.get(name); + if (transformer) { // transformers take a payload and return a transformed payload - const transformedPayload = cardTransformers[name](payload, options); + const transformedPayload = transformer(payload, options); card[1] = transformedPayload; } }); @@ -53,4 +94,4 @@ function mobiledocTransform(serializedMobiledoc, siteUrl, transformFunction, ite return JSON.stringify(mobiledoc); } -module.exports = mobiledocTransform; +export default mobiledocTransform; diff --git a/packages/url-utils/src/utils/plaintext-absolute-to-transform-ready.ts b/packages/url-utils/src/utils/plaintext-absolute-to-transform-ready.ts index 8ae0638e7..916ef8a23 100644 --- a/packages/url-utils/src/utils/plaintext-absolute-to-transform-ready.ts +++ b/packages/url-utils/src/utils/plaintext-absolute-to-transform-ready.ts @@ -1,13 +1,17 @@ -export {}; -const absoluteToTransformReady = require('./absolute-to-transform-ready'); +import absoluteToTransformReady, {type AbsoluteToTransformReadyOptionsInput} from './absolute-to-transform-ready'; -function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +function escapeRegExp(value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } -const plaintextAbsoluteToTransformReady = function plaintextAbsoluteToTransformReady(plaintext, rootUrl, itemPath, options) { +const plaintextAbsoluteToTransformReady = function plaintextAbsoluteToTransformReady( + plaintext: string, + rootUrl: string, + itemPath?: string | AbsoluteToTransformReadyOptionsInput | null, + options?: AbsoluteToTransformReadyOptionsInput +): string { // itemPath is optional, if it's an object may be the options param instead - if (typeof itemPath === 'object' && !options) { + if (typeof itemPath === 'object' && itemPath !== null && !options) { options = itemPath; itemPath = null; } @@ -24,4 +28,4 @@ const plaintextAbsoluteToTransformReady = function plaintextAbsoluteToTransformR }); }; -module.exports = plaintextAbsoluteToTransformReady; +export default plaintextAbsoluteToTransformReady; diff --git a/packages/url-utils/src/utils/plaintext-relative-to-transform-ready.ts b/packages/url-utils/src/utils/plaintext-relative-to-transform-ready.ts index ce3ce160b..764efaff5 100644 --- a/packages/url-utils/src/utils/plaintext-relative-to-transform-ready.ts +++ b/packages/url-utils/src/utils/plaintext-relative-to-transform-ready.ts @@ -1,19 +1,25 @@ -export {}; -const relativeToTransformReady = require('./relative-to-transform-ready'); +import relativeToTransformReady, {type RelativeToTransformReadyOptionsInput} from './relative-to-transform-ready'; -const plaintextRelativeToTransformReady = function plaintextRelativeToTransformReady(plaintext, rootUrl, itemPath, options) { - // itemPath is optional, if it's an object may be the options param instead - if (typeof itemPath === 'object' && !options) { - options = itemPath; - itemPath = null; +const plaintextRelativeToTransformReady = function plaintextRelativeToTransformReady( + plaintext: string, + rootUrl: string, + itemPath?: string | RelativeToTransformReadyOptionsInput | null, + options?: RelativeToTransformReadyOptionsInput +): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions: RelativeToTransformReadyOptionsInput | undefined = options; + + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; + resolvedItemPath = null; } // plaintext links look like "Link title [url]" // those are all we care about so we can do a fast regex here - return plaintext.replace(/ \[(\/.*?)\]/g, function (fullMatch, path) { - const newPath = relativeToTransformReady(`${path}`, rootUrl, itemPath, options); + return plaintext.replace(/ \[(\/.*?)\]/g, function (_fullMatch, path) { + const newPath = relativeToTransformReady(`${path}`, rootUrl, resolvedItemPath, resolvedOptions); return ` [${newPath}]`; }); }; -module.exports = plaintextRelativeToTransformReady; +export default plaintextRelativeToTransformReady; diff --git a/packages/url-utils/src/utils/plaintext-to-transform-ready.ts b/packages/url-utils/src/utils/plaintext-to-transform-ready.ts index c16cfc23f..afee41773 100644 --- a/packages/url-utils/src/utils/plaintext-to-transform-ready.ts +++ b/packages/url-utils/src/utils/plaintext-to-transform-ready.ts @@ -1,14 +1,24 @@ -export {}; -const plaintextRelativeToTransformReady = require('./plaintext-relative-to-transform-ready'); -const plaintextAbsoluteToTransformReady = require('./plaintext-absolute-to-transform-ready'); - -function plaintextToTransformReady(plaintext, siteUrl, itemPath, options) { - if (typeof itemPath === 'object' && !options) { - options = itemPath; - itemPath = null; +import plaintextAbsoluteToTransformReady from './plaintext-absolute-to-transform-ready'; +import type {AbsoluteToTransformReadyOptionsInput} from './absolute-to-transform-ready'; +import plaintextRelativeToTransformReady from './plaintext-relative-to-transform-ready'; + +export type PlaintextToTransformReadyOptions = AbsoluteToTransformReadyOptionsInput; + +function plaintextToTransformReady( + plaintext: string, + siteUrl: string, + itemPath?: string | PlaintextToTransformReadyOptions | null, + options?: PlaintextToTransformReadyOptions +): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions: PlaintextToTransformReadyOptions | undefined = options; + + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; } - const relativeTransformed = plaintextRelativeToTransformReady(plaintext, siteUrl, itemPath, options); - return plaintextAbsoluteToTransformReady(relativeTransformed, siteUrl, options); + + const relativeTransformed = plaintextRelativeToTransformReady(plaintext, siteUrl, resolvedItemPath, resolvedOptions); + return plaintextAbsoluteToTransformReady(relativeTransformed, siteUrl, resolvedOptions); } -module.exports = plaintextToTransformReady; +export default plaintextToTransformReady; diff --git a/packages/url-utils/src/utils/relative-to-absolute.ts b/packages/url-utils/src/utils/relative-to-absolute.ts index abb3282b5..4adf6e799 100644 --- a/packages/url-utils/src/utils/relative-to-absolute.ts +++ b/packages/url-utils/src/utils/relative-to-absolute.ts @@ -1,7 +1,9 @@ -export {}; -// require the whatwg compatible URL library (same behaviour in node and browser) -const {URL} = require('url'); -const urlJoin = require('./url-join'); +import {URL} from 'url'; +import urlJoin from './url-join'; +import type {SecureOptions, SecureOptionsInput} from './types'; + +export type RelativeToAbsoluteOptions = SecureOptions; +export type RelativeToAbsoluteOptionsInput = SecureOptionsInput; // NOTE: Ghost's relative->absolute handling is a little strange when the rootUrl // includes a subdirectory. Root-relative paths such as /content/image.jpg are @@ -22,24 +24,29 @@ const urlJoin = require('./url-join'); * @param {object} options * @returns {string} The passed in url or an absolute URL using */ -const relativeToAbsolute = function relativeToAbsolute(path, rootUrl, itemPath, _options) { +const relativeToAbsolute = function relativeToAbsolute(path: string, rootUrl: string, itemPath?: string | RelativeToAbsoluteOptionsInput | null, _options?: RelativeToAbsoluteOptionsInput): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions: RelativeToAbsoluteOptionsInput | undefined = _options; + // itemPath is optional, if it's an object it may be the options param instead - if (typeof itemPath === 'object' && !_options) { - _options = itemPath; - itemPath = null; + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; } // itemPath could be sent as a full url in which case, extract the pathname - if (itemPath && itemPath.match(/^http/)) { - const itemUrl = new URL(itemPath); - itemPath = itemUrl.pathname; + if (resolvedItemPath && resolvedItemPath.match(/^http/)) { + const itemUrl = new URL(resolvedItemPath); + resolvedItemPath = itemUrl.pathname; } - const defaultOptions = { + const defaultOptions: RelativeToAbsoluteOptions = { assetsOnly: false, staticImageUrlPrefix: 'content/images' }; - const options = Object.assign({}, defaultOptions, _options); + const options: RelativeToAbsoluteOptions = { + ...defaultOptions, + ...resolvedOptions + }; // return the path as-is if it's not an asset path and we're only modifying assets if (options.assetsOnly) { @@ -71,7 +78,7 @@ const relativeToAbsolute = function relativeToAbsolute(path, rootUrl, itemPath, } // return the path as-is if it's not root-relative and we have no itemPath - if (!itemPath && !path.match(/^\//)) { + if (!resolvedItemPath && !path.match(/^\//)) { return path; } @@ -81,7 +88,7 @@ const relativeToAbsolute = function relativeToAbsolute(path, rootUrl, itemPath, } const parsedRootUrl = new URL(rootUrl); - const basePath = path.startsWith('/') ? '' : itemPath; + const basePath = path.startsWith('/') ? '' : (resolvedItemPath ?? ''); const fullPath = urlJoin([parsedRootUrl.pathname, basePath, path], {rootUrl}); const absoluteUrl = new URL(fullPath, rootUrl); @@ -92,4 +99,4 @@ const relativeToAbsolute = function relativeToAbsolute(path, rootUrl, itemPath, return absoluteUrl.toString(); }; -module.exports = relativeToAbsolute; +export default relativeToAbsolute; diff --git a/packages/url-utils/src/utils/relative-to-transform-ready.ts b/packages/url-utils/src/utils/relative-to-transform-ready.ts index 0bf54a579..0e2a41a7d 100644 --- a/packages/url-utils/src/utils/relative-to-transform-ready.ts +++ b/packages/url-utils/src/utils/relative-to-transform-ready.ts @@ -1,24 +1,35 @@ -export {}; -const relativeToAbsolute = require('./relative-to-absolute'); - -const relativeToTransformReady = function (url, root, itemPath, _options) { - // itemPath is optional, if it's an object may be the options param instead - if (typeof itemPath === 'object' && !_options) { - _options = itemPath; - itemPath = null; +import relativeToAbsolute from './relative-to-absolute'; +import type { + TransformReadyReplacementOptions, + TransformReadyReplacementOptionsInput +} from './types'; + +export interface RelativeToTransformReadyOptions extends TransformReadyReplacementOptions { + staticImageUrlPrefix: string; + secure: boolean; +} + +export type RelativeToTransformReadyOptionsInput = Partial; + +const relativeToTransformReady = function (url: string, root: string, itemPath?: string | RelativeToTransformReadyOptionsInput | null, _options?: RelativeToTransformReadyOptionsInput): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions: RelativeToTransformReadyOptionsInput | undefined = _options; + + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; } - const defaultOptions = { + const options: RelativeToTransformReadyOptions = { replacementStr: '__GHOST_URL__', - staticImageUrlPrefix: 'content/images' - }; - const overrideOptions = { - secure: false + staticImageUrlPrefix: 'content/images', + secure: false, + ...resolvedOptions }; - const options = Object.assign({}, defaultOptions, _options, overrideOptions); + + options.secure = false; // convert to absolute - const absoluteUrl = relativeToAbsolute(url, root, itemPath, options); + const absoluteUrl = relativeToAbsolute(url, root, resolvedItemPath, options); if (absoluteUrl === url) { return url; @@ -40,4 +51,4 @@ const relativeToTransformReady = function (url, root, itemPath, _options) { return url; }; -module.exports = relativeToTransformReady; +export default relativeToTransformReady; diff --git a/packages/url-utils/src/utils/replace-permalink.ts b/packages/url-utils/src/utils/replace-permalink.ts index 0852a44d3..dd9ce769c 100644 --- a/packages/url-utils/src/utils/replace-permalink.ts +++ b/packages/url-utils/src/utils/replace-permalink.ts @@ -1,47 +1,55 @@ -export {}; -const moment = require('moment-timezone'); +import moment from 'moment-timezone'; + +interface PermalinkResource { + published_at?: string | number | Date | null; + slug: string; + id: string; + primary_author?: {slug: string} | null; + primary_tag?: {slug: string} | null; +} /** * creates the url path for a post based on blog timezone and permalink pattern */ -function replacePermalink(permalink, resource, timezone = 'UTC') { - const output = permalink; +function replacePermalink(permalink: string, resource: PermalinkResource, timezone: string = 'UTC'): string { const primaryTagFallback = 'all'; const publishedAtMoment = moment.tz(resource.published_at || Date.now(), timezone); - const permalinkLookUp = { - year: function () { + const permalinkLookUp: Record string> = { + year() { return publishedAtMoment.format('YYYY'); }, - month: function () { + month() { return publishedAtMoment.format('MM'); }, - day: function () { + day() { return publishedAtMoment.format('DD'); }, - author: function () { - return resource.primary_author.slug; + author() { + return resource.primary_author?.slug ?? primaryTagFallback; }, - primary_author: function () { - return resource.primary_author ? resource.primary_author.slug : primaryTagFallback; + primary_author() { + return resource.primary_author?.slug ?? primaryTagFallback; }, - primary_tag: function () { - return resource.primary_tag ? resource.primary_tag.slug : primaryTagFallback; + primary_tag() { + return resource.primary_tag?.slug ?? primaryTagFallback; }, - slug: function () { + slug() { return resource.slug; }, - id: function () { + id() { return resource.id; } }; // replace tags like :slug or :year with actual values const permalinkKeys = Object.keys(permalinkLookUp); - return output.replace(/(:[a-z_]+)/g, function (match) { - if (permalinkKeys.includes(match.substr(1))) { - return permalinkLookUp[match.substr(1)](); + return permalink.replace(/(:[a-z_]+)/g, function (match) { + const key = match.slice(1); + if (permalinkKeys.includes(key)) { + return permalinkLookUp[key](); } + return 'undefined'; }); } -module.exports = replacePermalink; +export default replacePermalink; diff --git a/packages/url-utils/src/utils/strip-subdirectory-from-path.ts b/packages/url-utils/src/utils/strip-subdirectory-from-path.ts index d2b6318ce..56a1a82b5 100644 --- a/packages/url-utils/src/utils/strip-subdirectory-from-path.ts +++ b/packages/url-utils/src/utils/strip-subdirectory-from-path.ts @@ -1,5 +1,4 @@ -export {}; -const {URL} = require('url'); +import {URL} from 'url'; /** * Removes the directory in the root url from the relative path @@ -8,7 +7,7 @@ const {URL} = require('url'); * @param {string} rootUrl Root URL (eg, 'https://mysite.com/my/subdir/) * @returns {string} Path relative to the rootUrl's path */ -const stripSubdirectoryFromPath = function stripSubdirectoryFromPath(path = '', rootUrl = '') { +const stripSubdirectoryFromPath = function stripSubdirectoryFromPath(path: string = '', rootUrl: string = ''): string { // force root to always have a trailing-slash for consistent behaviour if (!rootUrl.endsWith('/')) { rootUrl = `${rootUrl}/`; @@ -34,4 +33,4 @@ const stripSubdirectoryFromPath = function stripSubdirectoryFromPath(path = '', return path; }; -module.exports = stripSubdirectoryFromPath; +export default stripSubdirectoryFromPath; diff --git a/packages/url-utils/src/utils/to-transform-ready.ts b/packages/url-utils/src/utils/to-transform-ready.ts index ea87c29ce..52c4d1027 100644 --- a/packages/url-utils/src/utils/to-transform-ready.ts +++ b/packages/url-utils/src/utils/to-transform-ready.ts @@ -1,14 +1,23 @@ -export {}; -const relativeToAbsolute = require('./relative-to-absolute'); -const absoluteToTransformReady = require('./absolute-to-transform-ready'); - -function toTransformReady(url, siteUrl, itemPath, options) { - if (typeof itemPath === 'object' && !options) { - options = itemPath; - itemPath = null; +import absoluteToTransformReady, {type AbsoluteToTransformReadyOptionsInput} from './absolute-to-transform-ready'; +import relativeToAbsolute, {type RelativeToAbsoluteOptionsInput} from './relative-to-absolute'; + +export type ToTransformReadyOptions = RelativeToAbsoluteOptionsInput & AbsoluteToTransformReadyOptionsInput; + +function toTransformReady( + url: string, + siteUrl: string, + itemPath?: string | ToTransformReadyOptions | null, + options?: ToTransformReadyOptions +): string { + let resolvedItemPath: string | null = typeof itemPath === 'string' ? itemPath : null; + let resolvedOptions: ToTransformReadyOptions | undefined = options; + + if (typeof itemPath === 'object' && itemPath !== null && !resolvedOptions) { + resolvedOptions = itemPath; } - const absoluteUrl = relativeToAbsolute(url, siteUrl, itemPath, options); - return absoluteToTransformReady(absoluteUrl, siteUrl, options); + + const absoluteUrl = relativeToAbsolute(url, siteUrl, resolvedItemPath, resolvedOptions); + return absoluteToTransformReady(absoluteUrl, siteUrl, resolvedOptions); } -module.exports = toTransformReady; +export default toTransformReady; diff --git a/packages/url-utils/src/utils/transform-ready-to-absolute.ts b/packages/url-utils/src/utils/transform-ready-to-absolute.ts index c51e15f90..ab0b3ad33 100644 --- a/packages/url-utils/src/utils/transform-ready-to-absolute.ts +++ b/packages/url-utils/src/utils/transform-ready-to-absolute.ts @@ -1,13 +1,17 @@ -export {}; -function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +import type {TransformReadyReplacementOptions, TransformReadyReplacementOptionsInput} from './types'; + +function escapeRegExp(value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } -const transformReadyToAbsolute = function (str = '', root, _options = {}) { - const defaultOptions = { +const transformReadyToAbsolute = function (str: string = '', root: string, _options: TransformReadyReplacementOptionsInput = {}): string { + const defaultOptions: TransformReadyReplacementOptions = { replacementStr: '__GHOST_URL__' }; - const options = Object.assign({}, defaultOptions, _options); + const options: TransformReadyReplacementOptions = { + ...defaultOptions, + ..._options + }; if (!str || str.indexOf(options.replacementStr) === -1) { return str; @@ -18,4 +22,4 @@ const transformReadyToAbsolute = function (str = '', root, _options = {}) { return str.replace(replacementRegex, root.replace(/\/$/, '')); }; -module.exports = transformReadyToAbsolute; +export default transformReadyToAbsolute; diff --git a/packages/url-utils/src/utils/transform-ready-to-relative.ts b/packages/url-utils/src/utils/transform-ready-to-relative.ts index fbe7bf179..e10683648 100644 --- a/packages/url-utils/src/utils/transform-ready-to-relative.ts +++ b/packages/url-utils/src/utils/transform-ready-to-relative.ts @@ -1,13 +1,17 @@ -export {}; -function escapeRegExp(string) { - return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +import type {TransformReadyReplacementOptions, TransformReadyReplacementOptionsInput} from './types'; + +function escapeRegExp(value: string): string { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } -const transformReadyToRelative = function (str = '', root, _options = {}) { - const defaultOptions = { +const transformReadyToRelative = function (str: string = '', root: string, _options: TransformReadyReplacementOptionsInput = {}): string { + const defaultOptions: TransformReadyReplacementOptions = { replacementStr: '__GHOST_URL__' }; - const options = Object.assign({}, defaultOptions, _options); + const options: TransformReadyReplacementOptions = { + ...defaultOptions, + ..._options + }; if (!str || str.indexOf(options.replacementStr) === -1) { return str; @@ -22,4 +26,4 @@ const transformReadyToRelative = function (str = '', root, _options = {}) { return str.replace(replacementRegex, subdir); }; -module.exports = transformReadyToRelative; +export default transformReadyToRelative; diff --git a/packages/url-utils/src/utils/types.ts b/packages/url-utils/src/utils/types.ts new file mode 100644 index 000000000..580cd0705 --- /dev/null +++ b/packages/url-utils/src/utils/types.ts @@ -0,0 +1,88 @@ +export type UnknownRecord = Record; + +export interface AssetAwareOptions extends UnknownRecord { + assetsOnly: boolean; + staticImageUrlPrefix?: string; +} + +export type AssetAwareOptionsInput = Partial; + +export interface SecureOptions extends AssetAwareOptions { + secure?: boolean; +} + +export type SecureOptionsInput = Partial; + +export interface TransformReadyReplacementOptions extends UnknownRecord { + replacementStr: string; + withoutSubdirectory?: boolean; +} + +export type TransformReadyReplacementOptionsInput = Partial; + +export interface HtmlTransformOptions extends SecureOptions { + earlyExitMatchStr?: string; +} + +export type HtmlTransformOptionsInput = SecureOptionsInput & {earlyExitMatchStr?: string}; + +export interface MarkdownTransformOptions extends AssetAwareOptions { + ignoreProtocol: boolean; + earlyExitMatchStr?: string; +} + +export type MarkdownTransformOptionsInput = AssetAwareOptionsInput & {ignoreProtocol?: boolean; earlyExitMatchStr?: string}; + +export type MobiledocTransformType = 'relativeToAbsolute' | 'absoluteToRelative' | 'toTransformReady'; + +export type CardTransformer = (payload: unknown, options: UnknownRecord) => unknown; + +export interface MobiledocCardTransformer { + name: string; + relativeToAbsolute?: CardTransformer; + absoluteToRelative?: CardTransformer; + toTransformReady?: CardTransformer; +} + +export interface MobiledocTransformOptions extends SecureOptions { + cardTransformers: MobiledocCardTransformer[]; + siteUrl: string; + itemPath: string | null; + transformType: MobiledocTransformType; +} + +export type MobiledocTransformOptionsInput = Partial> & { + cardTransformers?: MobiledocCardTransformer[]; + siteUrl?: string; + itemPath?: string | null; + transformType?: MobiledocTransformType; +}; + +export type LexicalTransformType = MobiledocTransformType; +export type LexicalTransformFunction = (value: string) => string; +export interface LexicalUrlTransformMap { + [key: string]: string | LexicalUrlTransformMap; +} + +export interface LexicalNodeConfig { + getType(): string; + urlTransformMap?: LexicalUrlTransformMap; +} + +export type LexicalTransformRegistry = Partial>>; + +export interface LexicalTransformOptions extends SecureOptions { + nodes: LexicalNodeConfig[]; + transformMap: LexicalTransformRegistry; + transformType: LexicalTransformType; + siteUrl: string; + itemPath: string | null; +} + +export type LexicalTransformOptionsInput = Partial> & { + nodes?: LexicalNodeConfig[]; + transformMap?: LexicalTransformRegistry; + siteUrl?: string; + itemPath?: string | null; + transformType?: LexicalTransformType; +}; diff --git a/packages/url-utils/src/utils/url-join.ts b/packages/url-utils/src/utils/url-join.ts index 75817024b..bb8af6642 100644 --- a/packages/url-utils/src/utils/url-join.ts +++ b/packages/url-utils/src/utils/url-join.ts @@ -1,5 +1,8 @@ -export {}; -const deduplicateSubdirectory = require('./deduplicate-subdirectory'); +import deduplicateSubdirectory from './deduplicate-subdirectory'; + +export interface UrlJoinOptions { + rootUrl?: string; +} /** urlJoin * Returns a URL/path for internal use in Ghost. @@ -8,7 +11,7 @@ const deduplicateSubdirectory = require('./deduplicate-subdirectory'); * @param {string} options.rootUrl used for deduplicating any subdirectories * @return {string} URL concatinated URL/path of arguments. */ -function urlJoin(parts, options) { +function urlJoin(parts: string[], options: UrlJoinOptions = {}) { let prefixDoubleSlash = false; // Remove empty item at the beginning @@ -32,7 +35,11 @@ function urlJoin(parts, options) { url = url.replace(/^\//, '//'); } + if (!options.rootUrl) { + return url; + } + return deduplicateSubdirectory(url, options.rootUrl); } -module.exports = urlJoin; +export default urlJoin; diff --git a/packages/url-utils/test/unit/url-utils.test.js b/packages/url-utils/test/unit/url-utils.test.js index fac91cb96..a4a5325e8 100644 --- a/packages/url-utils/test/unit/url-utils.test.js +++ b/packages/url-utils/test/unit/url-utils.test.js @@ -4,7 +4,8 @@ require('../utils'); const assert = require('assert/strict'); const sinon = require('sinon'); -const UrlUtils = require('../../lib/UrlUtils'); +const UrlUtilsModule = require('../../lib/UrlUtils'); +const UrlUtils = UrlUtilsModule.default || UrlUtilsModule.UrlUtils; const configUrlHelpers = require('@tryghost/config-url-helpers'); const constants = { diff --git a/packages/url-utils/test/unit/utils/absolute-to-relative.test.js b/packages/url-utils/test/unit/utils/absolute-to-relative.test.js index eec1861c4..bc93e81ac 100644 --- a/packages/url-utils/test/unit/utils/absolute-to-relative.test.js +++ b/packages/url-utils/test/unit/utils/absolute-to-relative.test.js @@ -2,7 +2,7 @@ // const testUtils = require('./utils'); require('../../utils'); -const absoluteToRelative = require('../../../lib/utils/absolute-to-relative'); +const absoluteToRelative = require('../../../lib/utils/absolute-to-relative').default; describe('utils: absoluteToRelative()', function () { it('ignores relative URLs', function () { diff --git a/packages/url-utils/test/unit/utils/absolute-to-transform-ready.test.js b/packages/url-utils/test/unit/utils/absolute-to-transform-ready.test.js index 741248f87..fb92beab3 100644 --- a/packages/url-utils/test/unit/utils/absolute-to-transform-ready.test.js +++ b/packages/url-utils/test/unit/utils/absolute-to-transform-ready.test.js @@ -2,7 +2,7 @@ // const testUtils = require('./utils'); require('../../utils'); -const absoluteToTransformReady = require('../../../lib/utils/absolute-to-transform-ready'); +const absoluteToTransformReady = require('../../../lib/utils/absolute-to-transform-ready').default; describe('utils: absoluteToTransformReady()', function () { it('ignores relative URLs', function () { diff --git a/packages/url-utils/test/unit/utils/deduplicate-subdirectory.test.js b/packages/url-utils/test/unit/utils/deduplicate-subdirectory.test.js index 4eb62c3f7..fb52f72b8 100644 --- a/packages/url-utils/test/unit/utils/deduplicate-subdirectory.test.js +++ b/packages/url-utils/test/unit/utils/deduplicate-subdirectory.test.js @@ -2,7 +2,7 @@ // const testUtils = require('./utils'); require('../../utils'); -const deduplicateSubdirectory = require('../../../lib/utils/deduplicate-subdirectory'); +const deduplicateSubdirectory = require('../../../lib/utils/deduplicate-subdirectory').default; describe('utils: deduplicateSubdirectory()', function () { describe('with url', function () { diff --git a/packages/url-utils/test/unit/utils/html-absolute-to-relative.test.js b/packages/url-utils/test/unit/utils/html-absolute-to-relative.test.js index 4c3d28a3d..e2b7bd405 100644 --- a/packages/url-utils/test/unit/utils/html-absolute-to-relative.test.js +++ b/packages/url-utils/test/unit/utils/html-absolute-to-relative.test.js @@ -5,7 +5,7 @@ require('../../utils'); const sinon = require('sinon'); const cheerio = require('cheerio'); -const htmlAbsoluteToRelative = require('../../../lib/utils/html-absolute-to-relative'); +const htmlAbsoluteToRelative = require('../../../lib/utils/html-absolute-to-relative').default; describe('utils: htmlAbsoluteToRelative()', function () { const siteUrl = 'http://my-ghost-blog.com'; diff --git a/packages/url-utils/test/unit/utils/html-absolute-to-transform-ready.test.js b/packages/url-utils/test/unit/utils/html-absolute-to-transform-ready.test.js index 35bb59b02..add3e2e0f 100644 --- a/packages/url-utils/test/unit/utils/html-absolute-to-transform-ready.test.js +++ b/packages/url-utils/test/unit/utils/html-absolute-to-transform-ready.test.js @@ -6,8 +6,8 @@ const rewire = require('rewire'); const sinon = require('sinon'); const cheerio = require('cheerio'); -const htmlTransform = rewire('../../../lib/utils/html-transform'); -const htmlAbsoluteToTransformReady = require('../../../lib/utils/html-absolute-to-transform-ready'); +const htmlTransformModule = rewire('../../../lib/utils/html-transform'); +const htmlAbsoluteToTransformReady = require('../../../lib/utils/html-absolute-to-transform-ready').default; describe('utils: htmlAbsoluteToTransformReady()', function () { const siteUrl = 'http://my-ghost-blog.com'; @@ -239,7 +239,7 @@ describe('utils: htmlAbsoluteToTransformReady()', function () { beforeEach(function () { cheerioLoadSpy = sinon.spy(cheerio, 'load'); - rewireRestore = htmlTransform.__set__('cheerio', cheerio); + rewireRestore = htmlTransformModule.__set__('cheerio', cheerio); }); afterEach(function () { diff --git a/packages/url-utils/test/unit/utils/html-relative-to-absolute.test.js b/packages/url-utils/test/unit/utils/html-relative-to-absolute.test.js index 05e363151..46368c2d6 100644 --- a/packages/url-utils/test/unit/utils/html-relative-to-absolute.test.js +++ b/packages/url-utils/test/unit/utils/html-relative-to-absolute.test.js @@ -6,8 +6,8 @@ const sinon = require('sinon'); const rewire = require('rewire'); const cheerio = require('cheerio'); -const htmlTransform = rewire('../../../lib/utils/html-transform'); -const htmlRelativeToAbsolute = require('../../../lib/utils/html-relative-to-absolute'); +const htmlTransformModule = rewire('../../../lib/utils/html-transform'); +const htmlRelativeToAbsolute = require('../../../lib/utils/html-relative-to-absolute').default; describe('utils: htmlRelativeToAbsolute()', function () { const siteUrl = 'http://my-ghost-blog.com'; @@ -247,7 +247,7 @@ describe('utils: htmlRelativeToAbsolute()', function () { beforeEach(function () { cheerioLoadSpy = sinon.spy(cheerio, 'load'); - rewireRestore = htmlTransform.__set__('cheerio', cheerio); + rewireRestore = htmlTransformModule.__set__('cheerio', cheerio); }); afterEach(function () { diff --git a/packages/url-utils/test/unit/utils/html-relative-to-transform-ready.test.js b/packages/url-utils/test/unit/utils/html-relative-to-transform-ready.test.js index e184a5f1a..0f99adad0 100644 --- a/packages/url-utils/test/unit/utils/html-relative-to-transform-ready.test.js +++ b/packages/url-utils/test/unit/utils/html-relative-to-transform-ready.test.js @@ -5,7 +5,7 @@ require('../../utils'); const sinon = require('sinon'); const cheerio = require('cheerio'); -const htmlRelativeToTransformReady = require('../../../lib/utils/html-relative-to-transform-ready'); +const htmlRelativeToTransformReady = require('../../../lib/utils/html-relative-to-transform-ready').default; describe('utils: htmlRelativeToTransformReady()', function () { const siteUrl = 'http://my-ghost-blog.com'; diff --git a/packages/url-utils/test/unit/utils/is-ssl.test.js b/packages/url-utils/test/unit/utils/is-ssl.test.js index eb11d2af0..43a6b31fd 100644 --- a/packages/url-utils/test/unit/utils/is-ssl.test.js +++ b/packages/url-utils/test/unit/utils/is-ssl.test.js @@ -1,4 +1,4 @@ -const isSSL = require('../../../lib/utils/is-ssl'); +const isSSL = require('../../../lib/utils/is-ssl').default; describe('isSSL', function () { it('detects https protocol correctly', function () { diff --git a/packages/url-utils/test/unit/utils/lexical-absolute-to-relative.test.js b/packages/url-utils/test/unit/utils/lexical-absolute-to-relative.test.js index fe551bcc8..500a193da 100644 --- a/packages/url-utils/test/unit/utils/lexical-absolute-to-relative.test.js +++ b/packages/url-utils/test/unit/utils/lexical-absolute-to-relative.test.js @@ -2,8 +2,9 @@ // const testUtils = require('./utils'); require('../../utils'); -const UrlUtils = require('../../../lib/UrlUtils'); -const lexicalAbsoluteToRelative = require('../../../lib/utils/lexical-absolute-to-relative'); +const UrlUtilsModule = require('../../../lib/UrlUtils'); +const UrlUtils = UrlUtilsModule.default || UrlUtilsModule.UrlUtils; +const lexicalAbsoluteToRelative = require('../../../lib/utils/lexical-absolute-to-relative').default; describe('utils: lexicalAbsoluteToRelative()', function () { const siteUrl = 'http://my-ghost-blog.com'; diff --git a/packages/url-utils/test/unit/utils/lexical-absolute-to-transform-ready.test.js b/packages/url-utils/test/unit/utils/lexical-absolute-to-transform-ready.test.js index 10a37b285..4505b009c 100644 --- a/packages/url-utils/test/unit/utils/lexical-absolute-to-transform-ready.test.js +++ b/packages/url-utils/test/unit/utils/lexical-absolute-to-transform-ready.test.js @@ -2,8 +2,9 @@ // const testUtils = require('./utils'); require('../../utils'); -const UrlUtils = require('../../../lib/UrlUtils'); -const lexicalAbsoluteToTransformReady = require('../../../lib/utils/lexical-absolute-to-transform-ready'); +const UrlUtilsModule = require('../../../lib/UrlUtils'); +const UrlUtils = UrlUtilsModule.default || UrlUtilsModule.UrlUtils; +const lexicalAbsoluteToTransformReady = require('../../../lib/utils/lexical-absolute-to-transform-ready').default; describe('utils: lexicalAbsoluteToTransformReady()', function () { const siteUrl = 'http://my-ghost-blog.com'; diff --git a/packages/url-utils/test/unit/utils/lexical-relative-to-absolute.test.js b/packages/url-utils/test/unit/utils/lexical-relative-to-absolute.test.js index 0f4678dee..05536abc4 100644 --- a/packages/url-utils/test/unit/utils/lexical-relative-to-absolute.test.js +++ b/packages/url-utils/test/unit/utils/lexical-relative-to-absolute.test.js @@ -2,8 +2,9 @@ // const testUtils = require('./utils'); require('../../utils'); -const UrlUtils = require('../../../lib/UrlUtils'); -const lexicalRelativeToAbsolute = require('../../../lib/utils/lexical-relative-to-absolute'); +const UrlUtilsModule = require('../../../lib/UrlUtils'); +const UrlUtils = UrlUtilsModule.default || UrlUtilsModule.UrlUtils; +const lexicalRelativeToAbsolute = require('../../../lib/utils/lexical-relative-to-absolute').default; describe('utils: lexicalRelativeToAbsolute()', function () { const siteUrl = 'http://my-ghost-blog.com'; diff --git a/packages/url-utils/test/unit/utils/lexical-relative-to-transform-ready.test.js b/packages/url-utils/test/unit/utils/lexical-relative-to-transform-ready.test.js index ffed06fd1..0e57eb3c9 100644 --- a/packages/url-utils/test/unit/utils/lexical-relative-to-transform-ready.test.js +++ b/packages/url-utils/test/unit/utils/lexical-relative-to-transform-ready.test.js @@ -2,8 +2,9 @@ // const testUtils = require('./utils'); require('../../utils'); -const UrlUtils = require('../../../lib/UrlUtils'); -const lexicalRelativeToTransformReady = require('../../../lib/utils/lexical-relative-to-transform-ready'); +const UrlUtilsModule = require('../../../lib/UrlUtils'); +const UrlUtils = UrlUtilsModule.default || UrlUtilsModule.UrlUtils; +const lexicalRelativeToTransformReady = require('../../../lib/utils/lexical-relative-to-transform-ready').default; describe('utils: lexicalRelativeToTransformReady()', function () { const siteUrl = 'http://my-ghost-blog.com'; diff --git a/packages/url-utils/test/unit/utils/markdown-absolute-to-relative.test.js b/packages/url-utils/test/unit/utils/markdown-absolute-to-relative.test.js index 601077e34..e1e6e6a34 100644 --- a/packages/url-utils/test/unit/utils/markdown-absolute-to-relative.test.js +++ b/packages/url-utils/test/unit/utils/markdown-absolute-to-relative.test.js @@ -8,8 +8,10 @@ const rewire = require('rewire'); const sinon = require('sinon'); const remark = require('remark'); -const markdownTransform = rewire('../../../lib/utils/markdown-transform'); -const markdownAbsoluteToRelative = rewire('../../../lib/utils/markdown-absolute-to-relative'); +const markdownTransformModule = rewire('../../../lib/utils/markdown-transform'); +const markdownTransform = markdownTransformModule.default || markdownTransformModule; +const markdownAbsoluteToRelativeModule = rewire('../../../lib/utils/markdown-absolute-to-relative'); +const markdownAbsoluteToRelative = markdownAbsoluteToRelativeModule.default || markdownAbsoluteToRelativeModule; describe('utils: markdownAbsoluteToRelative()', function () { const siteUrl = 'http://my-ghost-blog.com'; @@ -123,8 +125,8 @@ Testing Inline with **markdown** beforeEach(function () { sandbox = sinon.createSandbox(); remarkSpy = sinon.spy(remark); - markdownTransform.__set__('remark', remarkSpy); - markdownAbsoluteToRelative.__set__('markdownTransform', markdownTransform); + markdownTransformModule.__set__('remark', remarkSpy); + markdownAbsoluteToRelativeModule.__set__('markdownTransform', markdownTransform); }); afterEach(function () { diff --git a/packages/url-utils/test/unit/utils/markdown-absolute-to-transform-ready.test.js b/packages/url-utils/test/unit/utils/markdown-absolute-to-transform-ready.test.js index 2632114e0..7722a4574 100644 --- a/packages/url-utils/test/unit/utils/markdown-absolute-to-transform-ready.test.js +++ b/packages/url-utils/test/unit/utils/markdown-absolute-to-transform-ready.test.js @@ -8,8 +8,10 @@ const rewire = require('rewire'); const sinon = require('sinon'); const remark = require('remark'); -const markdownTransform = rewire('../../../lib/utils/markdown-transform'); -const markdownAbsoluteToTransformReady = rewire('../../../lib/utils/markdown-absolute-to-transform-ready'); +const markdownTransformModule = rewire('../../../lib/utils/markdown-transform'); +const markdownTransform = markdownTransformModule.default || markdownTransformModule; +const markdownAbsoluteToTransformReadyModule = rewire('../../../lib/utils/markdown-absolute-to-transform-ready'); +const markdownAbsoluteToTransformReady = markdownAbsoluteToTransformReadyModule.default || markdownAbsoluteToTransformReadyModule; describe('utils: markdownAbsoluteToTransformReady()', function () { const siteUrl = 'http://my-ghost-blog.com'; @@ -123,8 +125,8 @@ Testing Inline with **markdown** beforeEach(function () { sandbox = sinon.createSandbox(); remarkSpy = sinon.spy(remark); - markdownTransform.__set__('remark', remarkSpy); - markdownAbsoluteToTransformReady.__set__('markdownTransform', markdownTransform); + markdownTransformModule.__set__('remark', remarkSpy); + markdownAbsoluteToTransformReadyModule.__set__('markdownTransform', markdownTransform); }); afterEach(function () { diff --git a/packages/url-utils/test/unit/utils/markdown-relative-to-absolute.test.js b/packages/url-utils/test/unit/utils/markdown-relative-to-absolute.test.js index 39e250f22..bcd83bd9c 100644 --- a/packages/url-utils/test/unit/utils/markdown-relative-to-absolute.test.js +++ b/packages/url-utils/test/unit/utils/markdown-relative-to-absolute.test.js @@ -8,8 +8,10 @@ const sinon = require('sinon'); const rewire = require('rewire'); const remark = require('remark'); -const markdownTransform = rewire('../../../lib/utils/markdown-transform'); -const markdownRelativeToAbsolute = rewire('../../../lib/utils/markdown-relative-to-absolute'); +const markdownTransformModule = rewire('../../../lib/utils/markdown-transform'); +const markdownTransform = markdownTransformModule.default || markdownTransformModule; +const markdownRelativeToAbsoluteModule = rewire('../../../lib/utils/markdown-relative-to-absolute'); +const markdownRelativeToAbsolute = markdownRelativeToAbsoluteModule.default || markdownRelativeToAbsoluteModule; describe('utils: markdownRelativeToAbsolute()', function () { const siteUrl = 'http://my-ghost-blog.com'; @@ -117,8 +119,8 @@ Testing Inline with **markdown** beforeEach(function () { sandbox = sinon.createSandbox(); remarkSpy = sinon.spy(remark); - markdownTransform.__set__('remark', remarkSpy); - markdownRelativeToAbsolute.__set__('markdownTransform', markdownTransform); + markdownTransformModule.__set__('remark', remarkSpy); + markdownRelativeToAbsoluteModule.__set__('markdownTransform', markdownTransform); }); afterEach(function () { diff --git a/packages/url-utils/test/unit/utils/markdown-relative-to-transform-ready.test.js b/packages/url-utils/test/unit/utils/markdown-relative-to-transform-ready.test.js index a783f307b..4b497b903 100644 --- a/packages/url-utils/test/unit/utils/markdown-relative-to-transform-ready.test.js +++ b/packages/url-utils/test/unit/utils/markdown-relative-to-transform-ready.test.js @@ -8,8 +8,10 @@ const sinon = require('sinon'); const rewire = require('rewire'); const remark = require('remark'); -const markdownTransform = rewire('../../../lib/utils/markdown-transform'); -const markdownRelativeToTransformReady = rewire('../../../lib/utils/markdown-relative-to-transform-ready'); +const markdownTransformModule = rewire('../../../lib/utils/markdown-transform'); +const markdownTransform = markdownTransformModule.default || markdownTransformModule; +const markdownRelativeToTransformReadyModule = rewire('../../../lib/utils/markdown-relative-to-transform-ready'); +const markdownRelativeToTransformReady = markdownRelativeToTransformReadyModule.default || markdownRelativeToTransformReadyModule; describe('utils: markdownRelativeToTransformReady()', function () { const siteUrl = 'http://my-ghost-blog.com'; @@ -143,8 +145,8 @@ Just testing beforeEach(function () { sandbox = sinon.createSandbox(); remarkSpy = sinon.spy(remark); - markdownTransform.__set__('remark', remarkSpy); - markdownRelativeToTransformReady.__set__('markdownTransform', markdownTransform); + markdownTransformModule.__set__('remark', remarkSpy); + markdownRelativeToTransformReadyModule.__set__('markdownTransform', markdownTransform); }); afterEach(function () { diff --git a/packages/url-utils/test/unit/utils/mobiledoc-absolute-to-relative.test.js b/packages/url-utils/test/unit/utils/mobiledoc-absolute-to-relative.test.js index b0d60684c..fd1e713e2 100644 --- a/packages/url-utils/test/unit/utils/mobiledoc-absolute-to-relative.test.js +++ b/packages/url-utils/test/unit/utils/mobiledoc-absolute-to-relative.test.js @@ -4,7 +4,7 @@ require('../../utils'); const sinon = require('sinon'); -const mobiledocAbsoluteToRelative = require('../../../lib/utils/mobiledoc-absolute-to-relative'); +const mobiledocAbsoluteToRelative = require('../../../lib/utils/mobiledoc-absolute-to-relative').default; describe('utils: mobiledocAbsoluteToRelative()', function () { const siteUrl = 'http://my-ghost-blog.com'; diff --git a/packages/url-utils/test/unit/utils/mobiledoc-absolute-to-transform-ready.test.js b/packages/url-utils/test/unit/utils/mobiledoc-absolute-to-transform-ready.test.js index 5105f8f3f..5b42b0f94 100644 --- a/packages/url-utils/test/unit/utils/mobiledoc-absolute-to-transform-ready.test.js +++ b/packages/url-utils/test/unit/utils/mobiledoc-absolute-to-transform-ready.test.js @@ -4,7 +4,7 @@ require('../../utils'); const sinon = require('sinon'); -const mobiledocAbsoluteToTransformReady = require('../../../lib/utils/mobiledoc-absolute-to-transform-ready'); +const mobiledocAbsoluteToTransformReady = require('../../../lib/utils/mobiledoc-absolute-to-transform-ready').default; describe('utils: mobiledocAbsoluteToTransformReady()', function () { const siteUrl = 'http://my-ghost-blog.com'; diff --git a/packages/url-utils/test/unit/utils/mobiledoc-relative-to-absolute.test.js b/packages/url-utils/test/unit/utils/mobiledoc-relative-to-absolute.test.js index 1de7842e9..19eef6e9b 100644 --- a/packages/url-utils/test/unit/utils/mobiledoc-relative-to-absolute.test.js +++ b/packages/url-utils/test/unit/utils/mobiledoc-relative-to-absolute.test.js @@ -4,7 +4,7 @@ require('../../utils'); const sinon = require('sinon'); -const mobiledocRelativeToAbsolute = require('../../../lib/utils/mobiledoc-relative-to-absolute'); +const mobiledocRelativeToAbsolute = require('../../../lib/utils/mobiledoc-relative-to-absolute').default; describe('utils: mobiledocRelativeToAbsolute()', function () { const siteUrl = 'http://my-ghost-blog.com'; diff --git a/packages/url-utils/test/unit/utils/mobiledoc-relative-to-transform-ready.test.js b/packages/url-utils/test/unit/utils/mobiledoc-relative-to-transform-ready.test.js index e6bf7cbb0..ca24afc5e 100644 --- a/packages/url-utils/test/unit/utils/mobiledoc-relative-to-transform-ready.test.js +++ b/packages/url-utils/test/unit/utils/mobiledoc-relative-to-transform-ready.test.js @@ -4,7 +4,7 @@ require('../../utils'); const sinon = require('sinon'); -const mobiledocRelativeToTransformReady = require('../../../lib/utils/mobiledoc-relative-to-transform-ready'); +const mobiledocRelativeToTransformReady = require('../../../lib/utils/mobiledoc-relative-to-transform-ready').default; describe('utils: mobiledocRelativeToTransformReady()', function () { const siteUrl = 'http://my-ghost-blog.com'; diff --git a/packages/url-utils/test/unit/utils/plaintext-absolute-to-transform-ready.test.js b/packages/url-utils/test/unit/utils/plaintext-absolute-to-transform-ready.test.js index 0581bb495..abf569b9f 100644 --- a/packages/url-utils/test/unit/utils/plaintext-absolute-to-transform-ready.test.js +++ b/packages/url-utils/test/unit/utils/plaintext-absolute-to-transform-ready.test.js @@ -2,7 +2,7 @@ // const testUtils = require('./utils'); require('../../utils'); -const plaintextAbsoluteToTransformReady = require('../../../lib/utils/plaintext-absolute-to-transform-ready'); +const plaintextAbsoluteToTransformReady = require('../../../lib/utils/plaintext-absolute-to-transform-ready').default; describe('utils: plaintextAbsoluteToTransformReady', function () { it('works', function () { diff --git a/packages/url-utils/test/unit/utils/plaintext-relative-to-transform-ready.test.js b/packages/url-utils/test/unit/utils/plaintext-relative-to-transform-ready.test.js index 2b928df3b..86209c081 100644 --- a/packages/url-utils/test/unit/utils/plaintext-relative-to-transform-ready.test.js +++ b/packages/url-utils/test/unit/utils/plaintext-relative-to-transform-ready.test.js @@ -2,7 +2,7 @@ // const testUtils = require('./utils'); require('../../utils'); -const plaintextRelativeToTransformReady = require('../../../lib/utils/plaintext-relative-to-transform-ready'); +const plaintextRelativeToTransformReady = require('../../../lib/utils/plaintext-relative-to-transform-ready').default; describe('utils: plaintextRelativeToTransformReady', function () { it('works', function () { diff --git a/packages/url-utils/test/unit/utils/plaintext-to-transform-ready.test.js b/packages/url-utils/test/unit/utils/plaintext-to-transform-ready.test.js index e1a371c13..415a6eea4 100644 --- a/packages/url-utils/test/unit/utils/plaintext-to-transform-ready.test.js +++ b/packages/url-utils/test/unit/utils/plaintext-to-transform-ready.test.js @@ -2,7 +2,7 @@ // const testUtils = require('./utils'); require('../../utils'); -const plaintextToTransformReady = require('../../../lib/utils/plaintext-to-transform-ready'); +const plaintextToTransformReady = require('../../../lib/utils/plaintext-to-transform-ready').default; describe('utils: plaintextToTransformReady', function () { it('works', function () { diff --git a/packages/url-utils/test/unit/utils/relative-to-absolute.test.js b/packages/url-utils/test/unit/utils/relative-to-absolute.test.js index 2c344a86d..5dcc3ad6c 100644 --- a/packages/url-utils/test/unit/utils/relative-to-absolute.test.js +++ b/packages/url-utils/test/unit/utils/relative-to-absolute.test.js @@ -2,7 +2,7 @@ // const testUtils = require('./utils'); require('../../utils'); -const relativeToAbsolute = require('../../../lib/utils/relative-to-absolute'); +const relativeToAbsolute = require('../../../lib/utils/relative-to-absolute').default; describe('utils: relativeToAbsolute()', function () { it('ignores absolute URLs', function () { diff --git a/packages/url-utils/test/unit/utils/relative-to-transform-ready.test.js b/packages/url-utils/test/unit/utils/relative-to-transform-ready.test.js index 7d5cc30d5..4d2cb48bb 100644 --- a/packages/url-utils/test/unit/utils/relative-to-transform-ready.test.js +++ b/packages/url-utils/test/unit/utils/relative-to-transform-ready.test.js @@ -2,7 +2,7 @@ // const testUtils = require('./utils'); require('../../utils'); -const relativeToTransformReady = require('../../../lib/utils/relative-to-transform-ready'); +const relativeToTransformReady = require('../../../lib/utils/relative-to-transform-ready').default; describe('utils: relativeToTransformReady()', function () { it('ignores absolute URLs', function () { diff --git a/packages/url-utils/test/unit/utils/replace-permalink.test.js b/packages/url-utils/test/unit/utils/replace-permalink.test.js index 10acfdecf..cb21fca92 100644 --- a/packages/url-utils/test/unit/utils/replace-permalink.test.js +++ b/packages/url-utils/test/unit/utils/replace-permalink.test.js @@ -3,7 +3,7 @@ require('../../utils'); const moment = require('moment-timezone'); -const replacePermalink = require('../../../lib/utils/replace-permalink'); +const replacePermalink = require('../../../lib/utils/replace-permalink').default; describe('utils: replacePermalink()', function () { it('permalink is /:slug/, timezone is default', function () { diff --git a/packages/url-utils/test/unit/utils/strip-subdirectory-from-path.test.js b/packages/url-utils/test/unit/utils/strip-subdirectory-from-path.test.js index 7855fd9fb..032456956 100644 --- a/packages/url-utils/test/unit/utils/strip-subdirectory-from-path.test.js +++ b/packages/url-utils/test/unit/utils/strip-subdirectory-from-path.test.js @@ -2,7 +2,7 @@ // const testUtils = require('./utils'); require('../../utils'); -const stripSubdirectoryFromPath = require('../../../lib/utils/strip-subdirectory-from-path'); +const stripSubdirectoryFromPath = require('../../../lib/utils/strip-subdirectory-from-path').default; describe('utils: stripSubdomainFromPath()', function () { it('ignores rootUrl with no subdirectory', function () { diff --git a/packages/url-utils/test/unit/utils/transform-ready-to-absolute.test.js b/packages/url-utils/test/unit/utils/transform-ready-to-absolute.test.js index 536a3117c..4c06cf41c 100644 --- a/packages/url-utils/test/unit/utils/transform-ready-to-absolute.test.js +++ b/packages/url-utils/test/unit/utils/transform-ready-to-absolute.test.js @@ -4,7 +4,7 @@ require('../../utils'); const fs = require('fs'); const path = require('path'); -const transformReadyToAbsolute = require('../../../lib/utils/transform-ready-to-absolute'); +const transformReadyToAbsolute = require('../../../lib/utils/transform-ready-to-absolute').default; describe('utils: transformReadyToAbsolute()', function () { describe('single url', function () { diff --git a/packages/url-utils/test/unit/utils/transform-ready-to-relative.test.js b/packages/url-utils/test/unit/utils/transform-ready-to-relative.test.js index d261b9ae6..2dc6ccc78 100644 --- a/packages/url-utils/test/unit/utils/transform-ready-to-relative.test.js +++ b/packages/url-utils/test/unit/utils/transform-ready-to-relative.test.js @@ -4,7 +4,7 @@ require('../../utils'); const fs = require('fs'); const path = require('path'); -const transformReadyToRelative = require('../../../lib/utils/transform-ready-to-relative'); +const transformReadyToRelative = require('../../../lib/utils/transform-ready-to-relative').default; describe('utils: transformReadyToRelative()', function () { describe('single url', function () { diff --git a/packages/url-utils/test/unit/utils/url-join.test.js b/packages/url-utils/test/unit/utils/url-join.test.js index af36673ba..b8c97b2a5 100644 --- a/packages/url-utils/test/unit/utils/url-join.test.js +++ b/packages/url-utils/test/unit/utils/url-join.test.js @@ -2,7 +2,7 @@ // const testUtils = require('./utils'); require('../../utils'); -const urlJoin = require('../../../lib/utils/url-join'); +const urlJoin = require('../../../lib/utils/url-join').default; describe('utils: urlJoin()', function () { it('should deduplicate slashes', function () { From ce86b1554151625ef4dbbb6fbfa563de321e8e2a Mon Sep 17 00:00:00 2001 From: Fabien O'Carroll Date: Wed, 22 Oct 2025 09:37:11 +0700 Subject: [PATCH 3/3] Fixed package.json and types --- packages/url-utils/package.json | 14 +++----- .../url-utils/src/utils/html-transform.ts | 2 +- .../url-utils/src/utils/markdown-transform.ts | 7 ++-- yarn.lock | 34 +++++++++++++++++++ 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/packages/url-utils/package.json b/packages/url-utils/package.json index 086498225..fa29871d2 100644 --- a/packages/url-utils/package.json +++ b/packages/url-utils/package.json @@ -26,20 +26,16 @@ }, "devDependencies": { "@tryghost/config-url-helpers": "^1.0.17", - "@types/cheerio": "^0.22.35", - "@types/lodash": "^4.17.15", - "@types/moment": "^2.13.0", - "@types/moment-timezone": "^0.5.30", - "@types/node": "^20.16.3", - "@types/remark": "^14.0.4", - "@types/unist": "^2.0.10", - "@types/unist-util-visit": "^4.1.0", + "@types/cheerio": "0.22.35", + "@types/lodash": "4.17.15", + "@types/node": "20.16.3", + "@types/unist": "3.0.3", "c8": "10.1.3", "mocha": "11.7.4", "rewire": "9.0.1", "should": "13.2.3", "sinon": "21.0.0", - "typescript": "^5.5.4" + "typescript": "5.5.4" }, "dependencies": { "cheerio": "^0.22.0", diff --git a/packages/url-utils/src/utils/html-transform.ts b/packages/url-utils/src/utils/html-transform.ts index 86235b42b..de364ae07 100644 --- a/packages/url-utils/src/utils/html-transform.ts +++ b/packages/url-utils/src/utils/html-transform.ts @@ -79,7 +79,7 @@ function htmlTransform( ['href', 'src', 'srcset', 'style'].forEach((attributeName) => { htmlContent('[' + attributeName + ']').each((ix, el) => { // ignore elems and html inside of elements - if (el.name === 'stream' || htmlContent(el).closest('code').length) { + if (('name' in el && el.name === 'stream') || htmlContent(el).closest('code').length) { addReplacement({ name: attributeName, originalValue: htmlContent(el).attr(attributeName) ?? '', diff --git a/packages/url-utils/src/utils/markdown-transform.ts b/packages/url-utils/src/utils/markdown-transform.ts index f4f68675b..49f5ee1fd 100644 --- a/packages/url-utils/src/utils/markdown-transform.ts +++ b/packages/url-utils/src/utils/markdown-transform.ts @@ -18,12 +18,13 @@ interface MarkdownReplacement { end: number; } -type RemarkNode = Node & { +type RemarkNode = { + type?: string; value?: string; url?: string; position?: { - start?: {offset?: number} & Record; - end?: {offset?: number} & Record; + start?: {offset?: number; line?: number; column?: number}; + end?: {offset?: number; line?: number; column?: number}; }; }; diff --git a/yarn.lock b/yarn.lock index 0ec82b7ad..7e6231d71 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3348,6 +3348,13 @@ dependencies: "@types/deep-eql" "*" +"@types/cheerio@0.22.35": + version "0.22.35" + resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.35.tgz#0d16dc1f24d426231c181b9c31847f673867595f" + integrity sha512-yD57BchKRvTV+JD53UZ6PD8KWY5g5rvvMLRnZR3EQBCZXiDT/HR+pKpMzFGlWNhFrXlo7VPZXtKvIEwZkAWOIA== + dependencies: + "@types/node" "*" + "@types/color-convert@*": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/color-convert/-/color-convert-2.0.4.tgz#843398ae71e951dc5415d202dfd5e43108823eeb" @@ -3399,6 +3406,11 @@ dependencies: "@types/node" "*" +"@types/lodash@4.17.15": + version "4.17.15" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.15.tgz#12d4af0ed17cc7600ce1f9980cec48fc17ad1e89" + integrity sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw== + "@types/minimatch@^3.0.3": version "3.0.5" resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" @@ -3421,6 +3433,13 @@ dependencies: undici-types "~7.12.0" +"@types/node@20.16.3": + version "20.16.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.16.3.tgz#7b4f9a37091cf03a0c2561bf76a9a55f03f4f523" + integrity sha512-/wdGiWRkMOm53gAsSyFMXFZHbVg7C6CbkrzHNpaHoYfsUWPg7m6ZRKtvQjgvQ9i8WT540a3ydRlRQbxjY30XxQ== + dependencies: + undici-types "~6.19.2" + "@types/node@^22.0.0": version "22.18.8" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.18.8.tgz#738d9dafa38f6e0c467687c158f8e1ca2d7d8eaa" @@ -3495,6 +3514,11 @@ resolved "https://registry.yarnpkg.com/@types/symlink-or-copy/-/symlink-or-copy-1.2.2.tgz#51b1c00b516a5774ada5d611e65eb123f988ef8d" integrity sha512-MQ1AnmTLOncwEf9IVU+B2e4Hchrku5N67NkgcAHW0p3sdzPe0FNMANxEm6OJUzPniEQGkeT3OROLlCwZJLWFZA== +"@types/unist@3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" + integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== + "@types/unist@^2.0.0", "@types/unist@^2.0.2": version "2.0.11" resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.11.tgz#11af57b127e32487774841f7a4e54eab166d03c4" @@ -12195,6 +12219,11 @@ typescript@5.2.2: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== +typescript@5.5.4: + version "5.5.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" + integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== + typescript@5.8.2: version "5.8.2" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.2.tgz#8170b3702f74b79db2e5a96207c15e65807999e4" @@ -12238,6 +12267,11 @@ underscore.string@~3.3.4: sprintf-js "^1.1.1" util-deprecate "^1.0.2" +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + undici-types@~6.21.0: version "6.21.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb"