Skip to content

Commit 4268c35

Browse files
committed
Migrated packages from Utils
refs https://github.com/TryGhost/Toolbox/issues/354 - these packages are more suited for SDK as they help people integrate and work with Ghost
2 parents d394f03 + 48d4af7 commit 4268c35

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+3315
-0
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
plugins: ['ghost'],
3+
extends: [
4+
'plugin:ghost/node'
5+
]
6+
};

packages/config-url-helpers/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2013-2022 Ghost Foundation
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

packages/config-url-helpers/README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Config Url Helpers
2+
3+
## Install
4+
5+
`npm install @tryghost/config-url-helpers --save`
6+
7+
or
8+
9+
`yarn add @tryghost/config-url-helpers`
10+
11+
12+
## Usage
13+
14+
15+
## Develop
16+
17+
This is a mono repository, managed with [lerna](https://lernajs.io/).
18+
19+
Follow the instructions for the top-level repo.
20+
1. `git clone` this repo & `cd` into it as usual
21+
2. Run `yarn` to install top-level dependencies.
22+
23+
24+
## Run
25+
26+
- `yarn dev`
27+
28+
29+
## Test
30+
31+
- `yarn lint` run just eslint
32+
- `yarn test` run lint and tests
33+
34+
35+
36+
37+
# Copyright & License
38+
39+
Copyright (c) 2013-2022 Ghost Foundation - Released under the [MIT license](LICENSE).

packages/config-url-helpers/index.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const configUrlHelpers = require('./lib/config-url-helpers');
2+
3+
/**
4+
* @typedef {Object} BoundHelpers
5+
* @property {configUrlHelpers.getSubdirFn} getSubdir
6+
* @property {configUrlHelpers.getSiteUrlFn} getSiteUrl
7+
* @property {configUrlHelpers.getAdminUrlFn} getAdminUrl
8+
*
9+
* @param {*} nconf
10+
*/
11+
module.exports.bindAll = (nconf) => {
12+
Object.keys(configUrlHelpers).forEach((helper) => {
13+
nconf[helper] = configUrlHelpers[helper].bind(nconf);
14+
});
15+
};
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
const deduplicateSubdirectory = require('./utils/deduplicate-subdirectory');
2+
3+
/**
4+
* Returns a subdirectory URL, if defined so in the config.
5+
* @callback getSubdirFn
6+
* @return {string} a subdirectory if configured.
7+
*/
8+
function getSubdir() {
9+
// Parse local path location
10+
let {pathname} = new URL(this.get('url'));
11+
let subdir;
12+
13+
// Remove trailing slash
14+
if (pathname !== '/') {
15+
pathname = pathname.replace(/\/$/, '');
16+
}
17+
18+
subdir = pathname === '/' ? '' : pathname;
19+
return subdir;
20+
}
21+
22+
/**
23+
* Returns the base URL of the site as set in the config.
24+
*
25+
* Secure:
26+
* If the request is secure, we want to force returning the site url as https.
27+
* Imagine Ghost runs with http, but nginx allows SSL connections.
28+
*
29+
* @callback getSiteUrlFn
30+
* @return {string} returns the url as defined in config, but always with a trailing `/`
31+
*/
32+
function getSiteUrl() {
33+
let siteUrl = this.get('url');
34+
35+
if (!siteUrl.match(/\/$/)) {
36+
siteUrl += '/';
37+
}
38+
39+
return siteUrl;
40+
}
41+
42+
/**
43+
*
44+
* @callback getAdminUrlFn
45+
* @returns {string} returns the url as defined in config, but always with a trailing `/`
46+
*/
47+
function getAdminUrl() {
48+
let adminUrl = this.get('admin:url');
49+
const subdir = this.getSubdir();
50+
51+
if (!adminUrl) {
52+
return;
53+
}
54+
55+
if (!adminUrl.match(/\/$/)) {
56+
adminUrl += '/';
57+
}
58+
59+
adminUrl = `${adminUrl}${subdir}`;
60+
61+
if (!adminUrl.match(/\/$/)) {
62+
adminUrl += '/';
63+
}
64+
65+
adminUrl = deduplicateSubdirectory(adminUrl, this.getSiteUrl());
66+
return adminUrl;
67+
}
68+
69+
module.exports = {
70+
getSubdir,
71+
getSiteUrl,
72+
getAdminUrl
73+
};
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const {URL} = require('url');
2+
3+
/**
4+
* Remove duplicated directories from the start of a path or url's path
5+
*
6+
* @param {string} url URL or pathname with possible duplicate subdirectory
7+
* @param {string} rootUrl Root URL with an optional subdirectory
8+
* @returns {string} URL or pathname with any duplicated subdirectory removed
9+
*/
10+
const deduplicateSubdirectory = function deduplicateSubdirectory(url, rootUrl) {
11+
// force root url to always have a trailing-slash for consistent behaviour
12+
if (!rootUrl.endsWith('/')) {
13+
rootUrl = `${rootUrl}/`;
14+
}
15+
16+
// Cleanup any extraneous slashes in url for consistent behaviour
17+
url = url.replace(/(^|[^:])\/\/+/g, '$1/');
18+
19+
const parsedRoot = new URL(rootUrl);
20+
21+
// do nothing if rootUrl does not have a subdirectory
22+
if (parsedRoot.pathname === '/') {
23+
return url;
24+
}
25+
26+
const subdir = parsedRoot.pathname.replace(/(^\/|\/$)+/g, '');
27+
// we can have subdirs that match TLDs so we need to restrict matches to
28+
// duplicates that start with a / or the beginning of the url
29+
const subdirRegex = new RegExp(`(^|/)${subdir}/${subdir}(/|$)`);
30+
31+
return url.replace(subdirRegex, `$1${subdir}/`);
32+
};
33+
34+
module.exports = deduplicateSubdirectory;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "@tryghost/config-url-helpers",
3+
"version": "1.0.2",
4+
"repository": "https://github.com/TryGhost/Utils/tree/main/packages/config-url-helpers",
5+
"author": "Ghost Foundation",
6+
"license": "MIT",
7+
"main": "index.js",
8+
"scripts": {
9+
"dev": "echo \"Implement me!\"",
10+
"test": "NODE_ENV=testing c8 --all --reporter text --reporter cobertura --check-coverage mocha './test/**/*.test.js'",
11+
"coverage": "c8 report -r html",
12+
"lint": "eslint . --ext .js --cache",
13+
"posttest": "yarn lint"
14+
},
15+
"files": [
16+
"index.js",
17+
"lib"
18+
],
19+
"publishConfig": {
20+
"access": "public"
21+
},
22+
"devDependencies": {
23+
"c8": "7.12.0",
24+
"mocha": "10.0.0",
25+
"should": "13.2.3",
26+
"sinon": "14.0.0"
27+
}
28+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
plugins: ['ghost'],
3+
extends: [
4+
'plugin:ghost/test'
5+
]
6+
};
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Switch these lines once there are useful utils
2+
// const testUtils = require('./utils');
3+
require('./utils');
4+
5+
const sinon = require('sinon');
6+
7+
const configUrlHelpers = require('../');
8+
9+
let nconf;
10+
11+
const fakeConfig = {
12+
url: '',
13+
adminUrl: null
14+
};
15+
16+
describe('Config URL Helpers', function () {
17+
before(function () {
18+
const configFaker = (arg) => {
19+
if (arg === 'url') {
20+
return fakeConfig.url;
21+
} else if (arg === 'admin:url') {
22+
return fakeConfig.adminUrl;
23+
}
24+
};
25+
26+
nconf = {
27+
get: sinon.stub().callsFake(configFaker)
28+
};
29+
30+
configUrlHelpers.bindAll(nconf);
31+
});
32+
33+
describe('getSubdir', function () {
34+
it('url has no subdir', function () {
35+
fakeConfig.url = 'http://my-ghost-blog.com/';
36+
37+
nconf.getSubdir().should.eql('');
38+
});
39+
40+
it('url has subdir', function () {
41+
fakeConfig.url = 'http://my-ghost-blog.com/blog';
42+
nconf.getSubdir().should.eql('/blog');
43+
44+
fakeConfig.url = 'http://my-ghost-blog.com/blog/';
45+
nconf.getSubdir().should.eql('/blog');
46+
47+
fakeConfig.url = 'http://my-ghost-blog.com/my/blog';
48+
nconf.getSubdir().should.eql('/my/blog');
49+
50+
fakeConfig.url = 'http://my-ghost-blog.com/my/blog/';
51+
nconf.getSubdir().should.eql('/my/blog');
52+
});
53+
54+
it('should not return a slash for subdir', function () {
55+
fakeConfig.url = 'http://my-ghost-blog.com';
56+
nconf.getSubdir().should.eql('');
57+
58+
fakeConfig.url = 'http://my-ghost-blog.com/';
59+
nconf.getSubdir().should.eql('');
60+
});
61+
});
62+
63+
describe('getSiteUrl', function () {
64+
it('returns config url', function () {
65+
fakeConfig.url = 'http://example.com/';
66+
67+
nconf.getSiteUrl().should.eql('http://example.com/');
68+
});
69+
70+
it('adds trailing slash', function () {
71+
fakeConfig.url = 'http://example.com';
72+
73+
nconf.getSiteUrl().should.eql('http://example.com/');
74+
});
75+
});
76+
77+
describe('getAdminUrl', function () {
78+
it('returns undefinied if no admin URL is set', function () {
79+
should.not.exist(nconf.getAdminUrl());
80+
});
81+
82+
it('returns config url', function () {
83+
fakeConfig.adminUrl = 'http://admin.example.com/';
84+
85+
nconf.getAdminUrl().should.eql('http://admin.example.com/');
86+
});
87+
88+
it('adds trailing slash', function () {
89+
fakeConfig.adminUrl = 'http://admin.example.com';
90+
91+
nconf.getAdminUrl().should.eql('http://admin.example.com/');
92+
});
93+
94+
it('returns with subdirectory correctly if not provided', function () {
95+
fakeConfig.url = 'http://example.com/blog/';
96+
fakeConfig.adminUrl = 'http://admin.example.com';
97+
98+
nconf.getAdminUrl().should.eql('http://admin.example.com/blog/');
99+
});
100+
101+
it('returns with subdirectory correctly if provided with slash', function () {
102+
fakeConfig.url = 'http://example.com/blog/';
103+
fakeConfig.adminUrl = 'http://admin.example.com/blog/';
104+
105+
nconf.getAdminUrl().should.eql('http://admin.example.com/blog/');
106+
});
107+
108+
it('returns with subdirectory correctly if provided without slash', function () {
109+
fakeConfig.url = 'http://example.com/blog/';
110+
fakeConfig.adminUrl = 'http://admin.example.com/blog';
111+
112+
nconf.getAdminUrl().should.eql('http://admin.example.com/blog/');
113+
});
114+
});
115+
});

0 commit comments

Comments
 (0)