@openvue/migrate 0.0.1-alpha.2

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/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018-2025 PrimeTek
4
+ Copyright (c) 2026 OpenVue Foundation and contributors
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # @openvue/migrate
2
+
3
+ Codemod that migrates a PrimeVue project to [OpenVue](https://github.com/openvi-foundation/openvue). OpenVue keeps PrimeVue's public API unchanged (`usePrimeVue`, `PrimeVueResolver`, the `primevue` Nuxt config key, `p-` CSS classes, pass-through options), so migrating is a package rename — which is exactly and only what this tool does.
4
+
5
+ ## Usage
6
+
7
+ One command, from the root of your project (for monorepos, run it at the workspace root so overrides land in the right place):
8
+
9
+ ```sh
10
+ npx @openvue/migrate
11
+ ```
12
+
13
+ It rewrites dependencies and source files, then runs your package manager's install (detected from the lockfile) so the migrated project builds immediately.
14
+
15
+ **Already installed OpenVue yourself?** If `openvue` is found in your `package.json` or `node_modules`, the tool switches to sources-only mode: dependencies and `node_modules` are left completely untouched, no install runs, and only source-file imports are rewritten.
16
+
17
+ | Option | Description |
18
+ | -------------- | -------------------------------------------------------- |
19
+ | `[dir]` | Directory to migrate (defaults to the current directory) |
20
+ | `--dry` | Report what would change without writing any files |
21
+ | `--no-install` | Skip the package manager install after rewriting |
22
+ | `--no-alias` | Do not add the `primevue` -> `openvue` override |
23
+ | `--force` | Run even if the git working tree has uncommitted changes |
24
+
25
+ The tool refuses to run on a dirty git tree by default, so the migration is always a single reviewable, revertible diff.
26
+
27
+ ## What it does
28
+
29
+ 1. **Renames dependencies** in every `package.json` (monorepos supported): `dependencies`, `devDependencies`, `peerDependencies`, `optionalDependencies`, `peerDependenciesMeta`, `resolutions`, `overrides` and `pnpm.overrides`, including selector keys like `**/primevue` or `primevue@^4`. `workspace:`/`catalog:`/`npm:` protocol values are preserved, and `pnpm-workspace.yaml` catalogs are rewritten too. If both `primevue` and `openvue` entries exist, yours wins and the leftover is removed.
30
+ 2. **Rewrites module specifiers** in `.js`, `.mjs`, `.cjs`, `.ts`, `.mts`, `.cts`, `.jsx`, `.tsx`, `.vue`, `.astro` and `.mdx` files — static imports, `import type`, `require()`, dynamic `import()`, Nuxt `modules` arrays, Vite `optimizeDeps`/`transpile` entries, and generated `components.d.ts` files. Subpath (`primevue/button`) and scoped (`@primevue/*`) specifiers are unambiguous and rewritten anywhere; a bare `'primevue'` string is only rewritten in import positions (or anywhere in `*.config.*` files), so runtime data like `provider: 'primevue'` is never touched.
31
+ 3. **Adds a compatibility override** (`"primevue": "npm:openvue@<version>"` — in `overrides`, `pnpm.overrides` or `resolutions` depending on your package manager) so third-party libraries that depend or peer-depend on `primevue` resolve to OpenVue. Remove it once your whole dependency graph is OpenVue-native, or skip it with `--no-alias`.
32
+ 4. **Audits what's left**: after rewriting, it scans sources, styles, HTML and tsconfig files for surviving PrimeVue references (interpolated `import(\`primevue/${name}\`)`, CSS `@import`s, CDN urls, tsconfig `paths`) and lists each one with file and line, so it never reports success while actionable references remain.
33
+
34
+ The rename mapping:
35
+
36
+ | From | To |
37
+ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- |
38
+ | `primevue`, `primevue/*` | `openvue`, `openvue/*` |
39
+ | `@primevue/core`, `@primevue/forms`, `@primevue/icons`, `@primevue/themes`, `@primevue/nuxt-module`, `@primevue/auto-import-resolver`, `@primevue/metadata`, `@primevue/mcp` | same names under `@openvue/*` |
40
+
41
+ Left untouched on purpose — OpenVue still uses these upstream packages: `@primeuix/*`, `primeicons`, `primeflex`, `tailwindcss-primeui`. The `primevue` Nuxt config key also stays, because OpenVue's Nuxt module kept it.
42
+
43
+ Renamed dependencies are pinned to the exact OpenVue version while OpenVue is in alpha; once stable releases start, the codemod will emit caret ranges instead.
44
+
45
+ ## Try OpenVue without changing any code
46
+
47
+ If you just want to evaluate OpenVue first, add only the override yourself instead of running the codemod — no source changes needed:
48
+
49
+ ```jsonc
50
+ // package.json (npm / bun; use pnpm.overrides for pnpm, resolutions for yarn)
51
+ {
52
+ "overrides": {
53
+ "primevue": "npm:openvue@0.0.1-alpha.1"
54
+ }
55
+ }
56
+ ```
57
+
58
+ Once you decide to stay, run `npx @openvue/migrate` for the real rename.
59
+
60
+ ## Requirements
61
+
62
+ - Node.js 18+
63
+ - Your project should be on PrimeVue 4.x (the fork point). Coming from v3 or earlier, apply the [PrimeVue v4 migration guide](https://primevue.org/guides/migration/v4/) as well — this tool warns you if it detects a pre-4.x version.
@@ -0,0 +1,107 @@
1
+ declare const OPENVUE_VERSION: string;
2
+ /**
3
+ * Maps a PrimeVue module specifier or package name to its OpenVue equivalent.
4
+ * Returns null when the specifier is not a PrimeVue package (or not one that exists in OpenVue).
5
+ */
6
+ declare function renameSpecifier(specifier: string): string | null;
7
+
8
+ interface SourceRewriteOptions {
9
+ configFile?: boolean;
10
+ }
11
+ interface SourceRewriteResult {
12
+ code: string;
13
+ count: number;
14
+ skippedKeys: string[];
15
+ unknownScoped: string[];
16
+ }
17
+ /**
18
+ * Rewrites PrimeVue module specifiers inside string literals of a source file (.js/.ts/.vue/...).
19
+ * Subpath (`primevue/x`) and scoped (`@primevue/x`) specifiers are unambiguous and rewritten in any
20
+ * position — this also covers require(), dynamic import(), Nuxt `modules` arrays and Vite config
21
+ * entries. A bare `primevue` literal is only rewritten in import context (or anywhere in config
22
+ * files), so runtime data like `provider: 'primevue'` is never touched in application code.
23
+ */
24
+ declare function rewriteSource(code: string, options?: SourceRewriteOptions): SourceRewriteResult;
25
+ interface PackageJsonRewriteResult {
26
+ text: string;
27
+ changed: boolean;
28
+ renamed: string[];
29
+ notes: string[];
30
+ unknownScoped: string[];
31
+ oldPrimevueRange: string | null;
32
+ }
33
+ /**
34
+ * Rewrites PrimeVue dependency entries in a package.json, preserving key order,
35
+ * indentation style and any workspace:/catalog:/npm: protocol values.
36
+ */
37
+ declare function rewritePackageJson(text: string): PackageJsonRewriteResult;
38
+ type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';
39
+ interface AliasRewriteResult {
40
+ text: string;
41
+ added: boolean;
42
+ }
43
+ /**
44
+ * Adds a package-manager override aliasing `primevue` to `openvue`, so third-party libraries that
45
+ * depend on or peer-depend on `primevue` resolve to OpenVue after the migration. Without this,
46
+ * rename-only migration breaks any project whose dependency graph references PrimeVue transitively.
47
+ */
48
+ declare function addCompatAlias(text: string, packageManager: PackageManager): AliasRewriteResult;
49
+ interface YamlRewriteResult {
50
+ text: string;
51
+ count: number;
52
+ }
53
+ /**
54
+ * Rewrites PrimeVue entries in pnpm-workspace.yaml (catalog/catalogs/overrides sections).
55
+ */
56
+ declare function rewriteWorkspaceYaml(text: string): YamlRewriteResult;
57
+
58
+ type MigrateMode = 'full' | 'sources-only';
59
+ interface MigrateOptions {
60
+ dir: string;
61
+ dry?: boolean;
62
+ alias?: boolean;
63
+ }
64
+ interface ChangedFile {
65
+ file: string;
66
+ changes: number;
67
+ renamed?: string[];
68
+ }
69
+ interface ResidualReference {
70
+ file: string;
71
+ line: number;
72
+ text: string;
73
+ }
74
+ interface MigrateResult {
75
+ mode: MigrateMode;
76
+ changedFiles: ChangedFile[];
77
+ warnings: string[];
78
+ notes: string[];
79
+ residuals: ResidualReference[];
80
+ failed: boolean;
81
+ oldPrimevueRange: string | null;
82
+ packageManager: PackageManager;
83
+ installCommand: string;
84
+ }
85
+ /**
86
+ * A project that already declares or installed OpenVue has handled the dependency switch itself —
87
+ * the migration then only rewrites source files and must not touch package.json or node_modules.
88
+ */
89
+ declare function hasOpenVue(dir: string): boolean;
90
+ /**
91
+ * Detects the package manager from lockfiles or the packageManager field, walking up parent
92
+ * directories so running from a workspace package still reports the monorepo's real manager.
93
+ */
94
+ declare function detectPackageManager(dir: string): PackageManager;
95
+ /**
96
+ * Scans for PrimeVue references that survived the rewrite (interpolated template literals, CSS
97
+ * imports, tsconfig paths, CDN urls, intentionally kept keys) so the CLI never reports success
98
+ * while actionable references remain.
99
+ */
100
+ declare function auditResiduals(dir: string, files: string[]): ResidualReference[];
101
+ /**
102
+ * Runs the PrimeVue -> OpenVue migration over a project directory.
103
+ * Pure file transformation — git preflight, install and console output live in the CLI.
104
+ */
105
+ declare function migrate(options: MigrateOptions): MigrateResult;
106
+
107
+ export { type ChangedFile, type MigrateMode, type MigrateOptions, type MigrateResult, OPENVUE_VERSION, type ResidualReference, addCompatAlias, auditResiduals, detectPackageManager, hasOpenVue, migrate, renameSpecifier, rewritePackageJson, rewriteSource, rewriteWorkspaceYaml };
package/dist/index.js ADDED
@@ -0,0 +1,472 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { spawnSync } from "child_process";
5
+ import { existsSync as existsSync2, realpathSync, statSync } from "fs";
6
+ import { resolve } from "path";
7
+ import { fileURLToPath } from "url";
8
+
9
+ // src/mappings.ts
10
+ import { readFileSync } from "fs";
11
+ var OPENVUE_VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
12
+ var SCOPED_PACKAGES = ["core", "forms", "icons", "themes", "nuxt-module", "auto-import-resolver", "metadata", "mcp"];
13
+ function renameSpecifier(specifier) {
14
+ if (specifier === "primevue") return "openvue";
15
+ if (specifier.startsWith("primevue/")) return "openvue" + specifier.slice("primevue".length);
16
+ if (specifier.startsWith("@primevue/")) {
17
+ const rest = specifier.slice("@primevue/".length);
18
+ const name = rest.split("/")[0];
19
+ if (SCOPED_PACKAGES.includes(name)) return "@openvue/" + rest;
20
+ }
21
+ return null;
22
+ }
23
+ function isUnknownScopedPackage(specifier) {
24
+ return specifier.startsWith("@primevue/") && renameSpecifier(specifier) === null;
25
+ }
26
+
27
+ // src/migrate.ts
28
+ import { existsSync, readFileSync as readFileSync2, writeFileSync } from "fs";
29
+ import { basename, dirname, extname, join as join2, relative } from "path";
30
+
31
+ // src/rewrite.ts
32
+ var STRING_LITERAL = /(['"`])((?:\\.|(?!\1)[^\\\r\n])*)\1/g;
33
+ var IMPORT_CONTEXT = /(?:\bfrom|\bimport)\s*$|(?:\bimport|\brequire)\s*\(\s*$/;
34
+ var VALUE_PROTOCOL = /^(workspace:|catalog:|npm:|file:|link:|portal:|git|https?:)/;
35
+ function rewriteSource(code, options = {}) {
36
+ let out = "";
37
+ let last = 0;
38
+ let count = 0;
39
+ const skippedKeys = [];
40
+ const unknownScoped = [];
41
+ let match;
42
+ STRING_LITERAL.lastIndex = 0;
43
+ while ((match = STRING_LITERAL.exec(code)) !== null) {
44
+ const [literal, quote, content] = match;
45
+ if (quote === "`" && content.includes("${")) continue;
46
+ const renamed = renameSpecifier(content);
47
+ if (renamed === null) {
48
+ if (isUnknownScopedPackage(content)) unknownScoped.push(content);
49
+ continue;
50
+ }
51
+ if (content === "primevue") {
52
+ if (/^\s*:/.test(code.slice(match.index + literal.length))) {
53
+ skippedKeys.push(content);
54
+ continue;
55
+ }
56
+ const preceding = code.slice(Math.max(0, match.index - 40), match.index);
57
+ if (!options.configFile && !IMPORT_CONTEXT.test(preceding)) continue;
58
+ }
59
+ out += code.slice(last, match.index) + quote + renamed + quote;
60
+ last = match.index + literal.length;
61
+ count++;
62
+ }
63
+ return { code: out + code.slice(last), count, skippedKeys, unknownScoped };
64
+ }
65
+ var DEPENDENCY_SECTIONS = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies", "peerDependenciesMeta", "resolutions", "overrides"];
66
+ function renameDependencyKey(key) {
67
+ const direct = renameSpecifier(key);
68
+ if (direct !== null) return direct;
69
+ const selector = /^(.+)@([^@]+)$/.exec(key);
70
+ if (selector !== null && renameSpecifier(selector[1]) !== null) return renameSpecifier(selector[1]);
71
+ const path = /^(.*\/)(primevue|@primevue\/.+)$/.exec(key);
72
+ if (path !== null && renameSpecifier(path[2]) !== null) return path[1] + renameSpecifier(path[2]);
73
+ return null;
74
+ }
75
+ function renameDependencies(deps, result) {
76
+ const next = {};
77
+ let changed = false;
78
+ for (const [key, value] of Object.entries(deps)) {
79
+ const renamedKey = renameDependencyKey(key) ?? key;
80
+ let nextValue = value;
81
+ if (renamedKey !== key) {
82
+ if (Object.prototype.hasOwnProperty.call(deps, renamedKey)) {
83
+ result.notes.push(`kept existing ${renamedKey} entry and removed ${key}`);
84
+ changed = true;
85
+ continue;
86
+ }
87
+ changed = true;
88
+ result.renamed.push(`${key} -> ${renamedKey}`);
89
+ } else if (isUnknownScopedPackage(key)) {
90
+ result.unknownScoped.push(key);
91
+ }
92
+ if (value !== null && typeof value === "object") {
93
+ const child = renameDependencies(value, result);
94
+ nextValue = child.next;
95
+ changed = changed || child.changed;
96
+ } else if (renamedKey !== key && typeof value === "string" && !VALUE_PROTOCOL.test(value)) {
97
+ nextValue = OPENVUE_VERSION;
98
+ }
99
+ next[renamedKey] = nextValue;
100
+ }
101
+ return { next, changed };
102
+ }
103
+ function serializePackageJson(pkg, originalText) {
104
+ const indent = /\n([ \t]+)"/.exec(originalText)?.[1] ?? " ";
105
+ const newline = originalText.endsWith("\n") ? "\n" : "";
106
+ return JSON.stringify(pkg, null, indent) + newline;
107
+ }
108
+ function rewritePackageJson(text) {
109
+ const pkg = JSON.parse(text);
110
+ const result = { renamed: [], notes: [], unknownScoped: [] };
111
+ let changed = false;
112
+ let oldPrimevueRange = null;
113
+ for (const section of DEPENDENCY_SECTIONS) {
114
+ const deps = pkg[section];
115
+ if (deps === null || typeof deps !== "object") continue;
116
+ if (typeof deps["primevue"] === "string" && oldPrimevueRange === null) oldPrimevueRange = deps["primevue"];
117
+ const renamed = renameDependencies(deps, result);
118
+ pkg[section] = renamed.next;
119
+ changed = changed || renamed.changed;
120
+ }
121
+ if (pkg.pnpm && pkg.pnpm.overrides && typeof pkg.pnpm.overrides === "object") {
122
+ const renamed = renameDependencies(pkg.pnpm.overrides, result);
123
+ pkg.pnpm.overrides = renamed.next;
124
+ changed = changed || renamed.changed;
125
+ }
126
+ if (!changed) return { text, changed: false, ...result, oldPrimevueRange };
127
+ return { text: serializePackageJson(pkg, text), changed: true, ...result, oldPrimevueRange };
128
+ }
129
+ function addCompatAlias(text, packageManager) {
130
+ const pkg = JSON.parse(text);
131
+ const alias = `npm:openvue@${OPENVUE_VERSION}`;
132
+ if (packageManager === "yarn") {
133
+ pkg.resolutions = pkg.resolutions ?? {};
134
+ if (pkg.resolutions["primevue"] !== void 0) return { text, added: false };
135
+ pkg.resolutions["primevue"] = alias;
136
+ } else if (packageManager === "pnpm") {
137
+ pkg.pnpm = pkg.pnpm ?? {};
138
+ pkg.pnpm.overrides = pkg.pnpm.overrides ?? {};
139
+ if (pkg.pnpm.overrides["primevue"] !== void 0) return { text, added: false };
140
+ pkg.pnpm.overrides["primevue"] = alias;
141
+ } else {
142
+ pkg.overrides = pkg.overrides ?? {};
143
+ if (pkg.overrides["primevue"] !== void 0) return { text, added: false };
144
+ pkg.overrides["primevue"] = alias;
145
+ }
146
+ return { text: serializePackageJson(pkg, text), added: true };
147
+ }
148
+ var YAML_DEPENDENCY_LINE = /^(\s*)(['"]?)(primevue|@primevue\/[a-z0-9-]+)\2(\s*:\s*)(\S.*?)(\s*)$/;
149
+ function rewriteWorkspaceYaml(text) {
150
+ let count = 0;
151
+ const lines = text.split("\n").map((line) => {
152
+ const match = YAML_DEPENDENCY_LINE.exec(line);
153
+ if (match === null) return line;
154
+ const [, indent, quote, name, separator, value, trailing] = match;
155
+ const renamed = renameSpecifier(name);
156
+ if (renamed === null) return line;
157
+ const nextValue = VALUE_PROTOCOL.test(value) ? value : OPENVUE_VERSION;
158
+ count++;
159
+ return `${indent}${quote}${renamed}${quote}${separator}${nextValue}${trailing}`;
160
+ });
161
+ return { text: lines.join("\n"), count };
162
+ }
163
+
164
+ // src/walk.ts
165
+ import { readdirSync } from "fs";
166
+ import { join } from "path";
167
+ var EXCLUDED_DIRS = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", ".git", ".nuxt", ".output", ".next", ".vite", ".cache", ".turbo"]);
168
+ function walk(dir) {
169
+ const files = [];
170
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
171
+ if (entry.isDirectory()) {
172
+ if (!EXCLUDED_DIRS.has(entry.name)) files.push(...walk(join(dir, entry.name)));
173
+ } else if (entry.isFile()) {
174
+ files.push(join(dir, entry.name));
175
+ }
176
+ }
177
+ return files;
178
+ }
179
+
180
+ // src/migrate.ts
181
+ var SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([".js", ".mjs", ".cjs", ".ts", ".mts", ".cts", ".jsx", ".tsx", ".vue", ".astro", ".mdx"]);
182
+ var AUDIT_EXTRA_EXTENSIONS = /* @__PURE__ */ new Set([".css", ".scss", ".sass", ".less", ".styl", ".html", ".json", ".yaml", ".yml"]);
183
+ var CONFIG_FILE = /(^|\.)config\.[cm]?[jt]s$/i;
184
+ var RESIDUAL_REFERENCE = /['"`]primevue(?=[/'"`])|@primevue\/|\/primevue@/;
185
+ function readText(file) {
186
+ const text = readFileSync2(file, "utf8");
187
+ return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
188
+ }
189
+ function hasOpenVue(dir) {
190
+ if (existsSync(join2(dir, "node_modules", "openvue"))) return true;
191
+ const packageJson = join2(dir, "package.json");
192
+ if (!existsSync(packageJson)) return false;
193
+ try {
194
+ const pkg = JSON.parse(readText(packageJson));
195
+ for (const section of ["dependencies", "devDependencies"]) {
196
+ const deps = pkg[section];
197
+ if (deps && typeof deps === "object" && Object.keys(deps).some((key) => key === "openvue" || key.startsWith("@openvue/"))) return true;
198
+ }
199
+ } catch {
200
+ }
201
+ return false;
202
+ }
203
+ function detectPackageManager(dir) {
204
+ let current = dir;
205
+ for (; ; ) {
206
+ if (existsSync(join2(current, "pnpm-lock.yaml")) || existsSync(join2(current, "pnpm-workspace.yaml"))) return "pnpm";
207
+ if (existsSync(join2(current, "yarn.lock"))) return "yarn";
208
+ if (existsSync(join2(current, "bun.lock")) || existsSync(join2(current, "bun.lockb"))) return "bun";
209
+ if (existsSync(join2(current, "package-lock.json"))) return "npm";
210
+ const packageJson = join2(current, "package.json");
211
+ if (existsSync(packageJson)) {
212
+ try {
213
+ const field = JSON.parse(readText(packageJson)).packageManager;
214
+ const name = typeof field === "string" ? field.split("@")[0] : "";
215
+ if (name === "pnpm" || name === "yarn" || name === "bun" || name === "npm") return name;
216
+ } catch {
217
+ }
218
+ }
219
+ const parent = dirname(current);
220
+ if (parent === current) return "npm";
221
+ current = parent;
222
+ }
223
+ }
224
+ var INSTALL_COMMANDS = {
225
+ npm: "npm install",
226
+ pnpm: "pnpm install",
227
+ yarn: "yarn install",
228
+ bun: "bun install"
229
+ };
230
+ var LOCKFILES = /* @__PURE__ */ new Set(["package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock", "bun.lock"]);
231
+ function isAuditFile(file) {
232
+ const name = basename(file);
233
+ if (LOCKFILES.has(name)) return false;
234
+ return SOURCE_EXTENSIONS.has(extname(file)) || AUDIT_EXTRA_EXTENSIONS.has(extname(file)) || name === "package.json" || /^(tsconfig|jsconfig)[^/\\]*\.json$/.test(name);
235
+ }
236
+ function auditResiduals(dir, files) {
237
+ const residuals = [];
238
+ for (const file of files) {
239
+ if (!isAuditFile(file)) continue;
240
+ const lines = readText(file).split("\n");
241
+ for (let i = 0; i < lines.length; i++) {
242
+ if (RESIDUAL_REFERENCE.test(lines[i]) && !lines[i].includes("npm:openvue")) {
243
+ residuals.push({ file: relative(dir, file).replace(/\\/g, "/"), line: i + 1, text: lines[i].trim() });
244
+ }
245
+ }
246
+ }
247
+ return residuals;
248
+ }
249
+ function migrate(options) {
250
+ const { dir, dry = false, alias = true } = options;
251
+ const mode = hasOpenVue(dir) ? "sources-only" : "full";
252
+ const changedFiles = [];
253
+ const warnings = [];
254
+ const notes = [];
255
+ const skippedKeyFiles = /* @__PURE__ */ new Set();
256
+ const unknownScoped = /* @__PURE__ */ new Set();
257
+ const files = walk(dir);
258
+ let oldPrimevueRange = null;
259
+ let rootPackageJsonFailed = false;
260
+ for (const file of files) {
261
+ const name = basename(file);
262
+ const displayPath = relative(dir, file).replace(/\\/g, "/");
263
+ if (mode === "sources-only" && (name === "package.json" || name === "pnpm-workspace.yaml")) continue;
264
+ if (name === "package.json") {
265
+ const text = readText(file);
266
+ let result;
267
+ try {
268
+ result = rewritePackageJson(text);
269
+ } catch {
270
+ warnings.push(`${displayPath}: could not be parsed as JSON, skipped.`);
271
+ if (displayPath === "package.json") rootPackageJsonFailed = true;
272
+ continue;
273
+ }
274
+ if (result.oldPrimevueRange !== null && oldPrimevueRange === null) oldPrimevueRange = result.oldPrimevueRange;
275
+ result.unknownScoped.forEach((pkg) => unknownScoped.add(pkg));
276
+ result.notes.forEach((note) => notes.push(`${displayPath}: ${note}`));
277
+ if (result.changed) {
278
+ if (!dry) writeFileSync(file, result.text);
279
+ changedFiles.push({ file: displayPath, changes: result.renamed.length + result.notes.length, renamed: result.renamed });
280
+ }
281
+ } else if (name === "pnpm-workspace.yaml") {
282
+ const text = readFileSync2(file, "utf8");
283
+ const result = rewriteWorkspaceYaml(text);
284
+ if (result.count > 0) {
285
+ if (!dry) writeFileSync(file, result.text);
286
+ changedFiles.push({ file: displayPath, changes: result.count });
287
+ }
288
+ } else if (SOURCE_EXTENSIONS.has(extname(file))) {
289
+ const text = readFileSync2(file, "utf8");
290
+ const result = rewriteSource(text, { configFile: CONFIG_FILE.test(name) });
291
+ if (result.skippedKeys.length > 0) skippedKeyFiles.add(displayPath);
292
+ result.unknownScoped.forEach((pkg) => unknownScoped.add(pkg));
293
+ if (result.count > 0) {
294
+ if (!dry) writeFileSync(file, result.code);
295
+ changedFiles.push({ file: displayPath, changes: result.count });
296
+ }
297
+ }
298
+ }
299
+ const packageManager = detectPackageManager(dir);
300
+ const rootPackageJson = join2(dir, "package.json");
301
+ if (mode === "full" && alias && changedFiles.length > 0 && !rootPackageJsonFailed && existsSync(rootPackageJson)) {
302
+ try {
303
+ const result = addCompatAlias(readText(rootPackageJson), packageManager);
304
+ if (result.added) {
305
+ if (!dry) writeFileSync(rootPackageJson, result.text);
306
+ notes.push(`package.json: ${dry ? "would add" : "added"} override "primevue" -> "npm:openvue" so third-party libraries that (peer-)depend on primevue resolve to OpenVue. Remove it once your dependencies are OpenVue-native.`);
307
+ }
308
+ } catch {
309
+ warnings.push("package.json: could not add the primevue -> openvue compatibility override; add it manually if any of your dependencies require primevue.");
310
+ }
311
+ }
312
+ if (oldPrimevueRange !== null) {
313
+ const majorMatch = /\d+/.exec(oldPrimevueRange);
314
+ const major = majorMatch ? parseInt(majorMatch[0], 10) : null;
315
+ if (major !== null && major < 4) {
316
+ warnings.push(
317
+ `Your project used primevue@${oldPrimevueRange}, which predates the OpenVue fork point (PrimeVue 4.x). Apply the PrimeVue v${major} -> v4 migration guide before or after this codemod \u2014 renaming packages alone will not be enough.`
318
+ );
319
+ }
320
+ }
321
+ for (const file of skippedKeyFiles) {
322
+ warnings.push(`${file}: found a quoted 'primevue' object key and left it unchanged \u2014 the Nuxt module config key is still 'primevue' in OpenVue. Review it if it was meant as something else (e.g. a bundler alias).`);
323
+ }
324
+ for (const pkg of unknownScoped) {
325
+ warnings.push(`${pkg} is not a package OpenVue publishes; it was left unchanged. Review it manually.`);
326
+ }
327
+ const residuals = dry ? [] : auditResiduals(dir, files);
328
+ return { mode, changedFiles, warnings, notes, residuals, failed: rootPackageJsonFailed, oldPrimevueRange, packageManager, installCommand: INSTALL_COMMANDS[packageManager] };
329
+ }
330
+
331
+ // src/index.ts
332
+ var HELP = `openvue-migrate ${OPENVUE_VERSION}
333
+
334
+ Migrates a PrimeVue project to OpenVue in one run: rewrites package names in
335
+ package.json / pnpm-workspace.yaml and module specifiers in source files, adds
336
+ a package-manager override so third-party libraries that depend on primevue
337
+ resolve to OpenVue, and installs the migrated dependencies.
338
+
339
+ If OpenVue is already installed (found in package.json or node_modules), only
340
+ source files are rewritten \u2014 dependencies and node_modules are left untouched
341
+ and no install runs.
342
+
343
+ Usage:
344
+ npx @openvue/migrate [dir] [options]
345
+
346
+ Options:
347
+ --dry Report what would change without writing any files
348
+ --no-install Skip the package manager install after rewriting
349
+ --no-alias Do not add the primevue -> openvue override to package.json
350
+ --force Run even if the git working tree has uncommitted changes
351
+ --help Show this help
352
+ --version Print the version
353
+ `;
354
+ var KNOWN_FLAGS = ["--dry", "--force", "--no-install", "--no-alias"];
355
+ var useColor = process.stdout.isTTY && !process.env.NO_COLOR;
356
+ var bold = (text) => useColor ? `\x1B[1m${text}\x1B[0m` : text;
357
+ var green = (text) => useColor ? `\x1B[32m${text}\x1B[0m` : text;
358
+ var yellow = (text) => useColor ? `\x1B[33m${text}\x1B[0m` : text;
359
+ function isGitTreeDirty(dir) {
360
+ const inRepo = spawnSync("git", ["rev-parse", "--is-inside-work-tree"], { cwd: dir, encoding: "utf8", shell: process.platform === "win32" });
361
+ if (inRepo.status !== 0 || inRepo.stdout.trim() !== "true") return false;
362
+ const status = spawnSync("git", ["status", "--porcelain"], { cwd: dir, encoding: "utf8", shell: process.platform === "win32" });
363
+ return status.status === 0 && status.stdout.trim().length > 0;
364
+ }
365
+ function run() {
366
+ const args = process.argv.slice(2);
367
+ if (args.includes("--help") || args.includes("-h")) {
368
+ console.log(HELP);
369
+ return 0;
370
+ }
371
+ if (args.includes("--version") || args.includes("-v")) {
372
+ console.log(OPENVUE_VERSION);
373
+ return 0;
374
+ }
375
+ const dry = args.includes("--dry");
376
+ const force = args.includes("--force");
377
+ const install = !args.includes("--no-install");
378
+ const alias = !args.includes("--no-alias");
379
+ const unknownFlags = args.filter((arg) => arg.startsWith("-") && !KNOWN_FLAGS.includes(arg));
380
+ if (unknownFlags.length > 0) {
381
+ console.error(`Unknown option: ${unknownFlags[0]}
382
+
383
+ ${HELP}`);
384
+ return 1;
385
+ }
386
+ const positional = args.filter((arg) => !arg.startsWith("-"));
387
+ const dir = resolve(positional[0] ?? ".");
388
+ if (!existsSync2(dir) || !statSync(dir).isDirectory()) {
389
+ console.error(`Not a directory: ${dir}`);
390
+ return 1;
391
+ }
392
+ if (!dry && !force && isGitTreeDirty(dir)) {
393
+ console.error(yellow("Your git working tree has uncommitted changes. Commit or stash them first so the migration is easy to review and revert, or re-run with --force."));
394
+ return 1;
395
+ }
396
+ console.log(bold(`openvue-migrate ${OPENVUE_VERSION}`) + (dry ? yellow(" (dry run \u2014 no files will be written)") : ""));
397
+ console.log(`Migrating ${dir}
398
+ `);
399
+ const result = migrate({ dir, dry, alias });
400
+ if (result.changedFiles.length === 0) {
401
+ console.log("No PrimeVue references found \u2014 nothing to do.");
402
+ } else {
403
+ const total = result.changedFiles.reduce((sum, file) => sum + file.changes, 0);
404
+ console.log(bold(`${dry ? "Would rewrite" : "Rewrote"} ${total} reference${total === 1 ? "" : "s"} in ${result.changedFiles.length} file${result.changedFiles.length === 1 ? "" : "s"}:`));
405
+ for (const file of result.changedFiles) {
406
+ console.log(green(` ${file.file}`) + ` (${file.changes})`);
407
+ for (const rename of file.renamed ?? []) {
408
+ console.log(` ${rename}`);
409
+ }
410
+ }
411
+ }
412
+ for (const note of result.notes) {
413
+ console.log(`
414
+ Note: ${note}`);
415
+ }
416
+ for (const warning of result.warnings) {
417
+ console.log("\n" + yellow(`Warning: ${warning}`));
418
+ }
419
+ if (result.residuals.length > 0) {
420
+ console.log("\n" + yellow(bold(`${result.residuals.length} PrimeVue reference${result.residuals.length === 1 ? "" : "s"} could not be rewritten automatically \u2014 review them:`)));
421
+ for (const residual of result.residuals.slice(0, 25)) {
422
+ console.log(yellow(` ${residual.file}:${residual.line}`) + ` ${residual.text.slice(0, 120)}`);
423
+ }
424
+ if (result.residuals.length > 25) console.log(yellow(` ...and ${result.residuals.length - 25} more.`));
425
+ console.log("Some of these may be intentional (the Nuxt 'primevue' config key, docs, comments); anything that is an import or dependency needs a manual fix.");
426
+ }
427
+ if (result.failed) {
428
+ console.error(yellow(bold("\nThe root package.json could not be rewritten \u2014 fix it (see warnings above) and re-run. Skipping the install so a broken migration is not masked.")));
429
+ return 1;
430
+ }
431
+ if (result.changedFiles.length > 0 && !dry) {
432
+ if (result.mode === "sources-only") {
433
+ console.log(bold("\nOpenVue is already installed \u2014 dependencies and node_modules were left untouched; only source files were rewritten."));
434
+ } else if (install) {
435
+ console.log(bold(`
436
+ Running ${result.installCommand}...
437
+ `));
438
+ const installRun = spawnSync(result.installCommand, { cwd: dir, stdio: "inherit", shell: true });
439
+ if (installRun.status !== 0) {
440
+ console.error(yellow(`
441
+ ${result.installCommand} failed \u2014 fix the install errors above and re-run it. The source migration itself is complete.`));
442
+ return typeof installRun.status === "number" ? installRun.status : 1;
443
+ }
444
+ console.log(bold("\nDone. Review the diff and run your build."));
445
+ } else {
446
+ console.log(bold("\nNext step:"));
447
+ console.log(` Run: ${result.installCommand}`);
448
+ }
449
+ }
450
+ return 0;
451
+ }
452
+ function isMainModule() {
453
+ if (!process.argv[1]) return false;
454
+ try {
455
+ return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(resolve(process.argv[1]));
456
+ } catch {
457
+ return false;
458
+ }
459
+ }
460
+ if (isMainModule()) process.exit(run());
461
+ export {
462
+ OPENVUE_VERSION,
463
+ addCompatAlias,
464
+ auditResiduals,
465
+ detectPackageManager,
466
+ hasOpenVue,
467
+ migrate,
468
+ renameSpecifier,
469
+ rewritePackageJson,
470
+ rewriteSource,
471
+ rewriteWorkspaceYaml
472
+ };
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@openvue/migrate",
3
+ "version": "0.0.1-alpha.2",
4
+ "author": "OpenVue Contributors",
5
+ "description": "Codemod that migrates a PrimeVue project to OpenVue by rewriting package names and import specifiers",
6
+ "homepage": "https://github.com/openvi-foundation/openvue",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/openvi-foundation/openvue.git",
11
+ "directory": "packages/migrate"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/openvi-foundation/openvue/issues"
15
+ },
16
+ "keywords": [
17
+ "primevue",
18
+ "openvue",
19
+ "migration",
20
+ "codemod",
21
+ "vue"
22
+ ],
23
+ "bin": {
24
+ "openvue-migrate": "./dist/index.js"
25
+ },
26
+ "type": "module",
27
+ "main": "./dist/index.js",
28
+ "module": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.js",
34
+ "default": "./dist/index.js"
35
+ }
36
+ },
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "README.md",
43
+ "LICENSE.md"
44
+ ],
45
+ "devDependencies": {
46
+ "@types/node": "^22.12.0",
47
+ "tsup": "^8.1.0",
48
+ "typescript": "^5.7.3",
49
+ "vitest": "^3.0.0"
50
+ },
51
+ "engines": {
52
+ "node": ">=18.0.0"
53
+ },
54
+ "scripts": {
55
+ "build": "NODE_ENV=production INPUT_DIR=src/ OUTPUT_DIR=dist/ pnpm run build:package",
56
+ "build:package": "pnpm run build:prebuild && tsup",
57
+ "build:prebuild": "node ./scripts/prebuild.mjs",
58
+ "test:unit": "tsc --noEmit && vitest run",
59
+ "test:unit:watch": "vitest watch",
60
+ "typecheck": "tsc --noEmit",
61
+ "dev:link": "pnpm link --global && npm link"
62
+ }
63
+ }