html-minifier-next 7.5.2 → 7.5.3
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 +4 -4
- package/cli.js +4 -5
- package/dist/types/lib/urls.d.ts +1 -1
- package/dist/types/lib/utils.d.ts +1 -1
- package/dist/types/presets.d.ts +1 -1
- package/package.json +10 -9
- package/src/htmlminifier.js +13 -13
- package/src/htmlparser.js +28 -28
- package/src/lib/attributes.js +3 -3
- package/src/lib/urls.js +4 -4
- package/src/lib/utils.js +1 -1
- package/src/presets.js +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# HTML Minifier Next
|
|
2
2
|
|
|
3
|
-
[](https://www.npmjs.com/package/html-minifier-next) [](https://github.com/j9t/html-minifier-next/actions) [](https://socket.dev/npm/package/html-minifier-next) [](https://github.com/j9t
|
|
3
|
+
[](https://www.npmjs.com/package/html-minifier-next) [](https://github.com/j9t/html-minifier-next/actions) [](https://socket.dev/npm/package/html-minifier-next) [](https://github.com/sponsors/j9t)
|
|
4
4
|
|
|
5
5
|
Your web page optimization precision tool: HTML Minifier Next (HMN) is a **highly effective, super-configurable, well-tested HTML minifier**, written in JavaScript, that also handles in-document CSS, JavaScript, and SVG minification.
|
|
6
6
|
|
|
@@ -40,13 +40,13 @@ Use `npx html-minifier-next --help` to check all available options:
|
|
|
40
40
|
| `--output <file>`, `-o <file>` | Specify output file (reads from `--input` file argument or STDIN; outputs to STDOUT if not specified) | File to file: `npx html-minifier-next input.html -o output.html`<br>File to file (explicit): `npx html-minifier-next -i input.html -o output.html`<br>Pipe to file: `cat input.html \| npx html-minifier-next -o output.html`<br>File to STDOUT: `npx html-minifier-next input.html` |
|
|
41
41
|
| `--file-ext <extensions>`, `-f <extensions>` | Specify file extension(s) to process (comma-separated, overrides config file setting); defaults to `html,htm,shtml,shtm`; use `*` for all files | `--file-ext=html,php`, `--file-ext='*'` |
|
|
42
42
|
| `--preset <name>`, `-p <name>` | Use a preset configuration (conservative or comprehensive) | `--preset=conservative` |
|
|
43
|
-
| `--config-file <file>`, `-c <file>` | Use a configuration file (defaults to
|
|
43
|
+
| `--config-file <file>`, `-c <file>` | Use a configuration file (defaults to html-minifier-next.config.json in the working directory, if present) | `--config-file=path/to/config.json` |
|
|
44
44
|
| `--verbose`, `-v` | Show detailed processing information (active options, file statistics) | `npx html-minifier-next --input-dir=src --output-dir=dist --verbose --collapse-whitespace` |
|
|
45
45
|
| `--dry`, `-d` | Dry run: Process and report statistics without writing output | `npx html-minifier-next input.html --dry --collapse-whitespace` |
|
|
46
46
|
|
|
47
47
|
### Configuration file
|
|
48
48
|
|
|
49
|
-
You can use a configuration file to specify options. If an
|
|
49
|
+
You can use a configuration file to specify options. If an html-minifier-next.config.json file is present in the current working directory—only there; subfolders (including `--input-dir`) and parent folders are not searched—the CLI picks it up automatically, with a note on STDERR confirming this. An explicit `--config-file` takes precedence. (The standalone `--zero` mode is deliberately config-free and ignores default config files.) The file can be either in JSON format or a JavaScript module that exports the configuration object:
|
|
50
50
|
|
|
51
51
|
**JSON configuration example:**
|
|
52
52
|
|
|
@@ -645,7 +645,7 @@ Parameters:
|
|
|
645
645
|
* `--save`: Saves the run as the baseline (e.g., on `main` before switching to a branch)
|
|
646
646
|
* `--core`: Disables the external minifiers (CSS, JS, SVG, URLs) to isolate HMN’s own processing time
|
|
647
647
|
* `--iterations=N`: Sets the number of timed iterations (default 5; the median is reported)
|
|
648
|
-
* `--config=PATH`: Uses an alternative options file (default
|
|
648
|
+
* `--config=PATH`: Uses an alternative options file (default html-minifier-next.config.json)
|
|
649
649
|
|
|
650
650
|
To compare branches (A/B run), execute `npm run benchmark -- --save` on `main`, then `npm run benchmark` on the branch to see the deltas. Add `--core` on both ends when measuring changes to HMN’s own code rather than the bundled minifiers.
|
|
651
651
|
|
package/cli.js
CHANGED
|
@@ -535,7 +535,7 @@ program.helpOption('-h, --help', 'Display help for command');
|
|
|
535
535
|
}
|
|
536
536
|
|
|
537
537
|
/**
|
|
538
|
-
* Parse comma-separated ignore patterns into an array
|
|
538
|
+
* Parse comma-separated ignore patterns into an array.
|
|
539
539
|
* @param {string} patterns - Comma-separated directory patterns (e.g., "libs,vendor")
|
|
540
540
|
* @returns {string[]} Array of trimmed pattern strings with normalized separators
|
|
541
541
|
*/
|
|
@@ -548,8 +548,8 @@ program.helpOption('-h, --help', 'Display help for command');
|
|
|
548
548
|
}
|
|
549
549
|
|
|
550
550
|
/**
|
|
551
|
-
* Check if a directory should be ignored based on ignore patterns
|
|
552
|
-
* Supports matching by directory name or relative path
|
|
551
|
+
* Check if a directory should be ignored based on ignore patterns.
|
|
552
|
+
* Supports matching by directory name or relative path.
|
|
553
553
|
* @param {string} dirPath - Absolute path to the directory
|
|
554
554
|
* @param {string[]} ignorePatterns - Array of patterns to match against (with forward slashes)
|
|
555
555
|
* @param {string} baseDir - Base directory for relative path calculation
|
|
@@ -791,9 +791,8 @@ program.helpOption('-h, --help', 'Display help for command');
|
|
|
791
791
|
}
|
|
792
792
|
|
|
793
793
|
// Prevent traversing into the output directory when it is inside the input directory
|
|
794
|
-
let inputReal;
|
|
795
794
|
let outputReal;
|
|
796
|
-
inputReal = await fs.promises.realpath(inputDir).catch(() => undefined);
|
|
795
|
+
const inputReal = await fs.promises.realpath(inputDir).catch(() => undefined);
|
|
797
796
|
try {
|
|
798
797
|
outputReal = await fs.promises.realpath(outputDir);
|
|
799
798
|
} catch {
|
package/dist/types/lib/urls.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - Otherwise: normalized absolute URL
|
|
8
8
|
*/
|
|
9
9
|
/**
|
|
10
|
-
* Create a URL minifier function for the given site context
|
|
10
|
+
* Create a URL minifier function for the given site context.
|
|
11
11
|
* @param {string} site - The site base URL (used to compute relative URLs)
|
|
12
12
|
* @returns {(url: string) => string} Minifier function that returns the shortest URL
|
|
13
13
|
*/
|
|
@@ -42,7 +42,7 @@ declare function isThenable(value: unknown): value is PromiseLike<any>;
|
|
|
42
42
|
/** @param {string} value */
|
|
43
43
|
declare function lowercase(value: string): string;
|
|
44
44
|
/**
|
|
45
|
-
* Asynchronously replace matches in a string
|
|
45
|
+
* Asynchronously replace matches in a string.
|
|
46
46
|
* @param {string} str - Input string
|
|
47
47
|
* @param {RegExp} regex - Regular expression with global flag
|
|
48
48
|
* @param {Function} asyncFn - Async function to process each match
|
package/dist/types/presets.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ export declare const presets: {
|
|
|
36
36
|
};
|
|
37
37
|
};
|
|
38
38
|
/**
|
|
39
|
-
* Get preset configuration by name
|
|
39
|
+
* Get preset configuration by name.
|
|
40
40
|
* @param {string} name - Preset name (“conservative” or “comprehensive”)
|
|
41
41
|
* @returns {object|null} Preset options object or null if not found
|
|
42
42
|
*/
|
package/package.json
CHANGED
|
@@ -10,17 +10,18 @@
|
|
|
10
10
|
"entities": "^8.0.0",
|
|
11
11
|
"lightningcss": "^1.33.0",
|
|
12
12
|
"svgo": "^4.0.2",
|
|
13
|
-
"terser": "^5.
|
|
13
|
+
"terser": "^5.50.0"
|
|
14
14
|
},
|
|
15
15
|
"description": "Highly effective, super-configurable, well-tested web page minifier (enhanced successor to HTML Minifier)",
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@commitlint/cli": "^21.2.
|
|
17
|
+
"@commitlint/cli": "^21.2.2",
|
|
18
18
|
"@eslint/js": "^10.0.1",
|
|
19
|
-
"@swc/core": "^1.15.
|
|
20
|
-
"@types/node": "^26.
|
|
21
|
-
"eslint": "^10.8.
|
|
19
|
+
"@swc/core": "^1.15.47",
|
|
20
|
+
"@types/node": "^26.2.0",
|
|
21
|
+
"eslint": "^10.8.1",
|
|
22
|
+
"globals": "^17.11.0",
|
|
22
23
|
"typescript": "^7.0.2",
|
|
23
|
-
"vite": "^8.1
|
|
24
|
+
"vite": "^8.2.1"
|
|
24
25
|
},
|
|
25
26
|
"engines": {
|
|
26
27
|
"node": ">=22.13"
|
|
@@ -88,11 +89,11 @@
|
|
|
88
89
|
"prepack": "npm run build",
|
|
89
90
|
"prepare": "git config core.hooksPath .githooks || true",
|
|
90
91
|
"serve": "vite",
|
|
91
|
-
"test": "npm run test:types && node --test
|
|
92
|
+
"test": "npm run test:types && node --test \"test/**/*.test.js\"",
|
|
92
93
|
"test:types": "tsc --noEmit && tsc --project tsconfig.test.json",
|
|
93
|
-
"test:watch": "node --test --watch
|
|
94
|
+
"test:watch": "node --test --watch \"test/**/*.test.js\""
|
|
94
95
|
},
|
|
95
96
|
"type": "module",
|
|
96
97
|
"types": "dist/types/htmlminifier.d.ts",
|
|
97
|
-
"version": "7.5.
|
|
98
|
+
"version": "7.5.3"
|
|
98
99
|
}
|
package/src/htmlminifier.js
CHANGED
|
@@ -910,23 +910,23 @@ async function createSortFns(value, options, uidIgnore, uidAttr, ignoredMarkupCh
|
|
|
910
910
|
// Memoize `sortClassNames` results—class lists often repeat in templates
|
|
911
911
|
const classNameCache = new LRU(500);
|
|
912
912
|
|
|
913
|
-
options.sortClassNames = function (/** @type {string} */
|
|
913
|
+
options.sortClassNames = function (/** @type {string} */ classNames) {
|
|
914
914
|
// Fast path: Single class (no spaces) needs no sorting
|
|
915
|
-
if (
|
|
916
|
-
return
|
|
915
|
+
if (classNames.indexOf(' ') === -1) {
|
|
916
|
+
return classNames;
|
|
917
917
|
}
|
|
918
918
|
|
|
919
919
|
// Check cache first
|
|
920
|
-
const cached = /** @type {string | undefined} */ (classNameCache.get(
|
|
920
|
+
const cached = /** @type {string | undefined} */ (classNameCache.get(classNames));
|
|
921
921
|
if (cached !== undefined) {
|
|
922
922
|
return cached;
|
|
923
923
|
}
|
|
924
924
|
|
|
925
925
|
// Expand UID tokens back to original content before sorting
|
|
926
926
|
// Fast path: Skip if no HTML comments (UID markers) present
|
|
927
|
-
let expandedValue =
|
|
928
|
-
if (uidReplacePattern &&
|
|
929
|
-
expandedValue =
|
|
927
|
+
let expandedValue = classNames;
|
|
928
|
+
if (uidReplacePattern && classNames.indexOf('<!--') !== -1) {
|
|
929
|
+
expandedValue = classNames.replace(uidReplacePattern, function (/** @type {string} */ _match, /** @type {string} */ index) {
|
|
930
930
|
return ignoredMarkupChunks[+index] || '';
|
|
931
931
|
});
|
|
932
932
|
// Reset `lastIndex` for pattern reuse
|
|
@@ -939,7 +939,7 @@ async function createSortFns(value, options, uidIgnore, uidAttr, ignoredMarkupCh
|
|
|
939
939
|
const result = sorted.join(' ');
|
|
940
940
|
|
|
941
941
|
// Cache the result
|
|
942
|
-
classNameCache.set(
|
|
942
|
+
classNameCache.set(classNames, result);
|
|
943
943
|
return result;
|
|
944
944
|
};
|
|
945
945
|
}
|
|
@@ -1148,11 +1148,11 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1148
1148
|
|
|
1149
1149
|
// Look for trailing whitespaces, bypass any inline tags
|
|
1150
1150
|
function trimTrailingWhitespace(/** @type {number} */ index, /** @type {string} */ nextTag) {
|
|
1151
|
-
for (let
|
|
1151
|
+
for (let prevTag = ''; index >= 0 && canTrimWhitespace(prevTag, emptyAttrs); index--) {
|
|
1152
1152
|
const str = buffer[index] ?? '';
|
|
1153
1153
|
const match = str.match(/^<\/([\w:-]+)>$/);
|
|
1154
1154
|
if (match) {
|
|
1155
|
-
|
|
1155
|
+
prevTag = match[1] ?? '';
|
|
1156
1156
|
} else if (/>$/.test(str) || (buffer[index] = collapseWhitespaceSmart(str, '', nextTag, emptyAttrs, emptyAttrs, options, inlineElements, inlineTextSet))) {
|
|
1157
1157
|
break;
|
|
1158
1158
|
}
|
|
@@ -1398,13 +1398,13 @@ async function minifyHTML(value, options, partialMarkup) {
|
|
|
1398
1398
|
// Collapse if both sides are element/closing tags or HTML comments, and neither is inline
|
|
1399
1399
|
if ((currentTagMatch || currentIsHtmlComment || currentClosingTagMatch) &&
|
|
1400
1400
|
(prevTagMatch || prevIsHtmlComment || prevClosingTagMatch)) {
|
|
1401
|
-
const
|
|
1401
|
+
const currentTagName = currentTagMatch ? resolveName(currentTagMatch[1] ?? '')
|
|
1402
1402
|
: currentClosingTagMatch ? resolveName(currentClosingTagMatch[1] ?? '') : null;
|
|
1403
|
-
const
|
|
1403
|
+
const prevTagName = prevTagMatch ? resolveName(prevTagMatch[1] ?? '')
|
|
1404
1404
|
: prevClosingTagMatch ? resolveName(prevClosingTagMatch[1] ?? '') : null;
|
|
1405
1405
|
|
|
1406
1406
|
// Don’t collapse between inline elements (HTML comments count as non-inline)
|
|
1407
|
-
if (!inlineElements.has(
|
|
1407
|
+
if (!inlineElements.has(currentTagName ?? '') && !inlineElements.has(prevTagName ?? '')) {
|
|
1408
1408
|
// Collapse whitespace respecting context rules
|
|
1409
1409
|
let collapsedText = prevText;
|
|
1410
1410
|
|
package/src/htmlparser.js
CHANGED
|
@@ -154,7 +154,7 @@ function buildAttrRegex(handler) {
|
|
|
154
154
|
|
|
155
155
|
/** @param {HTMLParserHandler} handler */
|
|
156
156
|
function getAttrRegexForHandler(handler) {
|
|
157
|
-
|
|
157
|
+
const cached = attrRegexCache.get(handler);
|
|
158
158
|
if (cached) return cached;
|
|
159
159
|
const compiled = buildAttrRegex(handler);
|
|
160
160
|
attrRegexCache.set(handler, compiled);
|
|
@@ -166,7 +166,7 @@ const attrRegexStickyCache = new WeakMap();
|
|
|
166
166
|
|
|
167
167
|
/** @param {HTMLParserHandler} handler */
|
|
168
168
|
function getAttrRegexStickyForHandler(handler) {
|
|
169
|
-
|
|
169
|
+
const cached = attrRegexStickyCache.get(handler);
|
|
170
170
|
if (cached) return cached;
|
|
171
171
|
const nonSticky = getAttrRegexForHandler(handler);
|
|
172
172
|
// Derive sticky version: Remove `^` anchor, add `y` flag
|
|
@@ -657,12 +657,12 @@ export class HTMLParser {
|
|
|
657
657
|
|
|
658
658
|
// `needle` must already be lowercase
|
|
659
659
|
function findTagInCurrentTable(/** @type {string} */ needle) {
|
|
660
|
-
let
|
|
661
|
-
for (
|
|
662
|
-
const entry = stack[
|
|
660
|
+
let stackIndex;
|
|
661
|
+
for (stackIndex = stack.length - 1; stackIndex >= 0; stackIndex--) {
|
|
662
|
+
const entry = stack[stackIndex];
|
|
663
663
|
const currentTag = entry?.lowerTag;
|
|
664
664
|
if (currentTag === needle) {
|
|
665
|
-
return
|
|
665
|
+
return stackIndex;
|
|
666
666
|
}
|
|
667
667
|
// Stop searching if hitting a table boundary
|
|
668
668
|
if (currentTag === 'table') {
|
|
@@ -672,25 +672,25 @@ export class HTMLParser {
|
|
|
672
672
|
return -1;
|
|
673
673
|
}
|
|
674
674
|
|
|
675
|
-
function parseEndTagAt(/** @type {number} */
|
|
676
|
-
// Close all open elements up to `
|
|
675
|
+
function parseEndTagAt(/** @type {number} */ stackIndex) {
|
|
676
|
+
// Close all open elements up to `stackIndex` (mirrors `parseEndTag`’s core branch);
|
|
677
677
|
// `end` handlers are synchronous—invoked without awaiting, as in `parseEndTag`
|
|
678
|
-
for (let i = stack.length - 1; i >=
|
|
678
|
+
for (let i = stack.length - 1; i >= stackIndex; i--) {
|
|
679
679
|
const entry = /** @type {NonNullable<(typeof stack)[number]>} */ (stack[i]);
|
|
680
680
|
if (handler.end) {
|
|
681
681
|
handler.end(entry.tag, entry.attrs, true);
|
|
682
682
|
}
|
|
683
683
|
}
|
|
684
|
-
stack.length =
|
|
685
|
-
lastTag =
|
|
686
|
-
lastTagLower =
|
|
684
|
+
stack.length = stackIndex;
|
|
685
|
+
lastTag = stackIndex ? (stack[stackIndex - 1]?.tag ?? '') : '';
|
|
686
|
+
lastTagLower = stackIndex ? (stack[stackIndex - 1]?.lowerTag ?? '') : '';
|
|
687
687
|
}
|
|
688
688
|
|
|
689
689
|
function closeIfFoundInCurrentTable(/** @type {string} */ tagName) {
|
|
690
|
-
const
|
|
691
|
-
if (
|
|
690
|
+
const stackIndex = findTagInCurrentTable(tagName);
|
|
691
|
+
if (stackIndex >= 0) {
|
|
692
692
|
// Close at the specific index to avoid re-searching
|
|
693
|
-
parseEndTagAt(
|
|
693
|
+
parseEndTagAt(stackIndex);
|
|
694
694
|
return true;
|
|
695
695
|
}
|
|
696
696
|
return false;
|
|
@@ -812,38 +812,38 @@ export class HTMLParser {
|
|
|
812
812
|
|
|
813
813
|
// `needle` must already be lowercase
|
|
814
814
|
function findTag(/** @type {string} */ needle) {
|
|
815
|
-
let
|
|
816
|
-
for (
|
|
817
|
-
if (stack[
|
|
815
|
+
let stackIndex;
|
|
816
|
+
for (stackIndex = stack.length - 1; stackIndex >= 0; stackIndex--) {
|
|
817
|
+
if (stack[stackIndex]?.lowerTag === needle) {
|
|
818
818
|
break;
|
|
819
819
|
}
|
|
820
820
|
}
|
|
821
|
-
return
|
|
821
|
+
return stackIndex;
|
|
822
822
|
}
|
|
823
823
|
|
|
824
824
|
async function parseEndTag(/** @type {string} */ tag, /** @type {string} */ tagName) {
|
|
825
|
-
let
|
|
825
|
+
let stackIndex;
|
|
826
826
|
const lowerTagName = tagName ? tagName.toLowerCase() : '';
|
|
827
827
|
|
|
828
828
|
// Find the closest opened tag of the same type
|
|
829
829
|
if (tagName) {
|
|
830
|
-
|
|
830
|
+
stackIndex = findTag(lowerTagName);
|
|
831
831
|
} else { // If no tag name is provided, clean shop
|
|
832
|
-
|
|
832
|
+
stackIndex = 0;
|
|
833
833
|
}
|
|
834
834
|
|
|
835
|
-
if (
|
|
835
|
+
if (stackIndex >= 0) {
|
|
836
836
|
// Close all the open elements, up the stack
|
|
837
|
-
for (let i = stack.length - 1; i >=
|
|
837
|
+
for (let i = stack.length - 1; i >= stackIndex; i--) {
|
|
838
838
|
if (handler.end) {
|
|
839
|
-
handler.end(stack[i]?.tag, stack[i]?.attrs, i >
|
|
839
|
+
handler.end(stack[i]?.tag, stack[i]?.attrs, i > stackIndex || !tag);
|
|
840
840
|
}
|
|
841
841
|
}
|
|
842
842
|
|
|
843
843
|
// Remove the open elements from the stack
|
|
844
|
-
stack.length =
|
|
845
|
-
lastTag =
|
|
846
|
-
lastTagLower =
|
|
844
|
+
stack.length = stackIndex;
|
|
845
|
+
lastTag = stackIndex ? (stack[stackIndex - 1]?.tag ?? '') : '';
|
|
846
|
+
lastTagLower = stackIndex ? (stack[stackIndex - 1]?.lowerTag ?? '') : '';
|
|
847
847
|
} else if (handler.partialMarkup && tagName) {
|
|
848
848
|
// In partial markup mode, preserve stray end tags
|
|
849
849
|
if (handler.end) {
|
package/src/lib/attributes.js
CHANGED
|
@@ -487,7 +487,7 @@ function cleanAttributeValue(tag, attrName, attrValue, options, attrs, minifyHTM
|
|
|
487
487
|
}
|
|
488
488
|
// Sync path (`minifyCSS` disabled—identity function)
|
|
489
489
|
if (cssResult && /^(?:[a-z-]+:[;\s]*)+$/i.test(cssResult)) return '';
|
|
490
|
-
return cssResult
|
|
490
|
+
return cssResult ?? attrValue;
|
|
491
491
|
}
|
|
492
492
|
return attrValue;
|
|
493
493
|
}
|
|
@@ -589,7 +589,7 @@ function cleanAttributeValue(tag, attrName, attrValue, options, attrs, minifyHTM
|
|
|
589
589
|
return originalAttrValue;
|
|
590
590
|
});
|
|
591
591
|
}
|
|
592
|
-
return cssResult
|
|
592
|
+
return cssResult ?? attrValue;
|
|
593
593
|
}
|
|
594
594
|
|
|
595
595
|
if (tag === 'iframe' && attrName === 'srcdoc') {
|
|
@@ -635,7 +635,7 @@ function chooseAttributeQuote(attrValue, options) {
|
|
|
635
635
|
*/
|
|
636
636
|
function normalizeAttr(attr, attrs, tag, options, minifyHTML) {
|
|
637
637
|
const attrName = options.name(attr.name);
|
|
638
|
-
|
|
638
|
+
const attrValue = attr.value;
|
|
639
639
|
|
|
640
640
|
// Entity decoding requires a lazy import—async only when `&` is present
|
|
641
641
|
if (options.decodeEntities && attrValue && attrValue.indexOf('&') !== -1) {
|
package/src/lib/urls.js
CHANGED
|
@@ -12,7 +12,7 @@ const REJECTED_SCHEMES = new Set(['data:', 'javascript:', 'mailto:']);
|
|
|
12
12
|
const DIRECTORY_INDEXES = ['index.html', 'index.htm'];
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Get the directory portion of a pathname (up to and including the last `/`)
|
|
15
|
+
* Get the directory portion of a pathname (up to and including the last `/`).
|
|
16
16
|
* @param {string} pathname
|
|
17
17
|
* @returns {string}
|
|
18
18
|
*/
|
|
@@ -22,7 +22,7 @@ function getDirectory(pathname) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* Compute a path-relative URL from a base directory to a target path
|
|
25
|
+
* Compute a path-relative URL from a base directory to a target path.
|
|
26
26
|
* @param {string} baseDir - Base directory path (must end with `/`)
|
|
27
27
|
* @param {string} targetPath - Target pathname
|
|
28
28
|
* @returns {string}
|
|
@@ -63,7 +63,7 @@ function relativize(baseDir, targetPath) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
|
-
* Remove directory index from the end of a pathname
|
|
66
|
+
* Remove directory index from the end of a pathname.
|
|
67
67
|
* @param {string} pathname
|
|
68
68
|
* @returns {string}
|
|
69
69
|
*/
|
|
@@ -77,7 +77,7 @@ function removeDirectoryIndex(pathname) {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/**
|
|
80
|
-
* Create a URL minifier function for the given site context
|
|
80
|
+
* Create a URL minifier function for the given site context.
|
|
81
81
|
* @param {string} site - The site base URL (used to compute relative URLs)
|
|
82
82
|
* @returns {(url: string) => string} Minifier function that returns the shortest URL
|
|
83
83
|
*/
|
package/src/lib/utils.js
CHANGED
|
@@ -109,7 +109,7 @@ function lowercase(value) {
|
|
|
109
109
|
// Replace async helper
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
|
-
* Asynchronously replace matches in a string
|
|
112
|
+
* Asynchronously replace matches in a string.
|
|
113
113
|
* @param {string} str - Input string
|
|
114
114
|
* @param {RegExp} regex - Regular expression with global flag
|
|
115
115
|
* @param {Function} asyncFn - Async function to process each match
|
package/src/presets.js
CHANGED
|
@@ -38,7 +38,7 @@ export const presets = {
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* Get preset configuration by name
|
|
41
|
+
* Get preset configuration by name.
|
|
42
42
|
* @param {string} name - Preset name (“conservative” or “comprehensive”)
|
|
43
43
|
* @returns {object|null} Preset options object or null if not found
|
|
44
44
|
*/
|