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.
- package/package.json +22 -0
- 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();
|