From ee05b0d5da15a49a8c6a6b3338e3eefc71f23d25 Mon Sep 17 00:00:00 2001 From: Anna Date: Fri, 12 Sep 2025 14:16:37 -0400 Subject: [PATCH] feat(tools): allow tsx tabs (#61936) Co-authored-by: Huyen Nguyen <25715018+huyenltnguyen@users.noreply.github.com> --- client/src/templates/Challenges/classic/editor.tsx | 1 + .../Challenges/classic/multifile-editor.tsx | 3 +++ client/utils/__fixtures__/challenges.ts | 14 ++++++++++++++ client/utils/sort-challengefiles.test.ts | 3 ++- client/utils/sort-challengefiles.ts | 2 ++ shared/utils/polyvinyl.ts | 2 +- .../parser/plugins/add-seed.test.js | 2 +- .../parser/plugins/utils/get-file-visitor.js | 4 ++-- 8 files changed, 26 insertions(+), 5 deletions(-) diff --git a/client/src/templates/Challenges/classic/editor.tsx b/client/src/templates/Challenges/classic/editor.tsx index 14755b40115..efbde68e0bc 100644 --- a/client/src/templates/Challenges/classic/editor.tsx +++ b/client/src/templates/Challenges/classic/editor.tsx @@ -188,6 +188,7 @@ const modeMap = { js: 'javascript', jsx: 'javascript', ts: 'typescript', + tsx: 'typescript', py: 'python', python: 'python' }; diff --git a/client/src/templates/Challenges/classic/multifile-editor.tsx b/client/src/templates/Challenges/classic/multifile-editor.tsx index f24ff29daab..18c68837425 100644 --- a/client/src/templates/Challenges/classic/multifile-editor.tsx +++ b/client/src/templates/Challenges/classic/multifile-editor.tsx @@ -17,6 +17,7 @@ export type VisibleEditors = { stylescss?: boolean; scriptjs?: boolean; indexts?: boolean; + indextsx?: boolean; mainpy?: boolean; }; type MultifileEditorProps = Pick< @@ -70,6 +71,7 @@ const MultifileEditor = (props: MultifileEditorProps) => { scriptjs, indexts, indexjsx, + indextsx, mainpy }, usesMultifileEditor, @@ -94,6 +96,7 @@ const MultifileEditor = (props: MultifileEditorProps) => { // The order of the keys should match the order set by sortChallengeFiles if (indexjsx) editorKeys.push('indexjsx'); + if (indextsx) editorKeys.push('indextsx'); if (indexhtml) editorKeys.push('indexhtml'); if (stylescss) editorKeys.push('stylescss'); if (scriptjs) editorKeys.push('scriptjs'); diff --git a/client/utils/__fixtures__/challenges.ts b/client/utils/__fixtures__/challenges.ts index de4cb6ec828..45ab1784330 100644 --- a/client/utils/__fixtures__/challenges.ts +++ b/client/utils/__fixtures__/challenges.ts @@ -70,5 +70,19 @@ export const challengeFiles: ChallengeFile[] = [ editableRegionBoundaries: [], usesMultifileEditor: true, path: 'index.jsx', + }, + { + contents: 'some tsx', + error: null, + ext: 'tsx', + head: '', + history: ['index.tsx'], + fileKey: 'indextsx', + name: 'index', + seed: 'some tsx', + tail: '', + editableRegionBoundaries: [], + usesMultifileEditor: true, + path: 'index.tsx', } ] diff --git a/client/utils/sort-challengefiles.test.ts b/client/utils/sort-challengefiles.test.ts index 34b658e04e2..51b962cf1a3 100644 --- a/client/utils/sort-challengefiles.test.ts +++ b/client/utils/sort-challengefiles.test.ts @@ -14,11 +14,12 @@ describe('sort-files', () => { expect(sorted.length).toEqual(expected.length); }); - it('should sort the objects into jsx, html, css, js, ts order', () => { + it('should sort the objects into jsx, tsx, html, css, js, ts order', () => { const sorted = sortChallengeFiles(challengeFiles); const sortedKeys = sorted.map(({ fileKey }) => fileKey); const expected = [ 'indexjsx', + 'indextsx', 'indexhtml', 'stylescss', 'scriptjs', diff --git a/client/utils/sort-challengefiles.ts b/client/utils/sort-challengefiles.ts index 3656061b830..4db2f4d52a9 100644 --- a/client/utils/sort-challengefiles.ts +++ b/client/utils/sort-challengefiles.ts @@ -4,6 +4,8 @@ export function sortChallengeFiles( return challengeFiles.toSorted((a, b) => { if (a.fileKey === 'indexjsx') return -1; if (b.fileKey === 'indexjsx') return 1; + if (a.fileKey === 'indextsx') return -1; + if (b.fileKey === 'indextsx') return 1; if (a.fileKey === 'indexhtml') return -1; if (b.fileKey === 'indexhtml') return 1; if (a.fileKey === 'stylescss') return -1; diff --git a/shared/utils/polyvinyl.ts b/shared/utils/polyvinyl.ts index 135ef92191c..768ada3514c 100644 --- a/shared/utils/polyvinyl.ts +++ b/shared/utils/polyvinyl.ts @@ -1,7 +1,7 @@ // originally based off of https://github.com/gulpjs/vinyl import invariant from 'invariant'; -const exts = ['js', 'html', 'css', 'jsx', 'ts', 'py'] as const; +const exts = ['js', 'html', 'css', 'jsx', 'ts', 'tsx', 'py'] as const; export type Ext = (typeof exts)[number]; export interface IncompleteChallengeFile { diff --git a/tools/challenge-parser/parser/plugins/add-seed.test.js b/tools/challenge-parser/parser/plugins/add-seed.test.js index 69fccc669ba..6009e17c2ae 100644 --- a/tools/challenge-parser/parser/plugins/add-seed.test.js +++ b/tools/challenge-parser/parser/plugins/add-seed.test.js @@ -195,7 +195,7 @@ describe('add-seed plugin', () => { expect.assertions(1); expect(() => plugin(cCodeAST, file)).toThrow( "On line 30 'c' is not a supported language.\n" + - ' Please use one of js, css, html, jsx or py' + ' Please use one of js, css, html, jsx, ts, tsx or py' ); }); diff --git a/tools/challenge-parser/parser/plugins/utils/get-file-visitor.js b/tools/challenge-parser/parser/plugins/utils/get-file-visitor.js index d29afd4228c..927b9a0f5c2 100644 --- a/tools/challenge-parser/parser/plugins/utils/get-file-visitor.js +++ b/tools/challenge-parser/parser/plugins/utils/get-file-visitor.js @@ -8,7 +8,7 @@ const keyToSection = { head: 'before-user-code', tail: 'after-user-code' }; -const supportedLanguages = ['js', 'css', 'html', 'jsx', 'py', 'ts']; +const supportedLanguages = ['js', 'css', 'html', 'jsx', 'py', 'ts', 'tsx']; const longToShortLanguages = { javascript: 'js', typescript: 'ts', @@ -54,7 +54,7 @@ function codeToData(node, seeds, seedKey, validate) { throw Error(`On line ${ position.start(node).line } '${shortLang}' is not a supported language. - Please use one of js, css, html, jsx or py + Please use one of js, css, html, jsx, ts, tsx or py `); const fileId = `index${shortLang}`;