Skip to content

Commit 17c3141

Browse files
committed
feat(cli): support mcp apps and improved error handling
- Added new app resources for icons and tokens lists, and example previews. - Improved error handling in the CLI tool for better user feedback. Signed-off-by: Cory Rylan <crylan@nvidia.com>
1 parent 4bafa47 commit 17c3141

32 files changed

Lines changed: 2262 additions & 239 deletions

pnpm-lock.yaml

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

projects/cli/package.json

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"dist/**/*.js"
3535
],
3636
"scripts": {
37-
"dev": "pnpm run nve:install && pnpm dlx @modelcontextprotocol/inspector@0.21.2 node ./dist/index.js mcp",
37+
"dev": "pnpm run nve:install && npx @modelcontextprotocol/inspector@0.21.2 node ./dist/index.js mcp",
3838
"ci": "wireit",
3939
"build": "wireit",
4040
"lint": "wireit",
@@ -47,22 +47,23 @@
4747
"nve:uninstall:node": "pnpm uninstall --global @nvidia-elements/cli"
4848
},
4949
"dependencies": {
50-
"@nvidia-elements/lint": "workspace:^",
5150
"@inquirer/prompts": "8.4.2",
5251
"@modelcontextprotocol/sdk": "1.29.0",
52+
"@nvidia-elements/code": "workspace:*",
53+
"@nvidia-elements/lint": "workspace:^",
5354
"adm-zip": "0.5.17",
5455
"archiver": "7.0.1",
5556
"marked": "18.0.3",
5657
"marked-terminal": "7.3.0",
58+
"open": "11.0.0",
5759
"ora": "9.4.0",
5860
"publint": "catalog:",
5961
"yargs": "18.0.0",
60-
"open": "11.0.0",
6162
"zod": "catalog:"
6263
},
6364
"devDependencies": {
64-
"@internals/tools": "workspace:*",
6565
"@internals/eslint": "workspace:*",
66+
"@internals/tools": "workspace:*",
6667
"@internals/vite": "workspace:*",
6768
"@vitest/coverage-istanbul": "catalog:",
6869
"@types/node": "catalog:",
@@ -117,6 +118,13 @@
117118
"command": "NODE_ENV=production vite build",
118119
"files": [
119120
"../internals/tools/dist/**/*.js",
121+
"../core/dist/bundles/index.js",
122+
"../code/dist/bundles/index.js",
123+
"../themes/dist/index.css",
124+
"../themes/dist/dark.css",
125+
"../themes/dist/high-contrast.css",
126+
"../styles/dist/typography.css",
127+
"../styles/dist/layout.css",
120128
"src/**",
121129
"!src/**/*.test.ts",
122130
"package.json",
@@ -135,6 +143,22 @@
135143
{
136144
"script": "../internals/tools:build",
137145
"cascade": false
146+
},
147+
{
148+
"script": "../core:build",
149+
"cascade": false
150+
},
151+
{
152+
"script": "../code:build",
153+
"cascade": false
154+
},
155+
{
156+
"script": "../themes:build",
157+
"cascade": false
158+
},
159+
{
160+
"script": "../styles:build",
161+
"cascade": false
138162
}
139163
]
140164
},

projects/cli/src/index.test.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ describe('index', () => {
9999
input: ''
100100
});
101101
expect(result.status).toBe(1);
102-
expect(result.stderr + result.stdout).toContain('Invalid values');
102+
expect(result.stdout).toBe('');
103+
expect(result.stderr).toContain('Invalid values');
103104
});
104105
});
105106

@@ -116,4 +117,38 @@ describe('index', () => {
116117
expect(combined).toContain('nve-bar');
117118
});
118119
});
120+
121+
describe('tool errors', () => {
122+
it('should exit with error when exact api lookup has no matches', () => {
123+
const result = spawnSync('node', ['dist/index.js', 'api.get', 'nve-badges'], {
124+
timeout: 10000,
125+
encoding: 'utf-8',
126+
input: ''
127+
});
128+
129+
expect(result.status).toBe(1);
130+
expect(result.stdout).toBe('');
131+
expect(result.stderr).toContain('No components or APIs found matching');
132+
expect(result.stderr).toContain('nve-badges');
133+
});
134+
135+
it('should print structured error results when they are available', () => {
136+
const result = spawnSync(
137+
'node',
138+
['dist/index.js', 'playground.create', '<nve-button nve-layout="column">hello</nve-button>'],
139+
{
140+
timeout: 10000,
141+
encoding: 'utf-8',
142+
input: '',
143+
env: { ...process.env, CI: 'true', ELEMENTS_PLAYGROUND_BASE_URL: 'https://playground.example' }
144+
}
145+
);
146+
const lintMessages = JSON.parse(result.stderr) as { message: string }[];
147+
148+
expect(result.status).toBe(1);
149+
expect(result.stdout).toBe('');
150+
expect(Array.isArray(lintMessages)).toBe(true);
151+
expect(lintMessages[0]?.message).toContain('Unexpected use of restricted attribute "nve-layout"');
152+
});
153+
});
119154
});

projects/cli/src/index.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const yargsInstance = yargs(hideBin(process.argv))
3232
}
3333

3434
if (message !== null) {
35-
console.log(colors.error(message));
35+
console.error(colors.error(message));
3636
}
3737
process.exit(1);
3838
});
@@ -45,6 +45,11 @@ yargsInstance.middleware(argv => {
4545
}
4646
});
4747

48+
async function exitWithToolError(result: unknown, message: string | undefined): Promise<never> {
49+
console.error(result === undefined ? colors.error(message ?? 'unknown error') : await renderResult(result));
50+
process.exit(1);
51+
}
52+
4853
yargsInstance.command(
4954
'$0',
5055
'About and help',
@@ -57,8 +62,7 @@ yargsInstance.command(
5762
await renderResult(result);
5863
process.exit(0);
5964
} else {
60-
console.log(colors.error(message ?? 'unknown error'));
61-
process.exit(1);
65+
await exitWithToolError(result, message);
6266
}
6367
} else {
6468
const greeting = colors.complete(`\x1b[?7l\n${JSON.parse(banner)}\n\n`);
@@ -102,7 +106,6 @@ tools
102106
optionalArgs.forEach(key => builder.option(key, argOptions(properties[key]!)));
103107
},
104108
// main handler for the command
105-
// eslint-disable-next-line max-statements
106109
async args => {
107110
const start = performance.now();
108111
const { result, status, message } = await runAsyncTool(args, tool);
@@ -121,8 +124,7 @@ tools
121124
await notifyIfUpdateAvailable(BUILD_SHA);
122125
process.exit(0);
123126
} else {
124-
console.log(colors.error(message ?? 'unknown error'));
125-
process.exit(1);
127+
await exitWithToolError(result, message);
126128
}
127129
},
128130
// middleware to get interactive arguments when missing

0 commit comments

Comments
 (0)