@maz-ui/upgrade 5.0.0-beta.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 ADDED
@@ -0,0 +1,346 @@
1
+ # @maz-ui/upgrade
2
+
3
+ Automated source rewrites for migrating a project from **Maz-UI v4** to **v5**.
4
+
5
+ Run it once at the start of your migration to clear the mechanical changes
6
+ from your todo list, then walk through the rest of the
7
+ [migration guide](https://maz-ui.com/guide/migration-v5) for the items that
8
+ need a human eye (theme `foundation.radius` reshape, `MazIcon` API,
9
+ `MazBadge` numeric sizes, `MazChart` `update-mode`).
10
+
11
+ The companion [`@maz-ui/mcp`](https://maz-ui.com/guide/mcp) server lets your
12
+ AI assistant read the migration guide and apply the manual steps with full
13
+ context.
14
+
15
+ ## Table of contents
16
+
17
+ - [@maz-ui/upgrade](#maz-uiupgrade)
18
+ - [Table of contents](#table-of-contents)
19
+ - [Quick start](#quick-start)
20
+ - [Usage](#usage)
21
+ - [Options](#options)
22
+ - [What it scans](#what-it-scans)
23
+ - [How it ignores files](#how-it-ignores-files)
24
+ - [Transform groups](#transform-groups)
25
+ - [`imports`](#imports)
26
+ - [`props`](#props)
27
+ - [`css`](#css)
28
+ - [`config`](#config)
29
+ - [`deps`](#deps)
30
+ - [Dependency install](#dependency-install)
31
+ - [Output](#output)
32
+ - [Exit codes](#exit-codes)
33
+ - [What it does NOT do](#what-it-does-not-do)
34
+ - [Programmatic API](#programmatic-api)
35
+ - [License](#license)
36
+
37
+ ## Quick start
38
+
39
+ ```bash
40
+ # 1. Preview the rewrites without touching anything
41
+ npx @maz-ui/upgrade ./ --dry-run
42
+
43
+ # 2. Apply when the diff looks good
44
+ npx @maz-ui/upgrade ./
45
+
46
+ # 3. Run your typecheck / tests, fix the few remaining manual items
47
+ # listed at the end of the migration guide
48
+ ```
49
+
50
+ Working in a Vue/Nuxt monorepo? Point at the app:
51
+
52
+ ```bash
53
+ npx @maz-ui/upgrade ./apps/web --dry-run
54
+ ```
55
+
56
+ ## Usage
57
+
58
+ ```text
59
+ npx @maz-ui/upgrade [options] <path...>
60
+ ```
61
+
62
+ - **`<path...>`** — one or more directories to walk. Each path is resolved
63
+ against the current working directory. At least one path is required;
64
+ pass `./` to scan the whole project.
65
+ - The tool walks every supported file under those paths, applies the
66
+ enabled transform groups, and writes the result back in place (or just
67
+ prints what would change with `--dry-run`).
68
+
69
+ ## Options
70
+
71
+ | Flag | Description |
72
+ | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
73
+ | `-n`, `--dry-run` | Print every file that would change without writing it back. Use this first. |
74
+ | `--only=<groups>` | Comma-separated list of transform groups to run. See [Transform groups](#transform-groups). Default: `imports,props,css,config,deps` (all of them). |
75
+ | `--no-gitignore` | Do not respect the project's `.gitignore`. The built-in safe list of build / dependency directories still applies. |
76
+ | `--no-install` | Do not run the package manager after rewriting `package.json` files. By default the CLI runs `<pm> install` once at least one `package.json` changed. |
77
+ | `-h`, `--help` | Print the help screen and exit. |
78
+ | `-v`, `--version` | Print the upgrade tool version and exit. |
79
+
80
+ Examples:
81
+
82
+ ```bash
83
+ # Only rewrite import paths and component props
84
+ npx @maz-ui/upgrade ./src --only=imports,props
85
+
86
+ # Same, but also include files your .gitignore would otherwise skip
87
+ npx @maz-ui/upgrade ./src --only=css --no-gitignore
88
+
89
+ # Multiple roots in one run
90
+ npx @maz-ui/upgrade ./apps/web ./packages/ui-kit --dry-run
91
+ ```
92
+
93
+ ## What it scans
94
+
95
+ The CLI walks every file matching one of these extensions inside the
96
+ provided paths:
97
+
98
+ - **Vue:** `.vue`
99
+ - **CSS:** `.css`
100
+ - **TypeScript:** `.ts`, `.tsx`, `.cts`, `.mts`
101
+ - **JavaScript:** `.js`, `.jsx`, `.cjs`, `.mjs`
102
+ - **Manifest:** every `package.json` (root + nested in monorepo workspaces)
103
+
104
+ That covers component templates, scoped/global styles, plus `nuxt.config.ts`,
105
+ `main.ts` / `main.js`, custom theme preset files, plugin registration files,
106
+ and anything else where a v4 pattern can appear.
107
+
108
+ ## How it ignores files
109
+
110
+ Two layers, applied in order:
111
+
112
+ 1. **Built-in safe list** (always on): `node_modules`, `dist`, `build`,
113
+ `.nuxt`, `.output`, `.next`, `.svelte-kit`, `.turbo`, `.cache`,
114
+ `coverage`, `.vercel`, `.netlify`.
115
+ 2. **Your `.gitignore`** — read at the project root and any nested
116
+ `.gitignore` files, the same way `prettier`, `eslint` or
117
+ `@tailwindcss/upgrade` work. Pass `--no-gitignore` to skip this layer
118
+ (the safe list above still applies).
119
+
120
+ If your build output, generated types, or vendored copies of maz-ui live
121
+ outside both lists, add them to a local `.gitignore` (or to a sibling
122
+ `.git/info/exclude`) so they're skipped on the next run.
123
+
124
+ ## Transform groups
125
+
126
+ By default all five groups run. Use `--only=<a,b,...>` to scope the run.
127
+ Each group is independent and idempotent: re-running the tool on
128
+ already-migrated code is a no-op.
129
+
130
+ ### `imports`
131
+
132
+ CSS subpath rename, applied to imports in `.vue` and JS/TS files:
133
+
134
+ | v4 | v5 |
135
+ | ---------------------------- | --------------------------- |
136
+ | `import 'maz-ui/styles'` | `import 'maz-ui/style.css'` |
137
+ | `import 'maz-ui/aos-styles'` | `import 'maz-ui/aos.css'` |
138
+
139
+ ### `props`
140
+
141
+ Component prop / slot / class renames, applied to `.vue` files only.
142
+ Patterns specific enough to rewrite globally (e.g. `left-icon` /
143
+ `right-icon`) are handled file-wide. Patterns that would otherwise be
144
+ ambiguous (`variant=`, `color="background"`) are rewritten **only inside
145
+ `<Maz...>` opening tags**, never on your own components.
146
+
147
+ | v4 | v5 |
148
+ | ----------------------------------------------------------------- | ----------------------------------------------------------- |
149
+ | `left-icon="x"` / `right-icon="x"` | `start-icon="x"` / `end-icon="x"` |
150
+ | `:left-icon="x"` / `:right-icon="x"` | `:start-icon="x"` / `:end-icon="x"` |
151
+ | `<template #left-icon>` / `<template #right-icon>` | `<template #start-icon>` / `<template #end-icon>` |
152
+ | `<template #icon-left>` / `<template #icon-right>` (MazContainer) | `<template #icon-start>` / `<template #icon-end>` |
153
+ | `footer-align="left"` / `footer-align="right"` | `footer-align="start"` / `footer-align="end"` |
154
+ | `<MazDrawer variant="left">` / `<MazDrawer variant="right">` | `<MazDrawer variant="start">` / `<MazDrawer variant="end">` |
155
+ | `<Maz... color="background">` | `<Maz... color="surface">` |
156
+ | `active-color="background"` | `active-color="surface"` |
157
+ | `rounded-size="base"` | `rounded-size="md"` |
158
+ | `.--has-left-icon` / `.--has-right-icon` (CSS selectors) | `.--has-start-icon` / `.--has-end-icon` |
159
+
160
+ ### `css`
161
+
162
+ CSS variable renames + `hsl(var(...))` collapse, applied to `.vue` style
163
+ blocks, `.css` files, and any inline strings in `.ts`/`.js`:
164
+
165
+ | v4 | v5 |
166
+ | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------- |
167
+ | `var(--maz-background)` / `var(--maz-background-700)` / `var(--maz-background-foreground)` | `var(--maz-surface)` / `var(--maz-surface-700)` / `var(--maz-surface-foreground)` |
168
+ | `var(--maz-border)` / `var(--maz-border-700)` | `var(--maz-divider)` / `var(--maz-divider-700)` |
169
+ | `hsl(var(--maz-primary))` | `var(--maz-primary)` |
170
+ | `hsl(var(--maz-primary) / 0.5)` | `color-mix(in srgb, var(--maz-primary) 0.5, transparent)` |
171
+
172
+ Foundation tokens that happen to start with the same prefix (e.g.
173
+ `--maz-border-width`, `--maz-border-radius`) are explicitly **not**
174
+ renamed.
175
+
176
+ ### `config`
177
+
178
+ Plugin / Nuxt module / preset config keys, applied to `.vue` and JS/TS
179
+ files:
180
+
181
+ | v4 | v5 |
182
+ | ----------------------------------------------------------------------- | ------------------------------------- |
183
+ | Nuxt `mazUi.css.injectMainCss` | `mazUi.css.injectCss` |
184
+ | Theme `strategy: 'hybrid'` | `strategy: 'runtime'` |
185
+ | `MazUiTheme` plugin options `injectCriticalCSS: …` / `injectFullCSS: …` | line removed (silently ignored in v5) |
186
+ | Nuxt theme `injectAllCSSOnServer: …` | line removed (silently ignored in v5) |
187
+ | Custom preset `colors.{light,dark}.background` | `colors.{light,dark}.surface` |
188
+ | Custom preset `colors.{light,dark}.border` | `colors.{light,dark}.divider` |
189
+
190
+ The preset color rename is scoped to `light: { … }` / `dark: { … }`
191
+ blocks, so unrelated CSS-in-JS / JSX style props elsewhere are left
192
+ alone.
193
+
194
+ ### `deps`
195
+
196
+ Dependency version bumps in every `package.json` the walk picks up
197
+ (root + nested workspaces). Bumps `maz-ui` and every `@maz-ui/*`
198
+ entry across `dependencies`, `devDependencies` and `peerDependencies`
199
+ to `^5.0.0`. Existing JSON indentation and trailing newline are
200
+ preserved; non-maz-ui dependencies are never touched.
201
+
202
+ | What | Behaviour |
203
+ | ---------------------------------------------------------------------------- | --------------------- |
204
+ | `maz-ui`, `@maz-ui/*` semver ranges (`^4.x`, `~4.x`, `4.x`, `4.0.0`, …) | Rewritten to `^5.0.0` |
205
+ | `workspace:*`, `link:…`, `file:…`, `portal:…`, `npm:…`, `http(s):…`, `git+…` | Left untouched |
206
+ | Dist tags (`latest`, `next`, `beta`, `alpha`, `canary`) | Left untouched |
207
+ | Anything else (`vue`, `chart.js`, your own packages, …) | Left untouched |
208
+
209
+ `vue-chartjs` is **not** removed automatically, even though `MazChart`
210
+ no longer depends on it — keep it if you use it directly elsewhere,
211
+ remove it manually otherwise.
212
+
213
+ ## Dependency install
214
+
215
+ Once the rewrite is done, if at least one `package.json` changed and
216
+ the `deps` group ran, the CLI:
217
+
218
+ 1. Detects your package manager from the lockfile in `cwd`:
219
+ `bun.lockb`/`bun.lock` → **bun**, `pnpm-lock.yaml` → **pnpm**,
220
+ `yarn.lock` → **yarn**, `package-lock.json` → **npm**, otherwise
221
+ defaults to **npm**.
222
+ 2. Runs `<pm> install` with inherited stdio so you see the install
223
+ progress live. The CLI exits with the install's exit code if it
224
+ fails.
225
+
226
+ Pass `--no-install` to skip step 2 — the CLI then prints the command
227
+ you should run manually:
228
+
229
+ ```text
230
+ package.json files updated. Run `pnpm install` to apply.
231
+ ```
232
+
233
+ `--dry-run` always skips the install regardless of `--no-install`.
234
+
235
+ ## Output
236
+
237
+ For each file that the tool changes, you get one line on stdout:
238
+
239
+ ```text
240
+ updated: src/components/Header.vue
241
+ updated: app.vue
242
+ updated: nuxt.config.ts
243
+ ```
244
+
245
+ In dry-run mode the prefix becomes `[dry-run] would update: ...`.
246
+
247
+ At the end of the run you get a summary:
248
+
249
+ ```text
250
+ Scanned 142 files, updated 27.
251
+ Groups applied: imports, props, css, config, deps
252
+
253
+ Detected package manager: pnpm. Running `pnpm install`…
254
+
255
+ [ … pnpm install output … ]
256
+
257
+ Next: see https://maz-ui.com/guide/migration-v5 for the manual steps
258
+ (foundation.radius → scales.rounded.md, MazIcon API, MazBadge sizes, MazChart update-mode).
259
+ ```
260
+
261
+ ## Exit codes
262
+
263
+ - `0` — completed successfully (zero or more files updated; install
264
+ step, if any, succeeded).
265
+ - `1` — argument error (unknown option or unknown group), no path
266
+ provided, or unhandled error during the walk.
267
+ - Any other code propagated from the package manager when the install
268
+ step fails (CLI exits with that exact status).
269
+
270
+ ## What it does NOT do
271
+
272
+ Some changes need a human (or your AI assistant via the
273
+ [`@maz-ui/mcp`](https://maz-ui.com/guide/mcp) server) — see the
274
+ [migration guide](https://maz-ui.com/guide/migration-v5) for the full
275
+ context:
276
+
277
+ - **`MazIcon` API simplification** — `name`/`path`/`src` props collapsing
278
+ into a single `icon` prop needs the original asset path to rewrite
279
+ correctly.
280
+ - **`MazBadge` numeric `size`** — picking the right keyword (`mini`/`xs`/…)
281
+ is a judgment call.
282
+ - **Theme preset radius reshape** — `foundation.radius` →
283
+ `scales.rounded.md` needs to move the value to a new sibling block,
284
+ which is too structural for a safe regex. The simpler color key renames
285
+ (`background` → `surface`, `border` → `divider`) are handled by the
286
+ `config` group, and the `package.json` version bump by the `deps`
287
+ group.
288
+ - **`MazChart` `update-mode` default** — switched from `'default'` to
289
+ `'none'` in v5; deciding whether to restore the v4 animation is up to
290
+ you.
291
+ - **Tailwind v3 → v4 migration** — if you ship your own Tailwind utilities
292
+ alongside maz-ui, run `npx @tailwindcss/upgrade` first; it covers 95% of
293
+ the Tailwind side. See the
294
+ [Tailwind integration page](https://maz-ui.com/guide/tailwind).
295
+
296
+ ## Programmatic API
297
+
298
+ Each transform is exported as a pure `(string) => string` function, so
299
+ you can call them from your own scripts or compose a custom orchestrator:
300
+
301
+ ```ts
302
+ import type { TransformGroup, TransformOptions } from '@maz-ui/upgrade'
303
+ import {
304
+ ALL_GROUPS,
305
+ transformConfig,
306
+ transformCssVars,
307
+ transformDeps,
308
+ transformFile,
309
+ transformHslVar,
310
+ transformImports,
311
+ transformPresetColors,
312
+ transformProps,
313
+ } from '@maz-ui/upgrade'
314
+
315
+ transformImports(`import 'maz-ui/styles'`)
316
+ // → `import 'maz-ui/style.css'`
317
+
318
+ transformProps(`<MazBtn left-icon="x" />`)
319
+ // → `<MazBtn start-icon="x" />`
320
+
321
+ transformHslVar(`color: hsl(var(--maz-primary) / 0.5)`)
322
+ // → `color: color-mix(in srgb, var(--maz-primary) 0.5, transparent)`
323
+
324
+ transformCssVars(`background: var(--maz-background-700);`)
325
+ // → `background: var(--maz-surface-700);`
326
+
327
+ transformPresetColors(`light: { background: '0 0% 100%', border: '220 13% 91%' }`)
328
+ // → `light: { surface: '0 0% 100%', divider: '220 13% 91%' }`
329
+
330
+ transformConfig(`{ css: { injectMainCss: true } }`)
331
+ // → `{ css: { injectCss: true } }`
332
+
333
+ transformDeps(`{ "dependencies": { "maz-ui": "^4.9.3" } }`)
334
+ // → `{ "dependencies": { "maz-ui": "^5.0.0" } }`
335
+
336
+ // Compose at the file level (extension-aware orchestration):
337
+ transformFile('nuxt.config.ts', source, { groups: ['imports', 'config'] })
338
+ // Pass no options to apply ALL_GROUPS.
339
+ transformFile('app.vue', source)
340
+ ```
341
+
342
+ `ALL_GROUPS` is exported as `readonly TransformGroup[]` (`['imports', 'props', 'css', 'config', 'deps']`) so you can derive your own subsets.
343
+
344
+ ## License
345
+
346
+ MIT
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+
package/dist/cli.mjs ADDED
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from 'node:child_process';
3
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
4
+ import { resolve, dirname } from 'node:path';
5
+ import process from 'node:process';
6
+ import { logger } from '@maz-ui/node';
7
+ import { globby } from 'globby';
8
+ import { transformFile, ALL_GROUPS } from './index.mjs';
9
+
10
+ const DEFAULT_IGNORES = [
11
+ "**/node_modules/**",
12
+ "**/dist/**",
13
+ "**/build/**",
14
+ "**/.nuxt/**",
15
+ "**/.output/**",
16
+ "**/.next/**",
17
+ "**/.svelte-kit/**",
18
+ "**/.turbo/**",
19
+ "**/.cache/**",
20
+ "**/coverage/**",
21
+ "**/.vercel/**",
22
+ "**/.netlify/**"
23
+ ];
24
+ function parseArgs(argv) {
25
+ const roots = [];
26
+ let dryRun = false;
27
+ let gitignore = true;
28
+ let install = true;
29
+ let groups = ALL_GROUPS;
30
+ for (let i = 2; i < argv.length; i++) {
31
+ const arg = argv[i];
32
+ if (arg === "--dry-run" || arg === "-n") {
33
+ dryRun = true;
34
+ } else if (arg === "--no-gitignore") {
35
+ gitignore = false;
36
+ } else if (arg === "--no-install") {
37
+ install = false;
38
+ } else if (arg === "--help" || arg === "-h") {
39
+ printHelp();
40
+ process.exit(0);
41
+ } else if (arg === "--version" || arg === "-v") {
42
+ printVersion();
43
+ process.exit(0);
44
+ } else if (arg.startsWith("--only=")) {
45
+ groups = parseGroups(arg.slice("--only=".length));
46
+ } else if (arg === "--only") {
47
+ groups = parseGroups(argv[++i] ?? "");
48
+ } else if (arg.startsWith("-")) {
49
+ logger.error(`Unknown option: ${arg}`);
50
+ process.exit(1);
51
+ } else {
52
+ roots.push(arg);
53
+ }
54
+ }
55
+ if (roots.length === 0) {
56
+ logger.error("At least one path is required.");
57
+ printHelp();
58
+ process.exit(1);
59
+ }
60
+ return { dryRun, groups, roots, gitignore, install };
61
+ }
62
+ function parseGroups(value) {
63
+ const parts = value.split(",").map((p) => p.trim()).filter(Boolean);
64
+ for (const p of parts) {
65
+ if (!ALL_GROUPS.includes(p)) {
66
+ logger.error(`Unknown group: ${p}. Valid: ${ALL_GROUPS.join(", ")}`);
67
+ process.exit(1);
68
+ }
69
+ }
70
+ return parts;
71
+ }
72
+ function printHelp() {
73
+ logger.log(`@maz-ui/upgrade \u2014 automated rewrites for migrating a project from Maz-UI v4 to v5.
74
+
75
+ Usage:
76
+ npx @maz-ui/upgrade [options] <path...>
77
+
78
+ Options:
79
+ -n, --dry-run Print what would change without writing any file.
80
+ --only=<groups> Comma-separated list of transform groups to apply.
81
+ Default: ${ALL_GROUPS.join(",")}.
82
+ --no-gitignore Do not respect .gitignore (scan everything except the
83
+ built-in safe list of build / dependency directories).
84
+ --no-install Do not run the package manager after rewriting
85
+ package.json files. Default: install runs after a
86
+ successful rewrite.
87
+ -h, --help Show this help.
88
+ -v, --version Print the upgrade tool version.
89
+
90
+ Transform groups:
91
+ imports maz-ui/styles \u2192 maz-ui/style.css, maz-ui/aos-styles \u2192 maz-ui/aos.css
92
+ props left-icon/right-icon \u2192 start-icon/end-icon (props, slots,
93
+ #icon-left/#icon-right, --has-*-icon classes), footer-align,
94
+ variant, color="background", active-color, rounded-size="base"
95
+ css --maz-background \u2192 --maz-surface, --maz-border \u2192 --maz-divider,
96
+ hsl(var(--maz-X)) collapse (incl. alpha \u2192 color-mix)
97
+ config Nuxt injectMainCss \u2192 injectCss, theme strategy 'hybrid' \u2192 'runtime',
98
+ removes dropped theme options (injectCriticalCSS, injectFullCSS,
99
+ injectAllCSSOnServer), preset colors.{light,dark}.background \u2192 surface
100
+ and .border \u2192 divider
101
+ deps Bumps every maz-ui / @maz-ui/* entry in package.json
102
+ (dependencies, devDependencies, peerDependencies) to ^5.0.0.
103
+ Workspace, link, file and url specs are left untouched.
104
+
105
+ Scans the given paths for .vue, .css, .ts/.tsx/.cts/.mts,
106
+ .js/.jsx/.cjs/.mjs and package.json files. By default it respects
107
+ your .gitignore (plus a built-in safe list of node_modules, dist,
108
+ build, .nuxt, .output, .next, .svelte-kit, .turbo, .cache, coverage,
109
+ .vercel, .netlify). Pass --no-gitignore to skip the .gitignore step.
110
+
111
+ After rewriting, if at least one package.json changed and the deps
112
+ group ran, the CLI detects your package manager (pnpm / yarn / bun /
113
+ npm \u2014 based on the lockfile in cwd) and runs <pm> install. Pass
114
+ --no-install to skip that step.
115
+
116
+ What it does NOT do (handle by hand or with the @maz-ui/mcp server):
117
+ - Reshape foundation.radius into scales.rounded.md (move + key restructure).
118
+ - Map MazIcon name=/path=/src= props to the new icon= API.
119
+ - Map numeric MazBadge size to the keyword scale.
120
+ - MazChart updateMode default change (judgment call).
121
+
122
+ Full migration guide: https://maz-ui.com/guide/migration-v5
123
+ `);
124
+ }
125
+ function printVersion() {
126
+ const pkg = JSON.parse(
127
+ readFileSync(resolve(dirname(new URL(import.meta.url).pathname), "..", "package.json"), "utf8")
128
+ );
129
+ logger.log(pkg.version);
130
+ }
131
+ function detectPackageManager(cwd) {
132
+ if (existsSync(resolve(cwd, "bun.lockb")) || existsSync(resolve(cwd, "bun.lock")))
133
+ return "bun";
134
+ if (existsSync(resolve(cwd, "pnpm-lock.yaml")))
135
+ return "pnpm";
136
+ if (existsSync(resolve(cwd, "yarn.lock")))
137
+ return "yarn";
138
+ if (existsSync(resolve(cwd, "package-lock.json")))
139
+ return "npm";
140
+ return "npm";
141
+ }
142
+ function runInstall(pm, cwd) {
143
+ const result = spawnSync(pm, ["install"], {
144
+ cwd,
145
+ stdio: "inherit",
146
+ shell: process.platform === "win32"
147
+ });
148
+ return result.status ?? 1;
149
+ }
150
+ function maybeInstallDeps(opts, depsChanged, cwd) {
151
+ if (!depsChanged || opts.dryRun)
152
+ return;
153
+ const pm = detectPackageManager(cwd);
154
+ if (!opts.install || !opts.groups.includes("deps")) {
155
+ logger.log(`
156
+ package.json files updated. Run \`${pm} install\` to apply.`);
157
+ return;
158
+ }
159
+ logger.log(`
160
+ Detected package manager: ${pm}. Running \`${pm} install\`\u2026
161
+ `);
162
+ const code = runInstall(pm, cwd);
163
+ if (code !== 0) {
164
+ logger.error(`
165
+ \`${pm} install\` exited with code ${code}.`);
166
+ process.exit(code);
167
+ }
168
+ }
169
+ async function run() {
170
+ const opts = parseArgs(process.argv);
171
+ const cwd = process.cwd();
172
+ let scanned = 0;
173
+ let changed = 0;
174
+ let depsChanged = false;
175
+ for (const root of opts.roots) {
176
+ const absoluteRoot = resolve(cwd, root);
177
+ const files = await globby(
178
+ [
179
+ "**/*.vue",
180
+ "**/*.css",
181
+ "**/*.{js,mjs,cjs,jsx}",
182
+ "**/*.{ts,mts,cts,tsx}",
183
+ "**/package.json"
184
+ ],
185
+ {
186
+ cwd: absoluteRoot,
187
+ absolute: true,
188
+ gitignore: opts.gitignore,
189
+ ignore: DEFAULT_IGNORES
190
+ }
191
+ );
192
+ for (const file of files) {
193
+ scanned += 1;
194
+ const before = readFileSync(file, "utf8");
195
+ const after = transformFile(file, before, { groups: opts.groups });
196
+ if (after === before)
197
+ continue;
198
+ changed += 1;
199
+ if (file.endsWith("package.json"))
200
+ depsChanged = true;
201
+ const shown = file.replace(`${cwd}/`, "");
202
+ logger.log(`${opts.dryRun ? "[dry-run] would update" : "updated"}: ${shown}`);
203
+ if (!opts.dryRun) {
204
+ writeFileSync(file, after, "utf8");
205
+ }
206
+ }
207
+ }
208
+ const prefix = opts.dryRun ? "would update" : "updated";
209
+ logger.log(`
210
+ Scanned ${scanned} files, ${prefix} ${changed}.`);
211
+ logger.log(`Groups applied: ${opts.groups.join(", ")}`);
212
+ maybeInstallDeps(opts, depsChanged, cwd);
213
+ logger.log(`
214
+ Next: see https://maz-ui.com/guide/migration-v5 for the manual steps`);
215
+ logger.log(`(foundation.radius \u2192 scales.rounded.md, MazIcon API, MazBadge sizes, MazChart update-mode).`);
216
+ }
217
+ run().catch((err) => {
218
+ logger.error(err);
219
+ process.exit(1);
220
+ });
@@ -0,0 +1,16 @@
1
+ declare function transformImports(content: string): string;
2
+ declare function transformProps(content: string): string;
3
+ declare function transformCssVars(content: string): string;
4
+ declare function transformHslVar(content: string): string;
5
+ declare function transformConfig(content: string): string;
6
+ declare function transformPresetColors(content: string): string;
7
+ declare function transformDeps(content: string): string;
8
+ type TransformGroup = 'imports' | 'props' | 'css' | 'config' | 'deps';
9
+ declare const ALL_GROUPS: readonly TransformGroup[];
10
+ interface TransformOptions {
11
+ groups?: readonly TransformGroup[];
12
+ }
13
+ declare function transformFile(filename: string, content: string, options?: TransformOptions): string;
14
+
15
+ export { ALL_GROUPS, transformConfig, transformCssVars, transformDeps, transformFile, transformHslVar, transformImports, transformPresetColors, transformProps };
16
+ export type { TransformGroup, TransformOptions };
@@ -0,0 +1,16 @@
1
+ declare function transformImports(content: string): string;
2
+ declare function transformProps(content: string): string;
3
+ declare function transformCssVars(content: string): string;
4
+ declare function transformHslVar(content: string): string;
5
+ declare function transformConfig(content: string): string;
6
+ declare function transformPresetColors(content: string): string;
7
+ declare function transformDeps(content: string): string;
8
+ type TransformGroup = 'imports' | 'props' | 'css' | 'config' | 'deps';
9
+ declare const ALL_GROUPS: readonly TransformGroup[];
10
+ interface TransformOptions {
11
+ groups?: readonly TransformGroup[];
12
+ }
13
+ declare function transformFile(filename: string, content: string, options?: TransformOptions): string;
14
+
15
+ export { ALL_GROUPS, transformConfig, transformCssVars, transformDeps, transformFile, transformHslVar, transformImports, transformPresetColors, transformProps };
16
+ export type { TransformGroup, TransformOptions };
package/dist/index.mjs ADDED
@@ -0,0 +1,129 @@
1
+ const IMPORT_PATH = /(['"])maz-ui\/(styles|aos-styles)\1/g;
2
+ function transformImports(content) {
3
+ return content.replace(IMPORT_PATH, (_, q, sub) => `${q}maz-ui/${sub === "styles" ? "style.css" : "aos.css"}${q}`);
4
+ }
5
+ const ICON_ATTR = /(\s:?)(left|right)-icon\b/g;
6
+ const ICON_SLOT = /#(left|right)-icon\b/g;
7
+ const ICON_LEFT_RIGHT_SLOT = /#icon-(left|right)\b/g;
8
+ const FOOTER_ALIGN = /(\sfooter-align\s*=\s*['"])(left|right)(['"])/g;
9
+ const ROUNDED_SIZE_BASE = /(\srounded-size\s*=\s*['"])base(['"])/g;
10
+ const ACTIVE_COLOR_BG = /(\sactive-color\s*=\s*['"])background(['"])/g;
11
+ const HAS_ICON_CLASS = /\.--has-(left|right)-icon\b/g;
12
+ const MAZ_OPEN_TAG = /<Maz[A-Z]\w*\b[^>]*>/g;
13
+ const VARIANT_LR = /(\svariant\s*=\s*['"])(left|right)(['"])/g;
14
+ const COLOR_BG = /(\scolor\s*=\s*['"])background(['"])/g;
15
+ function flipDirection(value) {
16
+ return value === "left" ? "start" : "end";
17
+ }
18
+ function transformProps(content) {
19
+ let out = content;
20
+ out = out.replace(ICON_ATTR, (_, prefix, dir) => `${prefix}${flipDirection(dir)}-icon`);
21
+ out = out.replace(ICON_SLOT, (_, dir) => `#${flipDirection(dir)}-icon`);
22
+ out = out.replace(ICON_LEFT_RIGHT_SLOT, (_, dir) => `#icon-${flipDirection(dir)}`);
23
+ out = out.replace(FOOTER_ALIGN, (_, p, dir, s) => `${p}${flipDirection(dir)}${s}`);
24
+ out = out.replace(ROUNDED_SIZE_BASE, (_, p, s) => `${p}md${s}`);
25
+ out = out.replace(ACTIVE_COLOR_BG, (_, p, s) => `${p}surface${s}`);
26
+ out = out.replace(HAS_ICON_CLASS, (_, dir) => `.--has-${flipDirection(dir)}-icon`);
27
+ out = out.replace(MAZ_OPEN_TAG, (tag) => tag.replace(VARIANT_LR, (_, p, dir, s) => `${p}${flipDirection(dir)}${s}`).replace(COLOR_BG, (_, p, s) => `${p}surface${s}`));
28
+ return out;
29
+ }
30
+ const MAZ_BACKGROUND = /--maz-background(?:(?![-\w])|(?=-(?:foreground|\d)))/g;
31
+ const MAZ_BORDER_COLOR = /--maz-border(?:(?![-\w])|(?=-(?:foreground|\d)))/g;
32
+ const MAZ_VAR = String.raw`var\(--(?:maz|m)-[\w-]+\)`;
33
+ const HSL_WITH_ALPHA = new RegExp(String.raw`hsl\(\s*(${MAZ_VAR})\s*\/\s*([^)]+)\)`, "g");
34
+ const HSL_NO_ALPHA = new RegExp(String.raw`hsl\(\s*(${MAZ_VAR})\s*\)`, "g");
35
+ function transformCssVars(content) {
36
+ return content.replace(MAZ_BACKGROUND, "--maz-surface").replace(MAZ_BORDER_COLOR, "--maz-divider");
37
+ }
38
+ function transformHslVar(content) {
39
+ return content.replace(HSL_WITH_ALPHA, (_, v, alpha) => `color-mix(in srgb, ${v} ${alpha.trim()}, transparent)`).replace(HSL_NO_ALPHA, (_, v) => v);
40
+ }
41
+ const NUXT_INJECT_MAIN_CSS = /\binjectMainCss\b/g;
42
+ const STRATEGY_HYBRID = /(\bstrategy\s*:\s*['"])hybrid(['"])/g;
43
+ const DEPRECATED_THEME_OPTIONS = /^[^\S\n]*(?:injectCriticalCSS|injectFullCSS|injectAllCSSOnServer)\s*:[^,\n]*,?[^\S\n]*\n?/gm;
44
+ function transformConfig(content) {
45
+ return content.replace(NUXT_INJECT_MAIN_CSS, "injectCss").replace(STRATEGY_HYBRID, (_, p, s) => `${p}runtime${s}`).replace(DEPRECATED_THEME_OPTIONS, "");
46
+ }
47
+ const LIGHT_DARK_BLOCK = /(\b(?:light|dark)\s*:\s*\{)([^{}]*)(\})/g;
48
+ function transformPresetColors(content) {
49
+ return content.replace(LIGHT_DARK_BLOCK, (_, open, body, close) => {
50
+ const renamed = body.replace(/(['"]?)background\1(\s*:)/g, (_match, q, suffix) => `${q}surface${q}${suffix}`).replace(/(['"]?)border\1(\s*:)/g, (_match, q, suffix) => `${q}divider${q}${suffix}`);
51
+ return `${open}${renamed}${close}`;
52
+ });
53
+ }
54
+ const MAZ_UI_PKG = /^(?:maz-ui|@maz-ui\/.+)$/;
55
+ const TARGET_VERSION = "^5.0.0";
56
+ const PROTECTED_PREFIX = /^(?:workspace|link|file|portal|npm|http|https|git\+|github):/;
57
+ function shouldBump(name, range) {
58
+ if (!MAZ_UI_PKG.test(name))
59
+ return false;
60
+ if (PROTECTED_PREFIX.test(range))
61
+ return false;
62
+ if (["latest", "next", "beta", "alpha", "canary", "*"].includes(range))
63
+ return false;
64
+ return range !== TARGET_VERSION;
65
+ }
66
+ function detectIndent(content) {
67
+ const match = content.match(/\n([ \t]+)"/);
68
+ return match ? match[1] : 2;
69
+ }
70
+ const DEP_FIELDS = ["dependencies", "devDependencies", "peerDependencies"];
71
+ function transformDeps(content) {
72
+ let pkg;
73
+ try {
74
+ pkg = JSON.parse(content);
75
+ } catch {
76
+ return content;
77
+ }
78
+ if (!pkg || typeof pkg !== "object" || Array.isArray(pkg))
79
+ return content;
80
+ let changed = false;
81
+ for (const field of DEP_FIELDS) {
82
+ const deps = pkg[field];
83
+ if (!deps || typeof deps !== "object" || Array.isArray(deps))
84
+ continue;
85
+ const map = deps;
86
+ for (const [name, range] of Object.entries(map)) {
87
+ if (typeof range !== "string")
88
+ continue;
89
+ if (shouldBump(name, range)) {
90
+ map[name] = TARGET_VERSION;
91
+ changed = true;
92
+ }
93
+ }
94
+ }
95
+ if (!changed)
96
+ return content;
97
+ const indent = detectIndent(content);
98
+ const trailingNewline = content.endsWith("\n") ? "\n" : "";
99
+ return JSON.stringify(pkg, null, indent) + trailingNewline;
100
+ }
101
+ const ALL_GROUPS = ["imports", "props", "css", "config", "deps"];
102
+ function transformFile(filename, content, options = {}) {
103
+ const groups = options.groups ?? ALL_GROUPS;
104
+ const enabled = (g) => groups.includes(g);
105
+ const isVue = filename.endsWith(".vue");
106
+ const isCss = filename.endsWith(".css");
107
+ const isJs = /\.[cm]?[jt]sx?$/.test(filename);
108
+ const isPackageJson = /(?:^|[\\/])package\.json$/.test(filename);
109
+ let out = content;
110
+ if (enabled("imports") && (isVue || isJs))
111
+ out = transformImports(out);
112
+ if (enabled("props") && isVue)
113
+ out = transformProps(out);
114
+ if (enabled("css")) {
115
+ if (isVue || isCss)
116
+ out = transformCssVars(out);
117
+ if (isVue || isCss || isJs)
118
+ out = transformHslVar(out);
119
+ }
120
+ if (enabled("config") && (isVue || isJs)) {
121
+ out = transformConfig(out);
122
+ out = transformPresetColors(out);
123
+ }
124
+ if (enabled("deps") && isPackageJson)
125
+ out = transformDeps(out);
126
+ return out;
127
+ }
128
+
129
+ export { ALL_GROUPS, transformConfig, transformCssVars, transformDeps, transformFile, transformHslVar, transformImports, transformPresetColors, transformProps };
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@maz-ui/upgrade",
3
+ "type": "module",
4
+ "version": "5.0.0-beta.0",
5
+ "description": "Automated source rewrites for migrating a project from Maz-UI v4 to v5.",
6
+ "author": "Louis Mazel <me@loicmazuel.com>",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/LouisMazel/maz-ui.git"
11
+ },
12
+ "bugs": "https://github.com/LouisMazel/maz-ui/issues",
13
+ "keywords": [
14
+ "maz-ui",
15
+ "upgrade",
16
+ "migration",
17
+ "v5"
18
+ ],
19
+ "sideEffects": false,
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.mjs"
27
+ }
28
+ },
29
+ "main": "./dist/index.mjs",
30
+ "types": "./dist/index.d.ts",
31
+ "bin": {
32
+ "maz-ui-upgrade": "./dist/cli.mjs"
33
+ },
34
+ "files": [
35
+ "LICENSE",
36
+ "README.md",
37
+ "dist"
38
+ ],
39
+ "engines": {
40
+ "node": ">=20.19.0"
41
+ },
42
+ "dependencies": {
43
+ "globby": "^16.2.0",
44
+ "@maz-ui/node": "5.0.0-beta.0"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^25.6.0",
48
+ "typescript": "^5.9.3",
49
+ "unbuild": "^3.6.1",
50
+ "vitest": "4.1.5",
51
+ "@maz-ui/eslint-config": "5.0.0-beta.0"
52
+ },
53
+ "lint-staged": {
54
+ "*.{js,ts,mjs,mts,cjs,md,yml,json}": "cross-env NODE_ENV=production eslint --fix"
55
+ },
56
+ "scripts": {
57
+ "build": "unbuild",
58
+ "dev": "unbuild --stub",
59
+ "typecheck": "tsc --noEmit --skipLibCheck",
60
+ "test:unit": "vitest run",
61
+ "test:unit:watch": "vitest watch",
62
+ "test:unit:coverage": "vitest run --coverage",
63
+ "lint": "cross-env NODE_ENV=production eslint .",
64
+ "lint:fix": "pnpm lint --fix"
65
+ }
66
+ }