@@ -8,6 +8,82 @@ import { spawnSync } from 'node:child_process';
88const __dirname = dirname ( fileURLToPath ( import . meta. url ) ) ;
99
1010describe ( 'happier bin preflight' , ( ) => {
11+ it ( 'works when protocol deps are vendored under protocol/node_modules (not hoisted to the CLI root)' , ( ) => {
12+ const repoRoot = resolve ( __dirname , '..' , '..' , '..' , '..' ) ;
13+ const tempRoot = mkdtempSync ( join ( tmpdir ( ) , 'happier-bin-preflight-' ) ) ;
14+
15+ mkdirSync ( resolve ( tempRoot , 'bin' ) , { recursive : true } ) ;
16+ mkdirSync ( resolve ( tempRoot , 'dist' ) , { recursive : true } ) ;
17+
18+ // Global-install / packed layout:
19+ // - protocol is present as a bundled workspace package
20+ // - protocol runtime deps are vendored under protocol/node_modules
21+ mkdirSync ( resolve ( tempRoot , 'node_modules' , '@happier-dev' , 'protocol' , 'node_modules' , 'base64-js' ) , {
22+ recursive : true ,
23+ } ) ;
24+ mkdirSync ( resolve ( tempRoot , 'node_modules' , '@happier-dev' , 'protocol' , 'node_modules' , '@noble' , 'hashes' ) , {
25+ recursive : true ,
26+ } ) ;
27+
28+ // CLI direct dep is expected at the CLI root.
29+ mkdirSync ( resolve ( tempRoot , 'node_modules' , 'tweetnacl' ) , { recursive : true } ) ;
30+
31+ cpSync ( resolve ( repoRoot , 'apps' , 'cli' , 'bin' , 'happier.mjs' ) , resolve ( tempRoot , 'bin' , 'happier.mjs' ) ) ;
32+
33+ writeFileSync (
34+ resolve ( tempRoot , 'node_modules' , '@happier-dev' , 'protocol' , 'package.json' ) ,
35+ JSON . stringify ( { name : '@happier-dev/protocol' , version : '0.0.0' } ) ,
36+ 'utf8' ,
37+ ) ;
38+ writeFileSync (
39+ resolve ( tempRoot , 'node_modules' , 'tweetnacl' , 'package.json' ) ,
40+ JSON . stringify ( { name : 'tweetnacl' , version : '0.0.0' , main : 'index.js' } ) ,
41+ 'utf8' ,
42+ ) ;
43+ writeFileSync ( resolve ( tempRoot , 'node_modules' , 'tweetnacl' , 'index.js' ) , 'module.exports = {};\n' , 'utf8' ) ;
44+
45+ writeFileSync (
46+ resolve ( tempRoot , 'node_modules' , '@happier-dev' , 'protocol' , 'node_modules' , 'base64-js' , 'package.json' ) ,
47+ JSON . stringify ( { name : 'base64-js' , version : '0.0.0' , main : 'index.js' } ) ,
48+ 'utf8' ,
49+ ) ;
50+ writeFileSync (
51+ resolve ( tempRoot , 'node_modules' , '@happier-dev' , 'protocol' , 'node_modules' , 'base64-js' , 'index.js' ) ,
52+ 'module.exports = {};\n' ,
53+ 'utf8' ,
54+ ) ;
55+
56+ writeFileSync (
57+ resolve ( tempRoot , 'node_modules' , '@happier-dev' , 'protocol' , 'node_modules' , '@noble' , 'hashes' , 'package.json' ) ,
58+ JSON . stringify ( { name : '@noble/hashes' , version : '0.0.0' , main : 'index.js' } ) ,
59+ 'utf8' ,
60+ ) ;
61+ writeFileSync (
62+ resolve ( tempRoot , 'node_modules' , '@happier-dev' , 'protocol' , 'node_modules' , '@noble' , 'hashes' , 'hmac.js' ) ,
63+ 'export {};\n' ,
64+ 'utf8' ,
65+ ) ;
66+ writeFileSync (
67+ resolve ( tempRoot , 'node_modules' , '@happier-dev' , 'protocol' , 'node_modules' , '@noble' , 'hashes' , 'sha512.js' ) ,
68+ 'export {};\n' ,
69+ 'utf8' ,
70+ ) ;
71+
72+ writeFileSync ( resolve ( tempRoot , 'dist' , 'index.mjs' ) , "console.log('ok');\n" , 'utf8' ) ;
73+
74+ const result = spawnSync ( process . execPath , [ resolve ( tempRoot , 'bin' , 'happier.mjs' ) ] , {
75+ cwd : tempRoot ,
76+ encoding : 'utf8' ,
77+ env : {
78+ ...process . env ,
79+ NODE_OPTIONS : '' ,
80+ } ,
81+ } ) ;
82+
83+ expect ( result . status ) . toBe ( 0 ) ;
84+ expect ( result . stdout ) . toContain ( 'ok' ) ;
85+ } ) ;
86+
1187 it ( 'prints a helpful error when tweetnacl is missing' , ( ) => {
1288 const repoRoot = resolve ( __dirname , '..' , '..' , '..' , '..' ) ;
1389 const tempRoot = mkdtempSync ( join ( tmpdir ( ) , 'happier-bin-preflight-' ) ) ;
0 commit comments