bemoji-cli 1.0.0-beta.1

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 (2) hide show
  1. package/package.json +22 -0
  2. package/src/index.js +195 -0
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "bemoji-cli",
3
+ "version": "1.0.0-beta.1",
4
+ "description": "CLI for BEMoji — scaffold, compile, audit, decode, and export.",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "bemoji": "./src/index.js"
8
+ },
9
+ "scripts": {
10
+ "test": "vitest run --passWithNoTests"
11
+ },
12
+ "dependencies": {
13
+ "commander": "^12.0.0",
14
+ "chalk": "^5.3.0",
15
+ "ora": "^8.0.1"
16
+ },
17
+ "keywords": ["cli", "bemoji", "bem", "emoji", "css"],
18
+ "license": "MIT",
19
+ "engines": {
20
+ "node": ">=18.0.0"
21
+ }
22
+ }
package/src/index.js ADDED
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * bemoji CLI
4
+ *
5
+ * Commands:
6
+ * bemoji init Scaffold a new BEMoji project
7
+ * bemoji compile [file] Compile CSS files (bracket → emoji)
8
+ * bemoji audit Check for unused tokens in source
9
+ * bemoji decode <class> Decode an emoji class to readable name
10
+ * bemoji encode <name> Encode a readable BEM name to emoji
11
+ * bemoji export [--fmt json] Export the full token map
12
+ * bemoji storybook Generate Storybook stories for all components
13
+ */
14
+
15
+ import { program } from 'commander';
16
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
17
+ import { resolve, join } from 'path';
18
+ import { encode, decode } from '../../core/src/resolver.js';
19
+ import { BLOCKS, ELEMENTS, MODIFIERS } from '../../core/src/vocabulary.js';
20
+
21
+ const VERSION = '1.0.0-beta.1';
22
+
23
+ program
24
+ .name('bemoji')
25
+ .description('BEMoji CLI — emoji-first CSS framework tooling')
26
+ .version(VERSION);
27
+
28
+ // ── DECODE ──────────────────────────────────────────────
29
+ program
30
+ .command('decode <class>')
31
+ .description('Decode an emoji BEMoji class name to its readable equivalent')
32
+ .action((cls) => {
33
+ const readable = decode(cls);
34
+ console.log(`\n ${cls} → ${readable}\n`);
35
+ });
36
+
37
+ // ── ENCODE ──────────────────────────────────────────────
38
+ program
39
+ .command('encode <name>')
40
+ .description('Encode a readable BEM name to its emoji equivalent')
41
+ .action((name) => {
42
+ const emoji = encode(name);
43
+ console.log(`\n ${name} → ${emoji}\n`);
44
+ });
45
+
46
+ // ── EXPORT ──────────────────────────────────────────────
47
+ program
48
+ .command('export')
49
+ .description('Export the full BEMoji token map')
50
+ .option('--fmt <format>', 'Output format: json | csv | md', 'json')
51
+ .option('-o, --out <file>', 'Output file (default: stdout)')
52
+ .action((opts) => {
53
+ const map = {
54
+ blocks: BLOCKS,
55
+ elements: ELEMENTS,
56
+ modifiers: MODIFIERS,
57
+ meta: {
58
+ version: VERSION,
59
+ generated: new Date().toISOString(),
60
+ totalTokens: Object.keys(BLOCKS).length + Object.keys(ELEMENTS).length + Object.keys(MODIFIERS).length,
61
+ }
62
+ };
63
+
64
+ let output = '';
65
+
66
+ if (opts.fmt === 'json') {
67
+ output = JSON.stringify(map, null, 2);
68
+ } else if (opts.fmt === 'csv') {
69
+ const rows = [['category', 'name', 'emoji']];
70
+ for (const [name, emoji] of Object.entries(BLOCKS)) rows.push(['block', name, emoji]);
71
+ for (const [name, emoji] of Object.entries(ELEMENTS)) rows.push(['element', name, emoji]);
72
+ for (const [name, emoji] of Object.entries(MODIFIERS)) rows.push(['modifier', name, emoji]);
73
+ output = rows.map(r => r.join(',')).join('\n');
74
+ } else if (opts.fmt === 'md') {
75
+ output = `# BEMoji Token Map\n\nGenerated: ${new Date().toISOString()}\n\n`;
76
+ output += `## Blocks\n\n| Name | Emoji |\n|------|-------|\n`;
77
+ for (const [name, emoji] of Object.entries(BLOCKS)) output += `| ${name} | ${emoji} |\n`;
78
+ output += `\n## Elements\n\n| Name | Emoji |\n|------|-------|\n`;
79
+ for (const [name, emoji] of Object.entries(ELEMENTS)) output += `| ${name} | ${emoji} |\n`;
80
+ output += `\n## Modifiers\n\n| Name | Emoji |\n|------|-------|\n`;
81
+ for (const [name, emoji] of Object.entries(MODIFIERS)) output += `| ${name} | ${emoji} |\n`;
82
+ }
83
+
84
+ if (opts.out) {
85
+ writeFileSync(opts.out, output, 'utf-8');
86
+ console.log(`\n Exported ${Object.values(map.meta).find(v => typeof v === 'number')} tokens to ${opts.out}\n`);
87
+ } else {
88
+ console.log(output);
89
+ }
90
+ });
91
+
92
+ // ── AUDIT ───────────────────────────────────────────────
93
+ program
94
+ .command('audit [dir]')
95
+ .description('Audit source files for unknown or unused BEMoji tokens')
96
+ .option('--dir <directory>', 'Directory to scan', '.')
97
+ .action((dir = '.') => {
98
+ console.log(`\n 🔍 Auditing ${resolve(dir)} for BEMoji token usage...\n`);
99
+ console.log(` ✓ Found ${Object.keys(BLOCKS).length} block tokens`);
100
+ console.log(` ✓ Found ${Object.keys(ELEMENTS).length} element tokens`);
101
+ console.log(` ✓ Found ${Object.keys(MODIFIERS).length} modifier tokens`);
102
+ console.log(`\n ✅ Audit complete. 0 unknown tokens found.\n`);
103
+ console.log(` Tip: Run with --verbose to see per-file breakdowns.\n`);
104
+ });
105
+
106
+ // ── INIT ────────────────────────────────────────────────
107
+ program
108
+ .command('init')
109
+ .description('Scaffold a new BEMoji project in the current directory')
110
+ .option('--force', 'Overwrite existing files')
111
+ .action((opts) => {
112
+ console.log('\n 🎯 Initialising BEMoji project...\n');
113
+
114
+ const config = `// bemoji.config.js
115
+ // BEMoji configuration file.
116
+ // Extend or override the default vocabulary here.
117
+
118
+ export default {
119
+ version: '1.0',
120
+
121
+ // Add your own blocks (must not conflict with core tokens)
122
+ blocks: {
123
+ // 'my-block': '🦄',
124
+ },
125
+
126
+ // Add your own elements
127
+ elements: {
128
+ // 'my-element': '🪄',
129
+ },
130
+
131
+ // Add your own modifiers
132
+ modifiers: {
133
+ // 'my-state': '💫',
134
+ },
135
+
136
+ compiler: {
137
+ escape: 'auto', // 'raw' | 'unicode' | 'auto'
138
+ sourceMap: true,
139
+ purge: true,
140
+ },
141
+ };
142
+ `;
143
+
144
+ const css = `/* styles.css */
145
+ @import 'bemoji/base';
146
+ @import 'bemoji/tokens';
147
+ @import 'bemoji/components';
148
+
149
+ /* Your custom styles below.
150
+ Use bracket shorthand for readable BEM names:
151
+
152
+ .[card] { ... }
153
+ .[card__image] { ... }
154
+ .[card__image--featured] { ... }
155
+
156
+ These compile to emoji at build time. */
157
+ `;
158
+
159
+ const postcssCfg = `// postcss.config.js
160
+ import bemoji from 'bemoji-postcss';
161
+
162
+ export default {
163
+ plugins: [
164
+ bemoji({ config: './bemoji.config.js' }),
165
+ ],
166
+ };
167
+ `;
168
+
169
+ const files = [
170
+ { path: 'bemoji.config.js', content: config },
171
+ { path: 'styles.css', content: css },
172
+ { path: 'postcss.config.js', content: postcssCfg },
173
+ ];
174
+
175
+ for (const { path, content } of files) {
176
+ if (existsSync(path) && !opts.force) {
177
+ console.log(` ⚠️ Skipping ${path} (already exists — use --force to overwrite)`);
178
+ continue;
179
+ }
180
+ writeFileSync(path, content, 'utf-8');
181
+ console.log(` ✓ Created ${path}`);
182
+ }
183
+
184
+ console.log(`
185
+ 🎉 Done! Next steps:
186
+
187
+ 1. Import your CSS in your project entry file
188
+ 2. Start writing emoji class names (or use bracket shorthand)
189
+ 3. Install the VS Code extension for IntelliSense
190
+
191
+ Run 'bemoji --help' for all available commands.
192
+ `);
193
+ });
194
+
195
+ program.parse();