Skip to content

Commit e83d459

Browse files
HotellCopilot
andauthored
chore: mitigate some high sev dependency issues, onboard svg-* project to build-verify task to provide guarantees of dep changes and refactoring (#1022)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d9d770d commit e83d459

15 files changed

Lines changed: 3208 additions & 3810 deletions

File tree

.github/workflows/pr.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,12 @@ jobs:
181181
if echo "$AFFECTED" | grep -q 'svg-icons'; then
182182
echo "Building svg-icons..."
183183
npx nx run svg-icons:build
184+
npx nx run svg-icons:build-verify
184185
fi
185186
if echo "$AFFECTED" | grep -q 'svg-sprites'; then
186187
echo "Building svg-sprites..."
187188
npx nx run svg-sprites:build
189+
npx nx run svg-sprites:build-verify
188190
fi
189191
190192
build-react:

importer/package.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"generate:react": "node generate.js --source=../assets --dest=./dist --extension=svg --target=react",
1313
"generate:pdf": "node generate.js --source=../assets --dest=./dist --extension=pdf",
1414
"build:android": "npm run create:android && npm run optimize:android && npm run finalize:android",
15-
"finalize:android": "replace '#212121' '@color/fluent_default_icon_tint' ./dist --exclude=\"*.selector\" --recursive --quiet && replace '\"http://schemas.android.com/apk/res/android\"' '\"http://schemas.android.com/apk/res/android\" android:autoMirrored=\"true\"' $(awk '$0=\"./dist/\"$0\".xml\"' rtl.txt)",
15+
"finalize:android": "node replace-in-files.js '#212121' '@color/fluent_default_icon_tint' ./dist --exclude=\"*.selector\" --recursive && node replace-in-files.js '\"http://schemas.android.com/apk/res/android\"' '\"http://schemas.android.com/apk/res/android\" android:autoMirrored=\"true\"' $(awk '$0=\"./dist/\"$0\".xml\"' rtl.txt)",
1616
"create:android": "npm run generate:svg-android && find ./dist/ -type d -exec sh -c 'tools/vd-tool/bin/vd-tool -c -in {} -out {}' \\;",
1717
"optimize:android": "find ./dist/ -type f -name '*.svg' -delete && find ./dist/ -type d ! -name '*_selector.xml' -exec sh -c 'avocado -q {}' \\;",
1818
"build:react": "npm run generate:react",
@@ -23,7 +23,7 @@
2323
"generate:font-filled": "node generateFont.js --source=dist --dest=dist/fonts --iconType=Filled --codepoints=../fonts/FluentSystemIcons-Filled.json",
2424
"generate:font-light": "node generateFont.js --source=dist --dest=dist/fonts --iconType=Light",
2525
"generate:font-resizable": "node generateFont.js --source=dist --dest=dist/fonts --iconType=Resizable",
26-
"build:fonts": "npm run generate:svg && find ./dist -type f -name '*.svg' -exec svgo --config svgo.config.mjs {} + && mkdir dist/fonts && npm run generate:font-regular && npm run generate:font-filled && npm run generate:font-light && npm run generate:font-resizable && replace '\\\\\\\\' '0x' dist/fonts/*.json",
26+
"build:fonts": "npm run generate:svg && find ./dist -type f -name '*.svg' -exec svgo --config svgo.config.mjs {} + && mkdir dist/fonts && npm run generate:font-regular && npm run generate:font-filled && npm run generate:font-light && npm run generate:font-resizable && node replace-in-files.js '\\\\' '0x' dist/fonts/*.json",
2727
"deploy:fonts": "npm run build:fonts && cp -a dist/fonts/* ../fonts && npm run clean",
2828
"generate:flutter-icon-lib-class": "node generate_flutter_lib_class.js --source=../fonts/FluentSystemIcons-Regular.json ../fonts/FluentSystemIcons-Filled.json ../fonts/FluentSystemIcons-Light.json --dest=dist/flutter",
2929
"generate:flutter-icon-demo-class": "node generate_flutter_demo_class.js --source=../fonts/FluentSystemIcons-Regular.json ../fonts/FluentSystemIcons-Filled.json ../fonts/FluentSystemIcons-Light.json --dest=dist/flutter",
@@ -34,12 +34,11 @@
3434
"license": "Microsoft",
3535
"devDependencies": {
3636
"avocado": "1.0.0",
37-
"fantasticon": "^1.2.3",
37+
"fantasticon": "^4.1.0",
3838
"glob": "^8.0.1",
3939
"lodash": "4.17.21",
4040
"mkdirp": "^1.0.4",
4141
"react": "~17.0.1",
42-
"replace": "^1.2.0",
4342
"shx": "^0.3.2",
4443
"svgo": "2.8.2",
4544
"yargs": "^14.2.3"

importer/replace-in-files.js

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
#!/usr/bin/env node
2+
// Copyright (c) Microsoft Corporation.
3+
// Licensed under the MIT license.
4+
5+
// @ts-check
6+
7+
// Simple file content replacer (replaces the `replace` npm package).
8+
// Provides both a CLI and a programmatic API.
9+
//
10+
// CLI usage: node replace-in-files.js <search> <replacement> <files...>
11+
// --recursive Search directories recursively
12+
// --exclude=<pattern> Exclude files matching pattern
13+
// --quiet Suppress output
14+
// --help Show this help message
15+
16+
const fs = require('node:fs');
17+
const path = require('node:path');
18+
const { parseArgs } = require('node:util');
19+
const glob = require('glob');
20+
21+
/**
22+
* @param {string} target
23+
* @param {{recursive?: boolean}} options
24+
* @returns {string[]}
25+
*/
26+
function getFiles(target, options) {
27+
const stat = fs.existsSync(target) && fs.statSync(target);
28+
if (!stat) {
29+
return glob.sync(target);
30+
}
31+
if (stat.isFile()) return [target];
32+
if (stat.isDirectory()) {
33+
const pattern = options.recursive ? '**/*' : '*';
34+
return glob.sync(pattern, { cwd: target, absolute: true, nodir: true });
35+
}
36+
return [];
37+
}
38+
39+
/**
40+
* Replace content in files matching the given paths.
41+
*
42+
* @param {{
43+
* search: string | RegExp,
44+
* replacement: string,
45+
* paths: string[],
46+
* recursive?: boolean,
47+
* include?: string,
48+
* exclude?: string,
49+
* quiet?: boolean,
50+
* }} options
51+
*/
52+
function replaceInFiles(options) {
53+
const { search, replacement, paths, recursive = false, include, exclude, quiet = false } = options;
54+
55+
let files = paths.flatMap(t => getFiles(t, { recursive }));
56+
57+
if (!quiet) {
58+
console.log(`[replace-in-files] Search: ${search instanceof RegExp ? search : JSON.stringify(search)}`);
59+
console.log(`[replace-in-files] Replacement: ${JSON.stringify(replacement)}`);
60+
console.log(`[replace-in-files] Paths: ${paths.join(', ')}`);
61+
}
62+
63+
console.log(`[replace-in-files] Files found: ${files.length}`);
64+
65+
if (include) {
66+
files = files.filter(f => path.matchesGlob(path.basename(f), include));
67+
}
68+
69+
if (exclude) {
70+
files = files.filter(f => !path.matchesGlob(path.basename(f), exclude));
71+
}
72+
73+
if (include || exclude) {
74+
console.log(`[replace-in-files] Files after filtering: ${files.length}`);
75+
}
76+
77+
let updatedCount = 0;
78+
79+
for (const file of files) {
80+
try {
81+
const content = fs.readFileSync(file, 'utf8');
82+
const updated = search instanceof RegExp
83+
? content.replace(search, replacement)
84+
: content.split(search).join(replacement);
85+
86+
if (content !== updated) {
87+
fs.writeFileSync(file, updated, 'utf8');
88+
updatedCount++;
89+
if (!quiet) {
90+
console.log(`[replace-in-files] Updated: ${file}`);
91+
}
92+
}
93+
} catch {
94+
if (!quiet) {
95+
console.warn(`[replace-in-files] Skipped (unreadable): ${file}`);
96+
}
97+
// Skip binary or unreadable files
98+
}
99+
}
100+
101+
console.log(`[replace-in-files] Done. ${updatedCount}/${files.length} file(s) updated.`);
102+
103+
}
104+
105+
module.exports = { replaceInFiles };
106+
107+
108+
function main(){
109+
110+
const { flags, search, replacement, paths } = getArgs();
111+
112+
replaceInFiles({
113+
search,
114+
replacement,
115+
paths,
116+
recursive: flags.recursive,
117+
exclude: flags.exclude,
118+
quiet: flags.quiet,
119+
});
120+
}
121+
122+
function getArgs(){
123+
124+
const HELP = `Usage: node replace-in-files.js [options] <search> <replacement> <files/dirs...>
125+
126+
Arguments:
127+
search String to search for
128+
replacement String to replace with
129+
files/dirs One or more files, directories, or glob patterns
130+
131+
Options:
132+
--recursive Search directories recursively
133+
--exclude=<pat> Exclude files whose basename matches <pat>
134+
--quiet Suppress output
135+
--help Show this help message
136+
`;
137+
138+
const { values: flags, positionals: positional } = parseArgs({
139+
args: process.argv.slice(2),
140+
options: {
141+
recursive: { type: 'boolean', default: false },
142+
quiet: { type: 'boolean', default: false },
143+
exclude: { type: 'string' },
144+
help: { type: 'boolean', default: false },
145+
},
146+
allowPositionals: true,
147+
});
148+
149+
if (flags.help) {
150+
process.stdout.write(HELP);
151+
process.exit(0);
152+
}
153+
154+
if (positional.length < 3) {
155+
console.error('Usage: node replace-in-files.js <search> <replacement> <files/dirs...>');
156+
console.error('Run with --help for more information.');
157+
process.exit(1);
158+
}
159+
160+
const [search, replacement, ...paths] = positional;
161+
162+
return { flags, search, replacement, paths };
163+
}
164+
165+
// --- CLI entry point ---
166+
if (require.main === module) {
167+
main();
168+
}

0 commit comments

Comments
 (0)