@brewedby/ds 1.0.0
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 +118 -0
- package/bin/fynd-ds.js +10 -0
- package/lib/installer.js +367 -0
- package/package.json +36 -0
- package/templates/FYND_DESIGN_SYSTEM.md +608 -0
- package/templates/fynd-tokens.css +492 -0
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# @brewedby/ds
|
|
2
|
+
|
|
3
|
+
> Fynd One Design System — CLI installer
|
|
4
|
+
|
|
5
|
+
Sets up design tokens, font declarations, icon library, and AI context (`CLAUDE.md`) in any Fynd project. Works with Astro, Next.js, Vite, React, and vanilla HTML.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
### One-time install in a project (recommended)
|
|
12
|
+
```bash
|
|
13
|
+
npx @brewedby/ds init
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Or install globally and run anywhere
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g @brewedby/ds
|
|
19
|
+
fynd-ds init
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## What it does
|
|
25
|
+
|
|
26
|
+
Runs an interactive setup that:
|
|
27
|
+
|
|
28
|
+
1. **Detects** your framework and package manager automatically
|
|
29
|
+
2. **Asks** where to place tokens, fonts, and whether to write `CLAUDE.md`
|
|
30
|
+
3. **Writes** `fynd-tokens.css` to your styles directory (with font paths patched for your project structure)
|
|
31
|
+
4. **Creates** a font directory with a `.gitkeep` placeholder for Fynd Sans files
|
|
32
|
+
5. **Writes** `CLAUDE.md` at project root (AI context for Claude / Cursor)
|
|
33
|
+
6. **Prepends** the token import to your global stylesheet
|
|
34
|
+
7. **Installs** `@fontsource/inter-display`, `@fontsource/inter`, and `lucide-react` if you want them
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## After install — the one manual step
|
|
39
|
+
|
|
40
|
+
Fynd Sans is a proprietary font and cannot be distributed via npm.
|
|
41
|
+
After running `fynd-ds init`, place the `.woff2` files in the fonts directory the installer created:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
FyndSans-Regular.woff2
|
|
45
|
+
FyndSans-Medium.woff2
|
|
46
|
+
FyndSans-SemiBold.woff2
|
|
47
|
+
FyndSans-Bold.woff2
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Obtain them from the design assets repo or contact **design@fynd.com**.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Files installed
|
|
55
|
+
|
|
56
|
+
| File | Purpose |
|
|
57
|
+
|------|---------|
|
|
58
|
+
| `{stylesDir}/fynd-tokens.css` | All CSS custom properties — tokens, spacing, radius, component tokens |
|
|
59
|
+
| `FYND_DESIGN_SYSTEM.md` | Full design system reference doc |
|
|
60
|
+
| `CLAUDE.md` | AI context file (if opted in) — Claude and Cursor read this automatically |
|
|
61
|
+
| `.fynd-ds-readme.md` | Quick-reference for the installed configuration |
|
|
62
|
+
| `{fontsDir}/.gitkeep` | Placeholder for Fynd Sans font files |
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Token reference
|
|
67
|
+
|
|
68
|
+
After install, use CSS variables directly:
|
|
69
|
+
|
|
70
|
+
```css
|
|
71
|
+
/* Typography */
|
|
72
|
+
font-family: var(--font-family--primary); /* Fynd Sans — headings */
|
|
73
|
+
font-family: var(--font-family--secondary); /* Inter Display — body */
|
|
74
|
+
font-family: var(--font-family--ui); /* Inter — buttons, nav */
|
|
75
|
+
|
|
76
|
+
/* Colors */
|
|
77
|
+
color: var(--text--title); /* #0e0e0e */
|
|
78
|
+
color: var(--text--subtext); /* #5b5c5d */
|
|
79
|
+
background: var(--blue--blue-fill); /* #f9fbff */
|
|
80
|
+
|
|
81
|
+
/* Spacing */
|
|
82
|
+
gap: var(--spacing--24); /* 24px */
|
|
83
|
+
|
|
84
|
+
/* Radius */
|
|
85
|
+
border-radius: var(--border-radius--pill); /* 250px — buttons */
|
|
86
|
+
border-radius: var(--border-radius--16); /* 16px — cards */
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Icons
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Installed automatically if you opt in
|
|
93
|
+
npm install lucide-react
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```jsx
|
|
97
|
+
import { ArrowRight, ShoppingBag } from 'lucide-react'
|
|
98
|
+
<ArrowRight size={16} strokeWidth={1.5} />
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Always use `strokeWidth={1.5}` — Fynd icon convention.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Publishing to internal registry
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Set your internal npm registry
|
|
109
|
+
npm config set @brewedby:registry https://your-registry.company.com
|
|
110
|
+
|
|
111
|
+
# Publish
|
|
112
|
+
npm publish
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Then any team member can run:
|
|
116
|
+
```bash
|
|
117
|
+
npx @brewedby/ds init
|
|
118
|
+
```
|
package/bin/fynd-ds.js
ADDED
package/lib/installer.js
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { execSync } = require('child_process');
|
|
6
|
+
const prompts = require('prompts');
|
|
7
|
+
const k = require('kleur');
|
|
8
|
+
|
|
9
|
+
const TEMPLATES_DIR = path.join(__dirname, '..', 'templates');
|
|
10
|
+
|
|
11
|
+
// ─────────────────────────────────────────────────────────────
|
|
12
|
+
// Helpers
|
|
13
|
+
// ─────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
function exists(p) {
|
|
16
|
+
return fs.existsSync(p);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function ensureDir(p) {
|
|
20
|
+
fs.mkdirSync(p, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function write(dest, content) {
|
|
24
|
+
ensureDir(path.dirname(dest));
|
|
25
|
+
fs.writeFileSync(dest, content, 'utf8');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function copy(src, dest) {
|
|
29
|
+
ensureDir(path.dirname(dest));
|
|
30
|
+
fs.copyFileSync(src, dest);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function readTemplate(name) {
|
|
34
|
+
return fs.readFileSync(path.join(TEMPLATES_DIR, name), 'utf8');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function detectPackageManager(cwd) {
|
|
38
|
+
if (exists(path.join(cwd, 'pnpm-lock.yaml'))) return 'pnpm';
|
|
39
|
+
if (exists(path.join(cwd, 'yarn.lock'))) return 'yarn';
|
|
40
|
+
if (exists(path.join(cwd, 'bun.lockb'))) return 'bun';
|
|
41
|
+
return 'npm';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function detectFramework(cwd) {
|
|
45
|
+
const pkg = path.join(cwd, 'package.json');
|
|
46
|
+
if (!exists(pkg)) return 'unknown';
|
|
47
|
+
const json = JSON.parse(fs.readFileSync(pkg, 'utf8'));
|
|
48
|
+
const deps = { ...json.dependencies, ...json.devDependencies };
|
|
49
|
+
if (deps['astro']) return 'astro';
|
|
50
|
+
if (deps['next']) return 'nextjs';
|
|
51
|
+
if (deps['vite']) return 'vite';
|
|
52
|
+
if (deps['react']) return 'react';
|
|
53
|
+
return 'vanilla';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function installDeps(packages, pm) {
|
|
57
|
+
const cmd = {
|
|
58
|
+
npm: `npm install --save-dev ${packages.join(' ')}`,
|
|
59
|
+
pnpm: `pnpm add -D ${packages.join(' ')}`,
|
|
60
|
+
yarn: `yarn add --dev ${packages.join(' ')}`,
|
|
61
|
+
bun: `bun add -d ${packages.join(' ')}`,
|
|
62
|
+
}[pm];
|
|
63
|
+
execSync(cmd, { stdio: 'inherit' });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ─────────────────────────────────────────────────────────────
|
|
67
|
+
// Framework-specific paths
|
|
68
|
+
// ─────────────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
const FRAMEWORK_CONFIG = {
|
|
71
|
+
astro: {
|
|
72
|
+
label: 'Astro',
|
|
73
|
+
tokensPath: 'src/styles/fynd-tokens.css',
|
|
74
|
+
fontsPath: 'public/fonts',
|
|
75
|
+
claudePath: 'CLAUDE.md',
|
|
76
|
+
globalsCss: 'src/styles/globals.css',
|
|
77
|
+
fontUrlBase: '/fonts',
|
|
78
|
+
},
|
|
79
|
+
nextjs: {
|
|
80
|
+
label: 'Next.js',
|
|
81
|
+
tokensPath: 'src/styles/fynd-tokens.css',
|
|
82
|
+
fontsPath: 'public/fonts',
|
|
83
|
+
claudePath: 'CLAUDE.md',
|
|
84
|
+
globalsCss: 'src/app/globals.css',
|
|
85
|
+
fontUrlBase: '/fonts',
|
|
86
|
+
},
|
|
87
|
+
vite: {
|
|
88
|
+
label: 'Vite / React',
|
|
89
|
+
tokensPath: 'src/styles/fynd-tokens.css',
|
|
90
|
+
fontsPath: 'src/assets/fonts',
|
|
91
|
+
claudePath: 'CLAUDE.md',
|
|
92
|
+
globalsCss: 'src/styles/globals.css',
|
|
93
|
+
fontUrlBase: '/assets/fonts',
|
|
94
|
+
},
|
|
95
|
+
react: {
|
|
96
|
+
label: 'React (CRA)',
|
|
97
|
+
tokensPath: 'src/styles/fynd-tokens.css',
|
|
98
|
+
fontsPath: 'public/fonts',
|
|
99
|
+
claudePath: 'CLAUDE.md',
|
|
100
|
+
globalsCss: 'src/index.css',
|
|
101
|
+
fontUrlBase: '/fonts',
|
|
102
|
+
},
|
|
103
|
+
vanilla: {
|
|
104
|
+
label: 'Vanilla HTML',
|
|
105
|
+
tokensPath: 'assets/css/fynd-tokens.css',
|
|
106
|
+
fontsPath: 'assets/fonts',
|
|
107
|
+
claudePath: 'CLAUDE.md',
|
|
108
|
+
globalsCss: null,
|
|
109
|
+
fontUrlBase: '/assets/fonts',
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// ─────────────────────────────────────────────────────────────
|
|
114
|
+
// Tokens file — patch font URL to match project's asset path
|
|
115
|
+
// ─────────────────────────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
function patchTokensFontPaths(content, fontUrlBase) {
|
|
118
|
+
return content.replace(
|
|
119
|
+
/url\('\/assets\/fonts\/(FyndSans-[^']+)'\)/g,
|
|
120
|
+
`url('${fontUrlBase}/$1')`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ─────────────────────────────────────────────────────────────
|
|
125
|
+
// Globals CSS import snippet
|
|
126
|
+
// ─────────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
function buildGlobalsSnippet(tokensPath, framework) {
|
|
129
|
+
const relativePath = tokensPath.startsWith('src/')
|
|
130
|
+
? './' + path.basename(tokensPath)
|
|
131
|
+
: '../' + tokensPath;
|
|
132
|
+
|
|
133
|
+
const googleFonts = [
|
|
134
|
+
`/* Fynd One Design System */`,
|
|
135
|
+
`@import '${relativePath}'; /* tokens + Fynd Sans @font-face */`,
|
|
136
|
+
``,
|
|
137
|
+
`/* Inter Display + Inter via @fontsource (run: npm install @fontsource/inter-display @fontsource/inter) */`,
|
|
138
|
+
`/* @import '@fontsource/inter-display/400.css'; */`,
|
|
139
|
+
`/* @import '@fontsource/inter-display/500.css'; */`,
|
|
140
|
+
`/* @import '@fontsource/inter-display/600.css'; */`,
|
|
141
|
+
`/* @import '@fontsource/inter-display/700.css'; */`,
|
|
142
|
+
`/* @import '@fontsource/inter/400.css'; */`,
|
|
143
|
+
`/* @import '@fontsource/inter/500.css'; */`,
|
|
144
|
+
`/* @import '@fontsource/inter/600.css'; */`,
|
|
145
|
+
].join('\n');
|
|
146
|
+
|
|
147
|
+
return googleFonts;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ─────────────────────────────────────────────────────────────
|
|
151
|
+
// .fynd-ds-readme.md — placed at project root, quick reference
|
|
152
|
+
// ─────────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
function buildProjectReadme(fw, config) {
|
|
155
|
+
return `# Fynd One Design System — Installed
|
|
156
|
+
|
|
157
|
+
**Framework:** ${config.label}
|
|
158
|
+
**Tokens:** \`${config.tokensPath}\`
|
|
159
|
+
**CLAUDE.md:** \`${config.claudePath}\`
|
|
160
|
+
|
|
161
|
+
## Next steps
|
|
162
|
+
|
|
163
|
+
### 1 — Add Fynd Sans font files
|
|
164
|
+
Place the following \`.woff2\` files in \`${config.fontsPath}/\`:
|
|
165
|
+
\`\`\`
|
|
166
|
+
${config.fontsPath}/FyndSans-Regular.woff2
|
|
167
|
+
${config.fontsPath}/FyndSans-Medium.woff2
|
|
168
|
+
${config.fontsPath}/FyndSans-SemiBold.woff2
|
|
169
|
+
${config.fontsPath}/FyndSans-Bold.woff2
|
|
170
|
+
\`\`\`
|
|
171
|
+
Get them from the design assets repo or contact design@fynd.com.
|
|
172
|
+
|
|
173
|
+
### 2 — Install open-source fonts
|
|
174
|
+
\`\`\`bash
|
|
175
|
+
npm install @fontsource/inter-display @fontsource/inter
|
|
176
|
+
\`\`\`
|
|
177
|
+
Then uncomment the \`@fontsource\` import lines in your global stylesheet.
|
|
178
|
+
|
|
179
|
+
### 3 — Install Lucide icons
|
|
180
|
+
\`\`\`bash
|
|
181
|
+
npm install lucide-react
|
|
182
|
+
\`\`\`
|
|
183
|
+
|
|
184
|
+
### 4 — Import tokens in your global stylesheet
|
|
185
|
+
\`\`\`css
|
|
186
|
+
@import './fynd-tokens.css'; /* always first */
|
|
187
|
+
\`\`\`
|
|
188
|
+
|
|
189
|
+
Refer to \`${config.claudePath}\` for full component patterns, token reference, and dos/don'ts.
|
|
190
|
+
`;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ─────────────────────────────────────────────────────────────
|
|
194
|
+
// Main run()
|
|
195
|
+
// ─────────────────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
async function run() {
|
|
198
|
+
const cwd = process.cwd();
|
|
199
|
+
|
|
200
|
+
console.log('');
|
|
201
|
+
console.log(k.bold().white(' Fynd One Design System') + k.dim(' — installer'));
|
|
202
|
+
console.log('');
|
|
203
|
+
|
|
204
|
+
const detectedFramework = detectFramework(cwd);
|
|
205
|
+
const detectedPm = detectPackageManager(cwd);
|
|
206
|
+
|
|
207
|
+
// ── Prompt 1: framework ──────────────────────────────────
|
|
208
|
+
const { framework } = await prompts({
|
|
209
|
+
type: 'select',
|
|
210
|
+
name: 'framework',
|
|
211
|
+
message: 'Which framework is this project using?',
|
|
212
|
+
choices: [
|
|
213
|
+
{ title: 'Astro', value: 'astro', description: detectedFramework === 'astro' ? '(detected)' : '' },
|
|
214
|
+
{ title: 'Next.js', value: 'nextjs', description: detectedFramework === 'nextjs' ? '(detected)' : '' },
|
|
215
|
+
{ title: 'Vite / React', value: 'vite', description: detectedFramework === 'vite' ? '(detected)' : '' },
|
|
216
|
+
{ title: 'React (CRA)', value: 'react', description: detectedFramework === 'react' ? '(detected)' : '' },
|
|
217
|
+
{ title: 'Vanilla HTML', value: 'vanilla', description: '' },
|
|
218
|
+
],
|
|
219
|
+
initial: ['astro','nextjs','vite','react','vanilla'].indexOf(detectedFramework) > -1
|
|
220
|
+
? ['astro','nextjs','vite','react','vanilla'].indexOf(detectedFramework)
|
|
221
|
+
: 0,
|
|
222
|
+
}, { onCancel: () => process.exit(0) });
|
|
223
|
+
|
|
224
|
+
const config = FRAMEWORK_CONFIG[framework];
|
|
225
|
+
|
|
226
|
+
// ── Prompt 2: tokens destination ────────────────────────
|
|
227
|
+
const { tokensPath } = await prompts({
|
|
228
|
+
type: 'text',
|
|
229
|
+
name: 'tokensPath',
|
|
230
|
+
message: 'Where should fynd-tokens.css be placed?',
|
|
231
|
+
initial: config.tokensPath,
|
|
232
|
+
}, { onCancel: () => process.exit(0) });
|
|
233
|
+
|
|
234
|
+
// ── Prompt 3: font files destination ────────────────────
|
|
235
|
+
const { fontsPath } = await prompts({
|
|
236
|
+
type: 'text',
|
|
237
|
+
name: 'fontsPath',
|
|
238
|
+
message: 'Where will Fynd Sans .woff2 files live?',
|
|
239
|
+
initial: config.fontsPath,
|
|
240
|
+
hint: 'You\'ll drop the files here after install',
|
|
241
|
+
}, { onCancel: () => process.exit(0) });
|
|
242
|
+
|
|
243
|
+
// ── Prompt 4: CLAUDE.md ──────────────────────────────────
|
|
244
|
+
const { writeClaude } = await prompts({
|
|
245
|
+
type: 'confirm',
|
|
246
|
+
name: 'writeClaude',
|
|
247
|
+
message: 'Write CLAUDE.md at project root? (recommended for Claude / Cursor)',
|
|
248
|
+
initial: true,
|
|
249
|
+
}, { onCancel: () => process.exit(0) });
|
|
250
|
+
|
|
251
|
+
// ── Prompt 5: install @fontsource packages ───────────────
|
|
252
|
+
const { installFontsource } = await prompts({
|
|
253
|
+
type: 'confirm',
|
|
254
|
+
name: 'installFontsource',
|
|
255
|
+
message: `Install @fontsource/inter-display + @fontsource/inter via ${detectedPm}?`,
|
|
256
|
+
initial: true,
|
|
257
|
+
}, { onCancel: () => process.exit(0) });
|
|
258
|
+
|
|
259
|
+
// ── Prompt 6: install lucide-react ──────────────────────
|
|
260
|
+
const { installLucide } = await prompts({
|
|
261
|
+
type: 'confirm',
|
|
262
|
+
name: 'installLucide',
|
|
263
|
+
message: `Install lucide-react (icon library) via ${detectedPm}?`,
|
|
264
|
+
initial: framework !== 'vanilla',
|
|
265
|
+
}, { onCancel: () => process.exit(0) });
|
|
266
|
+
|
|
267
|
+
console.log('');
|
|
268
|
+
|
|
269
|
+
// ── Derive font URL base from fontsPath ──────────────────
|
|
270
|
+
// e.g. "public/fonts" → "/fonts", "src/assets/fonts" → "/assets/fonts"
|
|
271
|
+
let fontUrlBase = config.fontUrlBase;
|
|
272
|
+
if (fontsPath !== config.fontsPath) {
|
|
273
|
+
// User changed the path — try to infer URL base
|
|
274
|
+
const stripped = fontsPath.replace(/^public/, '').replace(/^src\//, '/');
|
|
275
|
+
fontUrlBase = stripped.startsWith('/') ? stripped : `/${stripped}`;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ── Write fynd-tokens.css ────────────────────────────────
|
|
279
|
+
const step = (msg) => console.log(' ' + k.green('✔') + ' ' + msg);
|
|
280
|
+
const warn = (msg) => console.log(' ' + k.yellow('!') + ' ' + k.yellow(msg));
|
|
281
|
+
|
|
282
|
+
let tokensContent = readTemplate('fynd-tokens.css');
|
|
283
|
+
tokensContent = patchTokensFontPaths(tokensContent, fontUrlBase);
|
|
284
|
+
const tokensDest = path.join(cwd, tokensPath);
|
|
285
|
+
write(tokensDest, tokensContent);
|
|
286
|
+
step(`fynd-tokens.css → ${k.dim(tokensPath)}`);
|
|
287
|
+
|
|
288
|
+
// ── Write FYND_DESIGN_SYSTEM.md / CLAUDE.md ──────────────
|
|
289
|
+
const dsContent = readTemplate('FYND_DESIGN_SYSTEM.md');
|
|
290
|
+
if (writeClaude) {
|
|
291
|
+
const claudeDest = path.join(cwd, 'CLAUDE.md');
|
|
292
|
+
const alreadyExists = exists(claudeDest);
|
|
293
|
+
write(claudeDest, dsContent);
|
|
294
|
+
step(`CLAUDE.md → ${k.dim('CLAUDE.md')}${alreadyExists ? k.dim(' (overwritten)') : ''}`);
|
|
295
|
+
}
|
|
296
|
+
// Always write the full reference doc
|
|
297
|
+
write(path.join(cwd, 'FYND_DESIGN_SYSTEM.md'), dsContent);
|
|
298
|
+
step(`FYND_DESIGN_SYSTEM.md → ${k.dim('FYND_DESIGN_SYSTEM.md')}`);
|
|
299
|
+
|
|
300
|
+
// ── Create fonts directory with .gitkeep ─────────────────
|
|
301
|
+
const fontsDest = path.join(cwd, fontsPath);
|
|
302
|
+
ensureDir(fontsDest);
|
|
303
|
+
const gitkeep = path.join(fontsDest, '.gitkeep');
|
|
304
|
+
if (!exists(gitkeep)) {
|
|
305
|
+
write(gitkeep, '# Place FyndSans-*.woff2 font files here\n# Contact design@fynd.com to obtain them\n');
|
|
306
|
+
}
|
|
307
|
+
step(`Font directory ready → ${k.dim(fontsPath + '/')}`);
|
|
308
|
+
|
|
309
|
+
// ── Append import to globals CSS ─────────────────────────
|
|
310
|
+
if (config.globalsCss) {
|
|
311
|
+
const globalsPath = path.join(cwd, config.globalsCss);
|
|
312
|
+
const snippet = buildGlobalsSnippet(tokensPath, framework);
|
|
313
|
+
if (exists(globalsPath)) {
|
|
314
|
+
const existing = fs.readFileSync(globalsPath, 'utf8');
|
|
315
|
+
if (!existing.includes('fynd-tokens.css')) {
|
|
316
|
+
fs.writeFileSync(globalsPath, snippet + '\n\n' + existing, 'utf8');
|
|
317
|
+
step(`Import prepended → ${k.dim(config.globalsCss)}`);
|
|
318
|
+
} else {
|
|
319
|
+
warn(`Skipped globals.css — fynd-tokens.css already imported`);
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
write(globalsPath, snippet + '\n');
|
|
323
|
+
step(`Created ${k.dim(config.globalsCss)} with token import`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// ── Write quick-reference readme ─────────────────────────
|
|
328
|
+
const projectReadme = buildProjectReadme(framework, {
|
|
329
|
+
...config,
|
|
330
|
+
tokensPath,
|
|
331
|
+
fontsPath,
|
|
332
|
+
});
|
|
333
|
+
write(path.join(cwd, '.fynd-ds-readme.md'), projectReadme);
|
|
334
|
+
step(`.fynd-ds-readme.md → ${k.dim('.fynd-ds-readme.md')} (quick reference)`);
|
|
335
|
+
|
|
336
|
+
// ── Install packages ─────────────────────────────────────
|
|
337
|
+
const toInstall = [];
|
|
338
|
+
if (installFontsource) toInstall.push('@fontsource/inter-display', '@fontsource/inter');
|
|
339
|
+
if (installLucide) toInstall.push('lucide-react');
|
|
340
|
+
|
|
341
|
+
if (toInstall.length > 0) {
|
|
342
|
+
console.log('');
|
|
343
|
+
console.log(k.dim(' Installing packages...'));
|
|
344
|
+
try {
|
|
345
|
+
installDeps(toInstall, detectedPm);
|
|
346
|
+
step(`Installed: ${toInstall.join(', ')}`);
|
|
347
|
+
} catch {
|
|
348
|
+
warn(`Package install failed — run manually:`);
|
|
349
|
+
warn(` ${detectedPm} install ${toInstall.join(' ')}`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// ── Summary ───────────────────────────────────────────────
|
|
354
|
+
console.log('');
|
|
355
|
+
console.log(k.bold().white(' Fynd DS installed') + k.green(' ✔'));
|
|
356
|
+
console.log('');
|
|
357
|
+
console.log(k.dim(' One manual step remaining:'));
|
|
358
|
+
console.log('');
|
|
359
|
+
console.log(` ${k.white('Drop Fynd Sans .woff2 files into:')} ${k.cyan(fontsPath + '/')}`);
|
|
360
|
+
console.log(` ${k.dim('FyndSans-Regular.woff2, FyndSans-Medium.woff2, FyndSans-SemiBold.woff2, FyndSans-Bold.woff2')}`);
|
|
361
|
+
console.log(` ${k.dim('Obtain from the design assets repo or contact design@fynd.com')}`);
|
|
362
|
+
console.log('');
|
|
363
|
+
console.log(` ${k.dim('Full reference: FYND_DESIGN_SYSTEM.md')}`);
|
|
364
|
+
console.log('');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
module.exports = { run };
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@brewedby/ds",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Fynd One Design System installer — sets up tokens, fonts, icons and CLAUDE.md in any project",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"fynd",
|
|
7
|
+
"design-system",
|
|
8
|
+
"cli",
|
|
9
|
+
"tokens",
|
|
10
|
+
"css"
|
|
11
|
+
],
|
|
12
|
+
"license": "UNLICENSED",
|
|
13
|
+
"private": false,
|
|
14
|
+
"bin": {
|
|
15
|
+
"fynd-ds": "./bin/fynd-ds.js"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"bin/",
|
|
19
|
+
"lib/",
|
|
20
|
+
"templates/"
|
|
21
|
+
],
|
|
22
|
+
"scripts": {
|
|
23
|
+
"start": "node bin/fynd-ds.js"
|
|
24
|
+
},
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=16.0.0"
|
|
27
|
+
},
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/fynd/ds-cli"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"kleur": "^4.1.5",
|
|
34
|
+
"prompts": "^2.4.2"
|
|
35
|
+
}
|
|
36
|
+
}
|