vitepress-linkcard 1.3.0 → 2.0.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 +36 -22
- package/dist/.cjs.min.js +5 -5
- package/dist/.esm.min.js +5 -5
- package/dist/api.js +31 -3
- package/dist/assemble/html.js +61 -8
- package/dist/assemble/local-file-cache.js +119 -8
- package/dist/assemble/metadata.js +30 -2
- package/dist/assemble/parser.js +133 -19
- package/dist/assemble/style.js +117 -18
- package/dist/assemble/url.js +35 -4
- package/dist/assemble/xhr.js +71 -5
- package/dist/link-to-card-plugin.js +104 -24
- package/package.json +2 -2
- package/types/api.d.ts +42 -3
- package/types/assemble/html.d.ts +51 -3
- package/types/assemble/local-file-cache.d.ts +98 -8
- package/types/assemble/metadata.d.ts +25 -2
- package/types/assemble/parser.d.ts +31 -4
- package/types/assemble/style.d.ts +56 -7
- package/types/assemble/url.d.ts +35 -4
- package/types/assemble/xhr.d.ts +62 -4
- package/types/link-to-card-plugin.d.ts +27 -2
- package/types/types.d.ts +103 -4
|
@@ -1,9 +1,37 @@
|
|
|
1
1
|
import { parserMetadata, xhr } from '.';
|
|
2
2
|
import LocalFileCache from './local-file-cache';
|
|
3
|
+
/**
|
|
4
|
+
* Local file cache instance for storing fetched URL metadata.
|
|
5
|
+
* This cache persists metadata to disk to avoid repeated network requests.
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
3
8
|
const cache = new LocalFileCache();
|
|
4
9
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
10
|
+
* Retrieves metadata for a given URL, using cache when available.
|
|
11
|
+
*
|
|
12
|
+
* This function first checks if the metadata is already cached. If not, it fetches
|
|
13
|
+
* the HTML content from the URL, parses the metadata, and caches the result for
|
|
14
|
+
* future use.
|
|
15
|
+
*
|
|
16
|
+
* The metadata includes:
|
|
17
|
+
* - Title (from `<title>` or OGP tags)
|
|
18
|
+
* - Description (from meta description or OGP tags)
|
|
19
|
+
* - Logo/icon (from OGP image or favicon)
|
|
20
|
+
*
|
|
21
|
+
* @param url - The URL to fetch metadata from
|
|
22
|
+
* @returns The parsed URL metadata, or null if the URL cannot be fetched or parsed
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const metadata = getUrlMetadata('https://example.com')
|
|
27
|
+
* if (metadata) {
|
|
28
|
+
* console.log(metadata.title) // "Example Domain"
|
|
29
|
+
* console.log(metadata.description) // "Example website description"
|
|
30
|
+
* }
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @see {@link parserMetadata} for details on metadata extraction
|
|
34
|
+
* @see {@link LocalFileCache} for caching implementation
|
|
7
35
|
*/
|
|
8
36
|
export function getUrlMetadata(url) {
|
|
9
37
|
if (cache.has(url))
|
package/dist/assemble/parser.js
CHANGED
|
@@ -1,22 +1,75 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* HTML parser module for extracting metadata from web pages.
|
|
3
|
+
*
|
|
4
|
+
* This module provides functions to parse HTML strings and extract metadata such as
|
|
5
|
+
* title, description, and logo/icon URLs. It supports both standard HTML meta tags
|
|
6
|
+
* and Open Graph Protocol (OGP) tags.
|
|
7
|
+
*
|
|
8
|
+
* @module parser
|
|
9
|
+
* @todo Refactor to improve maintainability and add support for more meta tag formats
|
|
3
10
|
*/
|
|
4
11
|
import { isString } from '@luckrya/utility';
|
|
5
12
|
import { cleanPath, extractUrl } from './url';
|
|
13
|
+
/**
|
|
14
|
+
* Default logo URL used when no logo can be extracted from the page.
|
|
15
|
+
*/
|
|
6
16
|
const DEFAULT_LOGO = 'https://resources.whatwg.org/logo-url.svg';
|
|
17
|
+
/**
|
|
18
|
+
* Regular expression to match HTML tag content between opening and closing tags.
|
|
19
|
+
* @example Matches: `<title>Page Title</title>` → captures "Page Title"
|
|
20
|
+
*/
|
|
7
21
|
const HtmlTagContentReg = /(<[A-Za-z]+\s*[^>]*>(.*)<\/[A-Za-z]+>)/;
|
|
22
|
+
/**
|
|
23
|
+
* Regular expression to extract the `content` attribute value from HTML meta tags.
|
|
24
|
+
* @example Matches: `content="value"` or `content='value'`
|
|
25
|
+
*/
|
|
8
26
|
const ContentAttrValueHtmlMetaTagReg = /content=["|']([^>]*)["|']/;
|
|
27
|
+
/**
|
|
28
|
+
* Regular expression to extract the `href` attribute value from HTML link tags.
|
|
29
|
+
* @example Matches: `href="value"` or `href='value'`
|
|
30
|
+
*/
|
|
9
31
|
const HrefAttrValueHtmlLinkTagReg = /href=["|']([^>]*)["|']/;
|
|
32
|
+
/**
|
|
33
|
+
* Regular expression to match HTML title tags.
|
|
34
|
+
* @example Matches: `<title>Page Title</title>`
|
|
35
|
+
*/
|
|
10
36
|
const HtmlTitleTagReg = /(<title\s*[^>]*>(.*)<\/title>)/g;
|
|
11
|
-
|
|
12
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Creates a regular expression to match self-closing or non-closing HTML tags with a specific attribute.
|
|
39
|
+
*
|
|
40
|
+
* This function generates patterns to find meta or link tags that contain a specific attribute,
|
|
41
|
+
* which is useful for finding OGP tags like `og:title`, `og:description`, etc.
|
|
42
|
+
*
|
|
43
|
+
* @param attr - The attribute to search for (e.g., 'title', 'description', 'image')
|
|
44
|
+
* @param tag - The HTML tag name to match (default: 'meta')
|
|
45
|
+
* @returns A RegExp that matches tags containing the specified attribute
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* const reg = containArrSelfLosingHtmlTagReg('og:title')
|
|
50
|
+
* // Matches: <meta property="og:title" content="...">
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
13
55
|
const containArrSelfLosingHtmlTagReg = (attr, tag = 'meta') => new RegExp(`<${tag}\\s[^>]*\\w+=['|"]([a-zA-Z]|:|\\s)*${attr}['|"][^>]*\\/?>`);
|
|
14
56
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
57
|
+
* Extracts the page title from HTML string.
|
|
58
|
+
*
|
|
59
|
+
* Attempts to find the title in the following order:
|
|
60
|
+
* 1. Meta tags with 'title' attribute (e.g., `<meta property="og:title" content="...">`)
|
|
61
|
+
* 2. Standard HTML `<title>` tag
|
|
62
|
+
*
|
|
63
|
+
* @param htmlString - The HTML content to parse
|
|
64
|
+
* @returns The extracted title, or undefined if not found
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* const title = matchTitleByMetaTag('<title>Example Page</title>')
|
|
69
|
+
* // Returns: "Example Page"
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* @internal
|
|
20
73
|
*/
|
|
21
74
|
function matchTitleByMetaTag(htmlString) {
|
|
22
75
|
let title;
|
|
@@ -37,9 +90,22 @@ function matchTitleByMetaTag(htmlString) {
|
|
|
37
90
|
return title;
|
|
38
91
|
}
|
|
39
92
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
93
|
+
* Extracts the page description from HTML string.
|
|
94
|
+
*
|
|
95
|
+
* Searches for description in meta tags, supporting both standard and OGP formats:
|
|
96
|
+
* - `<meta name="description" content="...">`
|
|
97
|
+
* - `<meta property="og:description" content="...">`
|
|
98
|
+
*
|
|
99
|
+
* @param htmlString - The HTML content to parse
|
|
100
|
+
* @returns The extracted description, or undefined if not found
|
|
101
|
+
*
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* const desc = matchDescriptionByMetaTag('<meta name="description" content="A great site">')
|
|
105
|
+
* // Returns: "A great site"
|
|
106
|
+
* ```
|
|
107
|
+
*
|
|
108
|
+
* @internal
|
|
43
109
|
*/
|
|
44
110
|
function matchDescriptionByMetaTag(htmlString) {
|
|
45
111
|
let description;
|
|
@@ -52,9 +118,22 @@ function matchDescriptionByMetaTag(htmlString) {
|
|
|
52
118
|
return description;
|
|
53
119
|
}
|
|
54
120
|
/**
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
121
|
+
* Extracts the page logo/icon URL from HTML string.
|
|
122
|
+
*
|
|
123
|
+
* Attempts to find the logo in the following order:
|
|
124
|
+
* 1. OGP image tag: `<meta property="og:image" content="...">`
|
|
125
|
+
* 2. Icon link tag: `<link rel="icon" href="...">`
|
|
126
|
+
*
|
|
127
|
+
* @param htmlString - The HTML content to parse
|
|
128
|
+
* @returns The extracted logo URL (may be relative), or undefined if not found
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* ```typescript
|
|
132
|
+
* const logo = matchLogoByLinkOrMetaTag('<link rel="icon" href="/favicon.ico">')
|
|
133
|
+
* // Returns: "/favicon.ico"
|
|
134
|
+
* ```
|
|
135
|
+
*
|
|
136
|
+
* @internal
|
|
58
137
|
*/
|
|
59
138
|
function matchLogoByLinkOrMetaTag(htmlString) {
|
|
60
139
|
let logo;
|
|
@@ -76,11 +155,39 @@ function matchLogoByLinkOrMetaTag(htmlString) {
|
|
|
76
155
|
return logo;
|
|
77
156
|
}
|
|
78
157
|
/**
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
158
|
+
* Parses HTML string to extract structured metadata for link card generation.
|
|
159
|
+
*
|
|
160
|
+
* This is the main parsing function that extracts title, description, and logo
|
|
161
|
+
* from an HTML page. It handles both absolute and relative URLs, converting
|
|
162
|
+
* relative logo paths to absolute URLs when necessary.
|
|
163
|
+
*
|
|
164
|
+
* @param htmlString - The HTML content to parse
|
|
165
|
+
* @param url - The URL of the page (used to resolve relative logo URLs)
|
|
166
|
+
* @returns Parsed metadata object, or null if no valid metadata could be extracted
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```typescript
|
|
170
|
+
* const html = '<title>Example</title><meta name="description" content="Test">'
|
|
171
|
+
* const metadata = parserMetadata(html, 'https://example.com')
|
|
172
|
+
* // Returns: { title: 'Example', description: 'Test', logo: '...' }
|
|
173
|
+
* ```
|
|
174
|
+
*
|
|
175
|
+
* @remarks
|
|
176
|
+
* - Returns null if all metadata fields (title, description, logo) are empty
|
|
177
|
+
* - Relative logo URLs are converted to absolute URLs using the page's origin
|
|
178
|
+
* - Falls back to a default logo if no logo can be found
|
|
179
|
+
*
|
|
180
|
+
* @todo Handle protocol-relative URLs like `//img.example.com/logo.png`
|
|
82
181
|
*/
|
|
83
182
|
export function parserMetadata(htmlString, url) {
|
|
183
|
+
/**
|
|
184
|
+
* Converts a potentially relative logo URL to an absolute URL.
|
|
185
|
+
*
|
|
186
|
+
* @param logo - The logo URL (may be relative or absolute)
|
|
187
|
+
* @returns Absolute URL for the logo, or default logo if input is invalid
|
|
188
|
+
*
|
|
189
|
+
* @internal
|
|
190
|
+
*/
|
|
84
191
|
function absolute(logo) {
|
|
85
192
|
if (!logo)
|
|
86
193
|
return DEFAULT_LOGO;
|
|
@@ -99,8 +206,15 @@ export function parserMetadata(htmlString, url) {
|
|
|
99
206
|
return metadata;
|
|
100
207
|
}
|
|
101
208
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
209
|
+
* Checks if an object contains only undefined or empty string values.
|
|
210
|
+
*
|
|
211
|
+
* This utility function is used to determine if any valid metadata was extracted
|
|
212
|
+
* from a page. If all fields are empty, the metadata is considered invalid.
|
|
213
|
+
*
|
|
214
|
+
* @param obj - Object to check (typically a metadata object)
|
|
215
|
+
* @returns true if the object has no non-empty string values, false otherwise
|
|
216
|
+
*
|
|
217
|
+
* @internal
|
|
104
218
|
*/
|
|
105
219
|
function isEmptyStringObject(obj) {
|
|
106
220
|
return !Object.values(obj).filter((v) => isString(v)).length;
|
package/dist/assemble/style.js
CHANGED
|
@@ -1,13 +1,48 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Style generation utilities for link card rendering.
|
|
3
|
+
*
|
|
4
|
+
* This module provides functions to generate inline CSS styles and class names
|
|
5
|
+
* for the link card components. The styles support customizable colors and
|
|
6
|
+
* responsive design with text ellipsis for long content.
|
|
7
|
+
*
|
|
8
|
+
* @module style
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Converts a camelCase string to hyphenated kebab-case.
|
|
12
|
+
*
|
|
13
|
+
* This is used to convert JavaScript style property names (e.g., `backgroundColor`)
|
|
14
|
+
* to CSS property names (e.g., `background-color`).
|
|
15
|
+
*
|
|
16
|
+
* @param str - The camelCase string to convert
|
|
17
|
+
* @returns The hyphenated kebab-case string
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* hyphenate('backgroundColor') // Returns: 'background-color'
|
|
22
|
+
* hyphenate('fontSize') // Returns: 'font-size'
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* @internal
|
|
4
26
|
*/
|
|
5
27
|
function hyphenate(str) {
|
|
6
28
|
return str.replace(/\B([A-Z])/g, '-$1').toLowerCase();
|
|
7
29
|
}
|
|
8
30
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
31
|
+
* Joins style properties into a CSS string.
|
|
32
|
+
*
|
|
33
|
+
* Converts a JavaScript object of style properties into a semicolon-separated
|
|
34
|
+
* CSS string suitable for inline styles.
|
|
35
|
+
*
|
|
36
|
+
* @param style - Object containing CSS properties and values
|
|
37
|
+
* @returns A string of CSS declarations
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```typescript
|
|
41
|
+
* join({ fontSize: '16px', color: 'red' })
|
|
42
|
+
* // Returns: 'font-size: 16px; color: red;'
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @internal
|
|
11
46
|
*/
|
|
12
47
|
function join(style) {
|
|
13
48
|
return Object.entries(style)
|
|
@@ -19,15 +54,38 @@ function join(style) {
|
|
|
19
54
|
.join(' ');
|
|
20
55
|
}
|
|
21
56
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
57
|
+
* Wraps CSS declarations in an inline style attribute.
|
|
58
|
+
*
|
|
59
|
+
* @param style - Object containing CSS properties and values
|
|
60
|
+
* @returns A complete HTML style attribute string
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* inlineStyle({ color: 'red', fontSize: '16px' })
|
|
65
|
+
* // Returns: 'style="color: red; font-size: 16px;"'
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* @internal
|
|
24
69
|
*/
|
|
25
70
|
function inlineStyle(style) {
|
|
26
71
|
return `style="${join(style)}"`;
|
|
27
72
|
}
|
|
28
73
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
74
|
+
* Generates CSS properties for text ellipsis with line clamping.
|
|
75
|
+
*
|
|
76
|
+
* Creates styles that truncate text after a specified number of lines with
|
|
77
|
+
* an ellipsis. Uses both WebKit-specific properties and standard line-clamp.
|
|
78
|
+
*
|
|
79
|
+
* @param line - Maximum number of lines to display before truncation
|
|
80
|
+
* @returns CSS properties object for ellipsis effect
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* ellipsisStyle(2)
|
|
85
|
+
* // Returns styles that truncate text after 2 lines with "..."
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* @internal
|
|
31
89
|
*/
|
|
32
90
|
const ellipsisStyle = (line) => ({
|
|
33
91
|
'-webkit-box-orient': 'vertical',
|
|
@@ -41,12 +99,33 @@ const ellipsisStyle = (line) => ({
|
|
|
41
99
|
wordBreak: 'break-word'
|
|
42
100
|
});
|
|
43
101
|
/**
|
|
44
|
-
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
102
|
+
* Generates complete inline styles for all link card components.
|
|
103
|
+
*
|
|
104
|
+
* Creates a set of inline style strings for each part of the link card:
|
|
105
|
+
* - Container: main card box with border and background
|
|
106
|
+
* - Image: logo/icon display
|
|
107
|
+
* - Texts: wrapper for text content
|
|
108
|
+
* - Title: card title (2-line ellipsis)
|
|
109
|
+
* - Domain: domain name with underline
|
|
110
|
+
* - Description: description text (2-line ellipsis)
|
|
111
|
+
*
|
|
112
|
+
* The styles are inspired by VitePress's VPFeature component design.
|
|
113
|
+
*
|
|
114
|
+
* The container uses CSS custom properties for theming:
|
|
115
|
+
* - `--vitepress-linkcard-border-color`: Border color (default: #7d7d7dff)
|
|
116
|
+
* - `--vitepress-linkcard-bg-color`: Background color (default: transparent)
|
|
117
|
+
*
|
|
118
|
+
* @returns Object containing style attribute strings for each card component
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```typescript
|
|
122
|
+
* const styles = STYLE()
|
|
123
|
+
* // Use in HTML: <div ${styles.container}>...</div>
|
|
124
|
+
* ```
|
|
125
|
+
*
|
|
126
|
+
* @see {@link https://github.com/vuejs/vitepress/blob/main/src/client/theme-default/components/VPFeature.vue | VPFeature component}
|
|
48
127
|
*/
|
|
49
|
-
export const STYLE = (
|
|
128
|
+
export const STYLE = () => ({
|
|
50
129
|
a: inlineStyle({
|
|
51
130
|
color: 'unset !important',
|
|
52
131
|
display: 'block',
|
|
@@ -59,11 +138,12 @@ export const STYLE = (borderColor, bgColor) => ({
|
|
|
59
138
|
flexWrap: 'wrap',
|
|
60
139
|
gap: '10px',
|
|
61
140
|
borderRadius: '12px',
|
|
62
|
-
border: `1px solid
|
|
63
|
-
backgroundColor:
|
|
141
|
+
border: `1px solid var(--vp-c-bg-soft)`,
|
|
142
|
+
backgroundColor: `var(--vp-c-bg-soft)`,
|
|
64
143
|
boxSizing: 'border-box',
|
|
65
144
|
width: '100%',
|
|
66
|
-
height: '130px'
|
|
145
|
+
height: '130px',
|
|
146
|
+
transition: 'border-color 0.25s, background-color 0.25s'
|
|
67
147
|
}),
|
|
68
148
|
img: inlineStyle({
|
|
69
149
|
borderRadius: '0px 12px 12px 0px',
|
|
@@ -102,8 +182,27 @@ export const STYLE = (borderColor, bgColor) => ({
|
|
|
102
182
|
})
|
|
103
183
|
});
|
|
104
184
|
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
185
|
+
* Generates CSS class names with a custom prefix.
|
|
186
|
+
*
|
|
187
|
+
* When using the `classPrefix` option, this function creates consistent
|
|
188
|
+
* class names for all card components following a BEM-like naming convention.
|
|
189
|
+
*
|
|
190
|
+
* @param prefix - The prefix to prepend to all class names
|
|
191
|
+
* @returns Object mapping component names to their class names
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```typescript
|
|
195
|
+
* const classes = classNames('my-card')
|
|
196
|
+
* // Returns: {
|
|
197
|
+
* // container: 'my-card__container',
|
|
198
|
+
* // title: 'my-card__texts--title',
|
|
199
|
+
* // ...
|
|
200
|
+
* // }
|
|
201
|
+
* ```
|
|
202
|
+
*
|
|
203
|
+
* @remarks
|
|
204
|
+
* When class names are used instead of inline styles, you must provide
|
|
205
|
+
* your own CSS definitions for these classes.
|
|
107
206
|
*/
|
|
108
207
|
export const classNames = (prefix) => ({
|
|
109
208
|
container: `${prefix}__container`,
|
package/dist/assemble/url.js
CHANGED
|
@@ -1,13 +1,44 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* URL utility functions for parsing and manipulating URLs.
|
|
3
|
+
*
|
|
4
|
+
* @module url
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Parses a URL string and returns a URL object.
|
|
8
|
+
*
|
|
9
|
+
* This is a wrapper around the native URL constructor that provides
|
|
10
|
+
* a consistent interface for URL parsing throughout the codebase.
|
|
11
|
+
*
|
|
12
|
+
* @param url - The URL string to parse
|
|
13
|
+
* @returns A URL object containing the parsed components
|
|
14
|
+
* @throws {TypeError} If the URL string is invalid
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* const urlObj = extractUrl('https://example.com/path')
|
|
19
|
+
* console.log(urlObj.origin) // "https://example.com"
|
|
20
|
+
* console.log(urlObj.pathname) // "/path"
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/URL | MDN - URL}
|
|
4
24
|
*/
|
|
5
25
|
export function extractUrl(url) {
|
|
6
26
|
return new URL(url);
|
|
7
27
|
}
|
|
8
28
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
29
|
+
* Removes duplicate consecutive slashes from a path string.
|
|
30
|
+
*
|
|
31
|
+
* This function normalizes paths by replacing multiple consecutive slashes
|
|
32
|
+
* with a single slash. Useful for constructing clean URLs from path segments.
|
|
33
|
+
*
|
|
34
|
+
* @param path - The path string to clean
|
|
35
|
+
* @returns The cleaned path with no consecutive slashes
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* cleanPath('/path//to///file') // Returns: '/path/to/file'
|
|
40
|
+
* cleanPath('//images//logo.png') // Returns: '/images/logo.png'
|
|
41
|
+
* ```
|
|
11
42
|
*/
|
|
12
43
|
export function cleanPath(path) {
|
|
13
44
|
return path.replace(/\/\//g, '/');
|
package/dist/assemble/xhr.js
CHANGED
|
@@ -1,13 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* XHR module for fetching remote URL content.
|
|
3
|
+
*
|
|
4
|
+
* This module provides both synchronous and asynchronous HTTP GET request functionality
|
|
5
|
+
* with built-in caching. It works in both browser and Node.js environments by using
|
|
6
|
+
* the appropriate XMLHttpRequest implementation.
|
|
7
|
+
*
|
|
8
|
+
* @module xhr
|
|
9
|
+
* @todo Replace xmlhttprequest with a modern alternative (e.g., node-fetch or axios)
|
|
10
|
+
*/
|
|
1
11
|
// Refactor: xmlhttprequest will be replaced later
|
|
2
12
|
// @ts-expect-error: xmlhttprequest has no types
|
|
3
13
|
import xhrForNode from 'xmlhttprequest';
|
|
4
14
|
import { inBrowser, isString } from '@luckrya/utility';
|
|
5
|
-
|
|
15
|
+
/**
|
|
16
|
+
* In-memory cache for storing fetched HTML content by URL.
|
|
17
|
+
* Prevents redundant network requests for the same URLs.
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
6
20
|
const cache = new Map();
|
|
21
|
+
/**
|
|
22
|
+
* XMLHttpRequest implementation that works in both browser and Node.js.
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
7
25
|
const XHR = inBrowser ? window.XMLHttpRequest : xhrForNode.XMLHttpRequest;
|
|
8
26
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
27
|
+
* Performs a synchronous HTTP GET request to fetch HTML content from a URL.
|
|
28
|
+
*
|
|
29
|
+
* This function fetches the content synchronously, which means it blocks execution
|
|
30
|
+
* until the request completes. The response is cached to avoid redundant requests.
|
|
31
|
+
*
|
|
32
|
+
* @param url - The URL to fetch content from
|
|
33
|
+
* @returns The HTML content as a string, or undefined if the request fails
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* const html = sync('https://example.com')
|
|
38
|
+
* if (html) {
|
|
39
|
+
* console.log('Fetched HTML content')
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @remarks
|
|
44
|
+
* - Returns cached content if available
|
|
45
|
+
* - Only returns content if HTTP status is 200
|
|
46
|
+
* - Errors are logged to console but not thrown
|
|
47
|
+
* - Synchronous requests block the event loop - use with caution
|
|
48
|
+
*
|
|
49
|
+
* @see {@link async} for an asynchronous alternative
|
|
11
50
|
*/
|
|
12
51
|
export function sync(url) {
|
|
13
52
|
if (cache.has(url))
|
|
@@ -29,8 +68,35 @@ export function sync(url) {
|
|
|
29
68
|
return result;
|
|
30
69
|
}
|
|
31
70
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
71
|
+
* Performs an asynchronous HTTP GET request to fetch HTML content from a URL.
|
|
72
|
+
*
|
|
73
|
+
* This function fetches content asynchronously using Promises. The response is
|
|
74
|
+
* cached to avoid redundant requests.
|
|
75
|
+
*
|
|
76
|
+
* @param url - The URL to fetch content from
|
|
77
|
+
* @returns A Promise that resolves to the HTML content string, or undefined if the request fails
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* async function fetchPage() {
|
|
82
|
+
* try {
|
|
83
|
+
* const html = await async('https://example.com')
|
|
84
|
+
* if (html) {
|
|
85
|
+
* console.log('Fetched HTML content')
|
|
86
|
+
* }
|
|
87
|
+
* } catch (error) {
|
|
88
|
+
* console.error('Failed to fetch:', error)
|
|
89
|
+
* }
|
|
90
|
+
* }
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @remarks
|
|
94
|
+
* - Returns cached content if available
|
|
95
|
+
* - Only resolves with content if HTTP status is 200 and readyState is 4
|
|
96
|
+
* - Promise is rejected if the request throws an error
|
|
97
|
+
* - Preferred over `sync()` for non-blocking operations
|
|
98
|
+
*
|
|
99
|
+
* @see {@link sync} for a synchronous alternative
|
|
34
100
|
*/
|
|
35
101
|
export function async(url) {
|
|
36
102
|
return new Promise((resolve, reject) => {
|