@tryghost/url-utils 2.0.8 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +1 -0
- package/lib/{index.js → url-utils.js} +30 -68
- package/lib/utils/_html-transform.js +21 -3
- package/lib/utils/index.js +0 -2
- package/package.json +11 -10
- package/lib/utils/get-api-path.js +0 -18
- package/lib/utils/get-version-path.js +0 -20
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./lib/url-utils');
|
|
@@ -18,65 +18,30 @@ module.exports = class UrlUtils {
|
|
|
18
18
|
* @param {Function} options.getSubdir
|
|
19
19
|
* @param {Function} options.getSiteUrl
|
|
20
20
|
* @param {Function} options.getAdminUrl Ghost instance admin URL
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
* @param {('content' | 'admin')} [options.defaultApiType='content'] default API type to be used and is one of the values from options.apiVersions
|
|
21
|
+
* @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
|
|
22
|
+
* @param {('content' | 'admin')} [options.defaultApiType='content'] default API type to be used
|
|
24
23
|
* @param {Object} [options.slugs] object with 2 properties reserved and protected containing arrays of special case slugs
|
|
25
24
|
* @param {Number} [options.redirectCacheMaxAge]
|
|
26
|
-
* @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
|
|
27
25
|
* @param {String} [options.staticImageUrlPrefix='content/images'] static prefix for serving images. Should not be passed in, unless customizing ghost instance image storage
|
|
28
26
|
*/
|
|
29
27
|
constructor(options = {}) {
|
|
30
28
|
const defaultOptions = {
|
|
31
|
-
apiVersions: null,
|
|
32
29
|
slugs: null,
|
|
33
30
|
redirectCacheMaxAge: null,
|
|
34
31
|
baseApiPath: '/ghost/api',
|
|
35
|
-
defaultApiVersion: 'v4',
|
|
36
32
|
defaultApiType: 'content',
|
|
37
33
|
staticImageUrlPrefix: 'content/images'
|
|
38
34
|
};
|
|
39
35
|
|
|
40
36
|
this._config = assignOptions({}, defaultOptions, options);
|
|
41
37
|
|
|
42
|
-
this._defaultApiPathOptions = {
|
|
43
|
-
baseApiPath: this._config.baseApiPath,
|
|
44
|
-
version: this._config.defaultApiVersion,
|
|
45
|
-
type: this._config.defaultApiType,
|
|
46
|
-
apiVersions: this._config.apiVersions
|
|
47
|
-
};
|
|
48
|
-
|
|
49
38
|
this.getSubdir = options.getSubdir;
|
|
50
39
|
this.getSiteUrl = options.getSiteUrl;
|
|
51
40
|
this.getAdminUrl = options.getAdminUrl;
|
|
52
41
|
}
|
|
53
42
|
|
|
54
|
-
/**
|
|
55
|
-
* Returns API path combining base path and path for specific version asked or deprecated by default
|
|
56
|
-
* @param {Object} options
|
|
57
|
-
* @param {string} [options.version="v4"] for which to get the path (v2, v3, canary, etc)
|
|
58
|
-
* @param {string} [options.type="content"] (admin, content, members)
|
|
59
|
-
* @return {string} API Path for version
|
|
60
|
-
*/
|
|
61
|
-
getApiPath(options = {}) {
|
|
62
|
-
const _options = assignOptions({}, this._defaultApiPathOptions, options);
|
|
63
|
-
return utils.getApiPath(_options);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Returns path containing only the path for the specific version asked or deprecated by default
|
|
68
|
-
* @param {Object} options
|
|
69
|
-
* @param {string} [options.version="v4"] for which to get the path (v2, v3, canary, etc)
|
|
70
|
-
* @param {string} [options.type="content"] (admin, content)
|
|
71
|
-
* @return {string} API version path
|
|
72
|
-
*/
|
|
73
|
-
getVersionPath(options = {}) {
|
|
74
|
-
const _options = assignOptions({}, this._defaultApiPathOptions, options);
|
|
75
|
-
return utils.getVersionPath(_options);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
43
|
getProtectedSlugs() {
|
|
79
|
-
|
|
44
|
+
let subDir = this.getSubdir();
|
|
80
45
|
|
|
81
46
|
if (!_.isEmpty(subDir)) {
|
|
82
47
|
return this._config.slugs.concat([subDir.split('/').pop()]);
|
|
@@ -143,18 +108,18 @@ module.exports = class UrlUtils {
|
|
|
143
108
|
// This is probably not the right place for this, but it's the best place for now
|
|
144
109
|
// @TODO: rewrite, very hard to read, create private functions!
|
|
145
110
|
urlFor(context, data, absolute) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
111
|
+
let urlPath = '/';
|
|
112
|
+
let secure;
|
|
113
|
+
let imagePathRe;
|
|
114
|
+
let knownObjects = ['image', 'nav'];
|
|
115
|
+
let baseUrl;
|
|
116
|
+
let hostname;
|
|
117
|
+
|
|
118
|
+
// this will become really big
|
|
119
|
+
let knownPaths = {
|
|
120
|
+
home: '/',
|
|
121
|
+
sitemap_xsl: '/sitemap.xsl'
|
|
122
|
+
};
|
|
158
123
|
|
|
159
124
|
// Make data properly optional
|
|
160
125
|
if (_.isBoolean(data)) {
|
|
@@ -208,32 +173,29 @@ module.exports = class UrlUtils {
|
|
|
208
173
|
urlPath = urlPath.replace(/\/$/, '');
|
|
209
174
|
}
|
|
210
175
|
} else if (context === 'admin') {
|
|
211
|
-
|
|
176
|
+
let adminUrl = this.getAdminUrl() || this.getSiteUrl();
|
|
177
|
+
let adminPath = '/ghost/';
|
|
212
178
|
|
|
213
179
|
if (absolute) {
|
|
214
|
-
urlPath
|
|
180
|
+
urlPath = this.urlJoin(adminUrl, adminPath);
|
|
215
181
|
} else {
|
|
216
|
-
urlPath =
|
|
182
|
+
urlPath = adminPath;
|
|
217
183
|
}
|
|
218
184
|
} else if (context === 'api') {
|
|
219
|
-
|
|
220
|
-
let apiPath = this.
|
|
221
|
-
|
|
222
|
-
// CASE: with or without protocol? If your blog url (or admin url) is configured to http, it's still possible that e.g. nginx allows both https+http.
|
|
223
|
-
// So it depends how you serve your blog. The main focus here is to avoid cors problems.
|
|
224
|
-
// @TODO: rename cors
|
|
225
|
-
if (data && data.cors) {
|
|
226
|
-
if (!urlPath.match(/^https:/)) {
|
|
227
|
-
urlPath = urlPath.replace(/^.*?:\/\//g, '//');
|
|
228
|
-
}
|
|
229
|
-
}
|
|
185
|
+
let adminUrl = this.getAdminUrl() || this.getSiteUrl();
|
|
186
|
+
let apiPath = this._config.baseApiPath + '/';
|
|
230
187
|
|
|
231
|
-
if (data && data.
|
|
232
|
-
apiPath
|
|
188
|
+
if (data.type && ['admin', 'content'].includes(data.type)) {
|
|
189
|
+
apiPath += data.type;
|
|
190
|
+
} else {
|
|
191
|
+
apiPath += this._config.defaultApiType;
|
|
233
192
|
}
|
|
234
193
|
|
|
194
|
+
// Ensure we end with a trailing slash
|
|
195
|
+
apiPath += '/';
|
|
196
|
+
|
|
235
197
|
if (absolute) {
|
|
236
|
-
urlPath =
|
|
198
|
+
urlPath = this.urlJoin(adminUrl, apiPath);
|
|
237
199
|
} else {
|
|
238
200
|
urlPath = apiPath;
|
|
239
201
|
}
|
|
@@ -257,7 +219,7 @@ module.exports = class UrlUtils {
|
|
|
257
219
|
}
|
|
258
220
|
|
|
259
221
|
redirectToAdmin(status, res, adminPath) {
|
|
260
|
-
|
|
222
|
+
let redirectUrl = this.urlJoin(this.urlFor('admin', true), adminPath, '/');
|
|
261
223
|
|
|
262
224
|
if (status === 301) {
|
|
263
225
|
return this.redirect301(res, redirectUrl);
|
|
@@ -8,6 +8,18 @@ function extractSrcsetUrls(srcset = '') {
|
|
|
8
8
|
});
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
function extractStyleUrls(style = '') {
|
|
12
|
+
const urls = [];
|
|
13
|
+
const regex = /url\(['|"]([^)]+)['|"]\)/g;
|
|
14
|
+
let match;
|
|
15
|
+
|
|
16
|
+
while ((match = regex.exec(style)) !== null) {
|
|
17
|
+
urls.push(match[1]);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return urls;
|
|
21
|
+
}
|
|
22
|
+
|
|
11
23
|
function htmlTransform(html = '', siteUrl, transformFunction, itemPath, _options) {
|
|
12
24
|
const defaultOptions = {assetsOnly: false, secure: false};
|
|
13
25
|
const options = Object.assign({}, defaultOptions, _options || {});
|
|
@@ -42,7 +54,7 @@ function htmlTransform(html = '', siteUrl, transformFunction, itemPath, _options
|
|
|
42
54
|
}
|
|
43
55
|
|
|
44
56
|
// find all of the relative url attributes that we care about
|
|
45
|
-
['href', 'src', 'srcset'].forEach((attributeName) => {
|
|
57
|
+
['href', 'src', 'srcset', 'style'].forEach((attributeName) => {
|
|
46
58
|
htmlContent('[' + attributeName + ']').each((ix, el) => {
|
|
47
59
|
// ignore <stream> elems and html inside of <code> elements
|
|
48
60
|
if (el.name === 'stream' || htmlContent(el).closest('code').length) {
|
|
@@ -57,8 +69,14 @@ function htmlTransform(html = '', siteUrl, transformFunction, itemPath, _options
|
|
|
57
69
|
el = htmlContent(el);
|
|
58
70
|
const originalValue = el.attr(attributeName);
|
|
59
71
|
|
|
60
|
-
if (attributeName === 'srcset') {
|
|
61
|
-
|
|
72
|
+
if (attributeName === 'srcset' || attributeName === 'style') {
|
|
73
|
+
let urls;
|
|
74
|
+
|
|
75
|
+
if (attributeName === 'srcset') {
|
|
76
|
+
urls = extractSrcsetUrls(originalValue);
|
|
77
|
+
} else {
|
|
78
|
+
urls = extractStyleUrls(originalValue);
|
|
79
|
+
}
|
|
62
80
|
const absoluteUrls = urls.map(url => transformFunction(url, siteUrl, itemPath, options));
|
|
63
81
|
let transformedValue = originalValue;
|
|
64
82
|
|
package/lib/utils/index.js
CHANGED
|
@@ -3,8 +3,6 @@ module.exports = {
|
|
|
3
3
|
absoluteToTransformReady: require('./absolute-to-transform-ready'),
|
|
4
4
|
deduplicateDoubleSlashes: require('./deduplicate-double-slashes'),
|
|
5
5
|
deduplicateSubdirectory: require('./deduplicate-subdirectory'),
|
|
6
|
-
getApiPath: require('./get-api-path'),
|
|
7
|
-
getVersionPath: require('./get-version-path'),
|
|
8
6
|
htmlAbsoluteToRelative: require('./html-absolute-to-relative'),
|
|
9
7
|
htmlRelativeToAbsolute: require('./html-relative-to-absolute'),
|
|
10
8
|
htmlAbsoluteToTransformReady: require('./html-absolute-to-transform-ready'),
|
package/package.json
CHANGED
|
@@ -1,29 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tryghost/url-utils",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"repository": "https://github.com/TryGhost/SDK/tree/master/packages/url-utils",
|
|
5
5
|
"author": "Ghost Foundation",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"main": "
|
|
7
|
+
"main": "index.js",
|
|
8
8
|
"scripts": {
|
|
9
9
|
"dev": "echo \"Implement me!\"",
|
|
10
|
-
"test": "NODE_ENV=testing c8 --all --reporter text --reporter cobertura mocha './test/**/*.test.js'",
|
|
10
|
+
"test": "NODE_ENV=testing c8 --all --reporter text --reporter cobertura --reporter html mocha './test/**/*.test.js'",
|
|
11
11
|
"lint": "eslint . --ext .js --cache",
|
|
12
12
|
"posttest": "yarn lint"
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
|
-
"lib/"
|
|
15
|
+
"lib/",
|
|
16
|
+
"index.js"
|
|
16
17
|
],
|
|
17
18
|
"publishConfig": {
|
|
18
19
|
"access": "public"
|
|
19
20
|
},
|
|
20
21
|
"devDependencies": {
|
|
21
|
-
"@tryghost/config-url-helpers": "0.1.
|
|
22
|
-
"c8": "7.11.
|
|
23
|
-
"mocha": "
|
|
24
|
-
"rewire": "
|
|
22
|
+
"@tryghost/config-url-helpers": "0.1.8",
|
|
23
|
+
"c8": "7.11.2",
|
|
24
|
+
"mocha": "10.0.0",
|
|
25
|
+
"rewire": "6.0.0",
|
|
25
26
|
"should": "13.2.3",
|
|
26
|
-
"sinon": "
|
|
27
|
+
"sinon": "14.0.0"
|
|
27
28
|
},
|
|
28
29
|
"dependencies": {
|
|
29
30
|
"cheerio": "^0.22.0",
|
|
@@ -33,5 +34,5 @@
|
|
|
33
34
|
"remark-footnotes": "^1.0.0",
|
|
34
35
|
"unist-util-visit": "^2.0.0"
|
|
35
36
|
},
|
|
36
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "2f7fb1fef3f2464f000974c8e0e13e25eabd6f6d"
|
|
37
38
|
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
const getVersionPath = require('./get-version-path');
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Returns API path combining base path and path for specific version/type
|
|
5
|
-
* @param {Object} options
|
|
6
|
-
* @param {string} options.version (v2, v3, v4, canary, etc)
|
|
7
|
-
* @param {string} options.type (admin, content, members)
|
|
8
|
-
* @param {string} options.baseApiPath
|
|
9
|
-
* @param {Object} options.apiVersions
|
|
10
|
-
* @return {string} API Path for version
|
|
11
|
-
*/
|
|
12
|
-
function getApiPath(options) {
|
|
13
|
-
const versionPath = getVersionPath(options);
|
|
14
|
-
|
|
15
|
-
return `${options.baseApiPath}${versionPath}`;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
module.exports = getApiPath;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Returns API path combining base path and path for specific version/type
|
|
3
|
-
* @param {Object} options
|
|
4
|
-
* @param {string} options.version (v2, v3, canary, etc)
|
|
5
|
-
* @param {string} options.type (admin, content, members)
|
|
6
|
-
* @param {Object} options.apiVersions
|
|
7
|
-
* @return {string} API Path for version
|
|
8
|
-
*/
|
|
9
|
-
function getVersionPath(options) {
|
|
10
|
-
let {version, type} = options;
|
|
11
|
-
let versionData = options.apiVersions[version];
|
|
12
|
-
|
|
13
|
-
if (typeof versionData === 'string') {
|
|
14
|
-
versionData = options.apiVersions[versionData];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
return `/${versionData[type]}/`;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
module.exports = getVersionPath;
|