Skip to content

Commit 5f0c90d

Browse files
committed
fix(FaviconsBundlerPlugin): join URL containing sub-paths with a path, #156
1 parent 46e61de commit 5f0c90d

6 files changed

Lines changed: 119 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
# Changelog
22

3+
## 4.18.2 release (2025-03-14)
4+
5+
- fix(FaviconsBundlerPlugin): join URL containing sub-paths with a path, #156
6+
37
## 4.18.1 release (2025-03-13)
48

5-
- fix: output.publicPath ignored for FaviconsBundlerPlugin, #156
9+
- fix(FaviconsBundlerPlugin): output.publicPath ignored, #156
610

711
## 4.18.0 release (2025-02-03)
812

plugins/favicons-bundler-plugin/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const { favicons, config } = require('favicons');
44
const { black, blueBright, yellow } = require('ansis');
55
const PluginService = require('../../src/Plugin/PluginService');
66
const BundlerPlugin = require('../../src/');
7-
const { outToConsole } = require('../../src/Common/Helpers');
7+
const { joinUrl, outToConsole } = require('../../src/Common/Helpers');
88

99
class FaviconsBundlerPlugin {
1010
pluginName = 'favicons-bundler-plugin';
@@ -56,7 +56,7 @@ class FaviconsBundlerPlugin {
5656

5757
if (isUrlPublicPath) {
5858
let publicPath = bundlerPluginOption.getPublicPath();
59-
this.outputUrl = new URL(this.outputPath, publicPath).href;
59+
this.outputUrl = joinUrl(publicPath, this.outputPath);
6060
}
6161

6262
if (!enabled) {

src/Common/Helpers.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,34 @@ const isFunction = (value) => typeof value === 'function';
3737
*/
3838
const isUrl = (request) => /^(?:[a-z]+:)?\/\//.test(request);
3939

40+
/**
41+
* Join a base URL with multiple sub-paths, ensuring correct formatting (removes extra slashes).
42+
*
43+
* Example:
44+
* joinUrl('https://cdn.com/v1/', '/path/', '/to/') => 'https://cdn.com/v1/path/to'
45+
*
46+
* @param {...string} paths - The base URL and any number of sub-paths to join.
47+
* @return {string} The joined URL.
48+
*/
49+
const joinUrl = (...paths) => {
50+
let result = paths[0].replace(/\/+$/, '');
51+
let tail = '';
52+
53+
if (paths.length > 1) {
54+
tail = paths
55+
.slice(1) // skip the first part since it's already handled
56+
.filter(Boolean) // remove empty strings from the paths
57+
.map((p) => p.replace(/^\/+|\/+$/g, '')) // remove leading/trailing slashes from other parts
58+
.join('/');
59+
}
60+
61+
if (tail) {
62+
result += '/' + tail;
63+
}
64+
65+
return result;
66+
};
67+
4068
/**
4169
* Find a webpack plugin by instance name.
4270
*
@@ -269,6 +297,7 @@ module.exports = {
269297
isWin,
270298
isFunction,
271299
isUrl,
300+
joinUrl,
272301
findPlugin,
273302
pathToPosix,
274303
getFileExtension,

test/cases/plugin-favicons-publicPath-url/expected/index.html

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22
<html>
33
<head>
44
<title>Test</title>
5-
<link rel="icon" type="image/png" sizes="16x16" href="https://cdn.com/favicons/favicon-16x16.png">
6-
<link rel="icon" type="image/png" sizes="32x32" href="https://cdn.com/favicons/favicon-32x32.png">
7-
<link rel="icon" type="image/png" sizes="48x48" href="https://cdn.com/favicons/favicon-48x48.png">
8-
<link rel="icon" type="image/x-icon" href="https://cdn.com/favicons/favicon.ico">
9-
<link rel="manifest" href="https://cdn.com/favicons/manifest.webmanifest">
5+
<link rel="icon" type="image/png" sizes="16x16" href="https://cdn.com/v1/favicons/favicon-16x16.png">
6+
<link rel="icon" type="image/png" sizes="32x32" href="https://cdn.com/v1/favicons/favicon-32x32.png">
7+
<link rel="icon" type="image/png" sizes="48x48" href="https://cdn.com/v1/favicons/favicon-48x48.png">
8+
<link rel="icon" type="image/x-icon" href="https://cdn.com/v1/favicons/favicon.ico">
9+
<link rel="manifest" href="https://cdn.com/v1/favicons/manifest.webmanifest">
1010
<meta name="application-name" content="My App">
1111
<meta name="mobile-web-app-capable" content="yes">
1212
<meta name="theme-color" content="#fff">
13-
<link href="https://cdn.com/style.bundle.css" rel="stylesheet" />
14-
<script src="https://cdn.com/main.bundle.js" defer="defer"></script>
13+
<link href="https://cdn.com/v1/style.bundle.css" rel="stylesheet" />
14+
<script src="https://cdn.com/v1/main.bundle.js" defer="defer"></script>
1515
</head>
1616
<body>
1717
<h1>Hello World!</h1>

test/cases/plugin-favicons-publicPath-url/webpack.config.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module.exports = {
88

99
output: {
1010
path: path.join(__dirname, 'dist/'),
11-
publicPath: 'https://cdn.com/', // test URL in FaviconsBundlerPlugin
11+
publicPath: 'https://cdn.com/v1/', // test URL with sub paths in FaviconsBundlerPlugin
1212
},
1313

1414
plugins: [
@@ -29,8 +29,6 @@ module.exports = {
2929
// favicons configuration options, see https://github.com/itgalaxy/favicons#usage
3030
faviconOptions: {
3131
path: '/favicons',
32-
url: 'https://www.facebook.com',
33-
//path: 'https://cdn.com/favicons',
3432
appName: 'My App',
3533
icons: {
3634
favicons: true, // Create regular favicons.

test/unit.test.js

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import path from 'path';
22
import fs from 'fs';
33

4-
import { findPlugin, getFileExtension, parseVersion, compareVersions } from '../src/Common/Helpers';
4+
import { joinUrl, findPlugin, getFileExtension, parseVersion, compareVersions } from '../src/Common/Helpers';
55
import WeakMapIterable from '../src/Common/WeakMapIterable';
66
import VMScript from '../src/Common/VMScript';
77
import { HtmlParser } from '../src/Common/HtmlParser';
@@ -87,6 +87,80 @@ describe('misc', () => {
8787
});
8888
});
8989

90+
describe('joinUrl', () => {
91+
test(`'https://cdn.com', '/path/to'`, () => {
92+
const received = joinUrl('https://cdn.com', '/path/to');
93+
const expected = 'https://cdn.com/path/to';
94+
return expect(received).toStrictEqual(expected);
95+
});
96+
97+
test(`'https://cdn.com/', 'path/to'`, () => {
98+
const received = joinUrl('https://cdn.com/', 'path/to');
99+
const expected = 'https://cdn.com/path/to';
100+
return expect(received).toStrictEqual(expected);
101+
});
102+
103+
test(`'https://cdn.com', 'path/to'`, () => {
104+
const received = joinUrl('https://cdn.com', 'path/to');
105+
const expected = 'https://cdn.com/path/to';
106+
return expect(received).toStrictEqual(expected);
107+
});
108+
109+
test(`'https://cdn.com/v1/', '/path/to'`, () => {
110+
const received = joinUrl('https://cdn.com/v1/', '/path/to');
111+
const expected = 'https://cdn.com/v1/path/to';
112+
return expect(received).toStrictEqual(expected);
113+
});
114+
115+
test(`'https://cdn.com/v1/', '/path', '/to'`, () => {
116+
const received = joinUrl('https://cdn.com/v1/', '/path', '/to');
117+
const expected = 'https://cdn.com/v1/path/to';
118+
return expect(received).toStrictEqual(expected);
119+
});
120+
121+
test(`'https://cdn.com/v1/', '/path/', '/to/'`, () => {
122+
const received = joinUrl('https://cdn.com/v1/', '/path/', '/to/');
123+
const expected = 'https://cdn.com/v1/path/to';
124+
return expect(received).toStrictEqual(expected);
125+
});
126+
127+
test(`'//cdn.com/v1/', '/path/', '/to/'`, () => {
128+
const received = joinUrl('//cdn.com/v1/', '/path/', '/to/');
129+
const expected = '//cdn.com/v1/path/to';
130+
return expect(received).toStrictEqual(expected);
131+
});
132+
133+
test(`'/', 'path', 'to/'`, () => {
134+
const received = joinUrl('/', 'path', 'to/');
135+
const expected = '/path/to';
136+
return expect(received).toStrictEqual(expected);
137+
});
138+
139+
test(`'/path', ''`, () => {
140+
const received = joinUrl('/path', '');
141+
const expected = '/path';
142+
return expect(received).toStrictEqual(expected);
143+
});
144+
145+
test(`'/path', '', ''`, () => {
146+
const received = joinUrl('/path', '', '');
147+
const expected = '/path';
148+
return expect(received).toStrictEqual(expected);
149+
});
150+
151+
test(`'/path', '', '', 'to', ''`, () => {
152+
const received = joinUrl('/path', '', '', 'to', '');
153+
const expected = '/path/to';
154+
return expect(received).toStrictEqual(expected);
155+
});
156+
157+
test(`'/path'`, () => {
158+
const received = joinUrl('/path');
159+
const expected = '/path';
160+
return expect(received).toStrictEqual(expected);
161+
});
162+
});
163+
90164
describe('compareVersions', () => {
91165
test('1.2.99 < 1.2.100', () => {
92166
const expected = compareVersions('1.2.99', '<', '1.2.100');

0 commit comments

Comments
 (0)