@fluentui/react-icons-font-subsetting-webpack-plugin 2.0.317 → 2.0.319
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 +73 -24
- package/lib/index.d.ts +14 -1
- package/lib/index.js +61 -27
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,33 +1,82 @@
|
|
|
1
|
-
@fluentui/react-icons-font-subsetting-webpack-plugin
|
|
2
|
-
===
|
|
1
|
+
# @fluentui/react-icons-font-subsetting-webpack-plugin
|
|
3
2
|
|
|
4
|
-
This package includes a plugin for `webpack@>=5.0.0` to subset the icon font files used by `@fluentui/react-icons` when using
|
|
3
|
+
This package includes a plugin for `webpack@>=5.0.0` to subset the icon font files used by `@fluentui/react-icons` when using font-based icon implementations.
|
|
5
4
|
|
|
6
|
-
If `optimization.usedExports` is enabled (as it is by default), this plugin will subset the font files to only include the glyphs actually used by your build.
|
|
5
|
+
If `optimization.usedExports` is enabled (as it is by default in webpack `production` mode), this plugin will subset the font files to only include the glyphs actually used by your build.
|
|
6
|
+
|
|
7
|
+
## Supported Import Patterns
|
|
8
|
+
|
|
9
|
+
The plugin supports the following import patterns from `@fluentui/react-icons`:
|
|
10
|
+
|
|
11
|
+
### 1. Using the `fluentIconFont` condition
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
// Uses font implementation via resolve.conditionNames
|
|
15
|
+
import { AddRegular, DeleteFilled } from '@fluentui/react-icons';
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### 2. Using atomic imports from `@fluentui/react-icons/fonts/*`
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
// Direct atomic imports - no conditionNames required
|
|
22
|
+
import { AddRegular, AddFilled } from '@fluentui/react-icons/fonts/add';
|
|
23
|
+
import { DeleteRegular } from '@fluentui/react-icons/fonts/delete';
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Atomic imports provide better tree-shaking and faster build times for applications using a small number of icons.
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
### With `fluentIconFont` condition
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
// webpack.config.js
|
|
34
|
+
const {
|
|
35
|
+
default: FluentUIReactIconsFontSubsettingPlugin,
|
|
36
|
+
} = require('@fluentui/react-icons-font-subsetting-webpack-plugin');
|
|
37
|
+
|
|
38
|
+
module.exports = {
|
|
39
|
+
module: {
|
|
40
|
+
rules: [
|
|
41
|
+
// Treat the font files as webpack assets
|
|
42
|
+
{
|
|
43
|
+
test: /\.(ttf|woff2?)$/,
|
|
44
|
+
type: 'asset',
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
},
|
|
48
|
+
resolve: {
|
|
49
|
+
// Include 'fluentIconFont' to use the font implementation of the Fluent icons
|
|
50
|
+
conditionNames: ['fluentIconFont', 'import'],
|
|
51
|
+
},
|
|
52
|
+
plugins: [
|
|
53
|
+
// Include this plugin
|
|
54
|
+
new FluentUIReactIconsFontSubsettingPlugin(),
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### With atomic imports (no conditionNames required)
|
|
7
60
|
|
|
8
|
-
Usage
|
|
9
|
-
---
|
|
10
61
|
```js
|
|
11
62
|
// webpack.config.js
|
|
12
|
-
const {
|
|
63
|
+
const {
|
|
64
|
+
default: FluentUIReactIconsFontSubsettingPlugin,
|
|
65
|
+
} = require('@fluentui/react-icons-font-subsetting-webpack-plugin');
|
|
13
66
|
|
|
14
67
|
module.exports = {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
]
|
|
23
|
-
},
|
|
24
|
-
resolve: {
|
|
25
|
-
// Include 'fluentIconFont' to use the font implementation of the Fluent icons
|
|
26
|
-
conditionNames: ['fluentIconFont', 'import']
|
|
27
|
-
},
|
|
28
|
-
plugins: [
|
|
29
|
-
// Include this plugin
|
|
30
|
-
new FluentUIReactIconsFontSubsettingPlugin(),
|
|
68
|
+
module: {
|
|
69
|
+
rules: [
|
|
70
|
+
// Treat the font files as webpack assets
|
|
71
|
+
{
|
|
72
|
+
test: /\.(ttf|woff2?)$/,
|
|
73
|
+
type: 'asset',
|
|
74
|
+
},
|
|
31
75
|
],
|
|
76
|
+
},
|
|
77
|
+
plugins: [
|
|
78
|
+
// Include this plugin
|
|
79
|
+
new FluentUIReactIconsFontSubsettingPlugin(),
|
|
80
|
+
],
|
|
32
81
|
};
|
|
33
|
-
```
|
|
82
|
+
```
|
package/lib/index.d.ts
CHANGED
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
import * as webpack from
|
|
1
|
+
import * as webpack from 'webpack';
|
|
2
2
|
export default class FluentUIReactIconsFontSubsettingPlugin implements webpack.WebpackPluginInstance {
|
|
3
|
+
/**
|
|
4
|
+
* Entry point for the Webpack plugin that registers hooks to perform font subsetting for `@fluentui/react-icons`.
|
|
5
|
+
*
|
|
6
|
+
* This method is executed **once** by Webpack when the plugin is initialized during the compiler's
|
|
7
|
+
* bootstrap phase. The internal logic hooked into `optimizeAssets` is executed **once per compilation**
|
|
8
|
+
* (whenever Webpack processes the module graph and prepares to output assets) during the
|
|
9
|
+
* asset optimization stage.
|
|
10
|
+
*
|
|
11
|
+
* It analyzes the module graph to determine which specific icons are used from Fluent UI icon packages
|
|
12
|
+
* and triggers font subsetting to remove unused glyphs from the final output assets.
|
|
13
|
+
*
|
|
14
|
+
* @param compiler - The Webpack compiler instance.
|
|
15
|
+
*/
|
|
3
16
|
apply(compiler: webpack.Compiler): void;
|
|
4
17
|
}
|
package/lib/index.js
CHANGED
|
@@ -30,19 +30,34 @@ const webpack = __importStar(require("webpack"));
|
|
|
30
30
|
const subset_font_1 = __importDefault(require("subset-font"));
|
|
31
31
|
const path_1 = require("path");
|
|
32
32
|
const promises_1 = require("fs/promises");
|
|
33
|
-
const PLUGIN_NAME =
|
|
33
|
+
const PLUGIN_NAME = 'FluentUIReactIconsFontSubsettingPlugin';
|
|
34
34
|
const FONT_FILES_BASE_NAMES = [
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
];
|
|
40
|
-
const FONT_EXTENSIONS = [
|
|
41
|
-
'.ttf',
|
|
42
|
-
'.woff',
|
|
43
|
-
'.woff2'
|
|
35
|
+
'FluentSystemIcons-Filled',
|
|
36
|
+
'FluentSystemIcons-Resizable',
|
|
37
|
+
'FluentSystemIcons-Regular',
|
|
38
|
+
'FluentSystemIcons-Light',
|
|
44
39
|
];
|
|
40
|
+
const FONT_EXTENSIONS = ['.ttf', '.woff', '.woff2'];
|
|
41
|
+
/**
|
|
42
|
+
* Match both chunk files and atomic font imports:
|
|
43
|
+
* - lib/fonts/sizedIcons/chunk-0.js (chunk-based)
|
|
44
|
+
* - lib/atoms/fonts/access-time.js (atomic imports)
|
|
45
|
+
*/
|
|
46
|
+
const REACT_ICONS_FONT_MODULE_IMPORT_PATTERN = /react-icons[\/\\]lib(-cjs)?[\/\\](fonts[\/\\](sizedIcons|icons)[\/\\]chunk-\d+|atoms[\/\\]fonts[\/\\][\w-]+)\.js$/;
|
|
45
47
|
class FluentUIReactIconsFontSubsettingPlugin {
|
|
48
|
+
/**
|
|
49
|
+
* Entry point for the Webpack plugin that registers hooks to perform font subsetting for `@fluentui/react-icons`.
|
|
50
|
+
*
|
|
51
|
+
* This method is executed **once** by Webpack when the plugin is initialized during the compiler's
|
|
52
|
+
* bootstrap phase. The internal logic hooked into `optimizeAssets` is executed **once per compilation**
|
|
53
|
+
* (whenever Webpack processes the module graph and prepares to output assets) during the
|
|
54
|
+
* asset optimization stage.
|
|
55
|
+
*
|
|
56
|
+
* It analyzes the module graph to determine which specific icons are used from Fluent UI icon packages
|
|
57
|
+
* and triggers font subsetting to remove unused glyphs from the final output assets.
|
|
58
|
+
*
|
|
59
|
+
* @param compiler - The Webpack compiler instance.
|
|
60
|
+
*/
|
|
46
61
|
apply(compiler) {
|
|
47
62
|
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
|
48
63
|
compilation.hooks.optimizeAssets.tapPromise(PLUGIN_NAME, async () => {
|
|
@@ -51,8 +66,11 @@ class FluentUIReactIconsFontSubsettingPlugin {
|
|
|
51
66
|
for (const m of compilation.modules) {
|
|
52
67
|
if (isFluentUIReactFontChunk(m)) {
|
|
53
68
|
const usedModuleExports = compilation.moduleGraph.getUsedExports(m, undefined);
|
|
69
|
+
// Either all exports are used or there's no info on used exports
|
|
54
70
|
if (usedModuleExports === null || typeof usedModuleExports === 'boolean') {
|
|
55
|
-
//
|
|
71
|
+
// In development mode (or when optimization.usedExports is disabled),
|
|
72
|
+
// getUsedExports() returns `true` (all exports used) or `null` (no info).
|
|
73
|
+
// Subsetting requires knowing exactly which exports are used.
|
|
56
74
|
continue;
|
|
57
75
|
}
|
|
58
76
|
const pkgLibPath = (0, path_1.resolve)((0, path_1.dirname)(m.resource), '../..');
|
|
@@ -69,19 +87,24 @@ class FluentUIReactIconsFontSubsettingPlugin {
|
|
|
69
87
|
optimizationPromises.push(optimizeFontAsset(codepointMap, usedExports, compilation, assetName));
|
|
70
88
|
}
|
|
71
89
|
}
|
|
72
|
-
await
|
|
90
|
+
// IMPORTANT: actually await all subsetting work
|
|
91
|
+
await Promise.all(optimizationPromises);
|
|
73
92
|
});
|
|
74
93
|
});
|
|
75
94
|
}
|
|
76
95
|
}
|
|
77
96
|
exports.default = FluentUIReactIconsFontSubsettingPlugin;
|
|
78
97
|
async function optimizeFontAsset(codepointMap, usedExports, compilation, assetName) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
98
|
+
// Build subset text from the used exports set (usually small) instead of scanning all glyphs
|
|
99
|
+
let subsetText = '';
|
|
100
|
+
for (const glyphName of usedExports) {
|
|
101
|
+
const codepoint = codepointMap[glyphName];
|
|
102
|
+
if (codepoint !== undefined) {
|
|
103
|
+
subsetText += String.fromCodePoint(codepoint);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
83
106
|
let source = compilation.assets[assetName].source();
|
|
84
|
-
if (typeof source ===
|
|
107
|
+
if (typeof source === 'string') {
|
|
85
108
|
source = Buffer.from(source);
|
|
86
109
|
}
|
|
87
110
|
compilation.assets[assetName] = new webpack.sources.RawSource(await (0, subset_font_1.default)(source, subsetText, {
|
|
@@ -90,27 +113,38 @@ async function optimizeFontAsset(codepointMap, usedExports, compilation, assetNa
|
|
|
90
113
|
}
|
|
91
114
|
function getTargetFormat(assetName) {
|
|
92
115
|
switch ((0, path_1.extname)(assetName)) {
|
|
93
|
-
case
|
|
94
|
-
return
|
|
95
|
-
case
|
|
96
|
-
return
|
|
116
|
+
case '.woff':
|
|
117
|
+
return 'woff';
|
|
118
|
+
case '.woff2':
|
|
119
|
+
return 'woff2';
|
|
97
120
|
default:
|
|
98
|
-
return
|
|
121
|
+
return 'sfnt';
|
|
99
122
|
}
|
|
100
123
|
}
|
|
101
124
|
function isNormalModule(m) {
|
|
102
125
|
return m instanceof webpack.NormalModule;
|
|
103
126
|
}
|
|
104
127
|
function isFluentUIReactFontChunk(m) {
|
|
105
|
-
if (isNormalModule(m)) {
|
|
106
|
-
return
|
|
128
|
+
if (!isNormalModule(m)) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
const resource = m.resource;
|
|
132
|
+
if (!resource) {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
// Cheap pre-filter before regex
|
|
136
|
+
if (!resource.includes('react-icons')) {
|
|
137
|
+
return false;
|
|
107
138
|
}
|
|
108
|
-
return
|
|
139
|
+
return REACT_ICONS_FONT_MODULE_IMPORT_PATTERN.test(resource);
|
|
109
140
|
}
|
|
110
141
|
async function getFontAssetsAndCodepoints(pkgLibPath, compilation) {
|
|
111
142
|
const utilsFontsFolder = (0, path_1.resolve)(pkgLibPath, 'utils/fonts');
|
|
112
|
-
const codepoints = Object.fromEntries(await Promise.all(FONT_FILES_BASE_NAMES.map(async (fontBaseName) => [
|
|
113
|
-
|
|
143
|
+
const codepoints = Object.fromEntries(await Promise.all(FONT_FILES_BASE_NAMES.map(async (fontBaseName) => [
|
|
144
|
+
fontBaseName,
|
|
145
|
+
JSON.parse(await (0, promises_1.readFile)((0, path_1.resolve)(utilsFontsFolder, `${fontBaseName}.json`), 'utf8')),
|
|
146
|
+
])));
|
|
147
|
+
const fontPaths = new Map(FONT_FILES_BASE_NAMES.flatMap((fontBaseName) => FONT_EXTENSIONS.map((ext) => [(0, path_1.resolve)(utilsFontsFolder, `${fontBaseName}${ext}`), codepoints[fontBaseName]])));
|
|
114
148
|
const result = [];
|
|
115
149
|
for (const m of compilation.modules) {
|
|
116
150
|
if (isNormalModule(m) && fontPaths.has(m.resource)) {
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluentui/react-icons-font-subsetting-webpack-plugin",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.319",
|
|
4
4
|
"description": "Webpack plugin to subset the icon fonts used by @fluentui/react-icons based on which icons are used.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "webpack -c test/webpack.config.js",
|
|
8
|
+
"dev": "webpack serve -c test/webpack.config.js --open",
|
|
8
9
|
"build": "tsc -p ."
|
|
9
10
|
},
|
|
10
11
|
"engines": {
|
|
@@ -36,4 +37,4 @@
|
|
|
36
37
|
"files": [
|
|
37
38
|
"lib/*"
|
|
38
39
|
]
|
|
39
|
-
}
|
|
40
|
+
}
|