@fluentui/react-icons-atomic-webpack-loader 0.0.2 → 0.0.4
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 +49 -7
- package/lib/index.d.ts +16 -1
- package/lib/index.js +22 -6
- package/lib/modules.d.ts +47 -0
- package/lib/modules.js +75 -0
- package/lib/transform.d.ts +15 -1
- package/lib/transform.js +43 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,19 +3,21 @@
|
|
|
3
3
|
> **⚠️ 0.x** — this package is in early development and follows [zero-based major semver](https://0ver.org/).
|
|
4
4
|
> Breaking changes may occur in minor releases until 1.0.
|
|
5
5
|
|
|
6
|
-
Webpack loader that transforms barrel imports and re-exports from `@fluentui/react-icons` into atomic deep paths for better tree-shaking and smaller bundles.
|
|
6
|
+
Webpack loader that transforms barrel imports and re-exports from `@fluentui/react-icons` and `@fluentui/react-brand-icons` into atomic deep paths for better tree-shaking and smaller bundles.
|
|
7
7
|
|
|
8
8
|
## Before / After
|
|
9
9
|
|
|
10
10
|
```js
|
|
11
11
|
// Before — barrel import pulls in the entire icon set
|
|
12
12
|
import { AddFilled, bundleIcon, useIconContext } from '@fluentui/react-icons';
|
|
13
|
+
import { ProjectColor } from '@fluentui/react-brand-icons';
|
|
13
14
|
export { ArrowLeftRegular } from '@fluentui/react-icons';
|
|
14
15
|
|
|
15
16
|
// After — each reference resolves to a small, isolated module
|
|
16
17
|
import { AddFilled } from '@fluentui/react-icons/svg/add';
|
|
17
18
|
import { bundleIcon } from '@fluentui/react-icons/utils';
|
|
18
19
|
import { useIconContext } from '@fluentui/react-icons/providers';
|
|
20
|
+
import { ProjectColor } from '@fluentui/react-brand-icons/svg/project';
|
|
19
21
|
export { ArrowLeftRegular } from '@fluentui/react-icons/svg/arrow-left';
|
|
20
22
|
```
|
|
21
23
|
|
|
@@ -23,7 +25,7 @@ export { ArrowLeftRegular } from '@fluentui/react-icons/svg/arrow-left';
|
|
|
23
25
|
|
|
24
26
|
Add the loader to your webpack config as an [`enforce: 'pre'`](https://webpack.js.org/configuration/module/#ruleenforce) rule so it runs on the original source before any other loaders:
|
|
25
27
|
|
|
26
|
-
NOTE: Unlike most loaders, this one should NOT exclude `node_modules`. It needs to process files inside `node_modules` as well to transform barrel imports from `@fluentui/react-icons` in your third-party dependencies. Files that don't reference
|
|
28
|
+
NOTE: Unlike most loaders, this one should NOT exclude `node_modules`. It needs to process files inside `node_modules` as well to transform barrel imports from `@fluentui/react-icons` and `@fluentui/react-brand-icons` in your third-party dependencies. Files that don't reference a supported module are skipped via a fast pre-check, so there is no meaningful overhead.
|
|
27
29
|
|
|
28
30
|
```js
|
|
29
31
|
// webpack.config.js
|
|
@@ -59,11 +61,41 @@ module.exports = {
|
|
|
59
61
|
};
|
|
60
62
|
```
|
|
61
63
|
|
|
64
|
+
## Supported modules
|
|
65
|
+
|
|
66
|
+
| Module | Variants | Notes |
|
|
67
|
+
| ----------------------------- | ---------------------------- | ----------------------------- |
|
|
68
|
+
| `@fluentui/react-icons` | `svg`, `fonts`, `svg-sprite` | Has `/providers` and `/utils` |
|
|
69
|
+
| `@fluentui/react-brand-icons` | `svg` | Has `/utils`; no `/providers` |
|
|
70
|
+
|
|
62
71
|
## Options
|
|
63
72
|
|
|
64
|
-
| Option
|
|
65
|
-
|
|
|
66
|
-
| `iconVariant`
|
|
73
|
+
| Option | Type | Default | Description |
|
|
74
|
+
| ----------------- | -------------------------------------- | ----------- | -------------------------------------------------------------------------- |
|
|
75
|
+
| `iconVariant` | `'svg'` \| `'fonts'` \| `'svg-sprite'` | `'svg'` | Variant icons resolve to. Applied to every supported module. |
|
|
76
|
+
| `fallbackVariant` | `'svg'` \| `'fonts'` \| `'svg-sprite'` | `undefined` | Variant used for a module that does not support `iconVariant` (see below). |
|
|
77
|
+
|
|
78
|
+
### Variant resolution & `fallbackVariant`
|
|
79
|
+
|
|
80
|
+
`iconVariant` is applied to every supported module referenced in a file. Because not every module ships every variant (for example `@fluentui/react-brand-icons` only ships `svg`), the loader resolves the variant per module:
|
|
81
|
+
|
|
82
|
+
1. If the module supports `iconVariant`, it is used.
|
|
83
|
+
2. Otherwise, if `fallbackVariant` is set and supported by the module, it is used.
|
|
84
|
+
3. Otherwise, if `fallbackVariant` is set but also unsupported, the loader emits a warning and falls back to `svg`.
|
|
85
|
+
4. Otherwise (no `fallbackVariant`), the loader fails with a descriptive error.
|
|
86
|
+
|
|
87
|
+
Resolution is lazy and per file: only modules actually imported in a given file are checked, so a file that imports only `@fluentui/react-icons` never errors about brand icons.
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
// Resolve system icons to fonts, but keep brand icons on svg (their only variant).
|
|
91
|
+
{
|
|
92
|
+
loader: '@fluentui/react-icons-atomic-webpack-loader',
|
|
93
|
+
options: {
|
|
94
|
+
iconVariant: 'fonts',
|
|
95
|
+
fallbackVariant: 'svg',
|
|
96
|
+
},
|
|
97
|
+
}
|
|
98
|
+
```
|
|
67
99
|
|
|
68
100
|
### Using font icons
|
|
69
101
|
|
|
@@ -105,7 +137,9 @@ This changes icon resolution from `@fluentui/react-icons/svg/*` to `@fluentui/re
|
|
|
105
137
|
|
|
106
138
|
## How it works
|
|
107
139
|
|
|
108
|
-
The loader
|
|
140
|
+
The loader parses each module and rewrites import and re-export declarations that reference a supported module. Each named specifier is routed to an atomic subpath based on its name:
|
|
141
|
+
|
|
142
|
+
### `@fluentui/react-icons`
|
|
109
143
|
|
|
110
144
|
| Export type | Example | Resolved path |
|
|
111
145
|
| -------------- | ------------------------------------------------ | -------------------------------------------------------------------- |
|
|
@@ -113,9 +147,17 @@ The loader uses a Babel transform to rewrite import and re-export declarations t
|
|
|
113
147
|
| Context / hook | `useIconContext`, `IconDirectionContextProvider` | `@fluentui/react-icons/providers` |
|
|
114
148
|
| Utility | `bundleIcon`, `createFluentIcon` | `@fluentui/react-icons/utils` |
|
|
115
149
|
|
|
116
|
-
|
|
150
|
+
### `@fluentui/react-brand-icons`
|
|
151
|
+
|
|
152
|
+
| Export type | Example | Resolved path |
|
|
153
|
+
| -------------- | ----------------------------------------- | ----------------------------------------- |
|
|
154
|
+
| Icon component | `ProjectColor`, `CalendarTaskbar20Filled` | `@fluentui/react-brand-icons/svg/project` |
|
|
155
|
+
| Utility | `bundleIcon`, `createFluentIcon` | `@fluentui/react-brand-icons/utils` |
|
|
156
|
+
|
|
157
|
+
Files that don't reference a supported module are passed through untouched (fast pre-check).
|
|
117
158
|
|
|
118
159
|
## Requirements
|
|
119
160
|
|
|
120
161
|
- `webpack` >= 5
|
|
121
162
|
- `@fluentui/react-icons` >= 2 (with atomic subpath exports)
|
|
163
|
+
- `@fluentui/react-brand-icons` (with atomic subpath exports), if used
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
|
+
import type { IconVariant } from './modules';
|
|
1
2
|
import type { LoaderContext } from 'webpack';
|
|
3
|
+
export type { IconVariant };
|
|
2
4
|
export interface FluentIconsAtomicImportLoaderOptions {
|
|
3
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The icon variant to resolve atomic imports to. Defaults to `'svg'`.
|
|
7
|
+
*
|
|
8
|
+
* Not every module supports every variant (e.g. `@fluentui/react-brand-icons`
|
|
9
|
+
* only ships `svg`). When a referenced module does not support this variant,
|
|
10
|
+
* `fallbackVariant` is used instead.
|
|
11
|
+
*/
|
|
12
|
+
iconVariant?: IconVariant;
|
|
13
|
+
/**
|
|
14
|
+
* The variant to use for a referenced module that does not support
|
|
15
|
+
* `iconVariant`. When omitted and a module cannot honor `iconVariant`, the
|
|
16
|
+
* loader fails with a descriptive error.
|
|
17
|
+
*/
|
|
18
|
+
fallbackVariant?: IconVariant;
|
|
4
19
|
}
|
|
5
20
|
export default function fluentIconsAtomicImportLoader(this: LoaderContext<FluentIconsAtomicImportLoaderOptions>, sourceCode: string): void;
|
package/lib/index.js
CHANGED
|
@@ -1,18 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const transform_1 = require("./transform");
|
|
4
|
+
const modules_1 = require("./modules");
|
|
4
5
|
function fluentIconsAtomicImportLoader(sourceCode) {
|
|
5
6
|
const { resourcePath } = this;
|
|
6
|
-
|
|
7
|
+
// Cheap pre-skip only: a false positive here just means we parse the file and
|
|
8
|
+
// let the module record decide. Diagnostics are driven by actual imports.
|
|
9
|
+
if (!modules_1.SUPPORTED_MODULE_NAMES.some((name) => sourceCode.includes(name))) {
|
|
7
10
|
return this.callback(null, sourceCode);
|
|
8
11
|
}
|
|
9
|
-
const { iconVariant = 'svg' } = this.getOptions();
|
|
12
|
+
const { iconVariant = 'svg', fallbackVariant } = this.getOptions();
|
|
13
|
+
let code;
|
|
14
|
+
let map;
|
|
15
|
+
let diagnostics;
|
|
10
16
|
try {
|
|
11
|
-
|
|
12
|
-
return this.callback(null, code, map);
|
|
17
|
+
({ code, map, diagnostics } = (0, transform_1.transformSource)(sourceCode, { iconVariant, fallbackVariant, path: resourcePath }));
|
|
13
18
|
}
|
|
14
|
-
catch {
|
|
15
|
-
|
|
19
|
+
catch (error) {
|
|
20
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
21
|
+
return this.callback(new Error(`FluentIconsAtomicImportLoader: Failed to transform "${resourcePath}": ${reason}`));
|
|
16
22
|
}
|
|
23
|
+
for (const diagnostic of diagnostics) {
|
|
24
|
+
if (diagnostic.level === 'warning') {
|
|
25
|
+
this.emitWarning(new Error(`FluentIconsAtomicImportLoader: ${diagnostic.message}`));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const firstError = diagnostics.find((d) => d.level === 'error');
|
|
29
|
+
if (firstError) {
|
|
30
|
+
return this.callback(new Error(`FluentIconsAtomicImportLoader: ${firstError.message}`));
|
|
31
|
+
}
|
|
32
|
+
return this.callback(null, code, map);
|
|
17
33
|
}
|
|
18
34
|
exports.default = fluentIconsAtomicImportLoader;
|
package/lib/modules.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export type IconVariant = 'svg' | 'fonts' | 'svg-sprite';
|
|
2
|
+
/**
|
|
3
|
+
* The variant used as the ultimate safety net when neither the requested
|
|
4
|
+
* `iconVariant` nor the configured `fallbackVariant` is supported by a module.
|
|
5
|
+
* Every module supports `svg`, so it is always a valid resolution target.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEFAULT_SAFETY_VARIANT: IconVariant;
|
|
8
|
+
/**
|
|
9
|
+
* Describes how a single supported module's barrel imports are rewritten into
|
|
10
|
+
* atomic deep paths.
|
|
11
|
+
*/
|
|
12
|
+
export interface ModuleDescriptor {
|
|
13
|
+
/** The bare module specifier this descriptor applies to. */
|
|
14
|
+
name: string;
|
|
15
|
+
/** Icon variants this module ships atomic entry points for. */
|
|
16
|
+
supportedVariants: IconVariant[];
|
|
17
|
+
/**
|
|
18
|
+
* Resolves the atomic subpath for a single named import.
|
|
19
|
+
*
|
|
20
|
+
* @param importName - The imported binding name (e.g. `AddFilled`, `bundleIcon`).
|
|
21
|
+
* @param variant - The already-resolved, supported icon variant for this module.
|
|
22
|
+
*/
|
|
23
|
+
resolve(importName: string, variant: IconVariant): string;
|
|
24
|
+
}
|
|
25
|
+
export declare const MODULES: ModuleDescriptor[];
|
|
26
|
+
export declare const SUPPORTED_MODULE_NAMES: string[];
|
|
27
|
+
export declare function getModuleDescriptor(moduleName: string): ModuleDescriptor | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Outcome of resolving the effective variant for a single module given the
|
|
30
|
+
* requested `iconVariant` and optional `fallbackVariant`.
|
|
31
|
+
*
|
|
32
|
+
* - `variant` is the variant to rewrite with, when resolution succeeds.
|
|
33
|
+
* - `error` is set when the module cannot be satisfied and no fallback exists.
|
|
34
|
+
* - `warning` is set when the resolution succeeded only by degrading to the
|
|
35
|
+
* safety variant.
|
|
36
|
+
*/
|
|
37
|
+
export interface VariantResolution {
|
|
38
|
+
variant?: IconVariant;
|
|
39
|
+
error?: string;
|
|
40
|
+
warning?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolves the effective icon variant for a module, applying the
|
|
44
|
+
* `iconVariant` → `fallbackVariant` → safety-variant precedence. This is pure
|
|
45
|
+
* policy logic; callers decide how to surface `error`/`warning`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function resolveModuleVariant(descriptor: ModuleDescriptor, iconVariant: IconVariant, fallbackVariant: IconVariant | undefined): VariantResolution;
|
package/lib/modules.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveModuleVariant = exports.getModuleDescriptor = exports.SUPPORTED_MODULE_NAMES = exports.MODULES = exports.DEFAULT_SAFETY_VARIANT = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The variant used as the ultimate safety net when neither the requested
|
|
6
|
+
* `iconVariant` nor the configured `fallbackVariant` is supported by a module.
|
|
7
|
+
* Every module supports `svg`, so it is always a valid resolution target.
|
|
8
|
+
*/
|
|
9
|
+
exports.DEFAULT_SAFETY_VARIANT = 'svg';
|
|
10
|
+
const ICON_SUFFIX_REGEX = /(\d*)?(Regular|Filled|Light|Color)$/;
|
|
11
|
+
function isIconName(importName) {
|
|
12
|
+
return ICON_SUFFIX_REGEX.test(importName);
|
|
13
|
+
}
|
|
14
|
+
function toKebabCase(value) {
|
|
15
|
+
return value.replace(/[a-z\d](?=[A-Z])|[a-zA-Z](?=\d)|[A-Z](?=[A-Z][a-z])/g, '$&-').toLowerCase();
|
|
16
|
+
}
|
|
17
|
+
function iconBaseName(importName) {
|
|
18
|
+
return toKebabCase(importName.replace(ICON_SUFFIX_REGEX, ''));
|
|
19
|
+
}
|
|
20
|
+
const reactIcons = {
|
|
21
|
+
name: '@fluentui/react-icons',
|
|
22
|
+
supportedVariants: ['svg', 'fonts', 'svg-sprite'],
|
|
23
|
+
resolve(importName, variant) {
|
|
24
|
+
if (importName === 'useIconContext' || importName === 'IconDirectionContextProvider') {
|
|
25
|
+
return '@fluentui/react-icons/providers';
|
|
26
|
+
}
|
|
27
|
+
if (!isIconName(importName)) {
|
|
28
|
+
return '@fluentui/react-icons/utils';
|
|
29
|
+
}
|
|
30
|
+
return `@fluentui/react-icons/${variant}/${iconBaseName(importName)}`;
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
const reactBrandIcons = {
|
|
34
|
+
name: '@fluentui/react-brand-icons',
|
|
35
|
+
supportedVariants: ['svg'],
|
|
36
|
+
resolve(importName) {
|
|
37
|
+
if (!isIconName(importName)) {
|
|
38
|
+
return '@fluentui/react-brand-icons/utils';
|
|
39
|
+
}
|
|
40
|
+
return `@fluentui/react-brand-icons/svg/${iconBaseName(importName)}`;
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
exports.MODULES = [reactIcons, reactBrandIcons];
|
|
44
|
+
exports.SUPPORTED_MODULE_NAMES = exports.MODULES.map((m) => m.name);
|
|
45
|
+
function getModuleDescriptor(moduleName) {
|
|
46
|
+
return exports.MODULES.find((m) => m.name === moduleName);
|
|
47
|
+
}
|
|
48
|
+
exports.getModuleDescriptor = getModuleDescriptor;
|
|
49
|
+
/**
|
|
50
|
+
* Resolves the effective icon variant for a module, applying the
|
|
51
|
+
* `iconVariant` → `fallbackVariant` → safety-variant precedence. This is pure
|
|
52
|
+
* policy logic; callers decide how to surface `error`/`warning`.
|
|
53
|
+
*/
|
|
54
|
+
function resolveModuleVariant(descriptor, iconVariant, fallbackVariant) {
|
|
55
|
+
if (descriptor.supportedVariants.includes(iconVariant)) {
|
|
56
|
+
return { variant: iconVariant };
|
|
57
|
+
}
|
|
58
|
+
const supported = descriptor.supportedVariants.join(', ');
|
|
59
|
+
if (fallbackVariant === undefined) {
|
|
60
|
+
return {
|
|
61
|
+
error: `"${descriptor.name}" does not support iconVariant "${iconVariant}" ` +
|
|
62
|
+
`(supported: ${supported}). Provide a "fallbackVariant" option to resolve this.`,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (descriptor.supportedVariants.includes(fallbackVariant)) {
|
|
66
|
+
return { variant: fallbackVariant };
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
variant: exports.DEFAULT_SAFETY_VARIANT,
|
|
70
|
+
warning: `"${descriptor.name}" supports neither iconVariant "${iconVariant}" nor ` +
|
|
71
|
+
`fallbackVariant "${fallbackVariant}" (supported: ${supported}). ` +
|
|
72
|
+
`Falling back to "${exports.DEFAULT_SAFETY_VARIANT}".`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
exports.resolveModuleVariant = resolveModuleVariant;
|
package/lib/transform.d.ts
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
import MagicString from 'magic-string';
|
|
2
|
+
import type { IconVariant } from './modules';
|
|
2
3
|
interface TransformOptions {
|
|
3
|
-
|
|
4
|
+
/** The requested icon variant. Applied to every supported module. */
|
|
5
|
+
iconVariant: IconVariant;
|
|
6
|
+
/** The variant to fall back to when a module does not support `iconVariant`. */
|
|
7
|
+
fallbackVariant?: IconVariant;
|
|
4
8
|
path: string;
|
|
5
9
|
}
|
|
10
|
+
export interface Diagnostic {
|
|
11
|
+
level: 'error' | 'warning';
|
|
12
|
+
message: string;
|
|
13
|
+
}
|
|
6
14
|
export interface TransformResult {
|
|
7
15
|
code: string;
|
|
8
16
|
map: ReturnType<MagicString['generateMap']>;
|
|
17
|
+
/**
|
|
18
|
+
* Diagnostics gathered while rewriting. Only modules that are actually
|
|
19
|
+
* imported/re-exported (as reported by the parsed module record) contribute
|
|
20
|
+
* diagnostics, so mentions in comments or string literals never trigger one.
|
|
21
|
+
*/
|
|
22
|
+
diagnostics: Diagnostic[];
|
|
9
23
|
}
|
|
10
24
|
export declare function transformSource(source: string, options: TransformOptions): TransformResult;
|
|
11
25
|
export {};
|
package/lib/transform.js
CHANGED
|
@@ -6,22 +6,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.transformSource = void 0;
|
|
7
7
|
const oxc_parser_1 = require("oxc-parser");
|
|
8
8
|
const magic_string_1 = __importDefault(require("magic-string"));
|
|
9
|
-
const
|
|
10
|
-
const ICON_SUFFIX_REGEX = /(\d*)?(Regular|Filled|Light|Color)$/;
|
|
11
|
-
function getAtomicImportPath(importName, iconVariant) {
|
|
12
|
-
if (importName === 'useIconContext' || importName === 'IconDirectionContextProvider') {
|
|
13
|
-
return '@fluentui/react-icons/providers';
|
|
14
|
-
}
|
|
15
|
-
const isIcon = importName.match(ICON_SUFFIX_REGEX);
|
|
16
|
-
if (!isIcon) {
|
|
17
|
-
return '@fluentui/react-icons/utils';
|
|
18
|
-
}
|
|
19
|
-
const withoutSuffix = importName.replace(ICON_SUFFIX_REGEX, '');
|
|
20
|
-
const kebabCase = withoutSuffix.replace(/[a-z\d](?=[A-Z])|[a-zA-Z](?=\d)|[A-Z](?=[A-Z][a-z])/g, '$&-').toLowerCase();
|
|
21
|
-
return `@fluentui/react-icons/${iconVariant}/${kebabCase}`;
|
|
22
|
-
}
|
|
9
|
+
const modules_1 = require("./modules");
|
|
23
10
|
function transformSource(source, options) {
|
|
24
|
-
const { iconVariant, path } = options;
|
|
11
|
+
const { iconVariant, fallbackVariant, path } = options;
|
|
25
12
|
const result = (0, oxc_parser_1.parseSync)(path, source, {
|
|
26
13
|
sourceType: 'module',
|
|
27
14
|
});
|
|
@@ -30,8 +17,36 @@ function transformSource(source, options) {
|
|
|
30
17
|
}
|
|
31
18
|
const { staticImports, staticExports } = result.module;
|
|
32
19
|
const src = new magic_string_1.default(source);
|
|
20
|
+
const diagnostics = [];
|
|
21
|
+
// Resolve (and diagnose) each referenced module at most once.
|
|
22
|
+
const resolvedVariants = new Map();
|
|
23
|
+
/**
|
|
24
|
+
* Returns the variant to rewrite a referenced module with, or `null` when it
|
|
25
|
+
* could not be resolved (an error diagnostic has been recorded and the module
|
|
26
|
+
* should be left untouched).
|
|
27
|
+
*/
|
|
28
|
+
const variantFor = (descriptor) => {
|
|
29
|
+
if (resolvedVariants.has(descriptor.name)) {
|
|
30
|
+
return resolvedVariants.get(descriptor.name);
|
|
31
|
+
}
|
|
32
|
+
const resolution = (0, modules_1.resolveModuleVariant)(descriptor, iconVariant, fallbackVariant);
|
|
33
|
+
if (resolution.warning) {
|
|
34
|
+
diagnostics.push({ level: 'warning', message: resolution.warning });
|
|
35
|
+
}
|
|
36
|
+
if (resolution.error) {
|
|
37
|
+
diagnostics.push({ level: 'error', message: resolution.error });
|
|
38
|
+
}
|
|
39
|
+
const variant = resolution.variant ?? null;
|
|
40
|
+
resolvedVariants.set(descriptor.name, variant);
|
|
41
|
+
return variant;
|
|
42
|
+
};
|
|
33
43
|
for (const imp of staticImports) {
|
|
34
|
-
|
|
44
|
+
const moduleName = imp.moduleRequest.value;
|
|
45
|
+
const descriptor = (0, modules_1.getModuleDescriptor)(moduleName);
|
|
46
|
+
if (!descriptor)
|
|
47
|
+
continue;
|
|
48
|
+
const variant = variantFor(descriptor);
|
|
49
|
+
if (!variant)
|
|
35
50
|
continue;
|
|
36
51
|
const namedEntries = imp.entries.filter((e) => e.importName.kind === 'Name');
|
|
37
52
|
if (namedEntries.length === 0)
|
|
@@ -42,19 +57,19 @@ function transformSource(source, options) {
|
|
|
42
57
|
const names = otherEntries
|
|
43
58
|
.map((e) => (e.importName.kind === 'Default' ? e.localName.value : `* as ${e.localName.value}`))
|
|
44
59
|
.join(', ');
|
|
45
|
-
lines.push(`import ${names} from '${
|
|
60
|
+
lines.push(`import ${names} from '${moduleName}';`);
|
|
46
61
|
}
|
|
47
62
|
for (const entry of namedEntries) {
|
|
48
63
|
const importedName = entry.importName.name;
|
|
49
64
|
const localName = entry.localName.value;
|
|
50
|
-
const newSource =
|
|
65
|
+
const newSource = descriptor.resolve(importedName, variant);
|
|
51
66
|
const spec = importedName === localName ? importedName : `${importedName} as ${localName}`;
|
|
52
67
|
lines.push(`import { ${spec} } from '${newSource}';`);
|
|
53
68
|
}
|
|
54
69
|
src.overwrite(imp.start, imp.end, lines.join('\n'));
|
|
55
70
|
}
|
|
56
71
|
for (const exp of staticExports) {
|
|
57
|
-
const relevantEntries = exp.entries.filter((e) => e.moduleRequest
|
|
72
|
+
const relevantEntries = exp.entries.filter((e) => e.moduleRequest && (0, modules_1.getModuleDescriptor)(e.moduleRequest.value) && e.exportName.kind === 'Name');
|
|
58
73
|
if (relevantEntries.length === 0)
|
|
59
74
|
continue;
|
|
60
75
|
// Skip indirect re-exports (`import { X } from '...'; export { X };`): oxc reports these as static
|
|
@@ -64,17 +79,25 @@ function transformSource(source, options) {
|
|
|
64
79
|
continue;
|
|
65
80
|
const lines = [];
|
|
66
81
|
for (const entry of relevantEntries) {
|
|
82
|
+
const moduleName = entry.moduleRequest.value;
|
|
83
|
+
const descriptor = (0, modules_1.getModuleDescriptor)(moduleName);
|
|
84
|
+
const variant = variantFor(descriptor);
|
|
85
|
+
if (!variant)
|
|
86
|
+
continue;
|
|
67
87
|
const importedName = entry.importName.name;
|
|
68
88
|
const exportedName = entry.exportName.name;
|
|
69
|
-
const newSource =
|
|
89
|
+
const newSource = descriptor.resolve(importedName, variant);
|
|
70
90
|
const spec = importedName === exportedName ? importedName : `${importedName} as ${exportedName}`;
|
|
71
91
|
lines.push(`export { ${spec} } from '${newSource}';`);
|
|
72
92
|
}
|
|
93
|
+
if (lines.length === 0)
|
|
94
|
+
continue;
|
|
73
95
|
src.overwrite(exp.start, exp.end, lines.join('\n'));
|
|
74
96
|
}
|
|
75
97
|
return {
|
|
76
98
|
code: src.toString(),
|
|
77
99
|
map: src.generateMap({ hires: true }),
|
|
100
|
+
diagnostics,
|
|
78
101
|
};
|
|
79
102
|
}
|
|
80
103
|
exports.transformSource = transformSource;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluentui/react-icons-atomic-webpack-loader",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Webpack loader that transforms barrel imports and re-exports from @fluentui/react-icons into atomic deep paths",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|