@shaquillehinds/react-native-svg-icons 0.1.0 → 0.1.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/README.md +347 -491
- package/bin/find-icon.js +248 -0
- package/bin/install-rules.js +146 -0
- package/package.json +7 -1
- package/rules/AGENT_RULES.md +353 -0
package/bin/find-icon.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const PACKAGE_NAME = '@shaquillehinds/react-native-svg-icons';
|
|
8
|
+
|
|
9
|
+
const CANDIDATE_TYPE_PATHS = [
|
|
10
|
+
path.join(__dirname, '..', 'src', 'svgs', 'types.ts'),
|
|
11
|
+
path.join(__dirname, '..', 'lib', 'typescript', 'svgs', 'types.d.ts'),
|
|
12
|
+
path.join(__dirname, '..', 'lib', 'commonjs', 'svgs', 'types.d.ts'),
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const HELP = `
|
|
16
|
+
rnsi-icons — look up real icon names from ${PACKAGE_NAME}
|
|
17
|
+
|
|
18
|
+
Names in this set are irregular and some are misspelled. Search here rather
|
|
19
|
+
than guessing.
|
|
20
|
+
|
|
21
|
+
Usage
|
|
22
|
+
npx rnsi-icons <query> [--type filled|outline] [--exact] [--limit N]
|
|
23
|
+
npx rnsi-icons --list [--type filled|outline]
|
|
24
|
+
|
|
25
|
+
Options
|
|
26
|
+
--type <t> restrict to one variant (default: both)
|
|
27
|
+
--exact exact, case-sensitive match only
|
|
28
|
+
--limit N cap results (default 40, 0 for no cap)
|
|
29
|
+
--json machine-readable output
|
|
30
|
+
--shared with --list, only names present in both variants
|
|
31
|
+
--diff show names that exist in one variant but not the other
|
|
32
|
+
--help show this message
|
|
33
|
+
|
|
34
|
+
Examples
|
|
35
|
+
npx rnsi-icons search
|
|
36
|
+
npx rnsi-icons arrow --type outline --limit 100
|
|
37
|
+
npx rnsi-icons Trash --exact
|
|
38
|
+
npx rnsi-icons --diff
|
|
39
|
+
npx rnsi-icons --list --shared > icons.txt
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
function loadSource() {
|
|
43
|
+
for (const p of CANDIDATE_TYPE_PATHS) {
|
|
44
|
+
if (fs.existsSync(p)) return fs.readFileSync(p, 'utf8');
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function parseUnion(source, typeName) {
|
|
50
|
+
const start = source.indexOf('export type ' + typeName);
|
|
51
|
+
if (start === -1) return [];
|
|
52
|
+
const end = source.indexOf(';', start);
|
|
53
|
+
const block = source.slice(start, end === -1 ? undefined : end);
|
|
54
|
+
const names = [];
|
|
55
|
+
const re = /'([^']+)'/g;
|
|
56
|
+
let m;
|
|
57
|
+
while ((m = re.exec(block)) !== null) names.push(m[1]);
|
|
58
|
+
return names;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function flag(argv, name) {
|
|
62
|
+
return argv.includes('--' + name);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function value(argv, name, fallback) {
|
|
66
|
+
const i = argv.indexOf('--' + name);
|
|
67
|
+
if (i === -1 || i === argv.length - 1) return fallback;
|
|
68
|
+
return argv[i + 1];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function main() {
|
|
72
|
+
const argv = process.argv.slice(2);
|
|
73
|
+
|
|
74
|
+
if (flag(argv, 'help') || argv.includes('-h') || argv.length === 0) {
|
|
75
|
+
process.stdout.write(HELP);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const source = loadSource();
|
|
80
|
+
if (!source) {
|
|
81
|
+
console.error(
|
|
82
|
+
`rnsi-icons: could not locate the icon type definitions.\n` +
|
|
83
|
+
`Is ${PACKAGE_NAME} installed?`
|
|
84
|
+
);
|
|
85
|
+
process.exitCode = 1;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const filled = parseUnion(source, 'FilledIconName');
|
|
90
|
+
const outline = parseUnion(source, 'OutlineIconName');
|
|
91
|
+
|
|
92
|
+
if (!filled.length && !outline.length) {
|
|
93
|
+
console.error(
|
|
94
|
+
$lf(93),
|
|
95
|
+
'rnsi-icons: parsed zero icon names — the types file may have changed shape.'
|
|
96
|
+
);
|
|
97
|
+
process.exitCode = 1;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const asJson = flag(argv, 'json');
|
|
102
|
+
const exact = flag(argv, 'exact');
|
|
103
|
+
const type = value(argv, 'type', null);
|
|
104
|
+
const limitRaw = value(argv, 'limit', '40');
|
|
105
|
+
const limit = Number(limitRaw) === 0 ? Infinity : Number(limitRaw) || 40;
|
|
106
|
+
|
|
107
|
+
if (type && type !== 'filled' && type !== 'outline') {
|
|
108
|
+
console.error(
|
|
109
|
+
$lf(105),
|
|
110
|
+
`rnsi-icons: --type must be "filled" or "outline", got "${type}".`
|
|
111
|
+
);
|
|
112
|
+
process.exitCode = 1;
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const filledSet = new Set(filled);
|
|
117
|
+
const outlineSet = new Set(outline);
|
|
118
|
+
|
|
119
|
+
if (flag(argv, 'diff')) {
|
|
120
|
+
const onlyFilled = filled.filter((n) => !outlineSet.has(n));
|
|
121
|
+
const onlyOutline = outline.filter((n) => !filledSet.has(n));
|
|
122
|
+
if (asJson) {
|
|
123
|
+
process.stdout.write(
|
|
124
|
+
JSON.stringify({ onlyFilled, onlyOutline }, null, 2) + '\n'
|
|
125
|
+
);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
console.log(
|
|
129
|
+
$lf(120),
|
|
130
|
+
`filled: ${filled.length} outline: ${outline.length}`
|
|
131
|
+
);
|
|
132
|
+
console.log(
|
|
133
|
+
$lf(121),
|
|
134
|
+
`shared: ${filled.filter((n) => outlineSet.has(n)).length}\n`
|
|
135
|
+
);
|
|
136
|
+
console.log(
|
|
137
|
+
$lf(122),
|
|
138
|
+
'filled only: ' + (onlyFilled.join(', ') || '(none)')
|
|
139
|
+
);
|
|
140
|
+
console.log(
|
|
141
|
+
$lf(123),
|
|
142
|
+
'outline only: ' + (onlyOutline.join(', ') || '(none)')
|
|
143
|
+
);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let pool;
|
|
148
|
+
if (type === 'filled') pool = filled.map((n) => [n, 'filled']);
|
|
149
|
+
else if (type === 'outline') pool = outline.map((n) => [n, 'outline']);
|
|
150
|
+
else {
|
|
151
|
+
const all = new Set([...filled, ...outline]);
|
|
152
|
+
pool = [...all].sort().map((n) => {
|
|
153
|
+
const inF = filledSet.has(n);
|
|
154
|
+
const inO = outlineSet.has(n);
|
|
155
|
+
return [n, inF && inO ? 'both' : inF ? 'filled' : 'outline'];
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (flag(argv, 'list')) {
|
|
160
|
+
let rows = pool;
|
|
161
|
+
if (flag(argv, 'shared')) rows = rows.filter(([, v]) => v === 'both');
|
|
162
|
+
if (asJson) {
|
|
163
|
+
process.stdout.write(
|
|
164
|
+
JSON.stringify(
|
|
165
|
+
rows.map(([n, v]) => ({ name: n, variant: v })),
|
|
166
|
+
null,
|
|
167
|
+
2
|
|
168
|
+
) + '\n'
|
|
169
|
+
);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
rows.forEach(([n]) => console.log($lf(146), n));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const VALUE_FLAGS = new Set(['--type', '--limit']);
|
|
177
|
+
let query = null;
|
|
178
|
+
for (let i = 0; i < argv.length; i++) {
|
|
179
|
+
const arg = argv[i];
|
|
180
|
+
if (VALUE_FLAGS.has(arg)) {
|
|
181
|
+
i++; // skip this flag's value
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (arg.startsWith('-')) continue;
|
|
185
|
+
query = arg;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!query) {
|
|
190
|
+
console.error(
|
|
191
|
+
$lf(164),
|
|
192
|
+
'rnsi-icons: no query given. Run with --help for usage.'
|
|
193
|
+
);
|
|
194
|
+
process.exitCode = 1;
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const q = query.toLowerCase();
|
|
199
|
+
const matches = pool.filter(([n]) =>
|
|
200
|
+
exact ? n === query : n.toLowerCase().includes(q)
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
// Prefix matches first, then shortest — closest names surface at the top.
|
|
204
|
+
matches.sort((a, b) => {
|
|
205
|
+
const ap = a[0].toLowerCase().startsWith(q) ? 0 : 1;
|
|
206
|
+
const bp = b[0].toLowerCase().startsWith(q) ? 0 : 1;
|
|
207
|
+
if (ap !== bp) return ap - bp;
|
|
208
|
+
if (a[0].length !== b[0].length) return a[0].length - b[0].length;
|
|
209
|
+
return a[0].localeCompare(b[0]);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
if (asJson) {
|
|
213
|
+
process.stdout.write(
|
|
214
|
+
JSON.stringify(
|
|
215
|
+
matches.map(([n, v]) => ({ name: n, variant: v })),
|
|
216
|
+
null,
|
|
217
|
+
2
|
|
218
|
+
) + '\n'
|
|
219
|
+
);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (!matches.length) {
|
|
224
|
+
console.log($lf(191), `No icon matches "${query}".`);
|
|
225
|
+
console.log(
|
|
226
|
+
$lf(192),
|
|
227
|
+
'Try a shorter fragment, or run: npx rnsi-icons --list'
|
|
228
|
+
);
|
|
229
|
+
process.exitCode = 1;
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const shown = matches.slice(0, limit);
|
|
234
|
+
const width = Math.max(...shown.map(([n]) => n.length));
|
|
235
|
+
shown.forEach(([n, v]) => console.log($lf(199), n.padEnd(width + 2) + v));
|
|
236
|
+
|
|
237
|
+
if (matches.length > shown.length) {
|
|
238
|
+
console.log(
|
|
239
|
+
`\n… ${matches.length - shown.length} more. Use --limit 0 to show all.`
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
main();
|
|
245
|
+
function $lf(n) {
|
|
246
|
+
return '$lf|bin/find-icon.js:' + n + ' >';
|
|
247
|
+
// Automatically injected by Log Location Injector vscode extension
|
|
248
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
const PACKAGE_NAME = '@shaquillehinds/react-native-svg-icons';
|
|
8
|
+
const SLUG = 'react-native-svg-icons';
|
|
9
|
+
const SOURCE = path.join(__dirname, '..', 'rules', 'AGENT_RULES.md');
|
|
10
|
+
|
|
11
|
+
const TARGETS = {
|
|
12
|
+
agents: 'AGENTS.md',
|
|
13
|
+
cursor: path.join('.cursor', 'rules', SLUG + '.mdc'),
|
|
14
|
+
claude: path.join('.claude', 'rules', SLUG + '.md'),
|
|
15
|
+
codex: path.join('.codex', 'rules', SLUG + '.md'),
|
|
16
|
+
copilot: path.join('.github', 'instructions', SLUG + '.instructions.md'),
|
|
17
|
+
windsurf: path.join('.windsurf', 'rules', SLUG + '.md'),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const HELP = `
|
|
21
|
+
rnsi-rules — install the ${PACKAGE_NAME} agent rules
|
|
22
|
+
|
|
23
|
+
Usage
|
|
24
|
+
npx rnsi-rules [target|path] [--force] [--print]
|
|
25
|
+
|
|
26
|
+
Targets
|
|
27
|
+
(none) ./AGENTS.md
|
|
28
|
+
agents ./AGENTS.md
|
|
29
|
+
cursor ./.cursor/rules/${SLUG}.mdc (alwaysApply)
|
|
30
|
+
claude ./.claude/rules/${SLUG}.md
|
|
31
|
+
codex ./.codex/rules/${SLUG}.md
|
|
32
|
+
copilot ./.github/instructions/${SLUG}.instructions.md
|
|
33
|
+
windsurf ./.windsurf/rules/${SLUG}.md
|
|
34
|
+
<path> any path ending in .md or .mdc
|
|
35
|
+
|
|
36
|
+
Options
|
|
37
|
+
--force overwrite an existing file
|
|
38
|
+
--print write nothing, print the rules to stdout
|
|
39
|
+
--help show this message
|
|
40
|
+
|
|
41
|
+
Examples
|
|
42
|
+
npx rnsi-rules
|
|
43
|
+
npx rnsi-rules cursor
|
|
44
|
+
npx rnsi-rules docs/ai/svg-icons.md --force
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
function frontmatterFor(target) {
|
|
48
|
+
if (target === 'cursor') {
|
|
49
|
+
return [
|
|
50
|
+
'---',
|
|
51
|
+
`description: How to use ${PACKAGE_NAME} correctly`,
|
|
52
|
+
'globs:',
|
|
53
|
+
'alwaysApply: true',
|
|
54
|
+
'---',
|
|
55
|
+
'',
|
|
56
|
+
'',
|
|
57
|
+
].join('\n');
|
|
58
|
+
}
|
|
59
|
+
if (target === 'copilot') {
|
|
60
|
+
return ['---', "applyTo: '**/*.tsx,**/*.ts'", '---', '', ''].join('\n');
|
|
61
|
+
}
|
|
62
|
+
if (target === 'windsurf') {
|
|
63
|
+
return ['---', 'trigger: always_on', '---', '', ''].join('\n');
|
|
64
|
+
}
|
|
65
|
+
return '';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function resolveTarget(arg) {
|
|
69
|
+
if (!arg) return { target: 'agents', relPath: TARGETS.agents };
|
|
70
|
+
if (Object.prototype.hasOwnProperty.call(TARGETS, arg)) {
|
|
71
|
+
return { target: arg, relPath: TARGETS[arg] };
|
|
72
|
+
}
|
|
73
|
+
if (/\.mdx?$|\.mdc$/.test(arg)) {
|
|
74
|
+
return { target: 'custom', relPath: arg };
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function main() {
|
|
80
|
+
const argv = process.argv.slice(2);
|
|
81
|
+
const force = argv.includes('--force') || argv.includes('-f');
|
|
82
|
+
const print = argv.includes('--print');
|
|
83
|
+
const help = argv.includes('--help') || argv.includes('-h');
|
|
84
|
+
const positional = argv.filter((a) => !a.startsWith('-'))[0];
|
|
85
|
+
|
|
86
|
+
if (help) {
|
|
87
|
+
process.stdout.write(HELP);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let rules;
|
|
92
|
+
try {
|
|
93
|
+
rules = fs.readFileSync(SOURCE, 'utf8');
|
|
94
|
+
} catch (err) {
|
|
95
|
+
console.error(
|
|
96
|
+
`rnsi-rules: could not read the rules file at ${SOURCE}\n` +
|
|
97
|
+
`Is ${PACKAGE_NAME} installed?`
|
|
98
|
+
);
|
|
99
|
+
process.exitCode = 1;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (print) {
|
|
104
|
+
process.stdout.write(rules);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const resolved = resolveTarget(positional);
|
|
109
|
+
if (!resolved) {
|
|
110
|
+
console.error(
|
|
111
|
+
`rnsi-rules: unknown target "${positional}".\n` +
|
|
112
|
+
`Expected one of: ${Object.keys(TARGETS).join(', ')} — or a path ending in .md / .mdc.\n` +
|
|
113
|
+
`Run "npx rnsi-rules --help" for usage.`
|
|
114
|
+
);
|
|
115
|
+
process.exitCode = 1;
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const { target, relPath } = resolved;
|
|
120
|
+
const outPath = path.resolve(process.cwd(), relPath);
|
|
121
|
+
|
|
122
|
+
if (fs.existsSync(outPath) && !force) {
|
|
123
|
+
console.error(
|
|
124
|
+
`rnsi-rules: ${relPath} already exists. Re-run with --force to overwrite.`
|
|
125
|
+
);
|
|
126
|
+
process.exitCode = 1;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
131
|
+
fs.writeFileSync(outPath, frontmatterFor(target) + rules, 'utf8');
|
|
132
|
+
|
|
133
|
+
console.log($lf(139), `rnsi-rules: wrote ${relPath}`);
|
|
134
|
+
if (target === 'agents') {
|
|
135
|
+
console.log(
|
|
136
|
+
'Tip: if you already have an AGENTS.md, use a custom path instead and ' +
|
|
137
|
+
'link to it, e.g. npx rnsi-rules docs/ai/svg-icons.md'
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
main();
|
|
143
|
+
function $lf(n) {
|
|
144
|
+
return '$lf|bin/install-rules.js:' + n + ' >';
|
|
145
|
+
// Automatically injected by Log Location Injector vscode extension
|
|
146
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@shaquillehinds/react-native-svg-icons",
|
|
3
3
|
"description": "React Native SVG Icons",
|
|
4
4
|
"author": "Shaquille Hinds <shaqdulove@gmail.com> (https://github.com/shaquillehinds)",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.2",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"source": "./src/index.tsx",
|
|
8
8
|
"main": "./lib/commonjs/index.js",
|
|
@@ -22,12 +22,18 @@
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
|
+
"bin": {
|
|
26
|
+
"rnsi-rules": "./bin/install-rules.js",
|
|
27
|
+
"rnsi-icons": "./bin/find-icon.js"
|
|
28
|
+
},
|
|
25
29
|
"files": [
|
|
26
30
|
"lib",
|
|
27
31
|
"src",
|
|
28
32
|
"android",
|
|
29
33
|
"ios",
|
|
30
34
|
"cpp",
|
|
35
|
+
"rules",
|
|
36
|
+
"bin",
|
|
31
37
|
"*.podspec",
|
|
32
38
|
"react-native.config.js",
|
|
33
39
|
"!ios/build",
|