11#!/usr/bin/env node
22
3- import { promises as fs , readFileSync } from 'fs'
4- import path from 'path'
5- import { execFileSync } from 'child_process'
6- import process from 'process'
7-
8- const __dirname = path . dirname ( new URL ( import . meta. url ) . pathname )
3+ import { promises as fs , readFileSync } from 'node:fs'
4+ import path from 'node:path'
5+ import process from 'node:process'
6+ import { fileURLToPath } from 'node:url'
7+
8+ const __dirname = path . dirname ( fileURLToPath ( import . meta. url ) )
9+ const REGISTRY_URL = 'https://registry.npmjs.org/'
10+ const REQUEST_TIMEOUT = 10_000
11+
12+ const getPublishedPackage = async ( packageName ) => {
13+ const packageUrl = new URL ( encodeURIComponent ( packageName ) , REGISTRY_URL )
14+ const response = await fetch ( packageUrl , {
15+ headers : { Accept : 'application/vnd.npm.install-v1+json' } ,
16+ signal : AbortSignal . timeout ( REQUEST_TIMEOUT ) ,
17+ } )
18+
19+ if ( response . status === 404 ) {
20+ return { latestVersion : null , versions : new Set ( ) }
21+ }
22+ if ( ! response . ok ) {
23+ throw new Error ( `npm registry returned HTTP ${ response . status } for ${ packageName } ` )
24+ }
925
10- // Helper to run shell commands
11- const getPublishedVersion = ( packageName ) =>
12- execFileSync ( 'pnpm' , [ 'info' , packageName , 'version' ] , { encoding : 'utf-8' } ) . trim ( )
26+ const packageMetadata = await response . json ( )
27+ return {
28+ latestVersion : packageMetadata [ 'dist-tags' ] ?. latest || null ,
29+ versions : new Set ( Object . keys ( packageMetadata . versions || { } ) ) ,
30+ }
31+ }
1332
1433// Recursively find all directories containing package.json
1534const findNodeProjects = async ( dir ) => {
@@ -36,8 +55,8 @@ const findNodeProjects = async (dir) => {
3655 return projects
3756}
3857
39- // Compare local version with the latest npm version
40- const checkVersionMismatch = async ( projectPath ) => {
58+ // Check whether the exact local version has already been published to npm.
59+ const checkVersionPublished = async ( projectPath ) => {
4160 const packageJsonPath = path . join ( projectPath , 'package.json' )
4261
4362 try {
@@ -46,23 +65,22 @@ const checkVersionMismatch = async (projectPath) => {
4665 const localVersion = packageJson . version
4766 const packageName = packageJson . name
4867
49- // Get the latest version from npm using pnpm info
50- const npmVersion = getPublishedVersion ( packageName )
68+ const { latestVersion, versions } = await getPublishedPackage ( packageName )
5169
52- if ( localVersion === npmVersion ) {
53- console . log ( `✅ ${ packageName } is up to date (${ npmVersion } )` )
54- } else {
55- console . log ( `❌ ${ packageName } is outdated (local: ${ localVersion } , npm: ${ npmVersion } )` )
70+ if ( versions . has ( localVersion ) ) {
71+ const latestSuffix =
72+ localVersion === latestVersion ? '' : `; npm latest is ${ latestVersion || 'unset' } `
73+ console . log ( `✅ ${ packageName } @${ localVersion } is published${ latestSuffix } ` )
74+ return null
5675 }
5776
58- if ( localVersion !== npmVersion ) {
59- return { packageName, localVersion, latestVersion : npmVersion }
60- }
77+ console . log (
78+ `❌ ${ packageName } @${ localVersion } is not published (npm latest: ${ latestVersion || 'none' } )` ,
79+ )
80+ return { packageName, localVersion, latestVersion }
6181 } catch ( error ) {
6282 throw new Error ( `Failed to check npm version for ${ projectPath } ` , { cause : error } )
6383 }
64-
65- return null
6684}
6785
6886// Parse command-line arguments to get the project name if provided
@@ -79,8 +97,8 @@ const getArgs = () => {
7997 return specifiedProject
8098}
8199
82- // Main function to find outdated packages
83- const findOutdatedProjects = async ( ) => {
100+ // Main function to find unpublished local package versions
101+ const findUnpublishedProjects = async ( ) => {
84102 const specifiedProject = getArgs ( )
85103 let projects = await findNodeProjects ( path . join ( __dirname , '../ui' ) )
86104 console . log ( `Found ${ projects . length } projects under 'ui' directory.\n` )
@@ -100,29 +118,29 @@ const findOutdatedProjects = async () => {
100118 }
101119 }
102120
103- const outdatedProjects = [ ]
121+ const unpublishedProjects = [ ]
104122
105123 for ( const project of projects ) {
106- const result = await checkVersionMismatch ( project )
124+ const result = await checkVersionPublished ( project )
107125 if ( result ) {
108- outdatedProjects . push ( result )
126+ unpublishedProjects . push ( result )
109127 }
110128 }
111129
112130 console . log ( '\n==================================================\n' )
113131
114- if ( outdatedProjects . length === 0 ) {
115- console . log ( '✅ All projects have the latest versions pushed to npm.' )
132+ if ( unpublishedProjects . length === 0 ) {
133+ console . log ( '✅ Every local package version is published to npm.' )
116134 } else {
117- console . log ( 'Projects with outdated versions:\n' )
118- outdatedProjects . forEach ( ( { packageName, localVersion, latestVersion } ) => {
119- console . log ( `❌ ${ packageName } : Local version ${ localVersion } , NPM version ${ latestVersion } ` )
135+ console . log ( 'Local package versions not published to npm :\n' )
136+ unpublishedProjects . forEach ( ( { packageName, localVersion, latestVersion } ) => {
137+ console . log ( `❌ ${ packageName } : local ${ localVersion } , npm latest ${ latestVersion || 'none' } ` )
120138 } )
121139 process . exit ( 1 )
122140 }
123141}
124142
125- findOutdatedProjects ( ) . catch ( ( error ) => {
143+ findUnpublishedProjects ( ) . catch ( ( error ) => {
126144 console . error ( error )
127145 process . exitCode = 1
128146} )
0 commit comments