Skip to content

Commit 53cc57e

Browse files
Peter Wangmeta-codesync[bot]
authored andcommitted
friendly error message when cli does not exist
Summary: Added validateCliPath function to validate CLI path. This will provide the download link if CLI does not exist. Reviewed By: zjm-meta Differential Revision: D88069849 Privacy Context Container: L1334777 fbshipit-source-id: f94328d1bb073aa6eb664019df1a2f4bea840374
1 parent b1b17e0 commit 53cc57e

File tree

3 files changed

+168
-1
lines changed

3 files changed

+168
-1
lines changed

packages/vite-plugin-metaspatial/src/generate-glxf/cli-path-resolver.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@
88
import * as path from 'path';
99
import fs from 'fs-extra';
1010

11+
/**
12+
* Download URLs for Meta Spatial Editor by platform
13+
*/
14+
const DOWNLOAD_URLS = {
15+
darwin: 'https://developers.meta.com/horizon/downloads/package/meta-spatial-editor-for-mac',
16+
win32: 'https://developers.meta.com/horizon/downloads/package/meta-spatial-editor-for-windows',
17+
linux: 'https://developers.meta.com/horizon/downloads/package/meta-spatial-editor-for-linux',
18+
default: 'https://developers.meta.com/horizon/downloads/spatial-sdk/',
19+
} as const;
20+
1121
/**
1222
* Get the highest version directory from Meta Spatial Editor installation
1323
* @param directoryPath - Base directory path
@@ -71,3 +81,72 @@ export function resolveMetaSpatialCliPath(): string {
7181
return 'MetaSpatialEditorCLI';
7282
}
7383
}
84+
85+
/**
86+
* Validate that the Meta Spatial CLI exists and is accessible
87+
* @param cliPath - Path to the CLI executable
88+
* @throws Error with helpful message including download link if CLI not found
89+
*/
90+
export async function validateCliPath(cliPath: string): Promise<void> {
91+
const platform = process.platform as keyof typeof DOWNLOAD_URLS;
92+
const downloadUrl = DOWNLOAD_URLS[platform] || DOWNLOAD_URLS.default;
93+
94+
// For Linux, the CLI might be in PATH, so we skip file existence check
95+
// The spawn process will handle the error if it's not found
96+
if (platform === 'linux' && !path.isAbsolute(cliPath)) {
97+
return;
98+
}
99+
100+
try {
101+
const exists = await fs.pathExists(cliPath);
102+
103+
if (!exists) {
104+
const errorMessage = [
105+
`❌ Meta Spatial Editor CLI not found at: ${cliPath}`,
106+
'',
107+
'📥 Please download and install Meta Spatial Editor:',
108+
` ${downloadUrl}`,
109+
'',
110+
'🔧 Alternative solutions:',
111+
' 1. Set the META_SPATIAL_EDITOR_CLI_PATH environment variable to the correct path',
112+
' 2. Specify metaSpatialCliPath in your vite.config.js generateGLXF options',
113+
'',
114+
].join('\n');
115+
116+
throw new Error(errorMessage);
117+
}
118+
119+
// Check if the file is executable (Unix-like systems)
120+
if (platform !== 'win32') {
121+
try {
122+
await fs.access(cliPath, fs.constants.X_OK);
123+
} catch (error) {
124+
const errorMessage = [
125+
`❌ Meta Spatial Editor CLI found but is not executable: ${cliPath}`,
126+
'',
127+
'🔧 Make the file executable by running:',
128+
` chmod +x "${cliPath}"`,
129+
'',
130+
].join('\n');
131+
132+
throw new Error(errorMessage);
133+
}
134+
}
135+
} catch (error) {
136+
// Re-throw our custom errors, or wrap system errors
137+
if (error instanceof Error && error.message.includes('Meta Spatial Editor')) {
138+
throw error;
139+
}
140+
141+
const errorMessage = [
142+
`❌ Error checking Meta Spatial Editor CLI at: ${cliPath}`,
143+
` ${error instanceof Error ? error.message : String(error)}`,
144+
'',
145+
'📥 If you need to install Meta Spatial Editor, download it from:',
146+
` ${downloadUrl}`,
147+
'',
148+
].join('\n');
149+
150+
throw new Error(errorMessage);
151+
}
152+
}

packages/vite-plugin-metaspatial/src/generate-glxf/cli-wrapper.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import * as os from 'os';
1010
import * as path from 'path';
1111
import fs from 'fs-extra';
1212
import { processAssets } from './asset-processor.js';
13+
import { validateCliPath } from './cli-path-resolver.js';
1314

1415
/**
1516
* Execute Meta Spatial CLI and process the generated assets
@@ -21,6 +22,9 @@ export async function executeMetaSpatialExport(
2122
finalGltfDir: string,
2223
verbose: boolean = false,
2324
): Promise<void> {
25+
// Validate CLI path before attempting execution
26+
await validateCliPath(cliPath);
27+
2428
// Create temporary directory
2529
const tempDir = await fs.mkdtemp(
2630
path.join(os.tmpdir(), 'metaspatial-export-'),
@@ -31,6 +35,7 @@ export async function executeMetaSpatialExport(
3135

3236
if (verbose) {
3337
console.log(`🚀 Executing Meta Spatial CLI export:`);
38+
console.log(` CLI: ${cliPath}`);
3439
console.log(` Project: ${absoluteProjectPath}`);
3540
console.log(` Temp output: ${tempDir}`);
3641
}

packages/vite-plugin-metaspatial/tests/cli-path-resolver.test.ts

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*/
77

