@chaoswise/intl 3.0.0 → 3.1.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/bin/chaoswise-intl.js +20 -12
- package/bin/scripts/conf/default.js +49 -1
- package/bin/scripts/nozhcn.js +440 -0
- package/bin/scripts/util/babelOptions.js +49 -0
- package/bin/scripts/util/findZhCnInFile.js +377 -0
- package/bin/scripts/util/findZhCnInSvgFile.js +139 -0
- package/bin/scripts/util/fixI18nDefaultInFile.js +179 -0
- package/bin/scripts/util/fixZhCnInFile.js +217 -0
- package/bin/scripts/util/fixZhCnInSvgFile.js +206 -0
- package/package.json +3 -2
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fixZhCnInFile.js
|
|
3
|
+
*
|
|
4
|
+
* Fixes Chinese characters found in COMMENT nodes by patching the raw source
|
|
5
|
+
* using byte-offset positions from AST tokens.
|
|
6
|
+
*
|
|
7
|
+
* Strategy options:
|
|
8
|
+
* 'clean' - Remove only Chinese characters from comment value.
|
|
9
|
+
* If the cleaned value is whitespace-only, remove the entire
|
|
10
|
+
* comment token (including delimiters // or /* ... *\/).
|
|
11
|
+
* 'remove' - Remove the entire comment token unconditionally.
|
|
12
|
+
*
|
|
13
|
+
* Important: This function operates on raw source bytes:
|
|
14
|
+
* - It sorts findings by `start` in descending order so that later
|
|
15
|
+
* replacements do not invalidate earlier offsets.
|
|
16
|
+
* - Only `comment` type findings (which carry `start` and `end` byte
|
|
17
|
+
* offsets) are accepted.
|
|
18
|
+
*
|
|
19
|
+
* Returns the number of comment tokens patched (0 if nothing was modified).
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const fs = require('fs');
|
|
23
|
+
const log = require('./log');
|
|
24
|
+
|
|
25
|
+
// Chinese character range, same as primaryRegx default
|
|
26
|
+
const ZH_REGEX = /[\u4e00-\u9fa5]/g;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Compute the replacement string for a single comment token.
|
|
30
|
+
*
|
|
31
|
+
* In Babel AST (with tokens:true):
|
|
32
|
+
* CommentLine : value = text after '//' (no newline)
|
|
33
|
+
* CommentBlock : value = text between '/*' and '*\/'
|
|
34
|
+
*
|
|
35
|
+
* source.slice(start, end) gives the full token including delimiters.
|
|
36
|
+
*
|
|
37
|
+
* @param {{ type: string, value: string, start: number, end: number }} comment
|
|
38
|
+
* @param {'clean'|'remove'} strategy
|
|
39
|
+
* @returns {string|null} null means "delete the token entirely"
|
|
40
|
+
*/
|
|
41
|
+
function computeReplacement(comment, strategy) {
|
|
42
|
+
const { type, value } = comment;
|
|
43
|
+
|
|
44
|
+
if (strategy === 'remove') {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// strategy === 'clean': strip Chinese characters from value
|
|
49
|
+
const cleanedValue = value.replace(ZH_REGEX, '');
|
|
50
|
+
|
|
51
|
+
if (!cleanedValue.trim()) {
|
|
52
|
+
// Comment content is now empty → remove the token
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (type === 'CommentLine') {
|
|
57
|
+
return `//${cleanedValue}`;
|
|
58
|
+
}
|
|
59
|
+
// CommentBlock
|
|
60
|
+
return `/*${cleanedValue}*/`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* After removing a comment token, if the rest of the line is blank
|
|
65
|
+
* (only whitespace between the last newline and the token start),
|
|
66
|
+
* also remove the trailing newline so we don't leave empty lines.
|
|
67
|
+
*
|
|
68
|
+
* Special case – JSX expression-container comments { /* ... */ } (where
|
|
69
|
+
* the comment is the sole content of a JSX expression): the surrounding
|
|
70
|
+
* { } braces must also be removed, otherwise an orphan {} expression is left.
|
|
71
|
+
* We detect this by checking whether the characters directly flanking the
|
|
72
|
+
* comment token (past optional spaces) are { and }, and if so extend the
|
|
73
|
+
* deletion range to cover them as well.
|
|
74
|
+
*
|
|
75
|
+
* @param {string} source
|
|
76
|
+
* @param {number} tokenStart - char index of the comment token start
|
|
77
|
+
* @param {number} tokenEnd - char index just past the comment token end
|
|
78
|
+
* @returns {{ patchStart: number, patchEnd: number }}
|
|
79
|
+
*/
|
|
80
|
+
function calcDeletionRange(source, tokenStart, tokenEnd) {
|
|
81
|
+
// ── Determine the effective deletion boundaries ───────────────────────────
|
|
82
|
+
// For JSX expression-container comments `{ /* ... */ }`, we want to delete
|
|
83
|
+
// the wrapping braces as well, otherwise we leave an orphan `{}` expression.
|
|
84
|
+
let effectiveStart = tokenStart;
|
|
85
|
+
let effectiveEnd = tokenEnd;
|
|
86
|
+
|
|
87
|
+
// Walk backwards past optional spaces to find a potential `{`
|
|
88
|
+
let braceOpen = tokenStart - 1;
|
|
89
|
+
while (braceOpen >= 0 && source[braceOpen] === ' ') braceOpen--;
|
|
90
|
+
|
|
91
|
+
// Walk forwards past optional spaces to find a potential `}`
|
|
92
|
+
let braceClose = tokenEnd;
|
|
93
|
+
while (braceClose < source.length && source[braceClose] === ' ') braceClose++;
|
|
94
|
+
|
|
95
|
+
if (
|
|
96
|
+
braceOpen >= 0 && source[braceOpen] === '{' &&
|
|
97
|
+
braceClose < source.length && source[braceClose] === '}'
|
|
98
|
+
) {
|
|
99
|
+
// The comment is the sole content of a JSX `{...}` expression — include the braces.
|
|
100
|
+
effectiveStart = braceOpen;
|
|
101
|
+
effectiveEnd = braceClose + 1;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ── Guard: if tokenEnd somehow overshot a newline, clamp back ────────────
|
|
105
|
+
// Babel's comment.end should never include the trailing \n of a CommentLine,
|
|
106
|
+
// but be defensive: if the character just before effectiveEnd is a newline,
|
|
107
|
+
// we are already at the start of the next line — step back to the \n itself
|
|
108
|
+
// so the lineEnd walk below stays on the correct line.
|
|
109
|
+
let adjustedEnd = effectiveEnd;
|
|
110
|
+
if (adjustedEnd > effectiveStart && adjustedEnd > 0 &&
|
|
111
|
+
source[adjustedEnd - 1] === '\n' &&
|
|
112
|
+
(adjustedEnd >= source.length || source[adjustedEnd] !== '\n')) {
|
|
113
|
+
adjustedEnd--;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ── Decide whether to remove the whole line or just the token ────────────
|
|
117
|
+
let lineStart = effectiveStart;
|
|
118
|
+
while (lineStart > 0 && source[lineStart - 1] !== '\n') {
|
|
119
|
+
lineStart--;
|
|
120
|
+
}
|
|
121
|
+
const before = source.slice(lineStart, effectiveStart);
|
|
122
|
+
const onlyWhitespaceBefore = /^\s*$/.test(before);
|
|
123
|
+
|
|
124
|
+
let lineEnd = adjustedEnd;
|
|
125
|
+
while (lineEnd < source.length && source[lineEnd] !== '\n') {
|
|
126
|
+
lineEnd++;
|
|
127
|
+
}
|
|
128
|
+
const after = source.slice(adjustedEnd, lineEnd);
|
|
129
|
+
const onlyWhitespaceAfter = /^\s*$/.test(after);
|
|
130
|
+
|
|
131
|
+
if (onlyWhitespaceBefore && onlyWhitespaceAfter) {
|
|
132
|
+
// Remove the entire line including the trailing newline (if present)
|
|
133
|
+
const end = lineEnd < source.length ? lineEnd + 1 : lineEnd;
|
|
134
|
+
return { patchStart: lineStart, patchEnd: end };
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Comment is inline on a code line — just remove the effective range
|
|
138
|
+
return { patchStart: effectiveStart, patchEnd: effectiveEnd };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Fix all comment findings in a single file.
|
|
143
|
+
*
|
|
144
|
+
* @param {string} filePath - absolute file path
|
|
145
|
+
* @param {Finding[]} findings - array of { type:'comment', start, end, commentType, ... }
|
|
146
|
+
* @param {'clean'|'remove'} strategy
|
|
147
|
+
* @returns {number} count of comment findings actually patched (0 if none)
|
|
148
|
+
*/
|
|
149
|
+
module.exports = function fixZhCnInFile(filePath, findings, strategy = 'clean') {
|
|
150
|
+
// Filter to comment-type findings that have byte offsets
|
|
151
|
+
const commentFindings = findings.filter(
|
|
152
|
+
(f) => f.type === 'comment' && typeof f.start === 'number' && typeof f.end === 'number'
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
if (!commentFindings.length) return 0;
|
|
156
|
+
|
|
157
|
+
let source;
|
|
158
|
+
try {
|
|
159
|
+
source = fs.readFileSync(filePath, 'utf8');
|
|
160
|
+
} catch (err) {
|
|
161
|
+
log.error(`[nozhcn] Cannot read file for fixing: ${filePath} — ${err.message}`);
|
|
162
|
+
return 0;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Sort descending by start so we patch from the end backwards,
|
|
166
|
+
// preserving the validity of earlier byte offsets.
|
|
167
|
+
const sorted = [...commentFindings].sort((a, b) => b.start - a.start);
|
|
168
|
+
|
|
169
|
+
let patchedCount = 0;
|
|
170
|
+
|
|
171
|
+
for (const finding of sorted) {
|
|
172
|
+
const { start, end, commentType } = finding;
|
|
173
|
+
|
|
174
|
+
// Reconstruct the original comment object expected by computeReplacement
|
|
175
|
+
const originalSource = source.slice(start, end);
|
|
176
|
+
|
|
177
|
+
// Extract the raw value (without delimiters) from the source token
|
|
178
|
+
// so that computeReplacement works even if the finding.content is
|
|
179
|
+
// already formatted with delimiters.
|
|
180
|
+
let rawValue;
|
|
181
|
+
if (commentType === 'CommentLine') {
|
|
182
|
+
// source token = '//' + value
|
|
183
|
+
rawValue = originalSource.startsWith('//') ? originalSource.slice(2) : originalSource;
|
|
184
|
+
} else {
|
|
185
|
+
// source token = '/*' + value + '*/'
|
|
186
|
+
if (originalSource.startsWith('/*') && originalSource.endsWith('*/')) {
|
|
187
|
+
rawValue = originalSource.slice(2, -2);
|
|
188
|
+
} else {
|
|
189
|
+
rawValue = originalSource;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const pseudoComment = { type: commentType, value: rawValue, start, end };
|
|
194
|
+
const replacement = computeReplacement(pseudoComment, strategy);
|
|
195
|
+
|
|
196
|
+
if (replacement === null) {
|
|
197
|
+
// Delete the token (and possibly the whole line if it becomes empty)
|
|
198
|
+
const { patchStart, patchEnd } = calcDeletionRange(source, start, end);
|
|
199
|
+
source = source.slice(0, patchStart) + source.slice(patchEnd);
|
|
200
|
+
} else {
|
|
201
|
+
source = source.slice(0, start) + replacement + source.slice(end);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
patchedCount++;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (patchedCount > 0) {
|
|
208
|
+
try {
|
|
209
|
+
fs.writeFileSync(filePath, source, { encoding: 'utf-8' });
|
|
210
|
+
} catch (err) {
|
|
211
|
+
log.error(`[nozhcn] Cannot write file: ${filePath} — ${err.message}`);
|
|
212
|
+
return 0;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return patchedCount;
|
|
217
|
+
};
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fixZhCnInSvgFile.js
|
|
3
|
+
*
|
|
4
|
+
* Auto-fixes Chinese characters in standalone SVG files.
|
|
5
|
+
*
|
|
6
|
+
* Fixable types (from findZhCnInSvgFile.js):
|
|
7
|
+
* svg-comment ─ auto-fixable via comment clean/remove strategy
|
|
8
|
+
* svg-metadata ─ auto-fixable: remove element or empty its content
|
|
9
|
+
*
|
|
10
|
+
* Conditionally fixable types:
|
|
11
|
+
* svg-attr ─ attribute values (e.g. id="矩形", id="编组-11")
|
|
12
|
+
* typically design-tool layer-name artifacts; fixable
|
|
13
|
+
* when attrStrategy is set to 'clean' or 'remove'
|
|
14
|
+
*
|
|
15
|
+
* Non-fixable types (reported but not touched):
|
|
16
|
+
* svg-text ─ visible rendered text, requires design/i18n decision
|
|
17
|
+
*
|
|
18
|
+
* Strategies
|
|
19
|
+
* ──────────────────────────────────────────────────────────────────────
|
|
20
|
+
* commentStrategy ('clean' | 'remove')
|
|
21
|
+
* 'clean' : Remove only Chinese characters from the comment value.
|
|
22
|
+
* If the cleaned value is whitespace-only, delete the comment.
|
|
23
|
+
* 'remove' : Delete the entire comment unconditionally.
|
|
24
|
+
*
|
|
25
|
+
* metadataStrategy ('remove' | 'empty')
|
|
26
|
+
* 'remove' : Delete the entire <title>/<desc>/<metadata> element.
|
|
27
|
+
* 'empty' : Keep the element but empty its contents:
|
|
28
|
+
* <title>搜索</title> → <title></title>
|
|
29
|
+
*
|
|
30
|
+
* attrStrategy ('clean' | 'remove' | false)
|
|
31
|
+
* 'clean' : Strip Chinese (and CJK punctuation) from attribute values.
|
|
32
|
+
* If the cleaned value is empty, remove the attribute entirely.
|
|
33
|
+
* Renamed id attributes update internal url(#…) / href refs.
|
|
34
|
+
* 'remove' : Remove the entire attribute.
|
|
35
|
+
* false : Do not auto-fix attributes (default, backward-compat).
|
|
36
|
+
*
|
|
37
|
+
* Returns { fixed: number } (count of SVG elements/comments fixed)
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
const fs = require('fs');
|
|
41
|
+
const log = require('./log');
|
|
42
|
+
|
|
43
|
+
const ZH_REGEX = /[\u4e00-\u9fa5]/g;
|
|
44
|
+
const ZH_TEST = /[\u4e00-\u9fa5]/;
|
|
45
|
+
// Chinese chars + CJK symbols/punctuation + fullwidth forms (design-tool junk)
|
|
46
|
+
const ZH_CLEAN_REGEX = /[\u4e00-\u9fa5\u3000-\u303f\uff00-\uffef]/g;
|
|
47
|
+
|
|
48
|
+
function escapeRegExp(str) {
|
|
49
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Apply attribute fix strategy to the full source string.
|
|
54
|
+
* Handles id renames with internal SVG reference updates.
|
|
55
|
+
*/
|
|
56
|
+
function fixAttrs(source, strategy) {
|
|
57
|
+
const idRenames = new Map(); // oldValue → newValue | null
|
|
58
|
+
|
|
59
|
+
// Pass 1: Process ONLY id attributes with Chinese values
|
|
60
|
+
source = source.replace(
|
|
61
|
+
/(\s)(id)=(["'])([^"']*[\u4e00-\u9fa5][^"']*)\3/g,
|
|
62
|
+
(match, ws, attr, quote, value) => {
|
|
63
|
+
if (strategy === 'remove') {
|
|
64
|
+
idRenames.set(value, null);
|
|
65
|
+
return '';
|
|
66
|
+
}
|
|
67
|
+
// 'clean': strip Chinese + CJK punctuation
|
|
68
|
+
const cleaned = value.replace(ZH_CLEAN_REGEX, '').trim();
|
|
69
|
+
if (!cleaned) {
|
|
70
|
+
idRenames.set(value, null);
|
|
71
|
+
return '';
|
|
72
|
+
}
|
|
73
|
+
idRenames.set(value, cleaned);
|
|
74
|
+
return `${ws}id=${quote}${cleaned}${quote}`;
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
// Pass 2: Update internal references for renamed / removed ids
|
|
79
|
+
idRenames.forEach((newId, oldId) => {
|
|
80
|
+
const esc = escapeRegExp(oldId);
|
|
81
|
+
if (newId) {
|
|
82
|
+
source = source.replace(new RegExp(`url\\(#${esc}\\)`, 'g'), `url(#${newId})`);
|
|
83
|
+
source = source.replace(
|
|
84
|
+
new RegExp(`(xlink:href|href)=(["'])#${esc}\\2`, 'g'),
|
|
85
|
+
`$1=$2#${newId}$2`
|
|
86
|
+
);
|
|
87
|
+
} else {
|
|
88
|
+
// id was removed — neutralise dangling url() references
|
|
89
|
+
source = source.replace(new RegExp(`url\\(#${esc}\\)`, 'g'), 'none');
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Pass 3: Process remaining (non-id) attributes with Chinese values
|
|
94
|
+
source = source.replace(
|
|
95
|
+
/(\s)([\w:_-]+)=(["'])([^"']*[\u4e00-\u9fa5][^"']*)\3/g,
|
|
96
|
+
(match, ws, attr, quote, value) => {
|
|
97
|
+
if (attr === 'id') return match; // already handled
|
|
98
|
+
if (strategy === 'remove') return '';
|
|
99
|
+
const cleaned = value.replace(ZH_CLEAN_REGEX, '').trim();
|
|
100
|
+
if (!cleaned) return '';
|
|
101
|
+
return `${ws}${attr}=${quote}${cleaned}${quote}`;
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
return source;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Apply comment fix strategy to the full source string.
|
|
110
|
+
*/
|
|
111
|
+
function fixComments(source, strategy) {
|
|
112
|
+
return source.replace(/<!--([\s\S]*?)-->/g, (match, content) => {
|
|
113
|
+
if (!ZH_TEST.test(content)) return match;
|
|
114
|
+
if (strategy === 'remove') return '';
|
|
115
|
+
const cleaned = content.replace(ZH_REGEX, '');
|
|
116
|
+
if (!cleaned.trim()) return '';
|
|
117
|
+
return `<!--${cleaned}-->`;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Apply metadata element fix strategy to the full source string.
|
|
123
|
+
* Handles <title>, <desc>, <metadata> (case-insensitive, with attributes).
|
|
124
|
+
*/
|
|
125
|
+
function fixMetadata(source, strategy) {
|
|
126
|
+
return source.replace(/<(title|desc|metadata)(\s[^>]*)?>[\s\S]*?<\/\1>/gi, (match, tag, attrs) => {
|
|
127
|
+
if (!ZH_TEST.test(match)) return match;
|
|
128
|
+
if (strategy === 'remove') return '';
|
|
129
|
+
// 'empty': keep the opening/closing tags, remove content
|
|
130
|
+
const openTag = attrs ? `<${tag}${attrs}>` : `<${tag}>`;
|
|
131
|
+
return `${openTag}</${tag}>`;
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Fix Chinese in SVG comments and metadata elements.
|
|
137
|
+
*
|
|
138
|
+
* @param {string} filePath - absolute path to the .svg file
|
|
139
|
+
* @param {Finding[]} findings - from findZhCnInSvgFile (all types)
|
|
140
|
+
* @param {object} opts
|
|
141
|
+
* - commentStrategy 'clean' | 'remove' (default: 'clean')
|
|
142
|
+
* - metadataStrategy 'remove' | 'empty' (default: 'remove')
|
|
143
|
+
* - attrStrategy 'clean' | 'remove' | false (default: false)
|
|
144
|
+
* @returns {{ fixed: number }}
|
|
145
|
+
*/
|
|
146
|
+
module.exports = function fixZhCnInSvgFile(filePath, findings, opts) {
|
|
147
|
+
const {
|
|
148
|
+
commentStrategy = 'clean',
|
|
149
|
+
metadataStrategy = 'remove',
|
|
150
|
+
attrStrategy = false,
|
|
151
|
+
} = opts || {};
|
|
152
|
+
|
|
153
|
+
const hasFixableFindings = findings.some(
|
|
154
|
+
(f) => f.type === 'svg-comment' || f.type === 'svg-metadata' ||
|
|
155
|
+
(f.type === 'svg-attr' && attrStrategy)
|
|
156
|
+
);
|
|
157
|
+
if (!hasFixableFindings) return { fixed: 0 };
|
|
158
|
+
|
|
159
|
+
let source;
|
|
160
|
+
try {
|
|
161
|
+
source = fs.readFileSync(filePath, 'utf8');
|
|
162
|
+
} catch (err) {
|
|
163
|
+
log.error(`[nozhcn] Cannot read SVG file: ${filePath} — ${err.message}`);
|
|
164
|
+
return { fixed: 0 };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const before = source;
|
|
168
|
+
|
|
169
|
+
// Fix comments
|
|
170
|
+
const hasCommentFindings = findings.some((f) => f.type === 'svg-comment');
|
|
171
|
+
if (hasCommentFindings) {
|
|
172
|
+
source = fixComments(source, commentStrategy);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Fix metadata elements
|
|
176
|
+
const hasMetadataFindings = findings.some((f) => f.type === 'svg-metadata');
|
|
177
|
+
if (hasMetadataFindings) {
|
|
178
|
+
source = fixMetadata(source, metadataStrategy);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Fix attributes (opt-in)
|
|
182
|
+
const hasAttrFindings = findings.some((f) => f.type === 'svg-attr');
|
|
183
|
+
if (hasAttrFindings && attrStrategy) {
|
|
184
|
+
source = fixAttrs(source, attrStrategy);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (source === before) return { fixed: 0 };
|
|
188
|
+
|
|
189
|
+
// Clean up blank lines left behind by removed elements/comments
|
|
190
|
+
source = source.replace(/\n[ \t]*\n/g, '\n');
|
|
191
|
+
|
|
192
|
+
// Count how many fixable findings were addressed
|
|
193
|
+
const fixed = findings.filter(
|
|
194
|
+
(f) => f.type === 'svg-comment' || f.type === 'svg-metadata' ||
|
|
195
|
+
(f.type === 'svg-attr' && attrStrategy)
|
|
196
|
+
).length;
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
fs.writeFileSync(filePath, source, { encoding: 'utf-8' });
|
|
200
|
+
} catch (err) {
|
|
201
|
+
log.error(`[nozhcn] Cannot write SVG file: ${filePath} — ${err.message}`);
|
|
202
|
+
return { fixed: 0 };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return { fixed };
|
|
206
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chaoswise/intl",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"author": "cloudwiser",
|
|
5
5
|
"description": "intl",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"init": "npm i",
|
|
27
|
-
"build": "gulp build --gulpfile ./scripts/gulpfile.js"
|
|
27
|
+
"build": "gulp build --gulpfile ./scripts/gulpfile.js",
|
|
28
|
+
"test:nozhcn": "node --test --test-reporter=spec '__tests__/nozhcn/*.test.js'"
|
|
28
29
|
},
|
|
29
30
|
"keywords": [
|
|
30
31
|
"intl"
|