@dubium/icons 0.0.2-alpha.3 → 0.0.2-alpha.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.
Files changed (124) hide show
  1. package/README.md +286 -49
  2. package/dist/collection/AdjustmentsPlusIcon.d.ts +3 -0
  3. package/dist/collection/{Icon123Icon.js → AdjustmentsPlusIcon.js} +3 -3
  4. package/dist/collection/AdjustmentsQuestionIcon.d.ts +3 -0
  5. package/dist/collection/AdjustmentsQuestionIcon.js +18 -0
  6. package/dist/collection/AlarmPlusFilledIcon.d.ts +3 -0
  7. package/dist/collection/AlarmPlusFilledIcon.js +29 -0
  8. package/dist/collection/AlarmSnoozeFilledIcon.d.ts +3 -0
  9. package/dist/collection/AlarmSnoozeFilledIcon.js +29 -0
  10. package/dist/collection/{Icon360ViewIcon.d.ts → AlarmSnoozeIcon.d.ts} +2 -2
  11. package/dist/collection/{Icon360Icon.js → AlarmSnoozeIcon.js} +3 -3
  12. package/dist/collection/AlbumIcon.d.ts +3 -0
  13. package/dist/collection/{Icon24HoursIcon.js → AlbumIcon.js} +3 -3
  14. package/dist/collection/AlbumOffIcon.d.ts +3 -0
  15. package/dist/collection/AlbumOffIcon.js +18 -0
  16. package/dist/collection/AlertCircleFilledIcon.d.ts +3 -0
  17. package/dist/collection/AlertCircleFilledIcon.js +15 -0
  18. package/dist/collection/{Icon24HoursIcon.d.ts → AlertCircleIcon.d.ts} +2 -2
  19. package/dist/collection/{Icon2faIcon.js → AlertCircleIcon.js} +3 -3
  20. package/dist/collection/AlertHexagonIcon.d.ts +3 -0
  21. package/dist/collection/AlertHexagonIcon.js +18 -0
  22. package/dist/collection/AlignBoxBottomRightFilledIcon.d.ts +3 -0
  23. package/dist/collection/AlignBoxBottomRightFilledIcon.js +15 -0
  24. package/dist/collection/AlignBoxBottomRightIcon.d.ts +3 -0
  25. package/dist/collection/AlignBoxBottomRightIcon.js +18 -0
  26. package/dist/collection/AlignBoxCenterMiddleFilledIcon.d.ts +3 -0
  27. package/dist/collection/AlignBoxCenterMiddleFilledIcon.js +15 -0
  28. package/dist/collection/AlignBoxCenterMiddleIcon.d.ts +3 -0
  29. package/dist/collection/AlignBoxCenterMiddleIcon.js +18 -0
  30. package/dist/collection/AlignBoxLeftBottomFilledIcon.d.ts +3 -0
  31. package/dist/collection/AlignBoxLeftBottomFilledIcon.js +15 -0
  32. package/dist/collection/AlignBoxLeftBottomIcon.d.ts +3 -0
  33. package/dist/collection/AlignBoxLeftBottomIcon.js +18 -0
  34. package/dist/collection/AlignBoxLeftMiddleFilledIcon.d.ts +3 -0
  35. package/dist/collection/AlignBoxLeftMiddleFilledIcon.js +15 -0
  36. package/dist/collection/AlignBoxLeftMiddleIcon.d.ts +3 -0
  37. package/dist/collection/AlignBoxLeftMiddleIcon.js +18 -0
  38. package/dist/collection/index.d.ts +20 -7
  39. package/dist/collection/index.js +19 -6
  40. package/dist/collection/types.d.ts +17 -1
  41. package/dist/icon/Icon.container.d.ts +29 -0
  42. package/dist/icon/Icon.container.js +19 -0
  43. package/dist/icon/Icon.d.ts +5 -0
  44. package/dist/icon/Icon.js +20 -0
  45. package/dist/icon/Icon.types.d.ts +59 -0
  46. package/dist/icon/index.d.ts +13 -0
  47. package/dist/icon/index.js +2 -0
  48. package/dist/vite/icon/Icon.create.d.ts +35 -0
  49. package/dist/{spa → vite}/icon/Icon.create.js +1 -1
  50. package/dist/vite/icon/Icon.d.ts +66 -0
  51. package/dist/vite/icon/Icon.js +79 -0
  52. package/dist/vite/icon/Icon.types.d.ts +88 -0
  53. package/dist/vite/icon/index.d.ts +6 -0
  54. package/dist/vite/index.d.ts +10 -46
  55. package/dist/vite/index.js +6 -167
  56. package/dist/vite/plugin/index.d.ts +11 -0
  57. package/dist/vite/plugin/index.js +2 -0
  58. package/dist/vite/plugin/plugin.d.ts +41 -0
  59. package/dist/vite/plugin/plugin.js +105 -0
  60. package/dist/vite/plugin/plugin.types.d.ts +99 -0
  61. package/dist/vite/plugin/scanner/index.d.ts +9 -0
  62. package/dist/vite/plugin/scanner/scanner.imports.d.ts +5 -0
  63. package/dist/vite/plugin/scanner/scanner.imports.js +36 -0
  64. package/dist/vite/plugin/scanner/scanner.jsx.d.ts +24 -0
  65. package/dist/vite/plugin/scanner/scanner.jsx.js +58 -0
  66. package/dist/vite/plugin/scanner/scanner.lexical.d.ts +30 -0
  67. package/dist/vite/plugin/scanner/scanner.lexical.js +140 -0
  68. package/dist/vite/plugin/scanner/scanner.properties.d.ts +9 -0
  69. package/dist/vite/plugin/scanner/scanner.properties.js +78 -0
  70. package/dist/vite/plugin/scanner/scanner.regex.d.ts +21 -0
  71. package/dist/vite/plugin/scanner/scanner.regex.js +24 -0
  72. package/dist/vite/plugin/scanner/scanner.source.d.ts +18 -0
  73. package/dist/vite/plugin/scanner/scanner.source.js +23 -0
  74. package/dist/vite/plugin/scanner/scanner.types.d.ts +1 -0
  75. package/dist/vite/plugin/sources/index.d.ts +11 -0
  76. package/dist/vite/plugin/sources/sources.catalog.d.ts +17 -0
  77. package/dist/vite/plugin/sources/sources.catalog.js +19 -0
  78. package/dist/vite/plugin/sources/sources.files.d.ts +34 -0
  79. package/dist/vite/plugin/sources/sources.files.js +43 -0
  80. package/dist/vite/plugin/sources/sources.types.d.ts +1 -0
  81. package/dist/vite/plugin/sources/sources.validate.d.ts +13 -0
  82. package/dist/vite/plugin/sources/sources.validate.js +12 -0
  83. package/dist/vite/plugin/virtual/index.d.ts +9 -0
  84. package/dist/vite/plugin/virtual/virtual.constants.d.ts +15 -0
  85. package/dist/vite/plugin/virtual/virtual.constants.js +4 -0
  86. package/dist/vite/plugin/virtual/virtual.module.d.ts +23 -0
  87. package/dist/vite/plugin/virtual/virtual.module.js +27 -0
  88. package/dist/vite/provider/IconProvider.context.d.ts +14 -0
  89. package/dist/vite/provider/IconProvider.context.js +5 -0
  90. package/dist/{spa → vite}/provider/IconProvider.d.ts +3 -2
  91. package/dist/{spa → vite}/provider/IconProvider.js +1 -1
  92. package/dist/{spa → vite}/provider/index.d.ts +2 -2
  93. package/dist/{spa/provider/IconProvider.context.d.ts → vite/provider/useIconContext.d.ts} +4 -14
  94. package/dist/vite/provider/useIconContext.js +6 -0
  95. package/dist/vite/runtime/IconRuntime.registry.d.ts +54 -0
  96. package/dist/vite/runtime/IconRuntime.registry.js +58 -0
  97. package/dist/vite/runtime/index.d.ts +9 -0
  98. package/package.json +17 -17
  99. package/dist/collection/Icon123Icon.d.ts +0 -3
  100. package/dist/collection/Icon2faIcon.d.ts +0 -3
  101. package/dist/collection/Icon360Icon.d.ts +0 -3
  102. package/dist/collection/Icon360ViewIcon.js +0 -18
  103. package/dist/spa/icon/Icon.create.d.ts +0 -40
  104. package/dist/spa/icon/Icon.d.ts +0 -25
  105. package/dist/spa/icon/Icon.js +0 -58
  106. package/dist/spa/icon/Icon.registry.d.ts +0 -12
  107. package/dist/spa/icon/Icon.types.d.ts +0 -71
  108. package/dist/spa/icon/index.d.ts +0 -9
  109. package/dist/spa/index.d.ts +0 -8
  110. package/dist/spa/index.js +0 -5
  111. package/dist/spa/provider/IconProvider.context.js +0 -5
  112. package/dist/ssr/icon/Icon.container.d.ts +0 -26
  113. package/dist/ssr/icon/Icon.container.js +0 -23
  114. package/dist/ssr/icon/Icon.create.d.ts +0 -35
  115. package/dist/ssr/icon/Icon.create.js +0 -8
  116. package/dist/ssr/icon/Icon.d.ts +0 -6
  117. package/dist/ssr/icon/Icon.js +0 -27
  118. package/dist/ssr/icon/Icon.registry.d.ts +0 -12
  119. package/dist/ssr/icon/Icon.registry.js +0 -10
  120. package/dist/ssr/icon/Icon.types.d.ts +0 -97
  121. package/dist/ssr/icon/index.d.ts +0 -10
  122. package/dist/ssr/index.d.ts +0 -6
  123. package/dist/ssr/index.js +0 -4
  124. /package/dist/{spa → vite}/provider/IconProvider.types.d.ts +0 -0