88
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
9-
import { getHighestVersion, resolveMetaSpatialCliPath } from '../src/generate-glxf/cli-path-resolver.js';
9+
import { getHighestVersion, resolveMetaSpatialCliPath, validateCliPath } from '../src/generate-glxf/cli-path-resolver.js';
1010
import fs from 'fs-extra';
1111
import * as path from 'path';
1212

@@ -200,4 +200,87 @@ describe('CLI Path Resolver', () => {
200200
expect(result).not.toBe('/Applications/Meta Spatial Editor.app/Contents/MacOS/CLI');
201201
});
202202
});
203+
204+
describe('validateCliPath', () => {
205+
beforeEach(() => {
206+
// Reset to darwin for most tests
207+
Object.defineProperty(process, 'platform', {
208+
value: 'darwin',
209+
});
210+
});
211+
212+
it('should pass validation when CLI exists and is executable on macOS', async () => {
213+
const cliPath = '/Applications/Meta Spatial Editor.app/Contents/MacOS/CLI';
214+
215+
vi.mocked(fs.pathExists).mockResolvedValue(true);
216+
vi.mocked(fs.access).mockResolvedValue(undefined);
217+
218+
await expect(validateCliPath(cliPath)).resolves.toBeUndefined();
219+
});
220+
221+
it('should pass validation when CLI exists on Windows', async () => {
222+
Object.defineProperty(process, 'platform', {
223+
value: 'win32',
224+
});
225+
226+
const cliPath = 'C:\\Program Files\\Meta Spatial Editor\\v20\\Resources\\CLI.exe';
227+
228+
vi.mocked(fs.pathExists).mockResolvedValue(true);
229+
230+
await expect(validateCliPath(cliPath)).resolves.toBeUndefined();
231+
});
232+
233+
it('should skip validation for Linux with non-absolute path', async () => {
234+
Object.defineProperty(process, 'platform', {
235+
value: 'linux',
236+
});
237+
238+
const cliPath = 'MetaSpatialEditorCLI';
239+
240+
await expect(validateCliPath(cliPath)).resolves.toBeUndefined();
241+
expect(fs.pathExists).not.toHaveBeenCalled();
242+
});
243+
244+
it('should throw helpful error when CLI does not exist on macOS', async () => {
245+
const cliPath = '/Applications/Meta Spatial Editor.app/Contents/MacOS/CLI';
246+
247+
vi.mocked(fs.pathExists).mockResolvedValue(false);
248+
249+
await expect(validateCliPath(cliPath)).rejects.toThrow('Meta Spatial Editor CLI not found');
250+
await expect(validateCliPath(cliPath)).rejects.toThrow('meta-spatial-editor-for-mac');
251+
await expect(validateCliPath(cliPath)).rejects.toThrow('META_SPATIAL_EDITOR_CLI_PATH');
252+
});
253+
254+
it('should throw helpful error when CLI does not exist on Windows', async () => {
255+
Object.defineProperty(process, 'platform', {
256+
value: 'win32',
257+
});
258+
259+
const cliPath = 'C:\\Program Files\\Meta Spatial Editor\\v20\\Resources\\CLI.exe';
260+
261+
vi.mocked(fs.pathExists).mockResolvedValue(false);
262+
263+
await expect(validateCliPath(cliPath)).rejects.toThrow('Meta Spatial Editor CLI not found');
264+
await expect(validateCliPath(cliPath)).rejects.toThrow('meta-spatial-editor-for-windows');
265+
});
266+
267+
it('should throw helpful error when CLI exists but is not executable on macOS', async () => {
268+
const cliPath = '/Applications/Meta Spatial Editor.app/Contents/MacOS/CLI';
269+
270+
vi.mocked(fs.pathExists).mockResolvedValue(true);
271+
vi.mocked(fs.access).mockRejectedValue(new Error('Permission denied'));
272+
273+
await expect(validateCliPath(cliPath)).rejects.toThrow('not executable');
274+
await expect(validateCliPath(cliPath)).rejects.toThrow('chmod +x');
275+
});
276+
277+
it('should include download link in error message for unknown errors', async () => {
278+
const cliPath = '/Applications/Meta Spatial Editor.app/Contents/MacOS/CLI';
279+
280+
vi.mocked(fs.pathExists).mockRejectedValue(new Error('Unknown error'));
281+
282+
await expect(validateCliPath(cliPath)).rejects.toThrow('Error checking Meta Spatial Editor CLI');
283+
await expect(validateCliPath(cliPath)).rejects.toThrow('download');
284+
});
285+
});
203286
});

0 commit comments

Comments
 (0)