@sigx/lynx-plugin 0.4.1 → 0.4.2

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/dist/icons.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * 1. Loads `signalx.config.ts` and reads the `iconSets: [...]` field.
7
7
  * 2. Statically scans every `.tsx` / `.jsx` / `.ts` / `.js` file under the
8
- * project root for `<Icon set="…" name="…" />` usages.
8
+ * project root for icon usages (see `scanContent` for the exact patterns).
9
9
  * 3. Dynamically imports each adapter package (e.g. `@sigx/lynx-icons-fa-free`)
10
10
  * and resolves the used glyphs to `{ codepoint, svg }` records.
11
11
  * 4. Writes three generated files into `node_modules/.cache/sigx-lynx-icons/`
@@ -18,13 +18,33 @@
18
18
  *
19
19
  * The scanner is a one-shot regex pass at plugin start — adding a new icon
20
20
  * during `pnpm dev` requires a dev-server restart in v1. A real SWC-AST
21
- * Rspack loader is the planned upgrade.
21
+ * Rspack loader is the planned upgrade and would obviate the regex
22
+ * patterns by inspecting the JSX tree directly.
23
+ *
24
+ * **Patterns the scanner picks up (regex-based; not exhaustive):**
25
+ * - `<Icon set="X" name="Y" />` — both attribute orders
26
+ * - `<FaSolidIcon name="Y" />` / `<FaRegularIcon name="Y" />`
27
+ * / `<FaBrandIcon name="Y" />` / `<LucideIcon name="Y" />` — pinned
28
+ * components whose set id is hardcoded in their implementations. The
29
+ * set id mapping is in `PINNED_COMPONENTS` below.
30
+ * - `{ set: 'X', name: 'Y' }` — `IconSpec` object literals anywhere
31
+ * (prop value, const declaration, function argument). Both key orders.
32
+ *
33
+ * **What still needs `include: [...]` in signalx.config.ts:**
34
+ * - Dynamic names: `<Icon set="fas" name={someVar} />` or
35
+ * `<FaSolidIcon name={someVar} />` — the scanner only matches literal
36
+ * string attributes. JSON-driven UIs and runtime-computed icon names
37
+ * need explicit force-includes (or `include: ['*']` for the whole catalog).
38
+ * - User-defined pinned components — only the four built-in adapter
39
+ * pinned components are known to the scanner. A consumer who writes
40
+ * their own `<MyIcon name="…">` wrapper needs `include`.
41
+ * - Spread props: `<Icon {...spec} />`. Niche; use `include` if needed.
22
42
  */
23
43
  import type { RsbuildPluginAPI } from '@rsbuild/core';
24
44
  /**
25
- * Extract `<Icon set="…" name="…" />` (and the name-first variant) usages
26
- * from a single source string. Exported for unit testing — the prod path
27
- * calls this once per file from {@link scanProject}.
45
+ * Extract icon usages from a single source string. See the file-level
46
+ * JSDoc for the complete pattern list. Exported for unit testing — the
47
+ * prod path calls this once per file from {@link scanProject}.
28
48
  */
