@nqlib/nqui 0.1.1 → 0.1.3
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 +40 -2
- package/dist/styles.css +468 -21
- package/package.json +5 -1
- package/scripts/build-styles.js +0 -10
- package/scripts/examples/nextjs-layout.tsx +2 -0
- package/scripts/examples.js +76 -0
- package/scripts/framework.js +68 -0
- package/scripts/getPackageRoot.js +26 -0
- package/scripts/help.js +34 -0
- package/scripts/init-css.js +88 -543
- package/scripts/pipeline/emit.js +21 -0
- package/scripts/pipeline/extract.js +118 -0
- package/scripts/pipeline/index.js +90 -0
- package/scripts/pipeline/tokens.js +25 -0
- package/scripts/pipeline/transform.js +51 -0
- package/scripts/postcss.config.mjs +10 -0
- package/scripts/setup-helper.js +72 -0
- package/scripts/wizard.js +57 -0
package/scripts/init-css.js
CHANGED
|
@@ -1,580 +1,125 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* nqui CSS initialization
|
|
4
|
+
* nqui CSS initialization CLI
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx nqui init-css [output.css] [options]
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const possiblePaths = [
|
|
31
|
-
'src/app/globals.css',
|
|
32
|
-
'app/globals.css',
|
|
33
|
-
'src/app/layout.css',
|
|
34
|
-
'app/layout.css',
|
|
35
|
-
];
|
|
36
|
-
|
|
37
|
-
for (const path of possiblePaths) {
|
|
38
|
-
const fullPath = join(process.cwd(), path);
|
|
39
|
-
if (existsSync(fullPath)) {
|
|
40
|
-
return path;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Default fallback based on directory structure
|
|
45
|
-
if (existsSync(join(process.cwd(), 'src', 'app'))) {
|
|
46
|
-
return 'src/app/globals.css';
|
|
47
|
-
}
|
|
48
|
-
return 'app/globals.css';
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Function to find actual CSS file location for Vite
|
|
52
|
-
function findViteCssFile() {
|
|
53
|
-
const possiblePaths = [
|
|
54
|
-
'src/index.css',
|
|
55
|
-
'src/main.css',
|
|
56
|
-
'src/App.css',
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
for (const path of possiblePaths) {
|
|
60
|
-
const fullPath = join(process.cwd(), path);
|
|
61
|
-
if (existsSync(fullPath)) {
|
|
62
|
-
return path;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
return 'src/index.css';
|
|
67
|
-
}
|
|
10
|
+
import minimist from 'minimist';
|
|
11
|
+
import { resolve, dirname } from 'path';
|
|
12
|
+
import { readFileSync, existsSync } from 'fs';
|
|
13
|
+
import { getPackageRoot } from './getPackageRoot.js';
|
|
14
|
+
import { printHelp } from './help.js';
|
|
15
|
+
import { runPipeline } from './pipeline/index.js';
|
|
16
|
+
import { wizard, askAboutExamples } from './wizard.js';
|
|
17
|
+
import { detectFramework, findMainCssFile } from './framework.js';
|
|
18
|
+
import { generateSetupContent } from './setup-helper.js';
|
|
19
|
+
import { copyNextJsExamples } from './examples.js';
|
|
20
|
+
import { emit } from './pipeline/emit.js';
|
|
21
|
+
|
|
22
|
+
// Filter out command names from argv
|
|
23
|
+
const commandNames = ['init-css', 'nqui', 'nqui-init-css'];
|
|
24
|
+
const filteredArgs = process.argv.slice(2).filter(arg => !commandNames.includes(arg));
|
|
25
|
+
|
|
26
|
+
const args = minimist(filteredArgs, {
|
|
27
|
+
boolean: ['js', 'tokens-only', 'force', 'dry-run', 'wizard', 'setup', 'help', 'version', 'local-copy'],
|
|
28
|
+
alias: { h: 'help', v: 'version' },
|
|
29
|
+
});
|
|
68
30
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
case 'nextjs':
|
|
73
|
-
return findNextJsCssFile();
|
|
74
|
-
case 'vite':
|
|
75
|
-
return findViteCssFile();
|
|
76
|
-
case 'remix':
|
|
77
|
-
return 'app/root.css';
|
|
78
|
-
case 'create-react-app':
|
|
79
|
-
return 'src/index.css';
|
|
80
|
-
default:
|
|
81
|
-
return 'nqui.css';
|
|
82
|
-
}
|
|
31
|
+
if (args.help) {
|
|
32
|
+
printHelp();
|
|
33
|
+
process.exit(0);
|
|
83
34
|
}
|
|
84
35
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
if (existsSync(join(process.cwd(), 'vite.config.js')) ||
|
|
92
|
-
existsSync(join(process.cwd(), 'vite.config.ts'))) {
|
|
93
|
-
return 'vite';
|
|
94
|
-
}
|
|
95
|
-
if (existsSync(join(process.cwd(), 'remix.config.js'))) {
|
|
96
|
-
return 'remix';
|
|
97
|
-
}
|
|
98
|
-
if (existsSync(join(process.cwd(), 'src', 'index.css'))) {
|
|
99
|
-
return 'create-react-app';
|
|
100
|
-
}
|
|
101
|
-
return null;
|
|
36
|
+
if (args.version) {
|
|
37
|
+
const root = getPackageRoot();
|
|
38
|
+
const pkg = JSON.parse(readFileSync(resolve(root, 'package.json'), 'utf8'));
|
|
39
|
+
console.log(pkg.version);
|
|
40
|
+
process.exit(0);
|
|
102
41
|
}
|
|
103
42
|
|
|
104
|
-
|
|
105
|
-
// If we're in a published package, use dist/styles.css directly
|
|
106
|
-
if (isPublishedPackage) {
|
|
107
|
-
if (!existsSync(distStylesPath)) {
|
|
108
|
-
throw new Error(`Published package CSS file not found: ${distStylesPath}`);
|
|
109
|
-
}
|
|
110
|
-
return readFileSync(distStylesPath, 'utf-8');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Read the source CSS files (for local development)
|
|
114
|
-
if (!existsSync(indexCssPath)) {
|
|
115
|
-
throw new Error(`Source CSS file not found: ${indexCssPath}`);
|
|
116
|
-
}
|
|
117
|
-
if (!existsSync(colorsCssPath)) {
|
|
118
|
-
throw new Error(`Colors CSS file not found: ${colorsCssPath}`);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
let indexCss = readFileSync(indexCssPath, 'utf-8');
|
|
122
|
-
let colorsCss = readFileSync(colorsCssPath, 'utf-8');
|
|
123
|
-
|
|
124
|
-
// Extract :root and .dark blocks from colors.css (they're wrapped in @layer base)
|
|
125
|
-
// We need to unwrap them and merge with index.css's :root and .dark
|
|
126
|
-
const colorsRootMatch = colorsCss.match(/@layer base\s*\{\s*:root\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}\s*\.dark\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}\s*\}/s);
|
|
127
|
-
|
|
128
|
-
let colorsRootContent = '';
|
|
129
|
-
let colorsDarkContent = '';
|
|
130
|
-
|
|
131
|
-
if (colorsRootMatch) {
|
|
132
|
-
colorsRootContent = colorsRootMatch[1].trim();
|
|
133
|
-
colorsDarkContent = colorsRootMatch[2].trim();
|
|
134
|
-
} else {
|
|
135
|
-
// Fallback: try to extract without @layer base wrapper
|
|
136
|
-
const rootMatch = colorsCss.match(/:root\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}/s);
|
|
137
|
-
const darkMatch = colorsCss.match(/\.dark\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}/s);
|
|
138
|
-
if (rootMatch) colorsRootContent = rootMatch[1].trim();
|
|
139
|
-
if (darkMatch) colorsDarkContent = darkMatch[1].trim();
|
|
140
|
-
}
|
|
43
|
+
const output = args._[0] || 'nqui/index.css';
|
|
141
44
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
.replace(/@import\s+["']shadcn\/tailwind\.css["'];?\s*\n/g, '')
|
|
148
|
-
.replace(/@import\s+["']@fontsource-variable\/inter["'];?\s*\n/g, '')
|
|
149
|
-
.replace(/@import\s+["']\.\/styles\/colors\.css["'];?\s*\n/g, '')
|
|
150
|
-
.replace(/\/\*\s*Import enhanced color system\s*\*\//g, '');
|
|
151
|
-
|
|
152
|
-
// Extract :root and .dark blocks from index.css
|
|
153
|
-
const indexRootMatch = indexCss.match(/:root\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}/s);
|
|
154
|
-
const indexDarkMatch = indexCss.match(/\.dark\s*\{([^}]+(?:\{[^}]*\}[^}]*)*)\}/s);
|
|
155
|
-
|
|
156
|
-
let indexRootContent = '';
|
|
157
|
-
let indexDarkContent = '';
|
|
158
|
-
|
|
159
|
-
if (indexRootMatch) indexRootContent = indexRootMatch[1].trim();
|
|
160
|
-
if (indexDarkMatch) indexDarkContent = indexDarkMatch[1].trim();
|
|
161
|
-
|
|
162
|
-
// Merge :root blocks (colors.css first, then index.css)
|
|
163
|
-
const mergedRootContent = colorsRootContent
|
|
164
|
-
? colorsRootContent + '\n\n /* Additional variables from index.css */\n ' + indexRootContent
|
|
165
|
-
: indexRootContent;
|
|
166
|
-
|
|
167
|
-
// Merge .dark blocks (colors.css first, then index.css)
|
|
168
|
-
const mergedDarkContent = colorsDarkContent
|
|
169
|
-
? colorsDarkContent + '\n\n /* Additional variables from index.css */\n ' + indexDarkContent
|
|
170
|
-
: indexDarkContent;
|
|
171
|
-
|
|
172
|
-
// Replace :root and .dark in index.css with merged versions
|
|
173
|
-
if (indexRootMatch) {
|
|
174
|
-
indexCss = indexCss.replace(/:root\s*\{[^}]+(?:\{[^}]*\}[^}]*)*\}/s, `:root {\n ${mergedRootContent}\n}`);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (indexDarkMatch) {
|
|
178
|
-
indexCss = indexCss.replace(/\.dark\s*\{[^}]+(?:\{[^}]*\}[^}]*)*\}/s, `.dark {\n ${mergedDarkContent}\n}`);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// Remove duplicate @layer base blocks (there are two identical ones in index.css)
|
|
182
|
-
// Match @layer base blocks with proper brace counting
|
|
183
|
-
const lines = indexCss.split('\n');
|
|
184
|
-
let inLayerBase = false;
|
|
185
|
-
let layerBaseStart = -1;
|
|
186
|
-
let braceCount = 0;
|
|
187
|
-
const layerBaseBlocks = [];
|
|
188
|
-
|
|
189
|
-
for (let i = 0; i < lines.length; i++) {
|
|
190
|
-
const line = lines[i];
|
|
191
|
-
if (line.includes('@layer base')) {
|
|
192
|
-
if (!inLayerBase) {
|
|
193
|
-
inLayerBase = true;
|
|
194
|
-
layerBaseStart = i;
|
|
195
|
-
braceCount = (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length;
|
|
196
|
-
} else {
|
|
197
|
-
braceCount += (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length;
|
|
198
|
-
}
|
|
199
|
-
} else if (inLayerBase) {
|
|
200
|
-
braceCount += (line.match(/\{/g) || []).length - (line.match(/\}/g) || []).length;
|
|
201
|
-
if (braceCount === 0) {
|
|
202
|
-
layerBaseBlocks.push({ start: layerBaseStart, end: i });
|
|
203
|
-
inLayerBase = false;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// If we found duplicate @layer base blocks, remove all but the first
|
|
209
|
-
if (layerBaseBlocks.length > 1) {
|
|
210
|
-
const firstBlock = layerBaseBlocks[0];
|
|
211
|
-
const firstBlockContent = lines.slice(firstBlock.start, firstBlock.end + 1).join('\n');
|
|
212
|
-
|
|
213
|
-
// Check if all blocks are identical
|
|
214
|
-
const allIdentical = layerBaseBlocks.every(block => {
|
|
215
|
-
const blockContent = lines.slice(block.start, block.end + 1).join('\n');
|
|
216
|
-
return blockContent === firstBlockContent;
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
if (allIdentical) {
|
|
220
|
-
// Remove all but the first block (working backwards to preserve indices)
|
|
221
|
-
for (let i = layerBaseBlocks.length - 1; i > 0; i--) {
|
|
222
|
-
const block = layerBaseBlocks[i];
|
|
223
|
-
lines.splice(block.start, block.end - block.start + 1);
|
|
224
|
-
}
|
|
225
|
-
indexCss = lines.join('\n');
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// The :root and .dark blocks are already merged into indexCss
|
|
230
|
-
// Now we just need to add a comment header
|
|
231
|
-
let combinedCss = indexCss;
|
|
232
|
-
|
|
233
|
-
// Add comment before :root if it doesn't already have one
|
|
234
|
-
if (combinedCss.includes(':root') && !combinedCss.includes('nqui Color System')) {
|
|
235
|
-
const rootIndex = combinedCss.indexOf(':root');
|
|
236
|
-
combinedCss = combinedCss.slice(0, rootIndex) +
|
|
237
|
-
'/* ============================================\n' +
|
|
238
|
-
' nqui Color System & Design Tokens\n' +
|
|
239
|
-
' ============================================ */\n\n' +
|
|
240
|
-
combinedCss.slice(rootIndex);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Clean up extra blank lines
|
|
244
|
-
combinedCss = combinedCss.replace(/\n{4,}/g, '\n\n\n');
|
|
245
|
-
|
|
246
|
-
// Add header comment
|
|
247
|
-
const header = `/* nqui Design System CSS
|
|
248
|
-
*
|
|
249
|
-
* This file contains ALL design tokens, CSS variables, and color scales from nqui.
|
|
250
|
-
* It includes everything the library needs to style components correctly.
|
|
251
|
-
*
|
|
252
|
-
* IMPORTANT: This file does NOT include Tailwind CSS imports.
|
|
253
|
-
* You must have Tailwind CSS configured in your project.
|
|
254
|
-
*
|
|
255
|
-
* For Tailwind CSS v4, add this at the TOP of your CSS file:
|
|
256
|
-
* @import "tailwindcss";
|
|
257
|
-
*
|
|
258
|
-
* For Tailwind CSS v3, configure it in your tailwind.config.js
|
|
45
|
+
/**
|
|
46
|
+
* Generate index.css that imports from library package
|
|
47
|
+
*/
|
|
48
|
+
function generateIndexCssContent() {
|
|
49
|
+
return `/* nqui Design System
|
|
259
50
|
*
|
|
260
|
-
* This file
|
|
261
|
-
*
|
|
262
|
-
* - Complete color scales (100-600) for all semantic colors
|
|
263
|
-
* - Light and dark mode support
|
|
264
|
-
* - Base layer styles
|
|
265
|
-
* - Utility animations
|
|
51
|
+
* This file imports nqui styles from the library package.
|
|
52
|
+
* Import this file in your main CSS file (e.g. globals.css)
|
|
266
53
|
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
54
|
+
* Usage in your globals.css:
|
|
55
|
+
* @import "./nqui/index.css";
|
|
269
56
|
*/
|
|
270
57
|
|
|
58
|
+
@import "@nqlib/nqui/styles";
|
|
271
59
|
`;
|
|
272
|
-
|
|
273
|
-
return header + combinedCss;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function getFrameworkDirectives(projectType) {
|
|
277
|
-
switch (projectType) {
|
|
278
|
-
case 'nextjs':
|
|
279
|
-
return `@import "tailwindcss";
|
|
280
|
-
@source "./**/*.{js,ts,jsx,tsx,mdx}";
|
|
281
|
-
@source "../components/**/*.{js,ts,jsx,tsx,mdx}";
|
|
282
|
-
@source "../node_modules/@nqlib/nqui/dist/**/*.js";
|
|
283
|
-
@import "../node_modules/tw-animate-css/dist/tw-animate.css";
|
|
284
|
-
@custom-variant dark (&:is(.dark *));
|
|
285
|
-
|
|
286
|
-
`;
|
|
287
|
-
case 'vite':
|
|
288
|
-
case 'create-react-app':
|
|
289
|
-
return `@import "tailwindcss";
|
|
290
|
-
@import "../node_modules/tw-animate-css/dist/tw-animate.css";
|
|
291
|
-
|
|
292
|
-
`;
|
|
293
|
-
case 'remix':
|
|
294
|
-
return `@import "tailwindcss";
|
|
295
|
-
@import "../node_modules/tw-animate-css/dist/tw-animate.css";
|
|
296
|
-
|
|
297
|
-
`;
|
|
298
|
-
default:
|
|
299
|
-
return '';
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
// Function to ask user for input
|
|
304
|
-
function askQuestion(question) {
|
|
305
|
-
const rl = createInterface({
|
|
306
|
-
input: process.stdin,
|
|
307
|
-
output: process.stdout,
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
return new Promise((resolve) => {
|
|
311
|
-
rl.question(question, (answer) => {
|
|
312
|
-
rl.close();
|
|
313
|
-
resolve(answer.trim().toLowerCase());
|
|
314
|
-
});
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
// Function to find Next.js page.tsx file
|
|
319
|
-
function findNextJsPageFile() {
|
|
320
|
-
const possiblePaths = [
|
|
321
|
-
'src/app/page.tsx',
|
|
322
|
-
'app/page.tsx',
|
|
323
|
-
];
|
|
324
|
-
|
|
325
|
-
for (const path of possiblePaths) {
|
|
326
|
-
const fullPath = join(process.cwd(), path);
|
|
327
|
-
if (existsSync(fullPath)) {
|
|
328
|
-
return path;
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Default fallback based on directory structure
|
|
333
|
-
if (existsSync(join(process.cwd(), 'src', 'app'))) {
|
|
334
|
-
return 'src/app/page.tsx';
|
|
335
|
-
}
|
|
336
|
-
return 'app/page.tsx';
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Function to find Next.js layout.tsx file
|
|
340
|
-
function findNextJsLayoutFile() {
|
|
341
|
-
const possiblePaths = [
|
|
342
|
-
'src/app/layout.tsx',
|
|
343
|
-
'app/layout.tsx',
|
|
344
|
-
];
|
|
345
|
-
|
|
346
|
-
for (const path of possiblePaths) {
|
|
347
|
-
const fullPath = join(process.cwd(), path);
|
|
348
|
-
if (existsSync(fullPath)) {
|
|
349
|
-
return path;
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// Default fallback based on directory structure
|
|
354
|
-
if (existsSync(join(process.cwd(), 'src', 'app'))) {
|
|
355
|
-
return 'src/app/layout.tsx';
|
|
356
|
-
}
|
|
357
|
-
return 'app/layout.tsx';
|
|
358
60
|
}
|
|
359
61
|
|
|
360
|
-
|
|
361
|
-
async function copyNextJsExamples() {
|
|
362
|
-
const pageExamplePath = join(examplesDir, 'nextjs-page.tsx');
|
|
363
|
-
const layoutExamplePath = join(examplesDir, 'nextjs-layout.tsx');
|
|
364
|
-
|
|
365
|
-
if (!existsSync(pageExamplePath) || !existsSync(layoutExamplePath)) {
|
|
366
|
-
console.log('⚠️ Example files not found. Skipping example copy.');
|
|
367
|
-
return;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
const targetPagePath = findNextJsPageFile();
|
|
371
|
-
const targetLayoutPath = findNextJsLayoutFile();
|
|
372
|
-
const fullPagePath = resolve(process.cwd(), targetPagePath);
|
|
373
|
-
const fullLayoutPath = resolve(process.cwd(), targetLayoutPath);
|
|
374
|
-
|
|
375
|
-
// Check if files exist
|
|
376
|
-
const pageExists = existsSync(fullPagePath);
|
|
377
|
-
const layoutExists = existsSync(fullLayoutPath);
|
|
378
|
-
|
|
379
|
-
if (pageExists || layoutExists) {
|
|
380
|
-
console.log('\n📄 Found existing Next.js files:');
|
|
381
|
-
if (pageExists) console.log(` - ${targetPagePath}`);
|
|
382
|
-
if (layoutExists) console.log(` - ${targetLayoutPath}`);
|
|
383
|
-
|
|
384
|
-
const overwrite = await askQuestion('\n Do you want to overwrite them with nqui examples? (y/n): ');
|
|
385
|
-
if (overwrite !== 'y' && overwrite !== 'yes') {
|
|
386
|
-
console.log(' Skipping example files...');
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// Create directories if needed
|
|
392
|
-
const pageDir = dirname(fullPagePath);
|
|
393
|
-
const layoutDir = dirname(fullLayoutPath);
|
|
394
|
-
if (!existsSync(pageDir)) {
|
|
395
|
-
mkdirSync(pageDir, { recursive: true });
|
|
396
|
-
}
|
|
397
|
-
if (!existsSync(layoutDir)) {
|
|
398
|
-
mkdirSync(layoutDir, { recursive: true });
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
// Copy example files
|
|
62
|
+
(async () => {
|
|
402
63
|
try {
|
|
403
|
-
const
|
|
404
|
-
const
|
|
64
|
+
const root = getPackageRoot();
|
|
65
|
+
const framework = detectFramework();
|
|
66
|
+
const wiz = args.wizard ? await wizard() : {};
|
|
405
67
|
|
|
406
|
-
|
|
407
|
-
|
|
68
|
+
const outCssPath = resolve(process.cwd(), output);
|
|
69
|
+
const useLibraryImport = !args['local-copy']; // Default to library import
|
|
408
70
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
} catch (error) {
|
|
414
|
-
console.error('❌ Error copying example files:', error.message);
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
async function main() {
|
|
419
|
-
const args = process.argv.slice(2);
|
|
420
|
-
const targetPath = args[0];
|
|
421
|
-
|
|
422
|
-
try {
|
|
423
|
-
// Extract standalone CSS
|
|
424
|
-
const standaloneCss = extractStandaloneCSS();
|
|
425
|
-
|
|
426
|
-
let finalTargetPath;
|
|
427
|
-
let projectType = null;
|
|
428
|
-
|
|
429
|
-
if (targetPath) {
|
|
430
|
-
// User provided a custom path
|
|
431
|
-
finalTargetPath = resolve(process.cwd(), targetPath);
|
|
432
|
-
projectType = findProjectType();
|
|
71
|
+
if (useLibraryImport) {
|
|
72
|
+
// Create index.css that imports from library
|
|
73
|
+
const indexCssContent = generateIndexCssContent();
|
|
74
|
+
emit(outCssPath, indexCssContent, { force: args.force, dryRun: args['dry-run'] });
|
|
433
75
|
} else {
|
|
434
|
-
//
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
console.log('⚠️ Could not detect project type.');
|
|
446
|
-
console.log(` Creating standalone file: nqui.css`);
|
|
447
|
-
console.log(' You can import this file manually in your app.');
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
// Create target directory if it doesn't exist
|
|
452
|
-
const targetDir = dirname(finalTargetPath);
|
|
453
|
-
if (!existsSync(targetDir)) {
|
|
454
|
-
mkdirSync(targetDir, { recursive: true });
|
|
76
|
+
// Original behavior: generate local copy
|
|
77
|
+
await runPipeline({
|
|
78
|
+
root,
|
|
79
|
+
outCss: outCssPath,
|
|
80
|
+
outJs: args.js || wiz.js
|
|
81
|
+
? resolve(process.cwd(), output.replace('.css', '.tokens.js'))
|
|
82
|
+
: null,
|
|
83
|
+
tokensOnly: args['tokens-only'] || wiz.tokensOnly,
|
|
84
|
+
force: args.force,
|
|
85
|
+
dryRun: args['dry-run'],
|
|
86
|
+
});
|
|
455
87
|
}
|
|
456
88
|
|
|
457
|
-
//
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
if (existsSync(finalTargetPath)) {
|
|
463
|
-
console.log(`\n⚠️ File already exists: ${finalTargetPath}`);
|
|
464
|
-
|
|
465
|
-
// Read existing content
|
|
466
|
-
const existingContent = readFileSync(finalTargetPath, 'utf-8');
|
|
467
|
-
|
|
468
|
-
// Check if nqui CSS is already present
|
|
469
|
-
if (existingContent.includes('nqui Design System CSS') ||
|
|
470
|
-
existingContent.includes('nqui Color System')) {
|
|
471
|
-
console.log(' ⚠️ nqui CSS already present in file.');
|
|
472
|
-
const update = await askQuestion(' Do you want to update it? (y/n): ');
|
|
473
|
-
if (update !== 'y' && update !== 'yes') {
|
|
474
|
-
console.log(' Skipping...');
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
// Remove old nqui CSS and add new
|
|
478
|
-
const cleaned = existingContent.replace(
|
|
479
|
-
/\/\* nqui Design System CSS[\s\S]*?(?=\n\n|\n$|$)/,
|
|
480
|
-
''
|
|
481
|
-
).replace(
|
|
482
|
-
/\/\* ============================================[\s\S]*?============================================ \*\/[\s\S]*?}/g,
|
|
483
|
-
''
|
|
484
|
-
);
|
|
485
|
-
const hasTailwindImport = cleaned.includes('@import "tailwindcss"');
|
|
486
|
-
if (hasTailwindImport && frameworkDirectives) {
|
|
487
|
-
writeFileSync(finalTargetPath, cleaned.trim() + '\n\n' + standaloneCss, 'utf-8');
|
|
488
|
-
} else {
|
|
489
|
-
writeFileSync(finalTargetPath, fullCss + '\n\n' + cleaned.trim(), 'utf-8');
|
|
490
|
-
}
|
|
491
|
-
console.log(`✅ Updated nqui CSS in: ${finalTargetPath}`);
|
|
492
|
-
console.log('\n✨ Done!');
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
// Ask user what to do
|
|
497
|
-
console.log('\n What would you like to do?');
|
|
498
|
-
console.log(' 1. Overwrite the file (replace all content)');
|
|
499
|
-
console.log(' 2. Append nqui CSS to the file');
|
|
500
|
-
console.log(' 3. Create a separate file (nqui.css) and import it');
|
|
89
|
+
// Generate setup helper file (always, unless dry-run)
|
|
90
|
+
if (!args['dry-run'] && (args.setup || !args['tokens-only'])) {
|
|
91
|
+
const setupContent = generateSetupContent(framework, output, useLibraryImport);
|
|
92
|
+
const setupPath = resolve(process.cwd(), dirname(output), 'nqui-setup.css');
|
|
93
|
+
emit(setupPath, setupContent, { force: args.force, dryRun: false });
|
|
501
94
|
|
|
502
|
-
const
|
|
95
|
+
const mainCssFile = findMainCssFile(framework);
|
|
503
96
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
// Keep existing Tailwind setup, add nqui CSS
|
|
509
|
-
writeFileSync(finalTargetPath, existingContent + '\n\n' + standaloneCss, 'utf-8');
|
|
510
|
-
} else {
|
|
511
|
-
// Replace with framework directives + nqui CSS
|
|
512
|
-
writeFileSync(finalTargetPath, fullCss, 'utf-8');
|
|
513
|
-
}
|
|
514
|
-
console.log(`✅ Updated: ${finalTargetPath}`);
|
|
515
|
-
} else if (choice === '2') {
|
|
516
|
-
// Append
|
|
517
|
-
const hasTailwindImport = existingContent.includes('@import "tailwindcss"');
|
|
518
|
-
if (hasTailwindImport && frameworkDirectives) {
|
|
519
|
-
writeFileSync(finalTargetPath, existingContent + '\n\n' + standaloneCss, 'utf-8');
|
|
520
|
-
} else {
|
|
521
|
-
writeFileSync(finalTargetPath, fullCss + '\n\n' + existingContent, 'utf-8');
|
|
522
|
-
}
|
|
523
|
-
console.log(`✅ Appended nqui CSS to: ${finalTargetPath}`);
|
|
524
|
-
} else if (choice === '3') {
|
|
525
|
-
// Create separate file
|
|
526
|
-
const separatePath = join(dirname(finalTargetPath), 'nqui.css');
|
|
527
|
-
writeFileSync(separatePath, standaloneCss, 'utf-8');
|
|
528
|
-
console.log(`✅ Created: ${separatePath}`);
|
|
529
|
-
console.log(`\n📝 Next step: Import this file in ${finalTargetPath}:`);
|
|
530
|
-
const relativeSeparatePath = separatePath.replace(process.cwd() + '/', '');
|
|
531
|
-
const relativeTargetPath = finalTargetPath.replace(process.cwd() + '/', '');
|
|
532
|
-
const importPath = relativeSeparatePath.replace(dirname(relativeTargetPath) + '/', './');
|
|
533
|
-
console.log(` @import "${importPath}";`);
|
|
534
|
-
console.log('\n✨ Done!');
|
|
535
|
-
return;
|
|
536
|
-
} else {
|
|
537
|
-
console.log(' Invalid choice. Skipping...');
|
|
538
|
-
return;
|
|
539
|
-
}
|
|
540
|
-
} else {
|
|
541
|
-
// Create new file with framework directives + nqui CSS
|
|
542
|
-
writeFileSync(finalTargetPath, fullCss, 'utf-8');
|
|
543
|
-
console.log(`✅ Created: ${finalTargetPath}`);
|
|
544
|
-
if (frameworkDirectives) {
|
|
545
|
-
console.log(` Added ${projectType}-specific Tailwind setup`);
|
|
546
|
-
}
|
|
97
|
+
console.log(`\n📝 Next steps:`);
|
|
98
|
+
console.log(` 1. Copy the contents of ${dirname(output)}/nqui-setup.css`);
|
|
99
|
+
console.log(` 2. Paste them at the VERY TOP of your main CSS file`);
|
|
100
|
+
console.log(` (e.g. ${mainCssFile})\n`);
|
|
547
101
|
}
|
|
548
102
|
|
|
549
|
-
//
|
|
550
|
-
|
|
551
|
-
if (!
|
|
552
|
-
|
|
553
|
-
console.log(' 2. Add Tailwind imports at the top of this CSS file if needed');
|
|
103
|
+
// Ask about copying examples (if not in wizard mode, ask for Next.js projects)
|
|
104
|
+
let shouldCopyExamples = wiz.copyExamples;
|
|
105
|
+
if (!args['dry-run'] && !shouldCopyExamples && framework === 'nextjs' && !args.wizard) {
|
|
106
|
+
shouldCopyExamples = await askAboutExamples(framework);
|
|
554
107
|
}
|
|
555
|
-
console.log(' 3. The CSS file is ready to use!');
|
|
556
108
|
|
|
557
|
-
//
|
|
558
|
-
if (
|
|
559
|
-
const
|
|
560
|
-
if (
|
|
561
|
-
|
|
109
|
+
// Copy example files if requested
|
|
110
|
+
if (!args['dry-run'] && shouldCopyExamples) {
|
|
111
|
+
const copied = await copyNextJsExamples(framework, { force: args.force });
|
|
112
|
+
if (copied && copied.length === 0) {
|
|
113
|
+
// Files were skipped (user said no to overwrite)
|
|
114
|
+
console.log('\n⏭️ Example files skipped. Run with --force to overwrite existing files.\n');
|
|
562
115
|
}
|
|
563
116
|
}
|
|
564
117
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
console.error('❌ Error:', error.message);
|
|
568
|
-
if (error.message.includes('not found')) {
|
|
569
|
-
console.error(' Please ensure you are running this from the nqui package directory');
|
|
570
|
-
console.error(' or that the source CSS files exist.');
|
|
118
|
+
if (!args['dry-run']) {
|
|
119
|
+
console.log(`\n✅ nqui installed successfully`);
|
|
571
120
|
}
|
|
121
|
+
} catch (err) {
|
|
122
|
+
console.error('❌ Error:', err.message);
|
|
572
123
|
process.exit(1);
|
|
573
124
|
}
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
main().catch((error) => {
|
|
577
|
-
console.error('❌ Unexpected error:', error);
|
|
578
|
-
process.exit(1);
|
|
579
|
-
});
|
|
580
|
-
|
|
125
|
+
})();
|