Skip to content

Commit 93ea5b2

Browse files
authored
feat: handle absolute URIs in PUBLIC_PATH (#568)
Webpack allows `publicPath` to be an absolute URI (https://webpack.js.org/configuration/output/#outputpublicpath), in order to support deployments to a CDN. However, the browser history module expects a relative path for `basename` but uses the same environment variable (`PUBLIC_PATH`) to configure it. Since the path is always expected to be be suffixed to the absolute URI, we avoid introducing another configuration variable (thus maintaining full backwards compatibility) by extracting it at initialization time.
1 parent 4302286 commit 93ea5b2

File tree

5 files changed

+125
-2
lines changed

5 files changed

+125
-2
lines changed

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ export {
55
convertKeyNames,
66
getQueryParameters,
77
ensureDefinedConfig,
8+
parseURL,
9+
getPath,
810
} from './utils';
911
export {
1012
APP_TOPIC,

src/initialize.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Note that the env.config.js file in frontend-platform's root directory is NOT us
5454
initialization code, it's just there for the test suite and example application.
5555
*/
5656
import envConfig from 'env.config'; // eslint-disable-line import/no-unresolved
57-
57+
import { getPath } from './utils';
5858
import {
5959
publish,
6060
} from './pubSub';
@@ -99,7 +99,7 @@ import configureCache from './auth/LocalForageCache';
9999
*/
100100
export const history = (typeof window !== 'undefined')
101101
? createBrowserHistory({
102-
basename: getConfig().PUBLIC_PATH,
102+
basename: getPath(getConfig().PUBLIC_PATH),
103103
}) : createMemoryHistory();
104104

105105
/**

src/initialize.test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import PubSub from 'pubsub-js';
2+
import { createBrowserHistory } from 'history';
23
import {
34
APP_PUBSUB_INITIALIZED,
45
APP_CONFIG_INITIALIZED,
@@ -37,6 +38,7 @@ jest.mock('./auth');
3738
jest.mock('./analytics');
3839
jest.mock('./i18n');
3940
jest.mock('./auth/LocalForageCache');
41+
jest.mock('history');
4042

4143
let config = null;
4244
const newConfig = {
@@ -351,3 +353,12 @@ describe('initialize', () => {
351353
expect(logError).not.toHaveBeenCalled();
352354
});
353355
});
356+
357+
describe('history', () => {
358+
it('browser history called by default path', async () => {
359+
// import history from initialize;
360+
expect(createBrowserHistory).toHaveBeenCalledWith({
361+
basename: '/',
362+
});
363+
});
364+
});

src/utils.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,38 @@ export function convertKeyNames(object, nameMap) {
122122
return modifyObjectKeys(object, transformer);
123123
}
124124

125+
/**
126+
* Given a string URL return an element that has been parsed via href.
127+
* This element has the possibility to return different part of the URL.
128+
parser.protocol; // => "http:"
129+
parser.hostname; // => "example.com"
130+
parser.port; // => "3000"
131+
parser.pathname; // => "/pathname/"
132+
parser.search; // => "?search=test"
133+
parser.hash; // => "#hash"
134+
parser.host; // => "example.com:3000"
135+
* https://gist.github.com/jlong/2428561
136+
*
137+
* @param {string}
138+
* @returns {Object}
139+
*/
140+
export function parseURL(url) {
141+
const parser = document.createElement('a');
142+
parser.href = url;
143+
return parser;
144+
}
145+
146+
/**
147+
* Given a string URL return the path of the URL
148+
*
149+
*
150+
* @param {string}
151+
* @returns {string}
152+
*/
153+
export function getPath(url) {
154+
return parseURL(url).pathname;
155+
}
156+
125157
/**
126158
* *Deprecated*: A method which converts the supplied query string into an object of
127159
* key-value pairs and returns it. Defaults to the current query string - should perform like

src/utils.test.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import {
33
camelCaseObject,
44
snakeCaseObject,
55
convertKeyNames,
6+
parseURL,
7+
getPath,
68
getQueryParameters,
79
} from '.';
810

@@ -113,3 +115,79 @@ describe('getQueryParameters', () => {
113115
});
114116
});
115117
});
118+
119+
describe('ParseURL', () => {
120+
const testURL = 'http://example.com:3000/pathname/?search=test#hash';
121+
const parsedURL = parseURL(testURL);
122+
it('String URL is correctly parsed', () => {
123+
expect(parsedURL.toString()).toEqual(testURL);
124+
expect(parsedURL.href).toEqual(testURL);
125+
expect(typeof (parsedURL)).toEqual('object');
126+
});
127+
128+
it('should return protocol from URL', () => {
129+
expect(parsedURL.protocol).toEqual('http:');
130+
});
131+
132+
it('should return hostname from URL', () => {
133+
expect(parsedURL.hostname).toEqual('example.com');
134+
});
135+
136+
it('should return port from URL', () => {
137+
expect(parsedURL.port).toEqual('3000');
138+
});
139+
140+
it('should return pathname from URL', () => {
141+
expect(parsedURL.pathname).toEqual('/pathname/');
142+
});
143+
144+
it('should return search rom URL', () => {
145+
expect(parsedURL.search).toEqual('?search=test');
146+
});
147+
148+
it('should return hash from URL', () => {
149+
expect(parsedURL.hash).toEqual('#hash');
150+
});
151+
152+
it('should return host from URL', () => {
153+
expect(parsedURL.host).toEqual('example.com:3000');
154+
});
155+
});
156+
157+
describe('getPath', () => {
158+
it('Path is retrieved with full url', () => {
159+
const testURL = 'http://example.com:3000/pathname/?search=test#hash';
160+
161+
expect(getPath(testURL)).toEqual('/pathname/');
162+
});
163+
164+
it('Path is retrieved with only path', () => {
165+
const testURL = '/learning/';
166+
167+
expect(getPath(testURL)).toEqual('/learning/');
168+
});
169+
170+
it('Path is retrieved without protocol', () => {
171+
const testURL = '//example.com:3000/accounts/';
172+
173+
expect(getPath(testURL)).toEqual('/accounts/');
174+
});
175+
176+
it('Path is retrieved with base `/`', () => {
177+
const testURL = '/';
178+
179+
expect(getPath(testURL)).toEqual('/');
180+
});
181+
182+
it('Path is retrieved without port', () => {
183+
const testURL = 'https://example.com/accounts/';
184+
185+
expect(getPath(testURL)).toEqual('/accounts/');
186+
});
187+
188+
it('Path is retrieved without CDN shape', () => {
189+
const testURL = 'https://d20blt6w1kfasr.cloudfront.net/learning/';
190+
191+
expect(getPath(testURL)).toEqual('/learning/');
192+
});
193+
});

0 commit comments

Comments
 (0)