@@ -0,0 +1,88 @@
1
+ import type { ComponentType } from "react";
2
+ import type { IIconComponentProps } from "../../collection/types.js";
3
+ /**
4
+ * Тип пустого реестра иконок.
5
+ *
6
+ * Используется как default generic, чтобы keyof реестра
7
+ * не превращался в обычный string.
8
+ */
9
+ export type TEmptyIconRegistry = Readonly<Record<never, never>>;
10
+ /**
11
+ * React-компонент иконки.
12
+ */
13
+ export type TIcon = ComponentType<IIconComponentProps>;
14
+ /**
15
+ * Модуль динамически загружаемой иконки.
16
+ */
17
+ export type TIconModule = {
18
+ default: TIcon;
19
+ };
20
+ /**
21
+ * Lazy loader иконки.
22
+ */
23
+ export type TIconLoader = () => Promise<TIconModule>;
24
+ /**
25
+ * Runtime / custom registry иконок.
26
+ *
27
+ * В runtime ключом может быть любая строка.
28
+ */
29
+ export type TIconRegistry = Readonly<Record<string, TIconLoader>>;
30
+ /**
31
+ * Все runtime-экспорты collection/index.ts.
32
+ *
33
+ * Например:
34
+ *
35
+ * "AB2Icon"
36
+ * "AbacusIcon"
37
+ * "AccessibleIcon"
38
+ */
39
+ type TCollectionExportName = Extract<keyof typeof import("../../collection/index.js"), string>;
40
+ /**
41
+ * Удаляет суффикс "Icon".
42
+ *
43
+ * Например:
44
+ *
45
+ * "AbacusIcon" -> "Abacus"
46
+ * "AccessibleIcon" -> "Accessible"
47
+ */
48
+ type TRemoveIconSuffix<TName extends string> = TName extends `${infer TIconName}Icon` ? TIconName : never;
49
+ /**
50
+ * Все имена встроенных иконок пакета.
51
+ *
52
+ * Автоматически вычисляется из:
53
+ *
54
+ * src/icons/collection/index.ts
55
+ *
56
+ * Поэтому после sync:icons список обновляется автоматически.
57
+ */
58
+ export type TPackageIconName = TRemoveIconSuffix<TCollectionExportName>;
59
+ /**
60
+ * Допустимое имя иконки.
61
+ *
62
+ * Включает:
63
+ *
64
+ * 1. все встроенные иконки @sg/icons;
65
+ * 2. ключи кастомного registry, если он передан.
66
+ *
67
+ * @example
68
+ *
69
+ * ```ts
70
+ * type TName = TIconName
71
+ *
72
+ * // "AB2" | "Abacus" | "Accessible" | ...
73
+ * ```
74
+ *
75
+ * @example
76
+ *
77
+ * ```ts
78
+ * const customIcons = {
79
+ * CompanyLogo: () => import("./CompanyLogoIcon"),
80
+ * } as const
81
+ *
82
+ * type TName = TIconName<typeof customIcons>
83
+ *
84
+ * // package icons | "CompanyLogo"
85
+ * ```
86
+ */
87
+ export type TIconName<TCustomIcons extends TIconRegistry = TEmptyIconRegistry> = TPackageIconName | Extract<keyof TCustomIcons, string>;
88
+ export type { IIconComponentProps };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Публичный API Vite Icon.
3
+ */
4
+ export { Icon, type IconProps } from "./Icon.js";
5
+ export { createIcon } from "./Icon.create.js";
6
+ export type { IIconComponentProps, TEmptyIconRegistry, TIcon, TIconLoader, TIconModule, TIconName, TIconRegistry, TPackageIconName, } from "./Icon.types.js";
@@ -1,50 +1,14 @@
1
- import type { Plugin } from "vite";
2
- export interface DubiumIconsPluginOptions {
3
- /**
4
- * Имена компонентов, которые плагин должен анализировать.
5
- *
6
- * @default ["Icon"]
7
- */
8
- componentNames?: readonly string[];
9
- /**
10
- * Иконки, которые необходимо включить вручную.
11
- *
12
- * Используется для runtime-имён:
13
- * `<Icon name={valueFromApi} />`.
14
- */
15
- include?: readonly string[];
16
- /**
17
- * Имя npm-пакета с иконками.
18
- *
19
- * @default "@dubium/icons"
20
- */
21
- packageName?: string;
22
- /**
23
- * Каталоги приложения, которые нужно сканировать.
24
- * Пути могут быть абсолютными или относительными к Vite root.
25
- *
26
- * @default ["src"]
27
- */
28
- scan?: readonly string[];
29
- /**
30
- * Если true, сборка завершится ошибкой при обнаружении
31
- * `<Icon name={dynamicValue} />`.
32
- *
33
- * При false будет выведено предупреждение.
34
- *
35
- * @default false
36
- */
37
- strictDynamicNames?: boolean;
38
- }
39
1
  /**
40
- * Vite-плагин для compile-time извлечения используемых иконок.
2
+ * Публичный API иконок для Vite.
41
3
  *
42
- * Он сканирует JSX/TSX, находит статические:
43
- * `<Icon name="User" />`,
44
- * а затем создаёт virtual registry только для найденных имён.
4
+ * Источники иконок:
45
5
  *
46
- * Благодаря этому Rollup/Vite видит dynamic import() только реально
47
- * используемых иконок, и остальные иконки не попадают в dist приложения.
6
+ * 1. IconProvider;
7
+ * 2. runtime registry;
8
+ * 3. compile-time virtual registry.
48
9
  */
