@versini/ui-styles 8.5.0 → 9.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.
@@ -0,0 +1,93 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ const DEFAULT_EXTENSIONS = ["js", "jsx", "ts", "tsx"];
4
+ const DEFAULT_IGNORE = [
5
+ "node_modules",
6
+ "dist",
7
+ "build",
8
+ ".git",
9
+ "coverage",
10
+ ".next",
11
+ ".rslib",
12
+ "tmp",
13
+ "__tests__"
14
+ ];
15
+ const EXCLUDED_FILE_PATTERNS = [
16
+ /\.stories\.[jt]sx?$/,
17
+ /\.test\.[jt]sx?$/,
18
+ /\.spec\.[jt]sx?$/
19
+ ];
20
+ function shouldIgnore(filePath, ignorePatterns) {
21
+ const parts = filePath.split(path.sep);
22
+ return parts.some((part) => ignorePatterns.includes(part));
23
+ }
24
+ function hasAllowedExtension(filePath, extensions) {
25
+ const ext = path.extname(filePath).slice(1).toLowerCase();
26
+ return extensions.includes(ext);
27
+ }
28
+ function isExcludedFile(fileName) {
29
+ return EXCLUDED_FILE_PATTERNS.some((pattern) => pattern.test(fileName));
30
+ }
31
+ function scanDirectory(dirPath, rootPath, options, results) {
32
+ let entries;
33
+ try {
34
+ entries = fs.readdirSync(dirPath, { withFileTypes: true });
35
+ } catch {
36
+ return;
37
+ }
38
+ for (const entry of entries) {
39
+ const fullPath = path.join(dirPath, entry.name);
40
+ const relativePath = path.relative(rootPath, fullPath);
41
+ if (shouldIgnore(relativePath, options.ignore)) {
42
+ continue;
43
+ }
44
+ if (entry.isDirectory()) {
45
+ scanDirectory(fullPath, rootPath, options, results);
46
+ } else if (entry.isFile() && hasAllowedExtension(entry.name, options.extensions) && !isExcludedFile(entry.name)) {
47
+ try {
48
+ const content = fs.readFileSync(fullPath, "utf-8");
49
+ results.push({
50
+ absolutePath: fullPath,
51
+ relativePath,
52
+ content
53
+ });
54
+ } catch {
55
+ }
56
+ }
57
+ }
58
+ }
59
+ function scanFiles(targetPath, options) {
60
+ const resolvedPath = path.resolve(targetPath);
61
+ const fullOptions = {
62
+ extensions: options?.extensions || DEFAULT_EXTENSIONS,
63
+ ignore: options?.ignore || DEFAULT_IGNORE
64
+ };
65
+ if (!fs.existsSync(resolvedPath)) {
66
+ throw new Error(`Path does not exist: ${resolvedPath}`);
67
+ }
68
+ const stats = fs.statSync(resolvedPath);
69
+ const results = [];
70
+ if (stats.isFile()) {
71
+ if (hasAllowedExtension(resolvedPath, fullOptions.extensions)) {
72
+ const content = fs.readFileSync(resolvedPath, "utf-8");
73
+ results.push({
74
+ absolutePath: resolvedPath,
75
+ relativePath: path.basename(resolvedPath),
76
+ content
77
+ });
78
+ }
79
+ } else if (stats.isDirectory()) {
80
+ scanDirectory(resolvedPath, resolvedPath, fullOptions, results);
81
+ }
82
+ return results;
83
+ }
84
+ function getDefaultScanOptions() {
85
+ return {
86
+ extensions: [...DEFAULT_EXTENSIONS],
87
+ ignore: [...DEFAULT_IGNORE]
88
+ };
89
+ }
90
+ export {
91
+ getDefaultScanOptions as g,
92
+ scanFiles as s
93
+ };
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import fs, { readFileSync, existsSync } from "node:fs";
3
- import path, { dirname, resolve } from "node:path";
2
+ import { s as scanFiles, g as getDefaultScanOptions } from "./assets/fileScanner-Cis4RFj5.js";
3
+ import { readFileSync, existsSync } from "node:fs";
4
+ import { dirname, resolve } from "node:path";
4
5
  import { fileURLToPath } from "node:url";
5
6
  const TAILWIND_COLOR_PALETTES = [
6
7
  "red",
@@ -106,93 +107,6 @@ function findColorClasses(content) {
106
107
  }
107
108
  return allMatches;
108
109
  }
