@@ -14,6 +14,7 @@ let data: string;
1414let bin : string ;
1515let config : string ;
1616let versionDir : string ;
17+ let nativeFile : string ;
1718let version : string ;
1819let env : NodeJS . ProcessEnv ;
1920let platform : NodeJS . Platform ;
@@ -63,6 +64,9 @@ function createInstallation({
6364 } ) ,
6465 ) ;
6566 write ( join ( packageDir , "dist" , "bin.js" ) ) ;
67+ write ( join ( packageDir , "binding" , "index.cjs" ) ) ;
68+ nativeFile = join ( packageDir , ".." , "@voidzero-dev" , "vite-plus-native" , "vite-plus.node" ) ;
69+ write ( nativeFile ) ;
6670 write (
6771 join ( packageDir , ".." , "fixture-dep" , "package.json" ) ,
6872 '{"name":"fixture-dep","version":"1.0.0"}' ,
@@ -95,11 +99,12 @@ beforeEach(() => {
9599 env = { VP_HOME : data , HOME : join ( root , "home" ) , USERPROFILE : join ( root , "home" ) } ;
96100 platform = process . platform ;
97101 version = "0.3.0" ;
98- vi . mocked ( execFileSync ) . mockImplementation ( ( _file , args ) =>
99- ( args as string [ ] ) . includes ( "--version" )
102+ vi . mocked ( execFileSync ) . mockImplementation ( ( file , args ) => {
103+ if ( file === process . execPath ) return JSON . stringify ( [ nativeFile ] ) ;
104+ return ( args as string [ ] ) . includes ( "--version" )
100105 ? `vp v${ version } \nLocal vite-plus v9.9.9\n`
101- : dirsOutput ( ) ,
102- ) ;
106+ : dirsOutput ( ) ;
107+ } ) ;
103108} ) ;
104109
105110afterEach ( ( ) => {
@@ -225,6 +230,7 @@ describe("findReusableVitePlus", () => {
225230 "package.json" ,
226231 "node_modules/vite-plus/package.json" ,
227232 "node_modules/vite-plus/dist/bin.js" ,
233+ "node_modules/vite-plus/binding/index.cjs" ,
228234 "node_modules/.pnpm/vite-plus@0.3.0/node_modules/fixture-dep/package.json" ,
229235 ] ) ( "falls back when %s is missing" , ( file ) => {
230236 createInstallation ( ) ;
@@ -245,6 +251,59 @@ describe("findReusableVitePlus", () => {
245251 expect ( reuse ( ) ) . toBeUndefined ( ) ;
246252 } ) ;
247253
254+ it . each ( [ "package" , "file" ] ) (
255+ "falls back when the loaded native binding %s is missing" ,
256+ ( target ) => {
257+ createInstallation ( ) ;
258+ rmSync ( target === "package" ? dirname ( nativeFile ) : nativeFile , { recursive : true } ) ;
259+ expect ( reuse ( ) ) . toBeUndefined ( ) ;
260+ } ,
261+ ) ;
262+
263+ it ( "probes the installed binding loader with the action's Node runtime" , ( ) => {
264+ createInstallation ( ) ;
265+ expect ( reuse ( ) ) . toBe ( bin ) ;
266+ expect ( execFileSync ) . toHaveBeenCalledWith (
267+ process . execPath ,
268+ [ "--input-type=commonjs" , "--eval" , expect . any ( String ) , expect . stringContaining ( "index.cjs" ) ] ,
269+ expect . objectContaining ( {
270+ env : { ...env , NAPI_RS_ENFORCE_VERSION_CHECK : "1" } ,
271+ cwd : tmpdir ( ) ,
272+ timeout : 5000 ,
273+ } ) ,
274+ ) ;
275+ } ) ;
276+
277+ it ( "falls back when the native loader fails" , ( ) => {
278+ createInstallation ( ) ;
279+ vi . mocked ( execFileSync )
280+ . mockReturnValueOnce ( dirsOutput ( ) )
281+ . mockReturnValueOnce ( `vp v${ version } ` )
282+ . mockImplementationOnce ( ( ) => {
283+ throw new Error ( "Cannot find native binding" ) ;
284+ } ) ;
285+ expect ( reuse ( ) ) . toBeUndefined ( ) ;
286+ } ) ;
287+
288+ it . each ( [ "[]" , "null" , "invalid JSON" ] ) (
289+ "rejects a native probe without binding files: %s" ,
290+ ( output ) => {
291+ createInstallation ( ) ;
292+ vi . mocked ( execFileSync )
293+ . mockReturnValueOnce ( dirsOutput ( ) )
294+ . mockReturnValueOnce ( `vp v${ version } ` )
295+ . mockReturnValueOnce ( output ) ;
296+ expect ( reuse ( ) ) . toBeUndefined ( ) ;
297+ } ,
298+ ) ;
299+
300+ it ( "does not accept a native binding from outside the active installation" , ( ) => {
301+ createInstallation ( ) ;
302+ nativeFile = join ( root , "other-installation" , "vite-plus.node" ) ;
303+ write ( nativeFile ) ;
304+ expect ( reuse ( ) ) . toBeUndefined ( ) ;
305+ } ) ;
306+
248307 it . each ( [ "{" , '{"name":"vp-global","version":"0.3.1","dependencies":{"vite-plus":"0.3.0"}}' ] ) (
249308 "rejects invalid or mismatched wrapper metadata" ,
250309 ( content ) => {
@@ -323,11 +382,26 @@ describe("findReusableVitePlus", () => {
323382 expect ( reuse ( ) ) . toBe ( bin ) ;
324383 } ) ;
325384
326- it . each ( [ "no" , "invalid" ] ) ( "does not bypass VP_NODE_MANAGER=%s" , ( value ) => {
327- createInstallation ( ) ;
328- env . VP_NODE_MANAGER = value ;
329- expect ( reuse ( ) ) . toBeUndefined ( ) ;
330- expect ( execFileSync ) . not . toHaveBeenCalled ( ) ;
385+ describe . each ( [
386+ "VP_NODE_MANAGER" ,
387+ "VP_PM_MANAGER" ,
388+ "VP_NPM_MANAGER" ,
389+ "VP_PNPM_MANAGER" ,
390+ "VP_YARN_MANAGER" ,
391+ "VP_BUN_MANAGER" ,
392+ ] ) ( "%s installer override" , ( key ) => {
393+ it . each ( [ "no" , "invalid" ] ) ( "leaves %s to the installer" , ( value ) => {
394+ createInstallation ( ) ;
395+ env [ key ] = value ;
396+ expect ( reuse ( ) ) . toBeUndefined ( ) ;
397+ expect ( execFileSync ) . not . toHaveBeenCalled ( ) ;
398+ } ) ;
399+
400+ it . each ( [ "yes" , "" ] ) ( "reuses managed installations for %s" , ( value ) => {
401+ createInstallation ( ) ;
402+ env [ key ] = value ;
403+ expect ( reuse ( ) ) . toBe ( bin ) ;
404+ } ) ;
331405 } ) ;
332406
333407 it ( "falls back when a required shim is missing" , ( ) => {
0 commit comments