49
- export declare const dubiumIcons: (options?: DubiumIconsPluginOptions) => Plugin;
50
- export default dubiumIcons;
10
+ export { createIcon, Icon, type IconProps } from "./icon/index.js";
11
+ export { IconProvider, useIconContext } from "./provider/index.js";
12
+ export { getRuntimeIconLoader, getRuntimeIconsVersion, registerIcons, removeIconOwner, subscribeRuntimeIcons, } from "./runtime/index.js";
13
+ export type { IIconComponentProps, TEmptyIconRegistry, TIcon, TIconLoader, TIconModule, TIconName, TIconRegistry, TPackageIconName, } from "./icon/index.js";
14
+ export type { IIconContextValue, IIconProviderProps } from "./provider/index.js";
@@ -1,167 +1,6 @@
1
- import { readFile as e, readdir as t } from "node:fs/promises";
2
- import { extname as n, isAbsolute as r, join as i, relative as a, resolve as o } from "node:path";
3
- //#region src/icons/vite/index.ts
4
- var s = "virtual:@dubium/icons-registry", c = `\0${s}`, l = /* @__PURE__ */ new Set([
5
- ".js",
6
- ".jsx",
7
- ".mjs",
8
- ".cjs",
9
- ".ts",
10
- ".tsx"
11
- ]), u = /* @__PURE__ */ new Set([
12
- ".git",
13
- ".next",
14
- ".nuxt",
15
- ".output",
16
- ".turbo",
17
- "build",
18
- "coverage",
19
- "dist",
20
- "node_modules"
21
- ]), d = (e) => e.replaceAll("\\", "/"), f = (e, t) => {
22
- if (e.size !== t.size) return !1;
23
- for (let n of e) if (!t.has(n)) return !1;
24
- return !0;
25
- }, p = (e) => {
26
- let t = e.map((e) => e.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&"));
27
- return RegExp(`<\\s*(?:${t.join("|")})\\b`, "gu");
28
- }, m = (e, t) => {
29
- let n = null, r = !1, i = 0;
30
- for (let a = t; a < e.length; a += 1) {
31
- let t = e[a];
32
- if (n) {
33
- if (r) {
34
- r = !1;
35
- continue;
36
- }
37
- if (t === "\\") {
38
- r = !0;
39
- continue;
40
- }
41
- t === n && (n = null);
42
- continue;
43
- }
44
- if (t === "'" || t === "\"" || t === "`") {
45
- n = t;
46
- continue;
47
- }
48
- if (t === "{") {
49
- i += 1;
50
- continue;
51
- }
52
- if (t === "}") {
53
- i = Math.max(0, i - 1);
54
- continue;
55
- }
56
- if (t === ">" && i === 0) return a;
57
- }
58
- return -1;
59
- }, h = (e) => {
60
- let t = /\bname\s*=\s*(["'])(.*?)\1/su.exec(e);
61
- if (t) return t[2].trim();
62
- let n = /\bname\s*=\s*\{\s*(?:(["'])(.*?)\1|`([^`${}]*)`)\s*\}/su.exec(e);
63
- return n ? (n[2] ?? n[3] ?? "").trim() : null;
64
- }, g = (e) => /\bname\s*=/u.test(e), _ = (e, t) => {
65
- let n = /* @__PURE__ */ new Set(), r = p(t), i = 0;
66
- for (let t of e.matchAll(r)) {
67
- let r = t.index ?? 0, a = m(e, r);
68
- if (a === -1) continue;
69
- let o = e.slice(r, a + 1), s = h(o);
70
- if (s) {
71
- n.add(s);
72
- continue;
73
- }
74
- g(o) && (i += 1);
75
- }
76
- return {
77
- dynamicNames: i,
78
- icons: n
79
- };
80
- }, v = async (e) => {
81
- let r;
82
- try {
83
- r = await t(e, { withFileTypes: !0 });
84
- } catch {
85
- return [];
86
- }
87
- let a = [];
88
- for (let t of r) {
89
- let r = i(e, t.name);
90
- if (t.isDirectory()) {
91
- u.has(t.name) || a.push(...await v(r));
92
- continue;
93
- }
94
- t.isFile() && l.has(n(t.name).toLowerCase()) && a.push(r);
95
- }
96
- return a;
97
- }, y = (e, t) => `/**
98
- * AUTO-GENERATED in memory by @dubium/icons/vite.
99
- * Contains only icons used by the current application.
100
- */
101
- export const iconRegistry = {
102
- ${e.map((e) => `\t${JSON.stringify(e)}: () => import(${JSON.stringify(`${t}/icons/${e}`)}),`).join("\n")}
103
- }
104
- `, b = (t = {}) => {
105
- let i = t.componentNames?.length ? [...t.componentNames] : ["Icon"], u = t.packageName ?? "@dubium/icons", p = t.scan?.length ? [...t.scan] : ["src"], m = new Set(t.include ?? []), h = /* @__PURE__ */ new Map(), g = /* @__PURE__ */ new Map(), b = process.cwd(), x, S = () => {
106
- let e = new Set(m);
107
- for (let t of h.values()) for (let n of t) e.add(n);
108
- return [...e].sort((e, t) => e.localeCompare(t));
109
- }, C = async (t) => {
110
- let r = d(t);
111
- if (!l.has(n(t).toLowerCase())) return h.delete(r), g.delete(r), !1;
112
- let a;
113
- try {
114
- a = await e(t, "utf8");
115
- } catch {
116
- let e = h.delete(r), t = g.delete(r);
117
- return e || t;
118
- }
119
- let o = h.get(r) ?? /* @__PURE__ */ new Set(), s = g.get(r) ?? 0, c = _(a, i);
120
- return h.set(r, c.icons), c.dynamicNames > 0 ? g.set(r, c.dynamicNames) : g.delete(r), !f(o, c.icons) || s !== c.dynamicNames;
121
- }, w = async (e) => {
122
- h.clear(), g.clear();
123
- for (let t of p) {
124
- let n = await v(r(t) ? t : o(b, t));
125
- for (let t of n) e?.addWatchFile(t), await C(t);
126
- }
127
- }, T = () => g.size === 0 ? null : `[@dubium/icons] Найдены динамические name у <Icon>:\n${[...g.entries()].map(([e, t]) => ` - ${d(a(b, e))}: ${t}`).join("\n")}\nVite не может определить такие иконки на этапе сборки. Добавьте допустимые имена в dubiumIcons({ include: [...] }).`, E = () => {
128
- if (!x) return;
129
- let e = x.moduleGraph.getModuleById(c);
130
- e && x.moduleGraph.invalidateModule(e), x.ws.send({ type: "full-reload" });
131
- };
132
- return {
133
- name: "dubium-icons",
134
- enforce: "pre",
135
- configResolved(e) {
136
- b = e.root;
137
- },
138
- async buildStart() {
139
- await w(this);
140
- let e = T();
141
- e && (t.strictDynamicNames ? this.error(e) : this.warn(e));
142
- },
143
- configureServer(e) {
144
- x = e;
145
- let t = async (e) => {
146
- await C(e) && E();
147
- }, n = async (e) => {
148
- let t = d(e);
149
- (h.delete(t) || g.delete(t)) && E();
150
- };
151
- return e.watcher.on("add", t), e.watcher.on("unlink", n), () => {
152
- e.watcher.off("add", t), e.watcher.off("unlink", n);
153
- };
154
- },
155
- resolveId(e) {
156
- return e === s ? c : null;
157
- },
158
- load(e) {
159
- return e === c ? y(S(), u) : null;
160
- },
161
- async handleHotUpdate(e) {
162
- if (await C(e.file)) return E(), [];
163
- }
164
- };
165
- };
166
- //#endregion
167
- export { b as default, b as dubiumIcons };
1
+ import { IconProvider as e } from "./provider/IconProvider.js";
2
+ import { useIconContext as t } from "./provider/useIconContext.js";
3
+ import { getRuntimeIconLoader as n, getRuntimeIconsVersion as r, registerIcons as i, removeIconOwner as a, subscribeRuntimeIcons as o } from "./runtime/IconRuntime.registry.js";
4
+ import { Icon as s } from "./icon/Icon.js";
5
+ import { createIcon as c } from "./icon/Icon.create.js";
6
+ export { s as Icon, e as IconProvider, c as createIcon, n as getRuntimeIconLoader, r as getRuntimeIconsVersion, i as registerIcons, a as removeIconOwner, o as subscribeRuntimeIcons, t as useIconContext };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Публичный API Vite-плагина {@link dubiumIcons}.
3
+ *
4
+ * @remarks
5
+ * Плагин сканирует исходники приложения, строит виртуальный модуль
6
+ * `virtual:@dubium/icons-registry` с реестром только реально используемых
7
+ * иконок и разрешает его через `resolveId`/`load`.
8
+ */
9
+ export { dubiumIcons } from "./plugin.js";
10
+ export { dubiumIcons as default } from "./plugin.js";
11
+ export type { DubiumIconsPluginOptions, TDubiumIconSource } from "./plugin.types.js";
@@ -0,0 +1,2 @@
1
+ import { dubiumIcons as e } from "./plugin.js";
2
+ export { e as default, e as dubiumIcons };
@@ -0,0 +1,41 @@
1
+ import type { Plugin } from "vite";
2
+ import type { DubiumIconsPluginOptions } from "./plugin.types.js";
3
+ /**
4
+ * Плагин Vite для автоматического обнаружения иконок `@dubium/icons`.
5
+ *
6
+ * @remarks
7
+ * Плагин сканирует исходники проекта, находит статические использования
8
+ * Vite-компонента `Icon` (в виде JSX-тегов и строковых конфигураций) и генерирует
9
+ * виртуальный модуль `virtual:@dubium/icons-registry` с ленивыми загрузчиками
10
+ * только для используемых иконок.
11
+ *
12
+ * По умолчанию компонентом считается любой JSX-тег `<Icon>`, имя которого
13
+ * импортировано из `@dubium/icons/vite`. Набор имён можно переопределить через
14
+ * `options.componentNames`, а список свойств, значения которых трактуются как имена
15
+ * иконок, — через `options.propertyNames`. Теги прочих компонентов, например
16
+ * `<IconButton>`, игнорируются.
17
+ *
18
+ * Сканируются все файлы директории `src` (настраивается через `options.scan`).
19
+ * Источником иконок по умолчанию является пакет `@dubium/icons` с подстановкой
20
+ * имени в `importPattern`; локальные каталоги SVG-файлов подключаются через
21
+ * `options.sources`.
22
+ *
23
+ * Динамические имена вида `<Icon name={expression}>` не могут быть разрешены
24
+ * на этапе сборки — о них выводится предупреждение с указанием файлов.
25
+ *
26
+ * @param options - Опции плагина
27
+ * @returns Объект плагина Vite
28
+ *
29
+ * @example
30
+ *
31
+ * ```ts
32
+ * // vite.config.ts
33
+ * import { defineConfig } from "vite"
34
+ * import { dubiumIcons } from "@dubium/icons/vite/plugin"
35
+ *
36
+ * export default defineConfig({
37
+ * plugins: [dubiumIcons()],
38
+ * })
39
+ * ```
40
+ */
41
+ export declare const dubiumIcons: (options?: DubiumIconsPluginOptions) => Plugin;
@@ -0,0 +1,105 @@
1
+ import { scanSource as e } from "./scanner/scanner.source.js";
2
+ import { DEFAULT_EXTENSIONS as t, getSourceFiles as n, isPathInsideDirectory as r, normalizePath as i } from "./sources/sources.files.js";
3
+ import { buildLocalCatalog as a } from "./sources/sources.catalog.js";
4
+ import { validateSources as o } from "./sources/sources.validate.js";
5
+ import { RESOLVED_VIRTUAL_MODULE_ID as s } from "./virtual/virtual.constants.js";
6
+ import { createVirtualModuleSource as c } from "./virtual/virtual.module.js";
7
+ import { readFile as l } from "node:fs/promises";
8
+ import { extname as u, relative as d, resolve as f } from "node:path";
9
+ //#region src/icons/vite/plugin/plugin.ts
10
+ var p = (e, t) => {
11
+ if (e.size !== t.size) return !1;
12
+ for (let n of e) if (!t.has(n)) return !1;
13
+ return !0;
14
+ }, m = (m = {}) => {
15
+ let h = m.componentNames ? [...m.componentNames] : [], g = m.propertyNames ? [...m.propertyNames] : [], _ = m.scan ? [...m.scan] : ["src"], v = m.sources ? [...m.sources] : [{
16
+ type: "package",
17
+ importPattern: "@dubium/icons/icons/{name}"
18
+ }];
19
+ o(v);
20
+ let y = v.filter((e) => e.type === "local"), b = v.find((e) => e.type === "package"), x = /* @__PURE__ */ new Map(), S = /* @__PURE__ */ new Map(), C = [], w = process.cwd(), T, E = "", D = () => {
21
+ let e = /* @__PURE__ */ new Set();
22
+ for (let t of x.values()) for (let n of t) e.add(n);
23
+ return [...e].sort((e, t) => e.localeCompare(t));
24
+ }, O = async (n, r) => {
25
+ let a = i(n);
26
+ if (!t.has(u(n).toLowerCase())) {
27
+ let e = x.delete(a), t = S.delete(a);
28
+ return e || t;
29
+ }
30
+ let o;
31
+ try {
32
+ o = r ? await r() : await l(n, "utf8");
33
+ } catch {
34
+ let e = x.delete(a), t = S.delete(a);
35
+ return e || t;
36
+ }
37
+ let s = x.get(a) ?? /* @__PURE__ */ new Set(), c = S.get(a) ?? 0, d = e(o, h, g);
38
+ return x.set(a, d.icons), d.dynamicNames > 0 ? S.set(a, d.dynamicNames) : S.delete(a), !p(s, d.icons) || c !== d.dynamicNames;
39
+ }, k = async (e) => {
40
+ x.clear(), S.clear();
41
+ for (let t of _) {
42
+ let r = f(w, t), i;
43
+ try {
44
+ i = await n(r);
45
+ } catch {
46
+ e?.warn(`[@dubium/icons] Директория для сканирования недоступна: "${t}".\nПроверьте options.scan, существование пути и права доступа.`);
47
+ continue;
48
+ }
49
+ for (let t of i) e?.addWatchFile(t), await O(t);
50
+ }
51
+ }, A = async () => {
52
+ C = await Promise.all(y.map((e) => a(w, e)));
53
+ }, j = (e) => y.some((t) => r(f(w, t.path), e)), M = (e) => _.some((t) => r(f(w, t), e)), N = () => {
54
+ if (!T) return;
55
+ let e = T.moduleGraph.getModuleById(s);
56
+ e && T.moduleGraph.invalidateModule(e), T.ws.send({ type: "full-reload" });
57
+ }, P = (e) => {
58
+ if (S.size === 0) {
59
+ E = "";
60
+ return;
61
+ }
62
+ let t = [...S.entries()].sort(([e], [t]) => e.localeCompare(t)).map(([e, t]) => ` - ${i(d(w, e))}: ${t}`).join("\n");
63
+ t !== E && (E = t, e(`[@dubium/icons] Найдены динамические <Icon name={...}>:\n${t}\nДля MF/EventBus это допустимо, если loader регистрируется remote через runtime registry.`));
64
+ };
65
+ return {
66
+ name: "dubium-icons",
67
+ enforce: "pre",
68
+ configResolved(e) {
69
+ w = e.root;
70
+ },
71
+ async buildStart() {
72
+ await A(), await k(this), P((e) => this.warn(e));
73
+ },
74
+ configureServer(e) {
75
+ T = e;
76
+ for (let t of y) e.watcher.add(f(w, t.path));
77
+ let t = async (e) => {
78
+ let t = j(e), n = M(e) ? await O(e) : !1, r = () => {
79
+ N(), P((e) => {
80
+ T?.config.logger.warn(e);
81
+ });
82
+ };
83
+ if (t) {
84
+ await A(), r();
85
+ return;
86
+ }
87
+ n && r();
88
+ };
89
+ e.watcher.on("add", t), e.watcher.on("unlink", t), e.watcher.on("addDir", t), e.watcher.on("unlinkDir", t), e.httpServer?.once("close", () => {
90
+ e.watcher.off("add", t), e.watcher.off("unlink", t), e.watcher.off("addDir", t), e.watcher.off("unlinkDir", t);
91
+ });
92
+ },
93
+ resolveId(e) {
94
+ return e === "virtual:@dubium/icons-registry" ? s : null;
95
+ },
96
+ load(e) {
97
+ return e === s ? c(D(), C, b, (e) => this.resolve(e)) : null;
98
+ },
99
+ async handleHotUpdate(e) {
100
+ if (M(e.file) && await O(e.file, e.read)) return N(), P((e) => this.warn(e)), [];
101
+ }
102
+ };
103
+ };
104
+ //#endregion
105
+ export { m as dubiumIcons };
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Источник, из которого разрешаются имена иконок.
3
+ *
4
+ * @remarks
5
+ * Поддерживается два варианта: локальный каталог компонентов (`local`)
6
+ * и fallback-шаблон импорта из npm-пакета (`package`).
7
+ */
8
+ export type TDubiumIconSource = {
9
+ /**
10
+ * Локальный источник иконок.
11
+ *
12
+ * @example
13
+ * `UserIcon.tsx` при suffix `Icon` даёт имя `User`.
14
+ */
15
+ type: "local";
16
+ /**
17
+ * Путь к директории с компонентами относительно Vite root.
18
+ */
19
+ path: string;
20
+ /**
21
+ * Суффикс имени файла, по которому распознаются компоненты иконок.
22
+ *
23
+ * @default "Icon"
24
+ */
25
+ suffix?: string;
26
+ } | {
27
+ /**
28
+ * Package fallback для иконок, отсутствующих в local sources.
29
+ *
30
+ * @remarks
31
+ * Допускается не более одного `package` source.
32
+ * Шаблон должен содержать `{name}`.
33
+ */
34
+ type: "package";
35
+ /**
36
+ * Шаблон пути импорта.
37
+ *
38
+ * @example
39
+ * `@dubium/icons/icons/{name}`
40
+ */
41
+ importPattern: string;
42
+ };
43
+ /**
44
+ * Настройки Vite-плагина {@link dubiumIcons}.
45
+ */
46
+ export interface DubiumIconsPluginOptions {
47
+ /**
48
+ * Дополнительные имена JSX-компонентов, которые scanner должен считать
49
+ * компонентами иконок независимо от import source.
50
+ *
51
+ * @remarks
52
+ * Обычный `Icon` из `@dubium/icons/vite` определяется автоматически,
53
+ * включая alias. Эту настройку следует использовать только для собственных
54
+ * обёрток или нестандартных компонентов.
55
+ *
56
+ * @default []
57
+ */
58
+ componentNames?: readonly string[];
59
+ /**
60
+ * Дополнительные строковые поля, которые считаются ссылками на иконки.
61
+ *
62
+ * @remarks
63
+ * Пустой массив отключает сканирование дополнительных свойств.
64
+ *
65
+ * @default []
66
+ */
67
+ propertyNames?: readonly string[];
68
+ /**
69
+ * Директории проекта, которые нужно сканировать.
70
+ *
71
+ * @remarks
72
+ * Пустой массив полностью отключает сканирование директорий.
73
+ *
74
+ * @default ["src"]
75
+ */
76
+ scan?: readonly string[];
77
+ /**
78
+ * Источники иконок.
79
+ *
80
+ * @remarks
81
+ * Можно указать несколько `local` sources; они проверяются сверху вниз.
82
+ * Дополнительно можно указать максимум один `package` source,
83
+ * который используется как fallback.
84
+ *
85
+ * Пустой массив означает, что автоматические источники
86
+ * иконок использоваться не будут.
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * dubiumIcons({
91
+ * sources: [
92
+ * { type: "local", path: "src/icons" },
93
+ * { type: "package", importPattern: "@dubium/icons/icons/{name}" },
94
+ * ],
95
+ * })
96
+ * ```
97
+ */
98
+ sources?: readonly TDubiumIconSource[];
99
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Публичный API модуля сканера исходников.
3
+ *
4
+ * @remarks
5
+ * Экспортирует {@link scanSource} — функцию, которая собирает статические
6
+ * имена иконок из JSX-тегов, импортов `Icon` и строковых свойств.
7
+ */
8
+ export { scanSource } from "./scanner.source.js";
9
+ export type { ScanResult } from "./scanner.types.js";
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Определяет локальные имена компонента `Icon`, реально импортированного
3
+ * из `@dubium/icons/vite`.
4
+ */
5
+ export declare const findImportedIconComponentNames: (source: string) => string[];
@@ -0,0 +1,36 @@
1
+ import { REGEX as e } from "./scanner.regex.js";
2
+ import { isIdentifierCharacter as t, walkCode as n } from "./scanner.lexical.js";
3
+ //#region src/icons/vite/plugin/scanner/scanner.imports.ts
4
+ var r = "@dubium/icons/vite", i = (e, n, r) => {
5
+ if (!e.startsWith(r, n)) return !1;
6
+ let i = e[n - 1], a = e[n + r.length];
7
+ return !t(i) && !t(a);
8
+ }, a = (t) => t.replace(e.COMMENTS.MULTILINE, " ").replace(e.COMMENTS.SINGLE_LINE, " "), o = (t, n) => {
9
+ let i = t.slice(n), o = e.IMPORT.STATEMENT.exec(i);
10
+ if (!o) return null;
11
+ let [s, c, , l] = o;
12
+ if (l !== r) return null;
13
+ let u = [], d = a(c).split(",");
14
+ for (let t of d) {
15
+ let n = t.trim();
16
+ if (!n || n.startsWith("type ")) continue;
17
+ let r = e.IMPORT.ICON_SPECIFIER.exec(n);
18
+ r && u.push(r[1] ?? "Icon");
19
+ }
20
+ return {
21
+ componentNames: u,
22
+ endIndex: n + o[0].length
23
+ };
24
+ }, s = (e) => {
25
+ let t = /* @__PURE__ */ new Set();
26
+ return n(e, ({ index: n, skipTo: r }) => {
27
+ if (!i(e, n, "import")) return;
28
+ let a = o(e, n);
29
+ if (a) {
30
+ for (let e of a.componentNames) t.add(e);
31
+ r(a.endIndex);
32
+ }
33
+ }), [...t];
34
+ };
35
+ //#endregion
36
+ export { s as findImportedIconComponentNames };
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Находит позиции всех JSX-тегов компонентов иконок в исходном тексте.
3
+ *
4
+ * @remarks
5
+ * Общий lexical walker игнорирует комментарии, строки, template literals
6
+ * и RegExp literals, поэтому ложные `<Icon>` внутри них не учитываются.
7
+ */
8
+ export declare const findComponentTagStarts: (source: string, componentNames: readonly string[]) => number[];
9
+ /**
10
+ * Находит индекс закрывающей угловой скобки открывающего JSX-тега.
11
+ *
12
+ * @remarks
13
+ * Поиск учитывает содержимое строк и шаблонных строк, а также глубину вложенности
14
+ * фигурных скобок `{}` JSX-выражений.
15
+ */
16
+ export declare const findTagEnd: (source: string, startIndex: number) => number;
17
+ /**
18
+ * Извлекает статическое значение атрибута `name` из открывающего JSX-тега.
19
+ */
20
+ export declare const parseStaticName: (openingTag: string) => string | null;
21
+ /**
22
+ * Проверяет, присутствует ли у открывающего JSX-тега атрибут `name`.
23
+ */
24
+ export declare const hasNameAttribute: (openingTag: string) => boolean;