109
- const DEFAULT_EXTENSIONS = ["js", "jsx", "ts", "tsx"];
110
- const DEFAULT_IGNORE = [
111
- "node_modules",
112
- "dist",
113
- "build",
114
- ".git",
115
- "coverage",
116
- ".next",
117
- ".rslib",
118
- "tmp",
119
- "__tests__"
120
- ];
121
- const EXCLUDED_FILE_PATTERNS = [
122
- /\.stories\.[jt]sx?$/,
123
- /\.test\.[jt]sx?$/,
124
- /\.spec\.[jt]sx?$/
125
- ];
126
- function shouldIgnore(filePath, ignorePatterns) {
127
- const parts = filePath.split(path.sep);
128
- return parts.some((part) => ignorePatterns.includes(part));
129
- }
130
- function hasAllowedExtension(filePath, extensions) {
131
- const ext = path.extname(filePath).slice(1).toLowerCase();
132
- return extensions.includes(ext);
133
- }
134
- function isExcludedFile(fileName) {
135
- return EXCLUDED_FILE_PATTERNS.some((pattern) => pattern.test(fileName));
136
- }
137
- function scanDirectory(dirPath, rootPath, options, results) {
138
- let entries;
139
- try {
140
- entries = fs.readdirSync(dirPath, { withFileTypes: true });
141
- } catch {
142
- return;
143
- }
144
- for (const entry of entries) {
145
- const fullPath = path.join(dirPath, entry.name);
146
- const relativePath = path.relative(rootPath, fullPath);
147
- if (shouldIgnore(relativePath, options.ignore)) {
148
- continue;
149
- }
150
- if (entry.isDirectory()) {
151
- scanDirectory(fullPath, rootPath, options, results);
152
- } else if (entry.isFile() && hasAllowedExtension(entry.name, options.extensions) && !isExcludedFile(entry.name)) {
153
- try {
154
- const content = fs.readFileSync(fullPath, "utf-8");
155
- results.push({
156
- absolutePath: fullPath,
157
- relativePath,
158
- content
159
- });
160
- } catch {
161
- }
162
- }
163
- }
164
- }
165
- function scanFiles(targetPath, options) {
166
- const resolvedPath = path.resolve(targetPath);
167
- const fullOptions = {
168
- extensions: options?.extensions || DEFAULT_EXTENSIONS,
169
- ignore: options?.ignore || DEFAULT_IGNORE
170
- };
171
- if (!fs.existsSync(resolvedPath)) {
172
- throw new Error(`Path does not exist: ${resolvedPath}`);
173
- }
174
- const stats = fs.statSync(resolvedPath);
175
- const results = [];
176
- if (stats.isFile()) {
177
- if (hasAllowedExtension(resolvedPath, fullOptions.extensions)) {
178
- const content = fs.readFileSync(resolvedPath, "utf-8");
179
- results.push({
180
- absolutePath: resolvedPath,
181
- relativePath: path.basename(resolvedPath),
182
- content
183
- });
184
- }
185
- } else if (stats.isDirectory()) {
186
- scanDirectory(resolvedPath, resolvedPath, fullOptions, results);
187
- }
188
- return results;
189
- }
190
- function getDefaultScanOptions() {
191
- return {
192
- extensions: [...DEFAULT_EXTENSIONS],
193
- ignore: [...DEFAULT_IGNORE]
194
- };
195
- }
196
110
  const colors = {
197
111
  reset: "\x1B[0m",
198
112
  bright: "\x1B[1m",
@@ -0,0 +1,375 @@
1
+ #!/usr/bin/env node
2
+ import { s as scanFiles, g as getDefaultScanOptions } from "./assets/fileScanner-Cis4RFj5.js";
3
+ import fs from "node:fs";
4
+ import "node:path";
5
+ const PROSE_ELEMENT_MODIFIERS = [
6
+ "headings",
7
+ "h1",
8
+ "h2",
9
+ "h3",
10
+ "h4",
11
+ "h5",
12
+ "h6",
13
+ "p",
14
+ "a",
15
+ "blockquote",
16
+ "figure",
17
+ "figcaption",
18
+ "strong",
19
+ "em",
20
+ "kbd",
21
+ "code",
22
+ "pre",
23
+ "ol",
24
+ "ul",
25
+ "li",
26
+ "table",
27
+ "thead",
28
+ "tr",
29
+ "th",
30
+ "td",
31
+ "img",
32
+ "video",
33
+ "hr",
34
+ "lead"
35
+ ];
36
+ function migrateContent(content) {
37
+ const matches = [];
38
+ let migratedContent = content;
39
+ const lines = content.split("\n");
40
+ const processedLines = [];
41
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
42
+ let line = lines[lineIndex];
43
+ const lineNumber = lineIndex + 1;
44
+ line = replaceWithTracking(
45
+ line,
46
+ /--tw-prose-/g,
47
+ "--tw-plume-",
48
+ lineNumber,
49
+ matches
50
+ );
51
+ line = replaceWithTracking(
52
+ line,
53
+ /\bnot-prose\b/g,
54
+ "not-plume",
55
+ lineNumber,
56
+ matches
57
+ );
58
+ for (const modifier of PROSE_ELEMENT_MODIFIERS) {
59
+ const pattern = new RegExp(`\\bprose-${modifier}:`, "g");
60
+ line = replaceWithTracking(
61
+ line,
62
+ pattern,
63
+ `plume-${modifier}:`,
64
+ lineNumber,
65
+ matches
66
+ );
67
+ }
68
+ line = replaceWithTracking(
69
+ line,
70
+ /\bprose-dark\b/g,
71
+ "plume-dark",
72
+ lineNumber,
73
+ matches
74
+ );
75
+ line = replaceWithTracking(
76
+ line,
77
+ /\bprose-lighter\b/g,
78
+ "plume-lighter",
79
+ lineNumber,
80
+ matches
81
+ );
82
+ line = replaceWithTracking(
83
+ line,
84
+ /\bprose-light\b/g,
85
+ "plume-light",
86
+ lineNumber,
87
+ matches
88
+ );
89
+ line = replaceWithTracking(
90
+ line,
91
+ /(?<![-\w])prose(?!-\w)/g,
92
+ "plume",
93
+ lineNumber,
94
+ matches
95
+ );
96
+ processedLines.push(line);
97
+ }
98
+ migratedContent = processedLines.join("\n");
99
+ return {
100
+ originalContent: content,
101
+ migratedContent,
102
+ matches,
103
+ hasChanges: matches.length > 0
104
+ };
105
+ }
106
+ function replaceWithTracking(line, pattern, replacement, lineNumber, matches) {
107
+ const regex = new RegExp(pattern.source, pattern.flags);
108
+ let match = regex.exec(line);
109
+ while (match !== null) {
110
+ matches.push({
111
+ original: match[0],
112
+ replacement,
113
+ line: lineNumber,
114
+ column: match.index + 1
115
+ });
116
+ match = regex.exec(line);
117
+ }
118
+ const result = line.replace(pattern, replacement);
119
+ return result;
120
+ }
121
+ function migrateFile(filePath) {
122
+ const content = fs.readFileSync(filePath, "utf-8");
123
+ const result = migrateContent(content);
124
+ return {
125
+ ...result,
126
+ filePath
127
+ };
128
+ }
129
+ function migrateFileInPlace(filePath) {
130
+ const result = migrateFile(filePath);
131
+ if (result.hasChanges) {
132
+ fs.writeFileSync(filePath, result.migratedContent, "utf-8");
133
+ }
134
+ return result;
135
+ }
136
+ function needsMigration(content) {
137
+ const patterns = [
138
+ /--tw-prose-/,
139
+ /\bnot-prose\b/,
140
+ /\bprose-dark\b/,
141
+ /\bprose-light\b/,
142
+ /\bprose-lighter\b/,
143
+ /(?<![-\w])prose(?!-\w)/
144
+ ];
145
+ for (const modifier of PROSE_ELEMENT_MODIFIERS) {
146
+ patterns.push(new RegExp(`\\bprose-${modifier}:`));
147
+ }
148
+ return patterns.some((pattern) => pattern.test(content));
149
+ }
150
+ const VERSION = "1.0.0";
151
+ const colors = {
152
+ reset: "\x1B[0m",
153
+ bright: "\x1B[1m",
154
+ dim: "\x1B[2m",
155
+ red: "\x1B[31m",
156
+ green: "\x1B[32m",
157
+ yellow: "\x1B[33m",
158
+ cyan: "\x1B[36m"
159
+ };
160
+ function parseArgs(args) {
161
+ const defaults = getDefaultScanOptions();
162
+ const options = {
163
+ path: ".",
164
+ extensions: [...defaults.extensions, "mdx", "md", "jsx"],
165
+ ignore: defaults.ignore,
166
+ dryRun: false,
167
+ help: false,
168
+ version: false
169
+ };
170
+ let i = 0;
171
+ while (i < args.length) {
172
+ const arg = args[i];
173
+ switch (arg) {
174
+ case "-h":
175
+ case "--help":
176
+ options.help = true;
177
+ break;
178
+ case "-v":
179
+ case "--version":
180
+ options.version = true;
181
+ break;
182
+ case "--dry-run":
183
+ options.dryRun = true;
184
+ break;
185
+ case "-p":
186
+ case "--path":
187
+ i++;
188
+ if (i < args.length) {
189
+ options.path = args[i];
190
+ }
191
+ break;
192
+ case "-e":
193
+ case "--extensions":
194
+ i++;
195
+ if (i < args.length) {
196
+ options.extensions = args[i].split(",").map((e) => e.trim());
197
+ }
198
+ break;
199
+ case "-i":
200
+ case "--ignore":
201
+ i++;
202
+ if (i < args.length) {
203
+ options.ignore = args[i].split(",").map((p) => p.trim());
204
+ }
205
+ break;
206
+ default:
207
+ if (!arg.startsWith("-")) {
208
+ options.path = arg;
209
+ }
210
+ break;
211
+ }
212
+ i++;
213
+ }
214
+ return options;
215
+ }
216
+ function printHelp() {
217
+ console.log(`
218
+ ${colors.cyan}${colors.bright}ui-plume-migrate${colors.reset} v${VERSION}
219
+
220
+ A CLI tool to migrate from "prose" to "plume" typography prefix.
221
+
222
+ This tool automatically replaces all prose patterns with plume patterns:
223
+ - CSS variables: --tw-prose-* → --tw-plume-*
224
+ - Base class: prose → plume
225
+ - Color variants: prose-dark, prose-light, prose-lighter → plume-*
226
+ - Not-prose: not-prose → not-plume
227
+ - Element variants: prose-h1:, prose-p:, etc. → plume-*:
228
+
229
+ ${colors.bright}Usage:${colors.reset}
230
+ ui-plume-migrate [path] [options]
231
+
232
+ ${colors.bright}Arguments:${colors.reset}
233
+ path Path to scan (default: current directory)
234
+
235
+ ${colors.bright}Options:${colors.reset}
236
+ -p, --path <path> Path to scan (alternative to positional argument)
237
+ -e, --extensions <ext> Comma-separated file extensions
238
+ (default: js,jsx,ts,tsx,mdx,md)
239
+ -i, --ignore <patterns> Comma-separated patterns to ignore
240
+ (default: node_modules,dist,build,.git)
241
+ --dry-run Show what would change without making modifications
242
+ -h, --help Show this help message
243
+ -v, --version Show version number
244
+
245
+ ${colors.bright}Examples:${colors.reset}
246
+ ui-plume-migrate Migrate current directory
247
+ ui-plume-migrate ./src Migrate the src directory
248
+ ui-plume-migrate ./src --dry-run Preview changes without modifying files
249
+ ui-plume-migrate -e tsx,ts Only process TypeScript files
250
+ `);
251
+ }
252
+ function printVersion() {
253
+ console.log(`ui-plume-migrate v${VERSION}`);
254
+ }
255
+ function printResults(results, dryRun, duration) {
256
+ const filesWithChanges = results.filter((r) => r.hasChanges);
257
+ const totalMatches = filesWithChanges.reduce(
258
+ (sum, r) => sum + r.matches.length,
259
+ 0
260
+ );
261
+ console.log("");
262
+ if (filesWithChanges.length === 0) {
263
+ console.log(
264
+ `${colors.green}✓${colors.reset} No prose patterns found that need migration!`
265
+ );
266
+ console.log(
267
+ `${colors.dim} Scanned ${results.length} files in ${duration}ms${colors.reset}`
268
+ );
269
+ console.log("");
270
+ return;
271
+ }
272
+ const actionWord = dryRun ? "Would migrate" : "Migrated";
273
+ for (const result of filesWithChanges) {
274
+ console.log(
275
+ `${colors.cyan}${result.filePath}${colors.reset} (${result.matches.length} changes)`
276
+ );
277
+ const matchesByType = /* @__PURE__ */ new Map();
278
+ for (const match of result.matches) {
279
+ const key = `${match.original} → ${match.replacement}`;
280
+ matchesByType.set(key, (matchesByType.get(key) || 0) + 1);
281
+ }
282
+ for (const [change, count] of matchesByType) {
283
+ console.log(
284
+ ` ${colors.dim}${change}${colors.reset} ${colors.yellow}(${count}x)${colors.reset}`
285
+ );
286
+ }
287
+ console.log("");
288
+ }
289
+ console.log(`${colors.dim}${"─".repeat(50)}${colors.reset}`);
290
+ const fileWord = filesWithChanges.length === 1 ? "file" : "files";
291
+ const changeWord = totalMatches === 1 ? "change" : "changes";
292
+ if (dryRun) {
293
+ console.log(
294
+ `${colors.yellow}${colors.bright}Dry run:${colors.reset} ${totalMatches} ${changeWord} in ${filesWithChanges.length} ${fileWord} would be made`
295
+ );
296
+ console.log(
297
+ `${colors.dim}Run without --dry-run to apply changes${colors.reset}`
298
+ );
299
+ } else {
300
+ console.log(
301
+ `${colors.green}${colors.bright}${actionWord}:${colors.reset} ${totalMatches} ${changeWord} in ${filesWithChanges.length} ${fileWord}`
302
+ );
303
+ }
304
+ console.log(
305
+ `${colors.dim}Scanned ${results.length} files in ${duration}ms${colors.reset}`
306
+ );
307
+ console.log("");
308
+ }
309
+ function main() {
310
+ const args = process.argv.slice(2);
311
+ const options = parseArgs(args);
312
+ if (options.help) {
313
+ printHelp();
314
+ process.exit(0);
315
+ }
316
+ if (options.version) {
317
+ printVersion();
318
+ process.exit(0);
319
+ }
320
+ console.log("");
321
+ console.log(
322
+ `${colors.cyan}${colors.bright}ui-plume-migrate${colors.reset} - Migrating prose → plume...`
323
+ );
324
+ if (options.dryRun) {
325
+ console.log(
326
+ `${colors.yellow}(dry run - no files will be modified)${colors.reset}`
327
+ );
328
+ }
329
+ const startTime = Date.now();
330
+ let files;
331
+ try {
332
+ files = scanFiles(options.path, {
333
+ extensions: options.extensions,
334
+ ignore: options.ignore
335
+ });
336
+ } catch (error) {
337
+ const message = error instanceof Error ? error.message : String(error);
338
+ console.error(`${colors.red}Error:${colors.reset} ${message}`);
339
+ process.exit(1);
340
+ }
341
+ const results = [];
342
+ for (const file of files) {
343
+ if (!needsMigration(file.content)) {
344
+ results.push({
345
+ filePath: file.relativePath,
346
+ originalContent: file.content,
347
+ migratedContent: file.content,
348
+ matches: [],
349
+ hasChanges: false
350
+ });
351
+ continue;
352
+ }
353
+ if (options.dryRun) {
354
+ const { migrateContent: migrateContent2 } = require("./proseMigrator");
355
+ const migrationResult = migrateContent2(file.content);
356
+ results.push({
357
+ ...migrationResult,
358
+ filePath: file.relativePath
359
+ });
360
+ } else {
361
+ const result = migrateFileInPlace(file.absolutePath);
362
+ results.push({
363
+ ...result,
364
+ filePath: file.relativePath
365
+ });
366
+ }
367
+ }
368
+ const duration = Date.now() - startTime;
369
+ printResults(results, options.dryRun, duration);
370
+ const hasChanges = results.some((r) => r.hasChanges);
371
+ if (options.dryRun && hasChanges) {
372
+ process.exit(1);
373
+ }
374
+ }
375
+ main();
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- @versini/ui-styles v8.5.0
2
+ @versini/ui-styles v9.0.0
3
3
  © 2026 gizmette.com
