1616
1717import { execSync } from 'node:child_process' ;
1818import { mkdirSync , rmSync , writeFileSync } from 'node:fs' ;
19- import { homedir , tmpdir } from 'node:os' ;
19+ import { tmpdir } from 'node:os' ;
2020import { join } from 'node:path' ;
2121import type { TestSession } from '@salesforce/cli-plugins-testkit' ;
2222import { UI_BUNDLES_FOLDER } from '../../../../src/config/uiBundleDiscovery.js' ;
2323
24- /**
25- * Real home directory captured at module load, before TestSession overrides process.env.HOME.
26- * Used when running `sf ui-bundle generate` so the CLI finds linked plugin-templates
27- * (TestSession sets HOME to a temp dir, which hides linked plugins).
28- */
29- export const REAL_HOME = homedir ( ) ;
24+ const DEFAULT_SFDX_PROJECT = {
25+ packageDirectories : [ { path : 'force-app' , default : true } ] ,
26+ } ;
3027
3128/**
3229 * Relative path from project root to the uiBundles folder.
@@ -41,22 +38,6 @@ export function uiBundlePath(projectDir: string, uiBundleName?: string): string
4138 return uiBundleName ? join ( projectDir , UI_BUNDLES_PATH , uiBundleName ) : join ( projectDir , UI_BUNDLES_PATH ) ;
4239}
4340
44- /**
45- * Verify the global `sf` CLI is available and has the required commands.
46- * Must be called after TestSession.create() since the session sets a valid HOME.
47- */
48- export function ensureSfCli ( ) : void {
49- try {
50- execSync ( 'sf project generate --help' , { stdio : 'pipe' , timeout : 30_000 } ) ;
51- } catch {
52- throw new Error (
53- 'Global sf CLI with plugin-templates not found.\n' +
54- 'Install: npm install @salesforce/cli -g\n' +
55- 'CI installs @salesforce/cli@nightly via nut.yml.'
56- ) ;
57- }
58- }
59-
6041/**
6142 * Authenticate an org via TESTKIT_AUTH_URL without requiring DevHub.
6243 * Returns the authenticated username.
@@ -70,7 +51,6 @@ export function authOrgViaUrl(): string {
7051 throw new Error ( 'TESTKIT_AUTH_URL environment variable is not set.' ) ;
7152 }
7253
73- // Use --sfdx-url-file for cross-platform reliability
7454 const tmpFile = join ( tmpdir ( ) , `testkit-auth-${ Date . now ( ) } -${ Math . random ( ) . toString ( 36 ) . slice ( 2 ) } .txt` ) ;
7555 try {
7656 writeFileSync ( tmpFile , authUrl , 'utf8' ) ;
@@ -86,28 +66,32 @@ export function authOrgViaUrl(): string {
8666}
8767
8868/**
89- * Run `sf project generate --name <name>` inside the session directory .
90- * Returns the absolute path to the generated project root.
69+ * Create a minimal SFDX project directory with sfdx-project.json .
70+ * Returns the absolute path to the project root.
9171 */
9272export function createProject ( session : TestSession , name : string ) : string {
93- execSync ( `sf project generate --name ${ name } ` , {
94- cwd : session . dir ,
95- stdio : 'pipe' ,
96- } ) ;
97- return join ( session . dir , name ) ;
73+ const projectDir = join ( session . dir , name ) ;
74+ mkdirSync ( projectDir , { recursive : true } ) ;
75+ writeFileSync ( join ( projectDir , 'sfdx-project.json' ) , JSON . stringify ( DEFAULT_SFDX_PROJECT , null , 2 ) ) ;
76+ return projectDir ;
9877}
9978
10079/**
101- * Run `sf project generate` then `sf ui-bundle generate --name <uiBundleName>` inside
102- * the project. Returns the absolute path to the generated project root.
80+ * Create a uiBundle directory with the required .uibundle-meta.xml file.
81+ */
82+ export function createUiBundle ( projectDir : string , name : string ) : void {
83+ const dir = uiBundlePath ( projectDir , name ) ;
84+ mkdirSync ( dir , { recursive : true } ) ;
85+ writeFileSync ( join ( dir , `${ name } .uibundle-meta.xml` ) , '<UiBundle/>' ) ;
86+ }
87+
88+ /**
89+ * Create a uiBundle directory with the required .uibundle-meta.xml file inside
90+ * a project. Returns the absolute path to the project root.
10391 */
10492export function createProjectWithUiBundle ( session : TestSession , projectName : string , uiBundleName : string ) : string {
10593 const projectDir = createProject ( session , projectName ) ;
106- execSync ( `sf ui-bundle generate --name ${ uiBundleName } ` , {
107- cwd : projectDir ,
108- stdio : 'pipe' ,
109- env : { ...process . env , HOME : REAL_HOME , USERPROFILE : REAL_HOME } ,
110- } ) ;
94+ createUiBundle ( projectDir , uiBundleName ) ;
11195 return projectDir ;
11296}
11397
@@ -122,11 +106,7 @@ export function createProjectWithMultipleUiBundles(
122106) : string {
123107 const projectDir = createProject ( session , projectName ) ;
124108 for ( const name of uiBundleNames ) {
125- execSync ( `sf ui-bundle generate --name ${ name } ` , {
126- cwd : projectDir ,
127- stdio : 'pipe' ,
128- env : { ...process . env , HOME : REAL_HOME , USERPROFILE : REAL_HOME } ,
129- } ) ;
109+ createUiBundle ( projectDir , name ) ;
130110 }
131111 return projectDir ;
132112}
@@ -160,7 +140,7 @@ export function writeManifest(projectDir: string, uiBundleName: string, manifest
160140 *
161141 * The script is CommonJS (.cjs) to avoid ESM/shell quoting issues.
162142 */
163- export function createDevServerScript ( uiBundleDir : string , port : number ) : string {
143+ function createDevServerScript ( uiBundleDir : string , port : number ) : string {
164144 const script = [
165145 "const http = require('http');" ,
166146 'const server = http.createServer((_, res) => {' ,
0 commit comments