diff --git a/.changeset/config.json b/.changeset/config.json index 1b94957..ef56b12 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -3,9 +3,9 @@ "changelog": "@changesets/cli/changelog", "commit": false, "fixed": [], - "linked": [["@visual-json/core", "@visual-json/react"]], + "linked": [["@visual-json/core", "@visual-json/react", "@visual-json/vue"]], "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": ["@visual-json/web", "@visual-json/example-react"] + "ignore": ["@visual-json/web", "@visual-json/example-react", "@visual-json/example-vue"] } diff --git a/.gitignore b/.gitignore index 6fcac62..e80c462 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ coverage .env* !.env.example .DS_Store +*.bak .next next-env.d.ts test-results diff --git a/packages/@visual-json/react/package.json b/packages/@visual-json/react/package.json index 60ff94a..2583cc7 100644 --- a/packages/@visual-json/react/package.json +++ b/packages/@visual-json/react/package.json @@ -29,6 +29,8 @@ "scripts": { "build": "tsup", "dev": "tsup --watch", + "prepack": "node ../../../scripts/resolve-workspace-deps.mjs", + "postpack": "node ../../../scripts/restore-package-json.mjs", "check-types": "tsc --noEmit", "lint": "eslint .", "format": "prettier --write \"**/*.{ts,tsx}\"" diff --git a/packages/@visual-json/vue/package.json b/packages/@visual-json/vue/package.json index 204a10e..f275522 100644 --- a/packages/@visual-json/vue/package.json +++ b/packages/@visual-json/vue/package.json @@ -29,6 +29,8 @@ "scripts": { "build": "vite build", "dev": "vite build --watch", + "prepack": "node ../../../scripts/resolve-workspace-deps.mjs", + "postpack": "node ../../../scripts/restore-package-json.mjs", "check-types": "vue-tsc --noEmit", "lint": "eslint .", "format": "prettier --write \"**/*.{ts,vue}\"" diff --git a/scripts/resolve-workspace-deps.mjs b/scripts/resolve-workspace-deps.mjs new file mode 100644 index 0000000..22deccd --- /dev/null +++ b/scripts/resolve-workspace-deps.mjs @@ -0,0 +1,80 @@ +/** + * Resolves pnpm `workspace:*` dependencies to real version ranges before + * packing / publishing. Run as a `prepack` lifecycle script so the tarball + * sent to npm contains concrete versions while the source package.json keeps + * the workspace protocol for local development. + * + * A backup (package.json.bak) is written so `restore-package-json.mjs` can + * revert the file in `postpack`. + */ + +import { readFileSync, writeFileSync, copyFileSync, existsSync } from "fs"; +import { join, dirname } from "path"; +import { fileURLToPath } from "url"; + +const rootDir = join(dirname(fileURLToPath(import.meta.url)), ".."); +const pkgJsonPath = join(process.cwd(), "package.json"); +const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8")); + +function getWorkspacePackage(name) { + if (!name.startsWith("@")) return null; + const [scope, pkgName] = name.split("/"); + const candidate = join(rootDir, "packages", scope, pkgName, "package.json"); + if (!existsSync(candidate)) return null; + return JSON.parse(readFileSync(candidate, "utf-8")); +} + +function resolveVersion(version, depPkg) { + const specifier = version.slice("workspace:".length); + switch (specifier) { + case "*": + case "": + return depPkg.version; + case "^": + return `^${depPkg.version}`; + case "~": + return `~${depPkg.version}`; + default: + return specifier; + } +} + +let changed = false; + +for (const field of ["dependencies", "peerDependencies"]) { + const deps = pkg[field]; + if (!deps) continue; + + for (const [name, version] of Object.entries(deps)) { + if (!version.startsWith("workspace:")) continue; + const depPkg = getWorkspacePackage(name); + if (!depPkg) throw new Error(`Cannot resolve workspace dependency: ${name}`); + deps[name] = resolveVersion(version, depPkg); + console.log(` ${field} ${name}: ${version} → ${deps[name]}`); + changed = true; + } +} + +if (pkg.devDependencies) { + for (const [name, version] of Object.entries(pkg.devDependencies)) { + if (!version.startsWith("workspace:")) continue; + const depPkg = getWorkspacePackage(name); + if (depPkg?.private) { + delete pkg.devDependencies[name]; + console.log(` devDependencies ${name}: removed (private package)`); + changed = true; + } else if (depPkg) { + pkg.devDependencies[name] = resolveVersion(version, depPkg); + console.log( + ` devDependencies ${name}: ${version} → ${pkg.devDependencies[name]}` + ); + changed = true; + } + } +} + +if (changed) { + copyFileSync(pkgJsonPath, pkgJsonPath + ".bak"); + writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, 2) + "\n"); + console.log(`Resolved workspace deps for ${pkg.name}`); +} diff --git a/scripts/restore-package-json.mjs b/scripts/restore-package-json.mjs new file mode 100644 index 0000000..b290701 --- /dev/null +++ b/scripts/restore-package-json.mjs @@ -0,0 +1,14 @@ +/** + * Restores package.json from the backup created by resolve-workspace-deps.mjs. + * Run as a `postpack` lifecycle script. + */ + +import { existsSync, renameSync } from "fs"; +import { join } from "path"; + +const pkgJsonPath = join(process.cwd(), "package.json"); +const backupPath = pkgJsonPath + ".bak"; + +if (existsSync(backupPath)) { + renameSync(backupPath, pkgJsonPath); +}