4
4
  */
5
5
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@versini/ui-styles",
3
- "version": "8.5.0",
3
+ "version": "9.0.0",
4
4
  "license": "MIT",
5
5
  "author": "Arno Versini",
6
6
  "publishConfig": {
@@ -15,7 +15,8 @@
15
15
  "main": "dist/index.js",
16
16
  "types": "dist/index.d.ts",
17
17
  "bin": {
18
- "ui-doctor": "dist/cli/ui-doctor.js"
18
+ "ui-doctor": "dist/cli/ui-doctor.js",
19
+ "ui-plume-migrate": "dist/cli/ui-plume-migrate.js"
19
20
  },
20
21
  "files": [
21
22
  "dist",
@@ -41,7 +42,7 @@
41
42
  },
42
43
  "dependencies": {
43
44
  "@tailwindcss/container-queries": "0.1.1",
44
- "@versini/ui-typography": "1.0.0",
45
+ "@versini/ui-typography": "2.0.0",
45
46
  "culori": "4.0.2",
46
47
  "fs-extra": "11.3.3",
47
48
  "tailwindcss": "4.1.18"
@@ -49,5 +50,5 @@
49
50
  "devDependencies": {
50
51
  "rollup-plugin-copy": "3.5.0"
51
52
  },
52
- "gitHead": "6f0ba5933ab114566dc2065b20ca9ffb14c5aba4"
53
+ "gitHead": "ace8376be408d1f21ced7ba7a3259891476690e4"
53
54
  }