favicon-stealer 1.0.2 → 1.2.0

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/README.md CHANGED
@@ -21,7 +21,14 @@ import { Favicon } from 'favicon-stealer';
21
21
  | `size` | `number` | The size of the favicon in pixels. Default is 32. |
22
22
  | `className` | `string` | A class name to apply to the element. |
23
23
  | `timeout` | `number` | The timeout in milliseconds for fetching the favicon. Default is 1000 (1 second). |
24
+ | `lazy` | `boolean` | Whether to load the favicon lazily. Default is false. |
25
+ | `border` | `boolean` | Whether to show a border around the favicon. Default is false. |
26
+ | `padding` | `number` | The padding in pixels.(px) Default is 0. |
27
+ | `background` | `string` | The background color of the favicon. Default is transparent.(in hex) |
28
+ | `borderRadius` | `number` | The border radius in pixels.(px) Default is 0. |
24
29
 
30
+ # npm package
31
+ https://www.npmjs.com/package/favicon-stealer
25
32
 
26
33
  # license
27
34
  MIT License
@@ -29,4 +36,6 @@ MIT License
29
36
  # Changelog
30
37
  - v1.0.0: Initial release (2025.1.21)
31
38
  - v1.0.1: Add README.md (2025.1.21)
32
- - v1.0.2: Update license to MIT (2025.1.21)
39
+ - v1.0.2: Update license to MIT (2025.1.21)
40
+ - v1.1.0: Fix show bug(2025.2.1)
41
+ - v1.2.0: Add props(lazy, border, padding, background, borderRadius)(2025.2.1)
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ "use client";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const utils_1 = require("./lib/utils");
38
+ const react_1 = __importStar(require("react"));
39
+ const Favicon = ({ url, size = 32, className = "", timeout = 1000, // 1 second
40
+ border = false, padding = 0, background = "transparent", borderRadius = 0, lazy = false, }) => {
41
+ const domain = (0, utils_1.getDomain)(url);
42
+ const [imgSrc, setImgSrc] = (0, react_1.useState)(`https://${domain}/logo.svg`);
43
+ const [fallbackIndex, setFallbackIndex] = (0, react_1.useState)(0);
44
+ const [isLoading, setIsLoading] = (0, react_1.useState)(true);
45
+ const [hasError, setHasError] = (0, react_1.useState)(false);
46
+ const fallbackSources = [
47
+ `https://${domain}/logo.svg`,
48
+ `https://${domain}/logo.png`,
49
+ `https://${domain}/apple-touch-icon.png`,
50
+ `https://${domain}/apple-touch-icon-precomposed.png`,
51
+ `https://www.google.com/s2/favicons?domain=${domain}&sz=64`,
52
+ `https://icons.duckduckgo.com/ip3/${domain}.ico`,
53
+ `https://${domain}/favicon.ico`,
54
+ ];
55
+ (0, react_1.useEffect)(() => {
56
+ let timeoutId;
57
+ if (isLoading) {
58
+ timeoutId = setTimeout(() => {
59
+ handleError();
60
+ }, timeout);
61
+ }
62
+ return () => {
63
+ if (timeoutId) {
64
+ clearTimeout(timeoutId);
65
+ }
66
+ };
67
+ }, [imgSrc, isLoading]);
68
+ const handleError = () => {
69
+ const nextIndex = fallbackIndex + 1;
70
+ if (nextIndex < fallbackSources.length) {
71
+ setFallbackIndex(nextIndex);
72
+ setImgSrc(fallbackSources[nextIndex]);
73
+ setIsLoading(true);
74
+ }
75
+ else {
76
+ setHasError(true);
77
+ setIsLoading(false);
78
+ }
79
+ };
80
+ const handleLoad = () => {
81
+ setIsLoading(false);
82
+ setHasError(false);
83
+ };
84
+ return (react_1.default.createElement("div", { className: `relative inline-block
85
+ ${className}
86
+ ${border ? "border" : ""}
87
+ ${hasError ? "opacity-0" : ""}
88
+ ${padding ? `p-[${padding}px]` : ""}
89
+ ${borderRadius ? `rounded-[${borderRadius}px]` : ""}
90
+ `, style: {
91
+ width: size,
92
+ height: size,
93
+ background: background,
94
+ padding: padding ? `${padding}px` : 0,
95
+ borderRadius: borderRadius ? `${borderRadius}px` : 0,
96
+ } },
97
+ isLoading && (react_1.default.createElement("div", { className: "absolute inset-0 animate-pulse" },
98
+ react_1.default.createElement("div", { className: "w-full h-full rounded-md bg-gray-200/60" }))),
99
+ react_1.default.createElement("img", { src: imgSrc, alt: `${domain} logo`, width: size, height: size, loading: lazy ? "lazy" : "eager", onError: handleError, onLoad: handleLoad, className: `inline-block transition-opacity duration-300 ${isLoading ? "opacity-0" : "opacity-100"}`, style: {
100
+ objectFit: "contain",
101
+ display: hasError ? "none" : "inline-block",
102
+ } }),
103
+ hasError && (react_1.default.createElement("div", { className: "w-full h-full flex items-center justify-center bg-gray-100 rounded-md", style: { fontSize: `${size * 0.5}px` } }, domain.charAt(0).toUpperCase()))));
104
+ };
105
+ exports.default = Favicon;
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Favicon = void 0;
7
+ var Favicon_1 = require("./Favicon");
8
+ Object.defineProperty(exports, "Favicon", { enumerable: true, get: function () { return __importDefault(Favicon_1).default; } });
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDomain = void 0;
4
+ const getDomain = (url) => {
5
+ try {
6
+ // Add https:// protocol if not present
7
+ const urlWithProtocol = url.startsWith('http') ? url : `https://${url}`;
8
+ const domain = new URL(urlWithProtocol).hostname;
9
+ // Remove 'www.' prefix if exists
10
+ return domain.replace(/^www\./, '');
11
+ }
12
+ catch (error) {
13
+ // Return original input if URL parsing fails
14
+ return url;
15
+ }
16
+ };
17
+ exports.getDomain = getDomain;
@@ -0,0 +1,14 @@
1
+ import React from "react";
2
+ interface IProps {
3
+ url: string;
4
+ size?: number;
5
+ className?: string;
6
+ timeout?: number;
7
+ border?: boolean;
8
+ padding?: number;
9
+ background?: string;
10
+ borderRadius?: number;
11
+ lazy?: boolean;
12
+ }
13
+ declare const Favicon: ({ url, size, className, timeout, border, padding, background, borderRadius, lazy, }: IProps) => React.JSX.Element;
14
+ export default Favicon;
@@ -0,0 +1 @@
1
+ export { default as Favicon } from './Favicon';
@@ -0,0 +1 @@
1
+ export declare const getDomain: (url: string) => string;
package/package.json CHANGED
@@ -1,22 +1,30 @@
1
1
  {
2
2
  "name": "favicon-stealer",
3
- "version": "1.0.2",
4
- "main": "index.js",
3
+ "version": "1.2.0",
4
+ "main": "dist/index.js",
5
+ "types": "dist/types/index.d.ts",
5
6
  "scripts": {
6
7
  "test": "echo \"Error: no test specified\" && exit 1",
7
8
  "build": "tsc",
8
9
  "prepublishOnly": "npm run build"
9
10
  },
10
- "keywords": [],
11
- "author": "",
11
+ "keywords": [
12
+ "react",
13
+ "favicon",
14
+ "icon",
15
+ "logo",
16
+ "website",
17
+ "component"
18
+ ],
19
+ "author": "Corey Chiu",
12
20
  "license": "MIT",
13
- "description": "",
21
+ "description": "Get clear and consistent favicon of a website easily",
14
22
  "dependencies": {
15
23
  "react": "^19.0.0",
16
24
  "react-dom": "^19.0.0"
17
25
  },
18
26
  "devDependencies": {
19
- "@types/react": "^19.0.7",
27
+ "@types/react": "^19.0.8",
20
28
  "@types/react-dom": "^19.0.3",
21
29
  "typescript": "^5.7.3"
22
30
  }
package/src/Favicon.tsx CHANGED
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { getDomain } from "@/lib/utils";
3
+ import { getDomain } from "./lib/utils";
4
4
  import React, { useEffect, useState } from "react";
5
5
 
6
6
  interface IProps {
@@ -8,6 +8,11 @@ interface IProps {
8
8
  size?: number;
9
9
  className?: string;
10
10
  timeout?: number;
11
+ border?: boolean;
12
+ padding?: number;
13
+ background?: string;
14
+ borderRadius?: number;
15
+ lazy?: boolean;
11
16
  }
12
17
 
13
18
  const Favicon = ({
@@ -15,6 +20,11 @@ const Favicon = ({
15
20
  size = 32,
16
21
  className = "",
17
22
  timeout = 1000, // 1 second
23
+ border = false,
24
+ padding = 0,
25
+ background = "transparent",
26
+ borderRadius = 0,
27
+ lazy = false,
18
28
  }: IProps) => {
19
29
  const domain = getDomain(url);
20
30
  const [imgSrc, setImgSrc] = useState(`https://${domain}/logo.svg`);
@@ -67,8 +77,22 @@ const Favicon = ({
67
77
 
68
78
  return (
69
79
  <div
70
- className={`relative inline-block ${className}`}
71
- style={{ width: size, height: size }}
80
+ className={
81
+ `relative inline-block
82
+ ${className}
83
+ ${border ? "border" : ""}
84
+ ${hasError ? "opacity-0" : ""}
85
+ ${padding ? `p-[${padding}px]` : ""}
86
+ ${borderRadius ? `rounded-[${borderRadius}px]` : ""}
87
+ `
88
+ }
89
+ style={{
90
+ width: size,
91
+ height: size,
92
+ background: background,
93
+ padding: padding ? `${padding}px` : 0,
94
+ borderRadius: borderRadius ? `${borderRadius}px` : 0,
95
+ }}
72
96
  >
73
97
  {/* placeholder */}
74
98
  {isLoading && (
@@ -82,6 +106,7 @@ const Favicon = ({
82
106
  alt={`${domain} logo`}
83
107
  width={size}
84
108
  height={size}
109
+ loading={lazy ? "lazy" : "eager"}
85
110
  onError={handleError}
86
111
  onLoad={handleLoad}
87
112
  className={`inline-block transition-opacity duration-300 ${
package/tsconfig.json CHANGED
@@ -8,14 +8,18 @@
8
8
  "declarationDir": "dist/types",
9
9
  "strict": true,
10
10
  "moduleResolution": "node",
11
- "baseUrl": ".",
11
+ "baseUrl": "./src",
12
+ "rootDir": "./src",
12
13
  "paths": {
13
14
  "@/*": ["./*"]
14
15
  },
15
16
  "esModuleInterop": true
16
17
  },
17
18
  "include": [
18
- "src",
19
- "lib"
19
+ "src"
20
+ ],
21
+ "exclude": [
22
+ "node_modules",
23
+ "dist"
20
24
  ]
21
25
  }
File without changes