11#!/usr/bin/env node
2- import { spawnSync } from " node:child_process" ;
3- import { existsSync , readFileSync , readdirSync } from " node:fs" ;
4- import { dirname , join , relative } from " node:path" ;
2+ import { spawnSync } from ' node:child_process' ;
3+ import { existsSync , readFileSync , readdirSync } from ' node:fs' ;
4+ import { dirname , join , relative } from ' node:path' ;
55
66const root = process . cwd ( ) ;
7- const config = JSON . parse ( readFileSync ( join ( root , ".changeset/config.json" ) , "utf8" ) ) ;
7+ const config = JSON . parse (
8+ readFileSync ( join ( root , '.changeset/config.json' ) , 'utf8' )
9+ ) ;
810const ignored = new Set ( config . ignore || [ ] ) ;
9- const access = config . access || " public" ;
11+ const access = config . access || ' public' ;
1012
1113function readJson ( path ) {
12- return JSON . parse ( readFileSync ( path , " utf8" ) ) ;
14+ return JSON . parse ( readFileSync ( path , ' utf8' ) ) ;
1315}
1416
1517function packageJsonPathsFromPnpmWorkspace ( ) {
16- const workspace = join ( root , " pnpm-workspace.yaml" ) ;
18+ const workspace = join ( root , ' pnpm-workspace.yaml' ) ;
1719 if ( ! existsSync ( workspace ) ) return [ ] ;
1820
1921 const patterns = [ ] ;
20- for ( const rawLine of readFileSync ( workspace , " utf8" ) . split ( / \r ? \n / ) ) {
22+ for ( const rawLine of readFileSync ( workspace , ' utf8' ) . split ( / \r ? \n / ) ) {
2123 const line = rawLine . trim ( ) ;
22- if ( ! line . startsWith ( "- " ) ) continue ;
23- const pattern = line . slice ( 2 ) . replace ( / ^ [ ' \" ] | [ ' \" ] $ / g, "" ) ;
24- if ( ! pattern || pattern . startsWith ( "!" ) ) continue ;
24+ if ( ! line . startsWith ( '- ' ) ) continue ;
25+ const pattern = line . slice ( 2 ) . replace ( / ^ [ ' \" ] | [ ' \" ] $ / g, '' ) ;
26+ if ( ! pattern || pattern . startsWith ( '!' ) ) continue ;
2527 patterns . push ( pattern ) ;
2628 }
2729
2830 const paths = [ ] ;
2931 for ( const pattern of patterns ) {
30- if ( pattern . endsWith ( "/*" ) ) {
32+ if ( pattern . endsWith ( '/*' ) ) {
3133 const dir = join ( root , pattern . slice ( 0 , - 2 ) ) ;
3234 if ( ! existsSync ( dir ) ) continue ;
3335 for ( const entry of readdirSync ( dir , { withFileTypes : true } ) ) {
34- if ( entry . isDirectory ( ) ) paths . push ( join ( dir , entry . name , "package.json" ) ) ;
36+ if ( entry . isDirectory ( ) )
37+ paths . push ( join ( dir , entry . name , 'package.json' ) ) ;
3538 }
36- } else if ( pattern . endsWith ( " /**" ) ) {
39+ } else if ( pattern . endsWith ( ' /**' ) ) {
3740 // These workspaces are docs/examples and not published packages in this release flow.
3841 continue ;
3942 } else {
40- paths . push ( join ( root , pattern , " package.json" ) ) ;
43+ paths . push ( join ( root , pattern , ' package.json' ) ) ;
4144 }
4245 }
4346
@@ -46,19 +49,24 @@ function packageJsonPathsFromPnpmWorkspace() {
4649
4750function packageJsonPaths ( ) {
4851 const paths = new Set ( packageJsonPathsFromPnpmWorkspace ( ) ) ;
49- paths . add ( join ( root , " package.json" ) ) ;
52+ paths . add ( join ( root , ' package.json' ) ) ;
5053 return [ ...paths ] . filter ( existsSync ) ;
5154}
5255
5356function versionExists ( name , version ) {
54- const result = spawnSync ( "npm" , [ "view" , `${ name } @${ version } ` , "version" , "--json" ] , {
55- encoding : "utf8" ,
56- stdio : [ "ignore" , "pipe" , "pipe" ] ,
57- } ) ;
57+ const result = spawnSync (
58+ 'npm' ,
59+ [ 'view' , `${ name } @${ version } ` , 'version' , '--json' ] ,
60+ {
61+ encoding : 'utf8' ,
62+ stdio : [ 'ignore' , 'pipe' , 'pipe' ] ,
63+ }
64+ ) ;
5865
5966 if ( result . status === 0 ) return true ;
6067 const output = `${ result . stdout } \n${ result . stderr } ` ;
61- if ( output . includes ( "E404" ) || output . includes ( "No match found" ) ) return false ;
68+ if ( output . includes ( 'E404' ) || output . includes ( 'No match found' ) )
69+ return false ;
6270
6371 process . stdout . write ( result . stdout ) ;
6472 process . stderr . write ( result . stderr ) ;
@@ -67,13 +75,48 @@ function versionExists(name, version) {
6775
6876function distTag ( version ) {
6977 const prerelease = version . match ( / ^ [ ^ - ] + - ( [ 0 - 9 A - Z a - z - ] + ) / ) ;
70- return prerelease ? prerelease [ 1 ] : "latest" ;
78+ return prerelease ? prerelease [ 1 ] : 'latest' ;
79+ }
80+
81+ function runGit ( args ) {
82+ const result = spawnSync ( 'git' , args , {
83+ cwd : root ,
84+ encoding : 'utf8' ,
85+ stdio : [ 'ignore' , 'pipe' , 'pipe' ] ,
86+ } ) ;
87+ process . stdout . write ( result . stdout ) ;
88+ process . stderr . write ( result . stderr ) ;
89+ if ( result . status !== 0 ) process . exit ( result . status || 1 ) ;
90+ }
91+
92+ function hasLocalGitTag ( tagName ) {
93+ const result = spawnSync (
94+ 'git' ,
95+ [ 'rev-parse' , '--verify' , '--quiet' , `refs/tags/${ tagName } ` ] ,
96+ {
97+ cwd : root ,
98+ stdio : 'ignore' ,
99+ }
100+ ) ;
101+ return result . status === 0 ;
102+ }
103+
104+ function createGitTag ( tagName ) {
105+ if ( hasLocalGitTag ( tagName ) ) {
106+ console . log ( `Git tag ${ tagName } already exists locally.` ) ;
107+ } else {
108+ runGit ( [ 'tag' , tagName , '-m' , tagName ] ) ;
109+ }
110+
111+ // changesets/action parses this line, then pushes the tag and creates the GitHub release.
112+ console . log ( `New tag: ${ tagName } ` ) ;
71113}
72114
73115const staged = [ ] ;
74116for ( const packageJsonPath of packageJsonPaths ( ) ) {
75117 const pkg = readJson ( packageJsonPath ) ;
76- if ( ! pkg . name || ! pkg . version || pkg . private || ignored . has ( pkg . name ) ) continue ;
118+ if ( ! pkg . name || ! pkg . version || pkg . private || ignored . has ( pkg . name ) )
119+ continue ;
77120 if ( versionExists ( pkg . name , pkg . version ) ) {
78121 console . log ( `Skipping ${ pkg . name } @${ pkg . version } ; already published.` ) ;
79122 continue ;
@@ -82,42 +125,49 @@ for (const packageJsonPath of packageJsonPaths()) {
82125 const packageDir = dirname ( packageJsonPath ) ;
83126 const tag = distTag ( pkg . version ) ;
84127 const args = [
85- " stage" ,
86- " publish" ,
128+ ' stage' ,
129+ ' publish' ,
87130 packageDir ,
88- " --provenance" ,
89- " --access" ,
131+ ' --provenance' ,
132+ ' --access' ,
90133 pkg . publishConfig ?. access || access ,
91- " --tag" ,
134+ ' --tag' ,
92135 tag ,
93- " --json" ,
136+ ' --json' ,
94137 ] ;
95138
96139 console . log ( `Staging ${ pkg . name } @${ pkg . version } with dist-tag ${ tag } ...` ) ;
97- const result = spawnSync ( "npm" , args , {
140+ const result = spawnSync ( 'pnpm' , args , {
98141 cwd : root ,
99- encoding : " utf8" ,
100- stdio : [ " ignore" , " pipe" , " pipe" ] ,
142+ encoding : ' utf8' ,
143+ stdio : [ ' ignore' , ' pipe' , ' pipe' ] ,
101144 } ) ;
102145 process . stdout . write ( result . stdout ) ;
103146 process . stderr . write ( result . stderr ) ;
104147 if ( result . status !== 0 ) process . exit ( result . status || 1 ) ;
105148
106149 const stageId = result . stdout . match ( / " s t a g e I d " \s * : \s * " ( [ ^ " ] + ) " / ) ?. [ 1 ] ;
150+ const tagName = `${ pkg . name } @${ pkg . version } ` ;
151+ createGitTag ( tagName ) ;
152+
107153 staged . push ( {
108154 name : pkg . name ,
109155 version : pkg . version ,
110- path : relative ( root , packageDir ) || "." ,
156+ path : relative ( root , packageDir ) || '.' ,
111157 stageId,
112158 } ) ;
113159}
114160
115161if ( staged . length === 0 ) {
116- console . log ( " No unpublished packages to stage." ) ;
162+ console . log ( ' No unpublished packages to stage.' ) ;
117163} else {
118- console . log ( " Staged packages:" ) ;
164+ console . log ( ' Staged packages:' ) ;
119165 for ( const pkg of staged ) {
120- console . log ( `- ${ pkg . name } @${ pkg . version } ${ pkg . stageId ? ` (${ pkg . stageId } )` : "" } ` ) ;
166+ console . log (
167+ `- ${ pkg . name } @${ pkg . version } ${ pkg . stageId ? ` (${ pkg . stageId } )` : '' } `
168+ ) ;
121169 }
122- console . log ( "Approve staged packages with `npm stage approve <stage-id>` after review." ) ;
170+ console . log (
171+ 'Approve staged packages with `npm stage approve <stage-id>` after review.'
172+ ) ;
123173}
0 commit comments