@fluentui/react-icons-font-subsetting-webpack-plugin 2.0.170-rc.7
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 +33 -0
- package/lib/index.js +123 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
@fluentui/react-icons-font-subsetting-webpack-plugin
|
|
2
|
+
===
|
|
3
|
+
|
|
4
|
+
This package includes a plugin for `webpack@>=5.0.0` to subset the icon font files used by `@fluentui/react-icons` when using the `"fluentIcontFont"` condition in `resolve.conditionNames`.
|
|
5
|
+
|
|
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.
|
|
7
|
+
|
|
8
|
+
Usage
|
|
9
|
+
---
|
|
10
|
+
```js
|
|
11
|
+
// webpack.config.js
|
|
12
|
+
const {default: FluentUIReactIconsFontSubsettingPlugin} = require('@fluentui/react-icons-font-subsetting-webpack-plugin');
|
|
13
|
+
|
|
14
|
+
module.exports = {
|
|
15
|
+
module: {
|
|
16
|
+
rules: [
|
|
17
|
+
// Treat the font files as webpack assets
|
|
18
|
+
{
|
|
19
|
+
test: /\.(ttf|woff2?)$/,
|
|
20
|
+
type: 'asset',
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
resolve: {
|
|
25
|
+
// Include 'fluentIcontFont' to use the font implementation of the Fluent icons
|
|
26
|
+
conditionNames: ['fluentIcontFont', 'import']
|
|
27
|
+
},
|
|
28
|
+
plugins: [
|
|
29
|
+
// Include this plugin
|
|
30
|
+
new FluentUIReactIconsFontSubsettingPlugin(),
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
```
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
const webpack = __importStar(require("webpack"));
|
|
30
|
+
const subset_font_1 = __importDefault(require("subset-font"));
|
|
31
|
+
const path_1 = require("path");
|
|
32
|
+
const promises_1 = require("fs/promises");
|
|
33
|
+
const PLUGIN_NAME = "FluentUIReactIconsFontSubsettingPlugin";
|
|
34
|
+
const FONT_FILES_BASE_NAMES = [
|
|
35
|
+
"FluentSystemIcons-Filled",
|
|
36
|
+
"FluentSystemIcons-Resizable",
|
|
37
|
+
"FluentSystemIcons-Regular",
|
|
38
|
+
];
|
|
39
|
+
const FONT_EXTENSIONS = [
|
|
40
|
+
'.ttf',
|
|
41
|
+
'.woff',
|
|
42
|
+
'.woff2'
|
|
43
|
+
];
|
|
44
|
+
class FluentUIReactIconsFontSubsettingPlugin {
|
|
45
|
+
apply(compiler) {
|
|
46
|
+
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
|
47
|
+
compilation.hooks.optimizeAssets.tapPromise(PLUGIN_NAME, async () => {
|
|
48
|
+
// There could be multiple instances of `@fluentui/react-icons`, and they need to be subset separately
|
|
49
|
+
const packageToUsedFontExports = new Map();
|
|
50
|
+
for (const m of compilation.modules) {
|
|
51
|
+
if (isFluentUIReactFontChunk(m)) {
|
|
52
|
+
const usedModuleExports = compilation.moduleGraph.getUsedExports(m, undefined);
|
|
53
|
+
if (usedModuleExports === null || typeof usedModuleExports === 'boolean') {
|
|
54
|
+
// Either all exports are used or there's no info on used exports
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const pkgLibPath = (0, path_1.resolve)((0, path_1.dirname)(m.resource), '../..');
|
|
58
|
+
const usedPkgExports = packageToUsedFontExports.get(pkgLibPath) ?? new Set();
|
|
59
|
+
for (const icon of usedModuleExports) {
|
|
60
|
+
usedPkgExports.add(icon);
|
|
61
|
+
}
|
|
62
|
+
packageToUsedFontExports.set(pkgLibPath, usedPkgExports);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const optimizationPromises = [];
|
|
66
|
+
for (const [pkgLibPath, usedExports] of packageToUsedFontExports) {
|
|
67
|
+
for (const { assetName, codepoints: codepointMap } of await getFontAssetsAndCodepoints(pkgLibPath, compilation)) {
|
|
68
|
+
optimizationPromises.push(optimizeFontAsset(codepointMap, usedExports, compilation, assetName));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
await optimizationPromises;
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
exports.default = FluentUIReactIconsFontSubsettingPlugin;
|
|
77
|
+
async function optimizeFontAsset(codepointMap, usedExports, compilation, assetName) {
|
|
78
|
+
const subsetText = Object.entries(codepointMap)
|
|
79
|
+
.filter(([glyphName]) => usedExports.has(glyphName))
|
|
80
|
+
.map(([, codepoint]) => String.fromCodePoint(codepoint))
|
|
81
|
+
.join('');
|
|
82
|
+
let source = compilation.assets[assetName].source();
|
|
83
|
+
if (typeof source === "string") {
|
|
84
|
+
source = Buffer.from(source);
|
|
85
|
+
}
|
|
86
|
+
compilation.assets[assetName] = new webpack.sources.RawSource(await (0, subset_font_1.default)(source, subsetText, {
|
|
87
|
+
targetFormat: getTargetFormat(assetName),
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
function getTargetFormat(assetName) {
|
|
91
|
+
switch ((0, path_1.extname)(assetName)) {
|
|
92
|
+
case ".woff":
|
|
93
|
+
return "woff";
|
|
94
|
+
case ".woff2":
|
|
95
|
+
return "woff2";
|
|
96
|
+
default:
|
|
97
|
+
return "sfnt";
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function isNormalModule(m) {
|
|
101
|
+
return m instanceof webpack.NormalModule;
|
|
102
|
+
}
|
|
103
|
+
function isFluentUIReactFontChunk(m) {
|
|
104
|
+
if (isNormalModule(m)) {
|
|
105
|
+
return /react-icons[\/\\]lib(-cjs)?[\/\\]fonts[\/\\](sizedIcons|icons)[\/\\]chunk-\d+\.js$/.test(m.resource);
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
async function getFontAssetsAndCodepoints(pkgLibPath, compilation) {
|
|
110
|
+
const utilsFontsFolder = (0, path_1.resolve)(pkgLibPath, 'utils/fonts');
|
|
111
|
+
const codepoints = Object.fromEntries(await Promise.all(FONT_FILES_BASE_NAMES.map(async (fontBaseName) => [fontBaseName, JSON.parse(await (0, promises_1.readFile)((0, path_1.resolve)(utilsFontsFolder, `${fontBaseName}.json`), 'utf8'))])));
|
|
112
|
+
const fontPaths = new Map(FONT_FILES_BASE_NAMES.flatMap(fontBaseName => FONT_EXTENSIONS.map(ext => [(0, path_1.resolve)(utilsFontsFolder, `${fontBaseName}${ext}`), codepoints[fontBaseName]])));
|
|
113
|
+
const result = [];
|
|
114
|
+
for (const m of compilation.modules) {
|
|
115
|
+
if (isNormalModule(m) && fontPaths.has(m.resource)) {
|
|
116
|
+
const assetName = m.buildInfo?.filename;
|
|
117
|
+
if (assetName) {
|
|
118
|
+
result.push({ assetName, codepoints: fontPaths.get(m.resource) });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return result;
|
|
123
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fluentui/react-icons-font-subsetting-webpack-plugin",
|
|
3
|
+
"version": "2.0.170-rc.7",
|
|
4
|
+
"description": "Webpack plugin to subset the icon fonts used by @fluentui/react-icons based on which icons are used.",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "webpack -c test/webpack.config.js",
|
|
8
|
+
"build": "tsc"
|
|
9
|
+
},
|
|
10
|
+
"engines": {
|
|
11
|
+
"node": ">=14.5.0"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/microsoft/fluentui-system-icons.git",
|
|
16
|
+
"directory": "packages/react-icons-font-subsetting-webpack-plugin"
|
|
17
|
+
},
|
|
18
|
+
"author": "Michael Loughry <miclo@microsoft.com>",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/microsoft/fluentui-system-icons/issues"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/microsoft/fluentui-system-icons/packages/react-icons-font-subsetting-webpack-plugin#readme",
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"typescript": "^4.6.3",
|
|
26
|
+
"webpack": "^5.72.0",
|
|
27
|
+
"@fluentui/react-icons": "*"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"subset-font": "^1.4.0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"webpack": ">=5.0.0"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"lib/*"
|
|
37
|
+
]
|
|
38
|
+
}
|