@@ -2,10 +2,29 @@ import fs from 'node:fs';
22import path from 'node:path' ;
33
44const ROOT = process . cwd ( ) ;
5+ const META_DIR = process . env . UPGRADE_DEPS_META_DIR ;
6+
7+ const isFullSha = ( s ) => / ^ [ 0 - 9 a - f ] { 40 } $ / . test ( s ) ;
8+
9+ /** @type {Map<string, { old: string | null, new: string, tag?: string }> } */
10+ const changes = new Map ( ) ;
11+
12+ function recordChange ( name , oldValue , newValue , tag ) {
13+ const entry = { old : oldValue ?? null , new : newValue } ;
14+ if ( tag ) {
15+ entry . tag = tag ;
16+ }
17+ changes . set ( name , entry ) ;
18+ if ( oldValue !== newValue ) {
19+ console . log ( ` ${ name } : ${ oldValue ?? '(unset)' } -> ${ newValue } ` ) ;
20+ } else {
21+ console . log ( ` ${ name } : ${ newValue } (unchanged)` ) ;
22+ }
23+ }
524
625// ============ GitHub API ============
7- async function getLatestTagCommit ( owner , repo ) {
8- const res = await fetch ( `https://api.github.com/repos/${ owner } /${ repo } /tags` , {
26+ async function getLatestTag ( owner , repo ) {
27+ const res = await fetch ( `https://api.github.com/repos/${ owner } /${ repo } /tags?per_page=1 ` , {
928 headers : {
1029 Authorization : `token ${ process . env . GITHUB_TOKEN } ` ,
1130 Accept : 'application/vnd.github.v3+json' ,
@@ -18,11 +37,11 @@ async function getLatestTagCommit(owner, repo) {
1837 if ( ! Array . isArray ( tags ) || ! tags . length ) {
1938 throw new Error ( `No tags found for ${ owner } /${ repo } ` ) ;
2039 }
21- if ( ! tags [ 0 ] ?. commit ?. sha ) {
22- throw new Error ( `Invalid tag structure for ${ owner } /${ repo } : missing commit SHA` ) ;
40+ if ( ! tags [ 0 ] ?. commit ?. sha || ! tags [ 0 ] ?. name ) {
41+ throw new Error ( `Invalid tag structure for ${ owner } /${ repo } : missing SHA or name ` ) ;
2342 }
24- console . log ( `${ repo } -> ${ tags [ 0 ] . name } ` ) ;
25- return tags [ 0 ] . commit . sha ;
43+ console . log ( `${ repo } -> ${ tags [ 0 ] . name } ( ${ tags [ 0 ] . commit . sha . slice ( 0 , 7 ) } ) ` ) ;
44+ return { sha : tags [ 0 ] . commit . sha , tag : tags [ 0 ] . name } ;
2645}
2746
2847// ============ npm Registry ============
@@ -45,11 +64,16 @@ async function updateUpstreamVersions() {
4564 const filePath = path . join ( ROOT , 'packages/tools/.upstream-versions.json' ) ;
4665 const data = JSON . parse ( fs . readFileSync ( filePath , 'utf8' ) ) ;
4766
48- // rolldown -> rolldown/rolldown
49- data . rolldown . hash = await getLatestTagCommit ( 'rolldown' , 'rolldown' ) ;
50-
51- // vite -> vitejs/vite
52- data [ 'vite' ] . hash = await getLatestTagCommit ( 'vitejs' , 'vite' ) ;
67+ const oldRolldownHash = data . rolldown . hash ;
68+ const oldViteHash = data [ 'vite' ] . hash ;
69+ const [ rolldown , vite ] = await Promise . all ( [
70+ getLatestTag ( 'rolldown' , 'rolldown' ) ,
71+ getLatestTag ( 'vitejs' , 'vite' ) ,
72+ ] ) ;
73+ data . rolldown . hash = rolldown . sha ;
74+ data [ 'vite' ] . hash = vite . sha ;
75+ recordChange ( 'rolldown' , oldRolldownHash , rolldown . sha , rolldown . tag ) ;
76+ recordChange ( 'vite' , oldViteHash , vite . sha , vite . tag ) ;
5377
5478 fs . writeFileSync ( filePath , JSON . stringify ( data , null , 2 ) + '\n' ) ;
5579 console . log ( 'Updated .upstream-versions.json' ) ;
@@ -60,39 +84,96 @@ async function updatePnpmWorkspace(versions) {
6084 const filePath = path . join ( ROOT , 'pnpm-workspace.yaml' ) ;
6185 let content = fs . readFileSync ( filePath , 'utf8' ) ;
6286
63- // Update vitest-dev override (handle pre-release versions like -beta.1, -rc.0)
64- // Handle both quoted ('npm:vitest@^...') and unquoted (npm:vitest@^...) forms
65- content = content . replace (
66- / v i t e s t - d e v : ' ? n p m : v i t e s t @ \^ [ \d . ] + ( - [ \w . ] + ) ? ' ? / ,
67- `vitest-dev: 'npm:vitest@^${ versions . vitest } '` ,
68- ) ;
69-
70- // Update tsdown in catalog (handle pre-release versions)
71- content = content . replace ( / t s d o w n : \^ [ \d . ] + ( - [ \w . ] + ) ? / , `tsdown: ^${ versions . tsdown } ` ) ;
72-
73- // Update @oxc -node/cli in catalog
74- content = content . replace (
75- / ' @ o x c - n o d e \/ c l i ' : \^ [ \d . ] + ( - [ \w . ] + ) ? / ,
76- `'@oxc-node/cli': ^${ versions . oxcNodeCli } ` ,
77- ) ;
78-
79- // Update @oxc -node/core in catalog
80- content = content . replace (
81- / ' @ o x c - n o d e \/ c o r e ' : \^ [ \d . ] + ( - [ \w . ] + ) ? / ,
82- `'@oxc-node/core': ^${ versions . oxcNodeCore } ` ,
83- ) ;
84-
85- // Update oxfmt in catalog
86- content = content . replace ( / o x f m t : = [ \d . ] + ( - [ \w . ] + ) ? / , `oxfmt: =${ versions . oxfmt } ` ) ;
87-
88- // Update oxlint in catalog (but not oxlint-tsgolint)
89- content = content . replace ( / o x l i n t : = [ \d . ] + ( - [ \w . ] + ) ? \n / , `oxlint: =${ versions . oxlint } \n` ) ;
87+ // oxlint's trailing \n in the pattern disambiguates from oxlint-tsgolint.
88+ const entries = [
89+ {
90+ name : 'vitest' ,
91+ pattern : / v i t e s t - d e v : n p m : v i t e s t @ \^ ( [ \d . ] + (?: - [ \w . ] + ) ? ) / ,
92+ replacement : `vitest-dev: npm:vitest@^${ versions . vitest } ` ,
93+ newVersion : versions . vitest ,
94+ } ,
95+ {
96+ name : 'tsdown' ,
97+ pattern : / t s d o w n : \^ ( [ \d . ] + (?: - [ \w . ] + ) ? ) / ,
98+ replacement : `tsdown: ^${ versions . tsdown } ` ,
99+ newVersion : versions . tsdown ,
100+ } ,
101+ {
102+ name : '@oxc-node/cli' ,
103+ pattern : / ' @ o x c - n o d e \/ c l i ' : \^ ( [ \d . ] + (?: - [ \w . ] + ) ? ) / ,
104+ replacement : `'@oxc-node/cli': ^${ versions . oxcNodeCli } ` ,
105+ newVersion : versions . oxcNodeCli ,
106+ } ,
107+ {
108+ name : '@oxc-node/core' ,
109+ pattern : / ' @ o x c - n o d e \/ c o r e ' : \^ ( [ \d . ] + (?: - [ \w . ] + ) ? ) / ,
110+ replacement : `'@oxc-node/core': ^${ versions . oxcNodeCore } ` ,
111+ newVersion : versions . oxcNodeCore ,
112+ } ,
113+ {
114+ name : 'oxfmt' ,
115+ pattern : / o x f m t : = ( [ \d . ] + (?: - [ \w . ] + ) ? ) / ,
116+ replacement : `oxfmt: =${ versions . oxfmt } ` ,
117+ newVersion : versions . oxfmt ,
118+ } ,
119+ {
120+ name : 'oxlint' ,
121+ pattern : / o x l i n t : = ( [ \d . ] + (?: - [ \w . ] + ) ? ) \n / ,
122+ replacement : `oxlint: =${ versions . oxlint } \n` ,
123+ newVersion : versions . oxlint ,
124+ } ,
125+ {
126+ name : 'oxlint-tsgolint' ,
127+ pattern : / o x l i n t - t s g o l i n t : = ( [ \d . ] + (?: - [ \w . ] + ) ? ) / ,
128+ replacement : `oxlint-tsgolint: =${ versions . oxlintTsgolint } ` ,
129+ newVersion : versions . oxlintTsgolint ,
130+ } ,
131+ {
132+ name : '@oxc-project/runtime' ,
133+ pattern : / ' @ o x c - p r o j e c t \/ r u n t i m e ' : = ( [ \d . ] + (?: - [ \w . ] + ) ? ) / ,
134+ replacement : `'@oxc-project/runtime': =${ versions . oxcProjectRuntime } ` ,
135+ newVersion : versions . oxcProjectRuntime ,
136+ } ,
137+ {
138+ name : '@oxc-project/types' ,
139+ pattern : / ' @ o x c - p r o j e c t \/ t y p e s ' : = ( [ \d . ] + (?: - [ \w . ] + ) ? ) / ,
140+ replacement : `'@oxc-project/types': =${ versions . oxcProjectTypes } ` ,
141+ newVersion : versions . oxcProjectTypes ,
142+ } ,
143+ {
144+ name : 'oxc-minify' ,
145+ pattern : / o x c - m i n i f y : = ( [ \d . ] + (?: - [ \w . ] + ) ? ) / ,
146+ replacement : `oxc-minify: =${ versions . oxcMinify } ` ,
147+ newVersion : versions . oxcMinify ,
148+ } ,
149+ {
150+ name : 'oxc-parser' ,
151+ pattern : / o x c - p a r s e r : = ( [ \d . ] + (?: - [ \w . ] + ) ? ) / ,
152+ replacement : `oxc-parser: =${ versions . oxcParser } ` ,
153+ newVersion : versions . oxcParser ,
154+ } ,
155+ {
156+ name : 'oxc-transform' ,
157+ pattern : / o x c - t r a n s f o r m : = ( [ \d . ] + (?: - [ \w . ] + ) ? ) / ,
158+ replacement : `oxc-transform: =${ versions . oxcTransform } ` ,
159+ newVersion : versions . oxcTransform ,
160+ } ,
161+ ] ;
90162
91- // Update oxlint-tsgolint in catalog
92- content = content . replace (
93- / o x l i n t - t s g o l i n t : = [ \d . ] + ( - [ \w . ] + ) ? / ,
94- `oxlint-tsgolint: =${ versions . oxlintTsgolint } ` ,
95- ) ;
163+ for ( const { name, pattern, replacement, newVersion } of entries ) {
164+ let oldVersion ;
165+ content = content . replace ( pattern , ( _match , captured ) => {
166+ oldVersion = captured ;
167+ return replacement ;
168+ } ) ;
169+ if ( oldVersion === undefined ) {
170+ throw new Error (
171+ `Failed to match ${ name } in pnpm-workspace.yaml — the pattern ${ pattern } is stale, ` +
172+ `please update it in .github/scripts/upgrade-deps.mjs` ,
173+ ) ;
174+ }
175+ recordChange ( name , oldVersion , newVersion ) ;
176+ }
96177
97178 fs . writeFileSync ( filePath , content ) ;
98179 console . log ( 'Updated pnpm-workspace.yaml' ) ;
@@ -129,15 +210,93 @@ async function updateCorePackage(devtoolsVersion) {
129210 const filePath = path . join ( ROOT , 'packages/core/package.json' ) ;
130211 const pkg = JSON . parse ( fs . readFileSync ( filePath , 'utf8' ) ) ;
131212
132- // Update @vitejs /devtools in devDependencies
133- if ( pkg . devDependencies ?. [ '@vitejs/devtools' ] ) {
134- pkg . devDependencies [ '@vitejs/devtools' ] = `^ ${ devtoolsVersion } ` ;
213+ const currentDevtools = pkg . devDependencies ?. [ ' @vitejs/devtools' ] ;
214+ if ( ! currentDevtools ) {
215+ return ;
135216 }
217+ pkg . devDependencies [ '@vitejs/devtools' ] = `^${ devtoolsVersion } ` ;
218+ recordChange ( '@vitejs/devtools' , currentDevtools . replace ( / ^ [ \^ ~ ] / , '' ) , devtoolsVersion ) ;
136219
137220 fs . writeFileSync ( filePath , JSON . stringify ( pkg , null , 2 ) + '\n' ) ;
138221 console . log ( 'Updated packages/core/package.json' ) ;
139222}
140223
224+ // ============ Write metadata files for PR description ============
225+ function writeMetaFiles ( ) {
226+ if ( ! META_DIR ) {
227+ return ;
228+ }
229+
230+ fs . mkdirSync ( META_DIR , { recursive : true } ) ;
231+
232+ const versionsObj = Object . fromEntries ( changes ) ;
233+ fs . writeFileSync (
234+ path . join ( META_DIR , 'versions.json' ) ,
235+ JSON . stringify ( versionsObj , null , 2 ) + '\n' ,
236+ ) ;
237+
238+ const changed = [ ...changes . entries ( ) ] . filter ( ( [ , v ] ) => v . old !== v . new ) ;
239+ const unchanged = [ ...changes . entries ( ) ] . filter ( ( [ , v ] ) => v . old === v . new ) ;
240+
241+ const formatVersion = ( v ) => {
242+ if ( v . tag ) {
243+ return `${ v . tag } (${ v . new . slice ( 0 , 7 ) } )` ;
244+ }
245+ if ( isFullSha ( v . new ) ) {
246+ return v . new . slice ( 0 , 7 ) ;
247+ }
248+ return v . new ;
249+ } ;
250+ const formatOld = ( v ) => {
251+ if ( ! v . old ) {
252+ return '(unset)' ;
253+ }
254+ if ( isFullSha ( v . old ) ) {
255+ return v . old . slice ( 0 , 7 ) ;
256+ }
257+ return v . old ;
258+ } ;
259+
260+ const commitLines = [ 'feat(deps): upgrade upstream dependencies' , '' ] ;
261+ if ( changed . length ) {
262+ for ( const [ name , v ] of changed ) {
263+ commitLines . push ( `- ${ name } : ${ formatOld ( v ) } -> ${ formatVersion ( v ) } ` ) ;
264+ }
265+ } else {
266+ commitLines . push ( '- no version changes detected' ) ;
267+ }
268+ commitLines . push ( '' ) ;
269+ fs . writeFileSync ( path . join ( META_DIR , 'commit-message.txt' ) , commitLines . join ( '\n' ) ) ;
270+
271+ const bodyLines = [ '## Summary' , '' ] ;
272+ if ( changed . length ) {
273+ bodyLines . push ( 'Automated daily upgrade of upstream dependencies.' ) ;
274+ } else {
275+ bodyLines . push ( 'Automated daily upgrade run — no upstream version changes detected.' ) ;
276+ }
277+ bodyLines . push ( '' , '## Dependency updates' , '' ) ;
278+ if ( changed . length ) {
279+ bodyLines . push ( '| Package | From | To |' ) ;
280+ bodyLines . push ( '| --- | --- | --- |' ) ;
281+ for ( const [ name , v ] of changed ) {
282+ bodyLines . push ( `| \`${ name } \` | \`${ formatOld ( v ) } \` | \`${ formatVersion ( v ) } \` |` ) ;
283+ }
284+ } else {
285+ bodyLines . push ( '_No version changes._' ) ;
286+ }
287+ if ( unchanged . length ) {
288+ bodyLines . push ( '' , '<details><summary>Unchanged dependencies</summary>' , '' ) ;
289+ for ( const [ name , v ] of unchanged ) {
290+ bodyLines . push ( `- \`${ name } \`: \`${ formatVersion ( v ) } \`` ) ;
291+ }
292+ bodyLines . push ( '' , '</details>' ) ;
293+ }
294+ bodyLines . push ( '' , '## Code changes' , '' , '_No additional code changes recorded._' , '' ) ;
295+ fs . writeFileSync ( path . join ( META_DIR , 'pr-body.md' ) , bodyLines . join ( '\n' ) ) ;
296+
297+ console . log ( `Wrote metadata files to ${ META_DIR } ` ) ;
298+ }
299+
141300console . log ( 'Fetching latest versions…' ) ;
142301
143302const [
@@ -149,6 +308,11 @@ const [
149308 oxfmtVersion ,
150309 oxlintVersion ,
151310 oxlintTsgolintVersion ,
311+ oxcProjectRuntimeVersion ,
312+ oxcProjectTypesVersion ,
313+ oxcMinifyVersion ,
314+ oxcParserVersion ,
315+ oxcTransformVersion ,
152316] = await Promise . all ( [
153317 getLatestNpmVersion ( 'vitest' ) ,
154318 getLatestNpmVersion ( 'tsdown' ) ,
@@ -158,6 +322,11 @@ const [
158322 getLatestNpmVersion ( 'oxfmt' ) ,
159323 getLatestNpmVersion ( 'oxlint' ) ,
160324 getLatestNpmVersion ( 'oxlint-tsgolint' ) ,
325+ getLatestNpmVersion ( '@oxc-project/runtime' ) ,
326+ getLatestNpmVersion ( '@oxc-project/types' ) ,
327+ getLatestNpmVersion ( 'oxc-minify' ) ,
328+ getLatestNpmVersion ( 'oxc-parser' ) ,
329+ getLatestNpmVersion ( 'oxc-transform' ) ,
161330] ) ;
162331
163332console . log ( `vitest: ${ vitestVersion } ` ) ;
@@ -168,6 +337,11 @@ console.log(`@oxc-node/core: ${oxcNodeCoreVersion}`);
168337console . log ( `oxfmt: ${ oxfmtVersion } ` ) ;
169338console . log ( `oxlint: ${ oxlintVersion } ` ) ;
170339console . log ( `oxlint-tsgolint: ${ oxlintTsgolintVersion } ` ) ;
340+ console . log ( `@oxc-project/runtime: ${ oxcProjectRuntimeVersion } ` ) ;
341+ console . log ( `@oxc-project/types: ${ oxcProjectTypesVersion } ` ) ;
342+ console . log ( `oxc-minify: ${ oxcMinifyVersion } ` ) ;
343+ console . log ( `oxc-parser: ${ oxcParserVersion } ` ) ;
344+ console . log ( `oxc-transform: ${ oxcTransformVersion } ` ) ;
171345
172346await updateUpstreamVersions ( ) ;
173347await updatePnpmWorkspace ( {
@@ -178,8 +352,15 @@ await updatePnpmWorkspace({
178352 oxfmt : oxfmtVersion ,
179353 oxlint : oxlintVersion ,
180354 oxlintTsgolint : oxlintTsgolintVersion ,
355+ oxcProjectRuntime : oxcProjectRuntimeVersion ,
356+ oxcProjectTypes : oxcProjectTypesVersion ,
357+ oxcMinify : oxcMinifyVersion ,
358+ oxcParser : oxcParserVersion ,
359+ oxcTransform : oxcTransformVersion ,
181360} ) ;
182361await updateTestPackage ( vitestVersion ) ;
183362await updateCorePackage ( devtoolsVersion ) ;
184363
364+ writeMetaFiles ( ) ;
365+
185366console . log ( 'Done!' ) ;
0 commit comments