@@ -2,6 +2,7 @@ import { access, cp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
22import { createRequire } from 'node:module'
33import path , { dirname } from 'node:path'
44import { fileURLToPath } from 'node:url'
5+ import lockfile from 'proper-lockfile'
56
67const _require = createRequire ( import . meta. url )
78
@@ -17,8 +18,30 @@ const proxyApiPath = _require.resolve(
1718)
1819const extraSupportedExtensions = [ '.vue' ]
1920
21+ const LOCK_TIMEOUT_MS = 60000 // Max wait time
22+ const STALE_TIMEOUT_MS = 30000 // Auto-release stale locks
23+
24+ async function isFixtureValid (
25+ targetTsDir : string ,
26+ vueTscFlagFile : string ,
27+ currTsVersion : string ,
28+ ) {
29+ try {
30+ await access ( targetTsDir )
31+ const targetTsVersion = JSON . parse (
32+ await readFile ( path . resolve ( targetTsDir , 'package.json' ) , 'utf8' ) ,
33+ ) . version
34+ await access ( vueTscFlagFile )
35+ const fixtureFlagContent = await readFile ( vueTscFlagFile , 'utf8' )
36+ return (
37+ targetTsVersion === currTsVersion && fixtureFlagContent === proxyApiPath
38+ )
39+ } catch {
40+ return false
41+ }
42+ }
43+
2044export async function prepareVueTsc ( ) {
21- // 1. copy typescript to folder
2245 const targetTsDir = path . resolve ( _dirname , 'typescript-vue-tsc' )
2346 const vueTscFlagFile = path . resolve ( targetTsDir , 'vue-tsc-resolve-path' )
2447 const currTsVersion = _require ( 'typescript/package.json' ) . version
@@ -30,37 +53,49 @@ export async function prepareVueTsc() {
3053 )
3154 }
3255
33- let shouldBuildFixture = true
56+ if ( await isFixtureValid ( targetTsDir , vueTscFlagFile , currTsVersion ) ) {
57+ return { targetTsDir }
58+ }
59+
60+ let release : Awaited < ReturnType < typeof lockfile . lock > >
61+ try {
62+ release = await lockfile . lock ( _dirname , {
63+ lockfilePath : path . resolve ( _dirname , '.vue-tsc-fixture.lock' ) ,
64+ stale : STALE_TIMEOUT_MS ,
65+ retries : {
66+ retries : Math . ceil ( LOCK_TIMEOUT_MS / 1000 ) ,
67+ factor : 1 ,
68+ minTimeout : 1000 ,
69+ maxTimeout : 2000 ,
70+ randomize : true ,
71+ } ,
72+ } )
73+ } catch ( err ) {
74+ throw new Error (
75+ '[vite-plugin-checker] Failed to acquire lock for vue-tsc fixture preparation. ' +
76+ 'Another process may be holding the lock.\n' +
77+ String ( err ) ,
78+ )
79+ }
80+
3481 try {
35- await access ( targetTsDir )
36- const targetTsVersion = _require (
37- path . resolve ( targetTsDir , 'package.json' ) ,
38- ) . version
39- // check fixture versions before re-use
40- await access ( vueTscFlagFile )
41- const fixtureFlagContent = await readFile ( vueTscFlagFile , 'utf8' )
42- if (
43- targetTsVersion === currTsVersion &&
44- fixtureFlagContent === proxyApiPath
45- ) {
46- shouldBuildFixture = false
82+ // Double-check that the fixture is valid, another process may have built while trying to acquire the lock
83+ if ( await isFixtureValid ( targetTsDir , vueTscFlagFile , currTsVersion ) ) {
84+ return { targetTsDir }
4785 }
48- } catch {
49- // no matter what error, we should rebuild the fixture
50- shouldBuildFixture = true
51- }
5286
53- if ( shouldBuildFixture ) {
5487 await rm ( targetTsDir , { force : true , recursive : true } )
5588 await mkdir ( targetTsDir , { recursive : true } )
5689 const sourceTsDir = path . resolve ( _require . resolve ( 'typescript' ) , '../..' )
5790 await cp ( sourceTsDir , targetTsDir , { recursive : true } )
5891 await writeFile ( vueTscFlagFile , proxyApiPath )
5992
60- // 2. sync modification of lib/tsc.js with vue-tsc
93+ // sync modification of lib/tsc.js with vue-tsc
6194 await overrideTscJs (
6295 _require . resolve ( path . resolve ( targetTsDir , 'lib/typescript.js' ) ) ,
6396 )
97+ } finally {
98+ await release ( )
6499 }
65100
66101 return { targetTsDir }
0 commit comments