@@ -31,6 +31,12 @@ import {
3131 withAuthGuard ,
3232} from "../../lib/errors.js" ;
3333import { formatProjectCreated , writeJson } from "../../lib/formatters/index.js" ;
34+ import { renderTextTable } from "../../lib/formatters/text-table.js" ;
35+ import {
36+ COMMON_PLATFORMS ,
37+ isValidPlatform ,
38+ suggestPlatform ,
39+ } from "../../lib/platforms.js" ;
3440import { resolveOrg } from "../../lib/resolve-target.js" ;
3541import {
3642 buildOrgNotFoundError ,
@@ -48,43 +54,6 @@ type CreateFlags = {
4854 readonly json : boolean ;
4955} ;
5056
51- /**
52- * Common Sentry platform identifiers shown when platform arg is missing or invalid.
53- *
54- * These use hyphen-separated format matching Sentry's internal platform registry
55- * (see sentry/src/sentry/utils/platform_categories.py). This is a curated subset
56- * of the ~120 supported values — the full list is available via the API endpoint
57- * referenced in `buildPlatformError`.
58- */
59- const PLATFORMS = [
60- "javascript" ,
61- "javascript-react" ,
62- "javascript-nextjs" ,
63- "javascript-vue" ,
64- "javascript-angular" ,
65- "javascript-svelte" ,
66- "javascript-remix" ,
67- "javascript-astro" ,
68- "node" ,
69- "node-express" ,
70- "python" ,
71- "python-django" ,
72- "python-flask" ,
73- "python-fastapi" ,
74- "go" ,
75- "ruby" ,
76- "ruby-rails" ,
77- "php" ,
78- "php-laravel" ,
79- "java" ,
80- "android" ,
81- "dotnet" ,
82- "react-native" ,
83- "apple-ios" ,
84- "rust" ,
85- "elixir" ,
86- ] as const ;
87-
8857/**
8958 * Normalize common platform format mistakes.
9059 *
@@ -96,6 +65,19 @@ const PLATFORMS = [
9665 * Safe to auto-correct because the input is already invalid (dots are never
9766 * valid in platform identifiers) and the correction is unambiguous.
9867 */
68+ /** Reshape a flat list into rows of `cols` columns, padding the last row. */
69+ function toColumnRows ( items : string [ ] , cols : number ) : string [ ] [ ] {
70+ const rows : string [ ] [ ] = [ ] ;
71+ for ( let i = 0 ; i < items . length ; i += cols ) {
72+ const row = items . slice ( i , i + cols ) ;
73+ while ( row . length < cols ) {
74+ row . push ( "" ) ;
75+ }
76+ rows . push ( row ) ;
77+ }
78+ return rows ;
79+ }
80+
9981function normalizePlatform ( platform : string , stderr : Writer ) : string {
10082 if ( ! platform . includes ( "." ) ) {
10183 return platform ;
@@ -142,16 +124,37 @@ function isPlatformError(error: ApiError): boolean {
142124 * @param platform - The invalid platform string, if provided
143125 */
144126function buildPlatformError ( nameArg : string , platform ?: string ) : string {
145- const list = PLATFORMS . map ( ( p ) => ` ${ p } ` ) . join ( "\n" ) ;
146127 const heading = platform
147128 ? `Invalid platform '${ platform } '.`
148129 : "Platform is required." ;
149130
131+ let didYouMean = "" ;
132+ if ( platform ) {
133+ const suggestions = suggestPlatform ( platform ) ;
134+ if ( suggestions . length > 0 ) {
135+ const rows = toColumnRows ( suggestions , 3 ) ;
136+ const table = renderTextTable ( rows [ 0 ] ?? [ ] , rows . slice ( 1 ) , {
137+ headerSeparator : false ,
138+ } ) ;
139+ didYouMean = `\nDid you mean?\n${ table } ` ;
140+ }
141+ }
142+
143+ const platformRows = toColumnRows ( [ ...COMMON_PLATFORMS ] , 3 ) ;
144+ const platformTable = renderTextTable (
145+ platformRows [ 0 ] ?? [ ] ,
146+ platformRows . slice ( 1 ) ,
147+ {
148+ headerSeparator : false ,
149+ }
150+ ) ;
151+
150152 return (
151- `${ heading } \n\n` +
152- "Usage:\n" +
153+ `${ heading } \n` +
154+ didYouMean +
155+ "\nUsage:\n" +
153156 ` sentry project create ${ nameArg } <platform>\n\n` +
154- `Available platforms:\n\n${ list } \n \n` +
157+ `Common platforms:\n\n${ platformTable } \n` +
155158 "Run 'sentry project create <name> <platform>' with any valid Sentry platform identifier."
156159 ) ;
157160}
@@ -332,6 +335,10 @@ export const createCommand = buildCommand({
332335
333336 const platform = normalizePlatform ( platformArg , this . stderr ) ;
334337
338+ if ( ! isValidPlatform ( platform ) ) {
339+ throw new CliError ( buildPlatformError ( nameArg , platform ) ) ;
340+ }
341+
335342 const parsed = parseOrgProjectArg ( nameArg ) ;
336343
337344 let explicitOrg : string | undefined ;
0 commit comments