@cjser/filenamify 7.0.1-cjser.2 → 7.0.3-cjser.2
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/dist-cjser/browser.cjs +18 -7
- package/dist-cjser/index.cjs +20 -9
- package/filenamify.d.ts +4 -0
- package/filenamify.js +23 -7
- package/package.json +5 -5
- package/readme.md +4 -0
package/dist-cjser/browser.cjs
CHANGED
|
@@ -26,7 +26,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
));
|
|
27
27
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
28
|
|
|
29
|
-
// packages/@cjser/filenamify.tmp-
|
|
29
|
+
// packages/@cjser/filenamify.tmp-25-1788284040504/filenamify.js
|
|
30
30
|
var filenamify_exports = {};
|
|
31
31
|
__export(filenamify_exports, {
|
|
32
32
|
default: () => filenamify
|
|
@@ -36,6 +36,8 @@ var import_filename_reserved_regex = __toESM(require("@cjser/filename-reserved-r
|
|
|
36
36
|
var MAX_FILENAME_LENGTH = 100;
|
|
37
37
|
var reRelativePath = /^\.+(\\|\/)|^\.+$/;
|
|
38
38
|
var reTrailingDotsAndSpaces = /[. ]+$/;
|
|
39
|
+
var reTrailingSpaces = / +$/;
|
|
40
|
+
var reWindowsReservedName = (0, import_filename_reserved_regex.windowsReservedNameRegex)();
|
|
39
41
|
var reControlChars = /[\p{Control}\p{Format}\p{Zl}\p{Zp}\uFFF0-\uFFFF]/gu;
|
|
40
42
|
var reControlCharsTest = /[\p{Control}\p{Format}\p{Zl}\p{Zp}\uFFF0-\uFFFF]/u;
|
|
41
43
|
var isZeroWidthJoiner = (char) => char === "\u200D";
|
|
@@ -59,7 +61,18 @@ function truncateFilename(filename, maxLength) {
|
|
|
59
61
|
const extension = filename.slice(extensionIndex);
|
|
60
62
|
const baseBudget = Math.max(0, maxLength - extension.length);
|
|
61
63
|
const truncatedBase = truncateByGraphemeBudget(base, baseBudget);
|
|
62
|
-
return truncatedBase.replace(
|
|
64
|
+
return truncatedBase.replace(reTrailingSpaces, "") + extension;
|
|
65
|
+
}
|
|
66
|
+
function ensureNonWindowsReservedName(filename, replacement) {
|
|
67
|
+
if (!reWindowsReservedName.test(filename)) {
|
|
68
|
+
return filename;
|
|
69
|
+
}
|
|
70
|
+
const extensionIndex = filename.indexOf(".");
|
|
71
|
+
const base = extensionIndex === -1 ? filename : filename.slice(0, extensionIndex);
|
|
72
|
+
const baseWithoutTrailingSpaces = base.replace(reTrailingSpaces, "");
|
|
73
|
+
const extension = extensionIndex === -1 ? "" : filename.slice(extensionIndex);
|
|
74
|
+
const candidate = (baseWithoutTrailingSpaces + replacement + extension).replace(reTrailingDotsAndSpaces, "");
|
|
75
|
+
return reWindowsReservedName.test(candidate) ? baseWithoutTrailingSpaces + "!" + extension : candidate;
|
|
63
76
|
}
|
|
64
77
|
function filenamify(string, options = {}) {
|
|
65
78
|
if (typeof string !== "string") {
|
|
@@ -77,8 +90,8 @@ function filenamify(string, options = {}) {
|
|
|
77
90
|
string = string.replaceAll(reRepeatedReservedCharacters, "$1");
|
|
78
91
|
}
|
|
79
92
|
string = string.replace(reTrailingDotsAndSpaces, "");
|
|
80
|
-
string = string.replace(reRelativePath, replacement);
|
|
81
|
-
string = string.replace((0, import_filename_reserved_regex.default)(), replacement);
|
|
93
|
+
string = string.replace(reRelativePath, () => replacement);
|
|
94
|
+
string = string.replace((0, import_filename_reserved_regex.default)(), () => replacement);
|
|
82
95
|
string = string.replaceAll(reControlChars, (char) => isZeroWidthJoiner(char) ? char : replacement);
|
|
83
96
|
string = string.replace(reTrailingDotsAndSpaces, "");
|
|
84
97
|
if (string.length === 0) {
|
|
@@ -90,9 +103,7 @@ function filenamify(string, options = {}) {
|
|
|
90
103
|
const allowedLength = typeof options.maxLength === "number" ? options.maxLength : MAX_FILENAME_LENGTH;
|
|
91
104
|
string = truncateFilename(string, allowedLength);
|
|
92
105
|
string = string.replace(reTrailingDotsAndSpaces, "");
|
|
93
|
-
|
|
94
|
-
string += replacement;
|
|
95
|
-
}
|
|
106
|
+
string = ensureNonWindowsReservedName(string, replacement);
|
|
96
107
|
return string;
|
|
97
108
|
}
|
|
98
109
|
function truncateByGraphemeBudget(input, budget) {
|
package/dist-cjser/index.cjs
CHANGED
|
@@ -26,7 +26,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
));
|
|
27
27
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
28
|
|
|
29
|
-
// packages/@cjser/filenamify.tmp-
|
|
29
|
+
// packages/@cjser/filenamify.tmp-25-1788284040504/index.js
|
|
30
30
|
var index_exports = {};
|
|
31
31
|
__export(index_exports, {
|
|
32
32
|
default: () => filenamify,
|
|
@@ -34,11 +34,13 @@ __export(index_exports, {
|
|
|
34
34
|
});
|
|
35
35
|
module.exports = __toCommonJS(index_exports);
|
|
36
36
|
|
|
37
|
-
// packages/@cjser/filenamify.tmp-
|
|
37
|
+
// packages/@cjser/filenamify.tmp-25-1788284040504/filenamify.js
|
|
38
38
|
var import_filename_reserved_regex = __toESM(require("@cjser/filename-reserved-regex"), 1);
|
|
39
39
|
var MAX_FILENAME_LENGTH = 100;
|
|
40
40
|
var reRelativePath = /^\.+(\\|\/)|^\.+$/;
|
|
41
41
|
var reTrailingDotsAndSpaces = /[. ]+$/;
|
|
42
|
+
var reTrailingSpaces = / +$/;
|
|
43
|
+
var reWindowsReservedName = (0, import_filename_reserved_regex.windowsReservedNameRegex)();
|
|
42
44
|
var reControlChars = /[\p{Control}\p{Format}\p{Zl}\p{Zp}\uFFF0-\uFFFF]/gu;
|
|
43
45
|
var reControlCharsTest = /[\p{Control}\p{Format}\p{Zl}\p{Zp}\uFFF0-\uFFFF]/u;
|
|
44
46
|
var isZeroWidthJoiner = (char) => char === "\u200D";
|
|
@@ -62,7 +64,18 @@ function truncateFilename(filename, maxLength) {
|
|
|
62
64
|
const extension = filename.slice(extensionIndex);
|
|
63
65
|
const baseBudget = Math.max(0, maxLength - extension.length);
|
|
64
66
|
const truncatedBase = truncateByGraphemeBudget(base, baseBudget);
|
|
65
|
-
return truncatedBase.replace(
|
|
67
|
+
return truncatedBase.replace(reTrailingSpaces, "") + extension;
|
|
68
|
+
}
|
|
69
|
+
function ensureNonWindowsReservedName(filename, replacement) {
|
|
70
|
+
if (!reWindowsReservedName.test(filename)) {
|
|
71
|
+
return filename;
|
|
72
|
+
}
|
|
73
|
+
const extensionIndex = filename.indexOf(".");
|
|
74
|
+
const base = extensionIndex === -1 ? filename : filename.slice(0, extensionIndex);
|
|
75
|
+
const baseWithoutTrailingSpaces = base.replace(reTrailingSpaces, "");
|
|
76
|
+
const extension = extensionIndex === -1 ? "" : filename.slice(extensionIndex);
|
|
77
|
+
const candidate = (baseWithoutTrailingSpaces + replacement + extension).replace(reTrailingDotsAndSpaces, "");
|
|
78
|
+
return reWindowsReservedName.test(candidate) ? baseWithoutTrailingSpaces + "!" + extension : candidate;
|
|
66
79
|
}
|
|
67
80
|
function filenamify(string, options = {}) {
|
|
68
81
|
if (typeof string !== "string") {
|
|
@@ -80,8 +93,8 @@ function filenamify(string, options = {}) {
|
|
|
80
93
|
string = string.replaceAll(reRepeatedReservedCharacters, "$1");
|
|
81
94
|
}
|
|
82
95
|
string = string.replace(reTrailingDotsAndSpaces, "");
|
|
83
|
-
string = string.replace(reRelativePath, replacement);
|
|
84
|
-
string = string.replace((0, import_filename_reserved_regex.default)(), replacement);
|
|
96
|
+
string = string.replace(reRelativePath, () => replacement);
|
|
97
|
+
string = string.replace((0, import_filename_reserved_regex.default)(), () => replacement);
|
|
85
98
|
string = string.replaceAll(reControlChars, (char) => isZeroWidthJoiner(char) ? char : replacement);
|
|
86
99
|
string = string.replace(reTrailingDotsAndSpaces, "");
|
|
87
100
|
if (string.length === 0) {
|
|
@@ -93,9 +106,7 @@ function filenamify(string, options = {}) {
|
|
|
93
106
|
const allowedLength = typeof options.maxLength === "number" ? options.maxLength : MAX_FILENAME_LENGTH;
|
|
94
107
|
string = truncateFilename(string, allowedLength);
|
|
95
108
|
string = string.replace(reTrailingDotsAndSpaces, "");
|
|
96
|
-
|
|
97
|
-
string += replacement;
|
|
98
|
-
}
|
|
109
|
+
string = ensureNonWindowsReservedName(string, replacement);
|
|
99
110
|
return string;
|
|
100
111
|
}
|
|
101
112
|
function truncateByGraphemeBudget(input, budget) {
|
|
@@ -115,7 +126,7 @@ function truncateByGraphemeBudget(input, budget) {
|
|
|
115
126
|
return output;
|
|
116
127
|
}
|
|
117
128
|
|
|
118
|
-
// packages/@cjser/filenamify.tmp-
|
|
129
|
+
// packages/@cjser/filenamify.tmp-25-1788284040504/filenamify-path.js
|
|
119
130
|
var import_node_path = __toESM(require("node:path"), 1);
|
|
120
131
|
function filenamifyPath(filePath, options) {
|
|
121
132
|
filePath = import_node_path.default.resolve(filePath);
|
package/filenamify.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export type Options = {
|
|
|
4
4
|
|
|
5
5
|
Cannot contain: `<` `>` `:` `"` `/` `\` `|` `?` `*` or control characters.
|
|
6
6
|
|
|
7
|
+
If the replacement would leave the result as a Windows reserved name, `!` is used instead.
|
|
8
|
+
|
|
7
9
|
@default '!'
|
|
8
10
|
*/
|
|
9
11
|
readonly replacement?: string;
|
|
@@ -13,6 +15,8 @@ export type Options = {
|
|
|
13
15
|
|
|
14
16
|
Only the base of the filename is truncated, preserving the extension. If the extension itself is longer than `maxLength`, you will get a string that is longer than `maxLength`, so you need to check for that if you allow arbitrary extensions.
|
|
15
17
|
|
|
18
|
+
The result may also exceed `maxLength` when a suffix is needed to avoid a Windows reserved name.
|
|
19
|
+
|
|
16
20
|
Truncation is grapheme-aware and will not split Unicode characters (surrogate pairs or extended grapheme clusters). If the remaining budget (after accounting for the extension) is smaller than a whole grapheme, the base filename may be truncated to an empty string to avoid splitting.
|
|
17
21
|
|
|
18
22
|
Systems generally allow up to 255 characters, but we default to 100 for usability reasons.
|
package/filenamify.js
CHANGED
|
@@ -5,6 +5,8 @@ const MAX_FILENAME_LENGTH = 100;
|
|
|
5
5
|
|
|
6
6
|
const reRelativePath = /^\.+(\\|\/)|^\.+$/;
|
|
7
7
|
const reTrailingDotsAndSpaces = /[. ]+$/;
|
|
8
|
+
const reTrailingSpaces = / +$/;
|
|
9
|
+
const reWindowsReservedName = windowsReservedNameRegex();
|
|
8
10
|
|
|
9
11
|
// Remove all problematic characters except zero-width joiner (\u200D) needed for emoji
|
|
10
12
|
const reControlChars = /[\p{Control}\p{Format}\p{Zl}\p{Zp}\uFFF0-\uFFFF]/gu;
|
|
@@ -44,7 +46,23 @@ function truncateFilename(filename, maxLength) {
|
|
|
44
46
|
const truncatedBase = truncateByGraphemeBudget(base, baseBudget);
|
|
45
47
|
|
|
46
48
|
// Strip trailing spaces from base (not periods - they're not trailing in final filename)
|
|
47
|
-
return truncatedBase.replace(
|
|
49
|
+
return truncatedBase.replace(reTrailingSpaces, '') + extension;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function ensureNonWindowsReservedName(filename, replacement) {
|
|
53
|
+
if (!reWindowsReservedName.test(filename)) {
|
|
54
|
+
return filename;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const extensionIndex = filename.indexOf('.');
|
|
58
|
+
const base = extensionIndex === -1 ? filename : filename.slice(0, extensionIndex);
|
|
59
|
+
const baseWithoutTrailingSpaces = base.replace(reTrailingSpaces, '');
|
|
60
|
+
const extension = extensionIndex === -1 ? '' : filename.slice(extensionIndex);
|
|
61
|
+
const candidate = (baseWithoutTrailingSpaces + replacement + extension).replace(reTrailingDotsAndSpaces, '');
|
|
62
|
+
|
|
63
|
+
return reWindowsReservedName.test(candidate)
|
|
64
|
+
? baseWithoutTrailingSpaces + '!' + extension
|
|
65
|
+
: candidate;
|
|
48
66
|
}
|
|
49
67
|
|
|
50
68
|
export default function filenamify(string, options = {}) {
|
|
@@ -75,8 +93,8 @@ export default function filenamify(string, options = {}) {
|
|
|
75
93
|
// so they get stripped rather than replaced
|
|
76
94
|
string = string.replace(reTrailingDotsAndSpaces, '');
|
|
77
95
|
|
|
78
|
-
string = string.replace(reRelativePath, replacement);
|
|
79
|
-
string = string.replace(filenameReservedRegex(), replacement);
|
|
96
|
+
string = string.replace(reRelativePath, () => replacement);
|
|
97
|
+
string = string.replace(filenameReservedRegex(), () => replacement);
|
|
80
98
|
string = string.replaceAll(reControlChars, char => isZeroWidthJoiner(char) ? char : replacement);
|
|
81
99
|
|
|
82
100
|
// Trim trailing spaces and periods again (in case replacement created new ones)
|
|
@@ -99,10 +117,8 @@ export default function filenamify(string, options = {}) {
|
|
|
99
117
|
string = string.replace(reTrailingDotsAndSpaces, '');
|
|
100
118
|
|
|
101
119
|
// Check for Windows reserved names after truncation and stripping
|
|
102
|
-
// Windows compatibility takes precedence over maxLength, so
|
|
103
|
-
|
|
104
|
-
string += replacement;
|
|
105
|
-
}
|
|
120
|
+
// Windows compatibility takes precedence over maxLength, so the result may exceed it
|
|
121
|
+
string = ensureNonWindowsReservedName(string, replacement);
|
|
106
122
|
|
|
107
123
|
return string;
|
|
108
124
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cjser/filenamify",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.3-cjser.2",
|
|
4
4
|
"description": "Convert a string to a valid safe filename",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"dirname"
|
|
57
57
|
],
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@cjser/filename-reserved-regex": "4.0.
|
|
59
|
+
"@cjser/filename-reserved-regex": "4.0.1-cjser.2"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"ava": "^6.4.1",
|
|
@@ -65,11 +65,11 @@
|
|
|
65
65
|
"types": "./index.d.ts",
|
|
66
66
|
"main": "./dist-cjser/index.cjs",
|
|
67
67
|
"cjser": {
|
|
68
|
-
"sourceVersion": "7.0.
|
|
68
|
+
"sourceVersion": "7.0.3",
|
|
69
69
|
"cjserVersion": 2,
|
|
70
70
|
"original": {
|
|
71
71
|
"name": "filenamify",
|
|
72
|
-
"version": "7.0.
|
|
72
|
+
"version": "7.0.3",
|
|
73
73
|
"exports": {
|
|
74
74
|
".": {
|
|
75
75
|
"types": "./index.d.ts",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
},
|
|
83
83
|
"repository": "sindresorhus/filenamify",
|
|
84
84
|
"dependencies": {
|
|
85
|
-
"filename-reserved-regex": "^4.0.
|
|
85
|
+
"filename-reserved-regex": "^4.0.1"
|
|
86
86
|
},
|
|
87
87
|
"files": [
|
|
88
88
|
"filenamify-path.d.ts",
|
package/readme.md
CHANGED
|
@@ -54,6 +54,8 @@ String to use as replacement for reserved filename characters.
|
|
|
54
54
|
|
|
55
55
|
Cannot contain: `<` `>` `:` `"` `/` `\` `|` `?` `*` or control characters.
|
|
56
56
|
|
|
57
|
+
If the replacement would leave the result as a Windows reserved name, `!` is used instead.
|
|
58
|
+
|
|
57
59
|
##### maxLength
|
|
58
60
|
|
|
59
61
|
Type: `number`\
|
|
@@ -63,6 +65,8 @@ Truncate the filename to the given length.
|
|
|
63
65
|
|
|
64
66
|
Only the base of the filename is truncated, preserving the extension. If the extension itself is longer than `maxLength`, you will get a string that is longer than `maxLength`, so you need to check for that if you allow arbitrary extensions.
|
|
65
67
|
|
|
68
|
+
The result may also exceed `maxLength` when a suffix is needed to avoid a Windows reserved name.
|
|
69
|
+
|
|
66
70
|
Truncation is grapheme-aware and will not split Unicode characters (surrogate pairs or extended grapheme clusters). If the remaining budget (after accounting for the extension) is smaller than a whole grapheme, the base filename may be truncated to an empty string to avoid splitting.
|
|
67
71
|
|
|
68
72
|
Systems generally allow up to 255 characters, but we default to 100 for usability reasons.
|