#!/usr/bin/env bash # Publish @mosaic/* packages to npmjs.org as @mosaicstack/* # # This script patches each package.json to: # 1. Rename @mosaic/X → @mosaicstack/X # 2. Replace workspace:^ deps with resolved versions using @mosaicstack/* names # 3. Run npm publish # 4. Restore original package.json # # Usage: # scripts/publish-npmjs.sh [--dry-run] [--filter ] # # Requirements: # - NPM_TOKEN env var set (npmjs.org auth token) # - jq installed # - Run from monorepo root after `pnpm build` set -euo pipefail DRY_RUN=false FILTER="" while [[ $# -gt 0 ]]; do case "$1" in --dry-run) DRY_RUN=true; shift ;; --filter) FILTER="$2"; shift 2 ;; *) echo "Unknown option: $1"; exit 1 ;; esac done REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" # Collect all publishable package directories (non-private, has publishConfig) PACKAGE_DIRS=() for pkg_json in "$REPO_ROOT"/packages/*/package.json "$REPO_ROOT"/plugins/*/package.json "$REPO_ROOT"/apps/gateway/package.json; do [[ -f "$pkg_json" ]] || continue is_private=$(jq -r '.private // false' "$pkg_json") [[ "$is_private" == "true" ]] && continue PACKAGE_DIRS+=("$(dirname "$pkg_json")") done echo "Found ${#PACKAGE_DIRS[@]} publishable packages" # Build a version map: @mosaic/X → version declare -A VERSION_MAP for dir in "${PACKAGE_DIRS[@]}"; do name=$(jq -r '.name' "$dir/package.json") version=$(jq -r '.version' "$dir/package.json") VERSION_MAP["$name"]="$version" done # Configure npmjs auth if [[ -z "${NPM_TOKEN:-}" ]] && [[ "$DRY_RUN" == "false" ]]; then echo "ERROR: NPM_TOKEN is required for publishing. Set it or use --dry-run." exit 1 fi publish_package() { local dir="$1" local orig_json="$dir/package.json" local backup="$dir/package.json.bak" local name name=$(jq -r '.name' "$orig_json") # Apply filter if set if [[ -n "$FILTER" ]] && [[ "$name" != "$FILTER" ]]; then return 0 fi local new_name="${name/@mosaic\//@mosaicstack/}" echo "" echo "━━━ Publishing $name → $new_name ━━━" # Backup original cp "$orig_json" "$backup" # Patch: rename package local patched patched=$(jq --arg new_name "$new_name" '.name = $new_name' "$orig_json") # Patch: publishConfig to npmjs patched=$(echo "$patched" | jq '.publishConfig = {"registry": "https://registry.npmjs.org/", "access": "public"}') # Patch: replace workspace:^ dependencies with @mosaicstack/* and resolved versions for dep_field in dependencies devDependencies peerDependencies; do if echo "$patched" | jq -e ".$dep_field" > /dev/null 2>&1; then local deps deps=$(echo "$patched" | jq -r ".$dep_field // {} | keys[]") for dep in $deps; do local dep_version dep_version=$(echo "$patched" | jq -r ".$dep_field[\"$dep\"]") # Only transform @mosaic/* workspace deps if [[ "$dep" == @mosaic/* ]] && [[ "$dep_version" == workspace:* ]]; then local new_dep="${dep/@mosaic\//@mosaicstack/}" local resolved="${VERSION_MAP[$dep]:-}" if [[ -z "$resolved" ]]; then echo " WARNING: No version found for $dep — using '*'" resolved="*" else # workspace:^ means ^version, workspace:* means * if [[ "$dep_version" == "workspace:^" ]]; then resolved="^$resolved" fi fi # Rename the dep key and set the resolved version patched=$(echo "$patched" | jq \ --arg field "$dep_field" \ --arg old_dep "$dep" \ --arg new_dep "$new_dep" \ --arg version "$resolved" \ '.[$field] |= (del(.[$old_dep]) | .[$new_dep] = $version)') fi done fi done # Write patched package.json echo "$patched" > "$orig_json" echo " Patched: $new_name" # Publish if [[ "$DRY_RUN" == "true" ]]; then echo " [DRY RUN] npm publish --dry-run" (cd "$dir" && npm publish --dry-run 2>&1) || true else echo " Publishing to npmjs..." (cd "$dir" && npm publish 2>&1) || echo " WARNING: Publish failed (may already exist at this version)" fi # Restore original mv "$backup" "$orig_json" echo " Restored original package.json" } # Set up npmrc for npmjs if [[ -n "${NPM_TOKEN:-}" ]]; then echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > ~/.npmrc fi # Publish in dependency order: packages first, then plugins, then apps echo "" echo "=== Publishing packages ===" for dir in "$REPO_ROOT"/packages/*/; do [[ -f "$dir/package.json" ]] || continue publish_package "$dir" done echo "" echo "=== Publishing plugins ===" for dir in "$REPO_ROOT"/plugins/*/; do [[ -f "$dir/package.json" ]] || continue publish_package "$dir" done echo "" echo "=== Publishing apps ===" publish_package "$REPO_ROOT/apps/gateway" echo "" echo "Done."