Skip to content

Commit da50ac0

Browse files
committed
Support URLs in favicons
Resolves #2851
1 parent 13f6ae3 commit da50ac0

File tree

7 files changed

+56
-8
lines changed

7 files changed

+56
-8
lines changed

Diff for: CHANGELOG.md

+8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ title: Changelog
44

55
## Unreleased
66

7+
### Features
8+
9+
- The `--favicon` option may now be given a link starting with `https?://` instead of a path, #2851.
10+
11+
### Bug Fixes
12+
13+
- Fixed an issue where unrecognized languages would incorrectly be listed in the list of languages with translations, #2852.
14+
715
## v0.27.7 (2025-02-09)
816

917
### Features

Diff for: src/lib/internationalization/locales/en.cts

+1-1
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ export = {
404404
useHostedBaseUrlForAbsoluteLinks_requires_hostedBaseUrl:
405405
"The useHostedBaseUrlForAbsoluteLinks option requires that hostedBaseUrl be set",
406406
favicon_must_have_one_of_the_following_extensions_0:
407-
"Favicon must have on of the following extensions: {0}",
407+
"Favicon must have one of the following extensions: {0}",
408408
option_0_must_be_an_object: "The '{0}' option must be a non-array object",
409409
option_0_must_be_a_function: "The '{0}' option must be a function",
410410
option_0_must_be_object_with_urls: `{0} must be an object with string labels as keys and URL values`,

Diff for: src/lib/output/plugins/AssetsPlugin.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ export class AssetsPlugin extends RendererComponent {
5454
private onRenderBegin(event: RendererEvent) {
5555
const dest = join(event.outputDirectory, "assets");
5656

57-
if ([".ico", ".png", ".svg"].includes(extname(this.favicon))) {
57+
if (
58+
!/^https?:\/\//i.test(this.favicon) &&
59+
[".ico", ".png", ".svg"].includes(extname(this.favicon))
60+
) {
5861
copySync(
5962
this.favicon,
6063
join(dest, "favicon" + extname(this.favicon)),

Diff for: src/lib/output/themes/MarkedPlugin.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ export class MarkedPlugin extends ContextAwareRendererComponent {
334334
// that becomes a real thing.
335335
if (
336336
this.markdownLinkExternal &&
337-
/https?:\/\//i.test(href) &&
337+
/^https?:\/\//i.test(href) &&
338338
!(href + "/").startsWith(this.hostedBaseUrl)
339339
) {
340340
token.attrSet("target", "_blank");

Diff for: src/lib/output/themes/default/layouts/default.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ function favicon(context: DefaultThemeRenderContext) {
1010
const fav = context.options.getValue("favicon");
1111
if (!fav) return null;
1212

13+
if (/^https?:\/\//i.test(fav)) {
14+
return <link rel="icon" href={fav} />;
15+
}
16+
1317
switch (extname(fav)) {
1418
case ".ico":
1519
return <link rel="icon" href={context.relativeURL("assets/favicon.ico", true)} />;

Diff for: src/lib/utils/options/declaration.ts

+32-2
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,10 @@ export enum ParameterType {
358358
* Resolved according to the config directory.
359359
*/
360360
Path,
361+
/**
362+
* Resolved according to the config directory unless it starts with https?://
363+
*/
364+
UrlOrPath,
361365
Number,
362366
Boolean,
363367
Map,
@@ -418,10 +422,10 @@ export interface StringDeclarationOption extends DeclarationOptionBase {
418422
* Specifies the resolution strategy. If `Path` is provided, values will be resolved according to their
419423
* location in a file. If `String` or no value is provided, values will not be resolved.
420424
*/
421-
type?: ParameterType.String | ParameterType.Path;
425+
type?: ParameterType.String | ParameterType.Path | ParameterType.UrlOrPath;
422426

423427
/**
424-
* If not specified defaults to the empty string for both `String` and `Path`.
428+
* If not specified defaults to the empty string for all types.
425429
*/
426430
defaultValue?: string;
427431

@@ -569,6 +573,7 @@ export type DeclarationOption =
569573
export interface ParameterTypeToOptionTypeMap {
570574
[ParameterType.String]: string;
571575
[ParameterType.Path]: string;
576+
[ParameterType.UrlOrPath]: string;
572577
[ParameterType.Number]: number;
573578
[ParameterType.Boolean]: boolean;
574579
[ParameterType.Mixed]: unknown;
@@ -612,6 +617,19 @@ const converters: {
612617
option.validate?.(stringValue, i18n);
613618
return stringValue;
614619
},
620+
[ParameterType.UrlOrPath](value, option, i18n, configPath) {
621+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
622+
const stringValue = value == null ? "" : String(value);
623+
624+
if (/^https?:\/\//i.test(stringValue)) {
625+
option.validate?.(stringValue, i18n);
626+
return stringValue;
627+
}
628+
629+
const resolved = resolve(configPath, stringValue);
630+
option.validate?.(resolved, i18n);
631+
return resolved;
632+
},
615633
[ParameterType.Number](value, option, i18n) {
616634
const numValue = parseInt(String(value), 10) || 0;
617635
if (!valueIsWithinBounds(numValue, option.minValue, option.maxValue)) {
@@ -792,6 +810,18 @@ const defaultGetters: {
792810
? defaultStr
793811
: join(process.cwd(), defaultStr);
794812
},
813+
[ParameterType.UrlOrPath](option) {
814+
const defaultStr = option.defaultValue ?? "";
815+
if (defaultStr == "") {
816+
return "";
817+
}
818+
if (/^https?:\/\//i.test(defaultStr)) {
819+
return defaultStr;
820+
}
821+
return isAbsolute(defaultStr)
822+
? defaultStr
823+
: join(process.cwd(), defaultStr);
824+
},
795825
[ParameterType.Number](option) {
796826
return option.defaultValue ?? 0;
797827
},

Diff for: src/lib/utils/options/sources/typedoc.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -477,15 +477,18 @@ export function addTypeDocOptions(options: Pick<Options, "addDeclaration">) {
477477
help: (i18n) => i18n.help_favicon(),
478478
validate(value, i18n) {
479479
const allowedExtension = [".ico", ".png", ".svg"];
480-
if (!allowedExtension.includes(extname(value))) {
480+
if (
481+
!/^https?:\/\//i.test(value) &&
482+
!allowedExtension.includes(extname(value))
483+
) {
481484
throw new Error(
482485
i18n.favicon_must_have_one_of_the_following_extensions_0(
483486
allowedExtension.join(", "),
484487
),
485488
);
486489
}
487490
},
488-
type: ParameterType.Path,
491+
type: ParameterType.UrlOrPath,
489492
});
490493
options.addDeclaration({
491494
name: "sourceLinkExternal",
@@ -508,7 +511,7 @@ export function addTypeDocOptions(options: Pick<Options, "addDeclaration">) {
508511
name: "hostedBaseUrl",
509512
help: (i18n) => i18n.help_hostedBaseUrl(),
510513
validate(value, i18n) {
511-
if (!/https?:\/\//i.test(value)) {
514+
if (!/^https?:\/\//i.test(value)) {
512515
throw new Error(i18n.hostedBaseUrl_must_start_with_http());
513516
}
514517
},

0 commit comments

Comments
 (0)