29
49
  export declare function scanContent(content: string): Array<{
30
50
  set: string;
package/dist/icons.js CHANGED
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * 1. Loads `signalx.config.ts` and reads the `iconSets: [...]` field.
7
7
  * 2. Statically scans every `.tsx` / `.jsx` / `.ts` / `.js` file under the
8
- * project root for `<Icon set="…" name="…" />` usages.
8
+ * project root for icon usages (see `scanContent` for the exact patterns).
9
9
  * 3. Dynamically imports each adapter package (e.g. `@sigx/lynx-icons-fa-free`)
10
10
  * and resolves the used glyphs to `{ codepoint, svg }` records.
11
11
  * 4. Writes three generated files into `node_modules/.cache/sigx-lynx-icons/`
@@ -18,7 +18,27 @@
18
18
  *
19
19
  * The scanner is a one-shot regex pass at plugin start — adding a new icon
20
20
  * during `pnpm dev` requires a dev-server restart in v1. A real SWC-AST
21
- * Rspack loader is the planned upgrade.
21
+ * Rspack loader is the planned upgrade and would obviate the regex
22
+ * patterns by inspecting the JSX tree directly.
23
+ *
24
+ * **Patterns the scanner picks up (regex-based; not exhaustive):**
25
+ * - `<Icon set="X" name="Y" />` — both attribute orders
26
+ * - `<FaSolidIcon name="Y" />` / `<FaRegularIcon name="Y" />`
27
+ * / `<FaBrandIcon name="Y" />` / `<LucideIcon name="Y" />` — pinned
28
+ * components whose set id is hardcoded in their implementations. The
29
+ * set id mapping is in `PINNED_COMPONENTS` below.
30
+ * - `{ set: 'X', name: 'Y' }` — `IconSpec` object literals anywhere
31
+ * (prop value, const declaration, function argument). Both key orders.
32
+ *
33
+ * **What still needs `include: [...]` in signalx.config.ts:**
34
+ * - Dynamic names: `<Icon set="fas" name={someVar} />` or
35
+ * `<FaSolidIcon name={someVar} />` — the scanner only matches literal
36
+ * string attributes. JSON-driven UIs and runtime-computed icon names
37
+ * need explicit force-includes (or `include: ['*']` for the whole catalog).
38
+ * - User-defined pinned components — only the four built-in adapter
39
+ * pinned components are known to the scanner. A consumer who writes
40
+ * their own `<MyIcon name="…">` wrapper needs `include`.
41
+ * - Spread props: `<Icon {...spec} />`. Niche; use `include` if needed.
22
42
  */
23
43
  import { createRequire } from 'node:module';
24
44
  import { promises as fs, existsSync } from 'node:fs';
@@ -26,6 +46,39 @@ import { join } from 'node:path';
26
46
  import { pathToFileURL } from 'node:url';
27
47
  const SCAN_REGEX_SET_FIRST = /<Icon\s+[^>]*?\bset\s*=\s*["']([\w-]+)["'][^>]*?\bname\s*=\s*["']([\w-]+)["']/g;
28
48
  const SCAN_REGEX_NAME_FIRST = /<Icon\s+[^>]*?\bname\s*=\s*["']([\w-]+)["'][^>]*?\bset\s*=\s*["']([\w-]+)["']/g;
49
+ /**
50
+ * Known pinned per-set components exported by the workspace's adapter
51
+ * packages — `@sigx/lynx-icons-fa-free/components` and
52
+ * `@sigx/lynx-icons-lucide/components`. Each hardcodes its `set` id to
53
+ * the conventional value documented in the adapter's README; the
54
+ * scanner mirrors that mapping so `<FaSolidIcon name="user" />` is
55
+ * recognized as `set="fas", name="user"`.
56
+ *
57
+ * Consumers using non-conventional set ids in their config fall back
58
+ * to generic `<Icon>` (the pinned component wouldn't find its set at
59
+ * runtime either). New adapter packages adding pinned components add
60
+ * their entries here; the eventual SWC-AST loader replaces this with
61
+ * a per-package manifest.
62
+ */
63
+ const PINNED_COMPONENTS = {
64
+ FaSolidIcon: 'fas',
65
+ FaRegularIcon: 'far',
66
+ FaBrandIcon: 'fab',
67
+ LucideIcon: 'lucide',
68
+ };
69
+ const PINNED_COMPONENT_NAMES = Object.keys(PINNED_COMPONENTS).join('|');
70
+ const SCAN_REGEX_PINNED = new RegExp(`<(${PINNED_COMPONENT_NAMES})\\s+[^>]*?\\bname\\s*=\\s*["']([\\w-]+)["']`, 'g');
71
+ /**
72
+ * `IconSpec` object literal matchers — `{ set: 'X', name: 'Y' }` in
73
+ * either key order. Used for `<Tabs.Screen icon={{…}}>`, `<NavHeader
74
+ * backIcon={{…}}>`, `const spec = {…}` const declarations, function
75
+ * arguments, etc. Word-boundary anchored on the *first* key to avoid
76
+ * matching mid-identifier (e.g. `someset:`). False positives — any
77
+ * code object with both `set` and `name` string-valued keys — are
78
+ * harmless: the extra glyph just ships in the bundle.
79
+ */
80
+ const SCAN_REGEX_SPEC_SET_FIRST = /\bset\s*:\s*["']([\w-]+)["']\s*,\s*name\s*:\s*["']([\w-]+)["']/g;
81
+ const SCAN_REGEX_SPEC_NAME_FIRST = /\bname\s*:\s*["']([\w-]+)["']\s*,\s*set\s*:\s*["']([\w-]+)["']/g;
29
82
  /** Directories to skip when walking the project. */
30
83
  const SKIP_DIRS = new Set(['node_modules', 'dist', 'ios', 'android', 'Pods', '.git', '.cache', '.rspeedy']);
31
84
  /** File extensions worth scanning. */
@@ -64,13 +117,23 @@ function addUsage(used, set, name) {
64
117
  bucket.add(name);
65
118
  }
66
119
  /**
67
- * Extract `<Icon set="…" name="…" />` (and the name-first variant) usages
68
- * from a single source string. Exported for unit testing — the prod path
69
- * calls this once per file from {@link scanProject}.
120
+ * Extract icon usages from a single source string. See the file-level
121
+ * JSDoc for the complete pattern list. Exported for unit testing — the
122
+ * prod path calls this once per file from {@link scanProject}.
70
123
  */
71
124
  export function scanContent(content) {
72
- if (!content.includes('<Icon'))
125
+ // Fast-path skip: a file with none of these markers can't possibly
126
+ // contain an icon usage we'd match. `set:` covers both attribute and
127
+ // object-literal forms; the pinned-component prefixes are listed for
128
+ // the JSX form.
129
+ if (!content.includes('<Icon')
130
+ && !content.includes('<FaSolidIcon')
131
+ && !content.includes('<FaRegularIcon')
132
+ && !content.includes('<FaBrandIcon')
133
+ && !content.includes('<LucideIcon')
134
+ && !content.includes('set:')) {
73
135
  return [];
136
+ }
74
137
  const seen = new Set();
75
138
  const out = [];
76
139
  const push = (set, name) => {
@@ -80,10 +143,22 @@ export function scanContent(content) {
80
143
  seen.add(key);
81
144
  out.push({ set, name });
82
145
  };
146
+ // <Icon set="X" name="Y" /> — either attribute order.
83
147
  for (const m of content.matchAll(SCAN_REGEX_SET_FIRST))
84
148
  push(m[1], m[2]);
85
149
  for (const m of content.matchAll(SCAN_REGEX_NAME_FIRST))
86
150
  push(m[2], m[1]);
151
+ // <FaSolidIcon name="Y" /> etc. — set id resolved from PINNED_COMPONENTS.
152
+ for (const m of content.matchAll(SCAN_REGEX_PINNED)) {
153
+ const set = PINNED_COMPONENTS[m[1]];
154
+ if (set)
155
+ push(set, m[2]);
156
+ }
157
+ // { set: 'X', name: 'Y' } — IconSpec object literal, either key order.
158
+ for (const m of content.matchAll(SCAN_REGEX_SPEC_SET_FIRST))
159
+ push(m[1], m[2]);
160
+ for (const m of content.matchAll(SCAN_REGEX_SPEC_NAME_FIRST))
161
+ push(m[2], m[1]);
87
162
  return out;
88
163
  }
89
164
  async function scanProject(cwd) {
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sigx/lynx-plugin",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Rspack/Rspeedy plugin for SignalX Lynx dual-thread rendering",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,8 +34,8 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@lynx-js/react": "^0.121.0",
37
- "ws": "^8.18.3",
38
- "@sigx/lynx-runtime-internal": "0.4.1"
37
+ "ws": "^8.20.1",
38
+ "@sigx/lynx-runtime-internal": "0.4.2"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@rspack/core": ">=1.0.0",
@@ -44,8 +44,8 @@
44
44
  "@lynx-js/template-webpack-plugin": ">=0.1.0",
45
45
  "@lynx-js/runtime-wrapper-webpack-plugin": ">=0.1.0",
46
46
  "@lynx-js/webpack-dev-transport": ">=0.1.0",
47
- "@sigx/lynx-cli": "^0.4.1",
48
- "@sigx/lynx-icons": "^0.4.1"
47
+ "@sigx/lynx-cli": "^0.4.2",
48
+ "@sigx/lynx-icons": "^0.4.2"
49
49
  },
50
50
  "peerDependenciesMeta": {
51
51
  "@rspack/core": {
@@ -72,12 +72,12 @@
72
72
  },
73
73
  "devDependencies": {
74
74
  "@rsbuild/core": "^1.7.5",
75
- "@types/node": "^25.7.0",
75
+ "@types/node": "^25.9.1",
76
76
  "@types/ws": "^8.5.13",
77
- "@typescript/native-preview": "7.0.0-dev.20260511.1",
77
+ "@typescript/native-preview": "7.0.0-dev.20260521.1",
78
78
  "typescript": "^6.0.3",
79
- "@sigx/lynx-icons": "0.4.1",
80
- "@sigx/lynx-cli": "0.4.1"
79
+ "@sigx/lynx-cli": "0.4.2",
80
+ "@sigx/lynx-icons": "0.4.2"
81
81
  },
82
82
  "publishConfig": {
83
83
  "access": "public"