diff --git a/.eslintrc.json b/.eslintrc.json index 727fd51ac7..8d88924918 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,5 +1,11 @@ { - "extends": ["react-app", "plugin:@typescript-eslint/recommended"], + "extends": [ + // "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended" + // "plugin:react/recommended", + // "plugin:react/jsx-runtime" + ], "plugins": ["simple-import-sort"], "rules": { "no-restricted-imports": [ @@ -18,6 +24,8 @@ ] } ], + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/no-duplicate-enum-values": "off", "@typescript-eslint/no-empty-function": "off", "@typescript-eslint/interface-name-prefix": "off", "@typescript-eslint/camelcase": "off", diff --git a/package.json b/package.json index a2eb1fbb89..b6920b9dce 100644 --- a/package.json +++ b/package.json @@ -127,6 +127,8 @@ "@types/showdown": "^2.0.1", "@types/uuid": "^9.0.0", "@types/xml2js": "^0.4.11", + "@typescript-eslint/eslint-plugin": "^7.4.0", + "@typescript-eslint/parser": "^7.4.0", "babel-core": "6", "babel-runtime": "^6.26.0", "buffer": "^6.0.3", @@ -134,6 +136,7 @@ "constants-browserify": "^1.0.0", "coveralls": "^3.1.1", "cross-env": "^7.0.3", + "eslint": "^8.57.0", "eslint-plugin-simple-import-sort": "^12.0.0", "https-browserify": "^1.0.0", "husky": "^9.0.0", @@ -151,7 +154,7 @@ "stream-browserify": "^3.0.0", "stream-http": "^3.2.0", "timers-browserify": "^2.0.12", - "typescript": "~4.9.0", + "typescript": "^5.4.3", "url": "^0.11.1", "webpack-bundle-analyzer": "^4.9.0" }, diff --git a/src/commons/XMLParser/XMLParserHelper.ts b/src/commons/XMLParser/XMLParserHelper.ts index 5e106fc2ba..c5d5bb3106 100644 --- a/src/commons/XMLParser/XMLParserHelper.ts +++ b/src/commons/XMLParser/XMLParserHelper.ts @@ -167,7 +167,11 @@ const makeQuestions = (task: XmlParseStrTask): [Question[], number] => { const makeMCQ = (problem: XmlParseStrCProblem, question: BaseQuestion): IMCQQuestion => { const choicesVal: MCQChoice[] = []; - const solution = problem.SNIPPET ? problem.SNIPPET[0].SOLUTION : undefined; + const snippet = problem.SNIPPET; + // FIXME: I think `XmlParseStrCProblem` type definition is incorrect + // FIXME: Remove `as unknown as keyof typeof snippet` when fixed + // @ts-expect-error broken type definition to be fixed above + const solution = snippet ? snippet[0 as unknown as keyof typeof snippet].SOLUTION : undefined; let solutionVal = 0; problem.CHOICE.forEach((choice: XmlParseStrProblemChoice, i: number) => { choicesVal.push({ @@ -269,7 +273,8 @@ const exportLibrary = (library: Library) => { name: library.external.name } } - }; + // FIXME: Replace any with proper type + } as any; if (library.external.symbols.length !== 0) { /* tslint:disable:no-string-literal */ @@ -327,7 +332,8 @@ export const assessmentToXml = ( }, TEXT: question.content, CHOICE: [] as any[] - }; + // FIXME: Replace any with proper type + } as any; if (question.library.chapter !== -1) { /* tslint:disable:no-string-literal */ diff --git a/src/commons/collabEditing/CollabEditingHelper.ts b/src/commons/collabEditing/CollabEditingHelper.ts index ddccb15648..c7a5d29ee8 100644 --- a/src/commons/collabEditing/CollabEditingHelper.ts +++ b/src/commons/collabEditing/CollabEditingHelper.ts @@ -1,14 +1,14 @@ import Constants from '../utils/Constants'; -const protocolMap = { +const protocolMap = Object.freeze({ 'http:': 'ws:', 'https:': 'wss:' -}; +}); export function getSessionUrl(sessionId: string, ws?: boolean): string { const url = new URL(sessionId, Constants.sharedbBackendUrl); - if (ws) { - url.protocol = protocolMap[url.protocol]; + if (ws && Object.keys(protocolMap).includes(url.protocol)) { + url.protocol = protocolMap[url.protocol as keyof typeof protocolMap]; } return url.toString(); } diff --git a/src/commons/documentation/Documentation.ts b/src/commons/documentation/Documentation.ts index 25b1f332bf..f14fc4eb45 100644 --- a/src/commons/documentation/Documentation.ts +++ b/src/commons/documentation/Documentation.ts @@ -3,7 +3,14 @@ import { deviceTypes } from 'src/features/remoteExecution/RemoteExecutionTypes'; import { externalLibraries } from '../application/types/ExternalTypes'; -const externalLibrariesDocumentation = {}; +type DocType = { + caption: string; + value: string; + meta: string; + docHTML?: string; +}; + +const externalLibrariesDocumentation: Record = {}; const MAX_CAPTION_LENGTH = 27; @@ -15,13 +22,14 @@ function shortenCaption(name: string): string { return (name = name.substring(0, MAX_CAPTION_LENGTH - 3) + '...'); } -function mapExternalLibraryName(name: string) { +function mapExternalLibraryName(name: string): DocType { if (name in SourceDocumentation.ext_lib) { + const key = name as keyof typeof SourceDocumentation.ext_lib; return { - caption: shortenCaption(name), - value: name, - meta: SourceDocumentation.ext_lib[name].meta, - docHTML: SourceDocumentation.ext_lib[name].description + caption: shortenCaption(key), + value: key, + meta: SourceDocumentation.ext_lib[key].meta, + docHTML: SourceDocumentation.ext_lib[key].description }; } else { return { @@ -42,7 +50,7 @@ for (const deviceType of deviceTypes) { deviceType.internalFunctions.map(mapExternalLibraryName); } -const builtinDocumentation = {}; +const builtinDocumentation: Record = {}; Object.entries(SourceDocumentation.builtins).forEach((chapterDoc: any) => { const [chapter, docs] = chapterDoc; diff --git a/src/commons/editingWorkspace/EditingWorkspace.tsx b/src/commons/editingWorkspace/EditingWorkspace.tsx index c684a2923c..0f8c7e091e 100644 --- a/src/commons/editingWorkspace/EditingWorkspace.tsx +++ b/src/commons/editingWorkspace/EditingWorkspace.tsx @@ -723,7 +723,7 @@ const EditingWorkspace: React.FC = props => { }; function uniq(a: string[]) { - const seen = {}; + const seen: Record = {}; return a.filter(item => (seen.hasOwnProperty(item) ? false : (seen[item] = true))); } diff --git a/src/commons/editor/Editor.tsx b/src/commons/editor/Editor.tsx index 1e14e4fad4..6bccbd28b8 100644 --- a/src/commons/editor/Editor.tsx +++ b/src/commons/editor/Editor.tsx @@ -15,6 +15,7 @@ import { IAceEditor } from 'react-ace/lib/types'; import { HotKeys } from 'react-hotkeys'; import { EditorBinding } from '../WorkspaceSettingsContext'; import { getModeString, selectMode } from '../utils/AceHelper'; +import { objectEntries } from '../utils/TypeHelper'; import { KeyFunction, keyBindings } from './EditorHotkeys'; import { AceMouseEvent, HighlightedLines, Position } from './EditorTypes'; @@ -537,7 +538,7 @@ const EditorBase = React.memo((props: EditorProps & LocalStateProps) => { ] ); - aceEditorProps.commands = Object.entries(keyHandlers) + aceEditorProps.commands = objectEntries(keyHandlers) .filter(([_, exec]) => exec) .map(([name, exec]) => ({ name, bindKey: keyBindings[name], exec: exec! })); diff --git a/src/commons/editor/tabs/utils.ts b/src/commons/editor/tabs/utils.ts index d65ce70ca4..227c10f3d8 100644 --- a/src/commons/editor/tabs/utils.ts +++ b/src/commons/editor/tabs/utils.ts @@ -27,7 +27,9 @@ export const getShortestUniqueFilePaths = (originalFilePaths: string[]): string[ // Split each original file path into path segments and store the mapping from file // path to path segments for O(1) lookup. Since we only deal with the BrowserFS file // system, the path separator will always be '/'. - const filePathSegments: Record = originalFilePaths.reduce( + const filePathSegments: Record = originalFilePaths.reduce< + typeof filePathSegments + >( (segments, filePath) => ({ ...segments, // It is necessary to remove empty segments to deal with the very first '/' in @@ -48,15 +50,18 @@ export const getShortestUniqueFilePaths = (originalFilePaths: string[]): string[ // to any original file path which transforms into it. const shortenedToOriginalFilePaths: Record = Object.entries( filePathSegments - ).reduce((filePaths, [originalFilePath, filePathSegments]) => { - // Note that if there are fewer path segments than the number being sliced, - // all of the path segments will be returned without error. - const shortenedFilePath = '/' + filePathSegments.slice(-numOfPathSegments).join('/'); - return { - ...filePaths, - [shortenedFilePath]: (filePaths[shortenedFilePath] ?? []).concat(originalFilePath) - }; - }, {}); + ).reduce( + (filePaths, [originalFilePath, filePathSegments]) => { + // Note that if there are fewer path segments than the number being sliced, + // all of the path segments will be returned without error. + const shortenedFilePath = '/' + filePathSegments.slice(-numOfPathSegments).join('/'); + return { + ...filePaths, + [shortenedFilePath]: (filePaths[shortenedFilePath] ?? []).concat(originalFilePath) + }; + }, + {} + ); // Each shortened file path that only has a single corresponding original file // path is added to the unique shortened file paths record and their entry in // the file path segments record is removed to prevent further processing. diff --git a/src/commons/sagas/WorkspaceSaga/index.ts b/src/commons/sagas/WorkspaceSaga/index.ts index 6cd5760f9d..57ccf7f0fe 100644 --- a/src/commons/sagas/WorkspaceSaga/index.ts +++ b/src/commons/sagas/WorkspaceSaga/index.ts @@ -490,7 +490,7 @@ export default function* WorkspaceSaga(): SagaIterator { yield call([CseMachine, CseMachine.clear]); const globals: Array<[string, any]> = action.payload.library.globals as Array<[string, any]>; for (const [key, value] of globals) { - window[key] = value; + window[key as any] = value; } yield put( actions.endClearContext( diff --git a/src/commons/sideContent/SideContentHelper.ts b/src/commons/sideContent/SideContentHelper.ts index 43188fbab2..7971b0f82a 100644 --- a/src/commons/sideContent/SideContentHelper.ts +++ b/src/commons/sideContent/SideContentHelper.ts @@ -40,7 +40,7 @@ const requireProvider = (x: string) => { }; if (!(x in exports)) throw new Error(`Dynamic require of ${x} is not supported`); - return exports[x]; + return exports[x as keyof typeof exports] as any; }; type RawTab = (provider: ReturnType) => { default: ModuleSideContent }; diff --git a/src/commons/sideContent/content/SideContentContestVoting.tsx b/src/commons/sideContent/content/SideContentContestVoting.tsx index e5f299d8e6..2af8989e25 100644 --- a/src/commons/sideContent/content/SideContentContestVoting.tsx +++ b/src/commons/sideContent/content/SideContentContestVoting.tsx @@ -97,8 +97,8 @@ const SideContentContestVoting: React.FC = ({ [currentDraggedItem] ); - const contestEntryRefs = useRef({}); - const tierContainerRefs = useRef({}); + const contestEntryRefs = useRef>({}); + const tierContainerRefs = useRef>({}); const tierBoard = useMemo(() => { return TIERS.map((tier, index) => ( diff --git a/src/commons/utils/DisplayBufferService.ts b/src/commons/utils/DisplayBufferService.ts index 79347a8df0..3e9f8e57b6 100644 --- a/src/commons/utils/DisplayBufferService.ts +++ b/src/commons/utils/DisplayBufferService.ts @@ -22,15 +22,17 @@ class BufferService { public attachConsole(workspaceLocation: WorkspaceLocation): () => void { const bufferCallback = (log: string) => this.push(log, workspaceLocation); - const defaultConsole = {}; + const defaultConsole: Record = {}; Object.entries(consoleOverloads).forEach(([method, overload]) => { - defaultConsole[method] = console[method]; - console[method] = overload(bufferCallback); + const key = method as keyof typeof consoleOverloads; + defaultConsole[method] = console[key]; + console[key] = overload(bufferCallback); }); return () => { Object.entries(consoleOverloads).forEach(([method]) => { - console[method] = defaultConsole[method]; + const key = method as keyof typeof consoleOverloads; + console[key] = defaultConsole[key]; }); }; } diff --git a/src/commons/utils/JsSlangHelper.ts b/src/commons/utils/JsSlangHelper.ts index 37d6f0b23d..ab88e89db8 100644 --- a/src/commons/utils/JsSlangHelper.ts +++ b/src/commons/utils/JsSlangHelper.ts @@ -180,7 +180,7 @@ export function makeElevatedContext(context: Context) { if (prop === 'head') { return fakeFrame; } - return target[prop]; + return target[prop as keyof typeof target]; } }); @@ -189,7 +189,7 @@ export function makeElevatedContext(context: Context) { if (prop === '0') { return proxyGlobalEnv; } - return target[prop]; + return target[prop as keyof typeof target]; } }); @@ -198,7 +198,7 @@ export function makeElevatedContext(context: Context) { if (prop === 'environments') { return proxyEnvs; } - return target[prop]; + return target[prop as keyof typeof target]; } }); @@ -210,7 +210,7 @@ export function makeElevatedContext(context: Context) { case 'runtime': return proxyRuntime; default: - return target[prop]; + return target[prop as keyof typeof target]; } } }); diff --git a/src/commons/utils/TypeHelper.ts b/src/commons/utils/TypeHelper.ts index 5ff79ba1b9..e93d425ab8 100644 --- a/src/commons/utils/TypeHelper.ts +++ b/src/commons/utils/TypeHelper.ts @@ -10,6 +10,32 @@ export type KeysOfType = { [K in keyof O]: O[K] extends T ? K : never; }[keyof O]; +/** + * Does union(keyof ) for each member of T. This is unlike + * `keyof T` which would give keyof(union()). + * @param T - The union type to extract keys from + */ +export type DistributedKeyOf> = T extends any ? keyof T : never; + +/** + * Generates a "set difference" of two types, keeping the properties of T that are not + * present in S. + * @param T - The type to extract keys from + * @param S - The type to compare against + */ +export type Diff, S extends Record> = Pick< + T, + Exclude +>; + +/** + * Merges two types together, keeping the properties of T and adding the + * properties of U that are not present in T. + * @param T - The first type + * @param U - The second type (also "universal" set of properties to add to T) + */ +export type Merge, U extends Record> = T & Diff; + // Adapted from https://github.com/piotrwitek/typesafe-actions/blob/a1fe54bb150ac1b935bb9ca78361d2d024d2efaf/src/type-helpers.ts#L117-L130 export type ActionType> = { [k in keyof T]: ReturnType; @@ -100,3 +126,26 @@ export const assertType = // Keep the original type as inferred by TS ): T => obj; + +/** + * Type safe `Object.keys` + */ +export function objectKeys(obj: Record): T[] { + return Object.keys(obj) as T[]; +} + +/** + * Type safe `Object.values` + */ +export function objectValues(obj: Record) { + return Object.values(obj) as T[]; +} + +/** + * Type safe `Object.entries` + */ +export function objectEntries( + obj: Partial> +): [K, V][] { + return Object.entries(obj) as [K, V][]; +} diff --git a/src/commons/workspace/__tests__/WorkspaceReducer.ts b/src/commons/workspace/__tests__/WorkspaceReducer.ts index 080f4c56f6..1ad5546ecc 100644 --- a/src/commons/workspace/__tests__/WorkspaceReducer.ts +++ b/src/commons/workspace/__tests__/WorkspaceReducer.ts @@ -136,7 +136,7 @@ describe('BROWSE_REPL_HISTORY_DOWN', () => { actions.forEach(action => { let result = WorkspaceReducer(replDownDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...replDownDefaultState, [location]: { @@ -203,7 +203,7 @@ describe('BROWSE_REPL_HISTORY_UP', () => { actions.forEach(action => { let result = WorkspaceReducer(replUpDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...replUpDefaultState, [location]: { @@ -280,7 +280,7 @@ describe('CLEAR_REPL_INPUT', () => { actions.forEach(action => { const result = WorkspaceReducer(clearReplDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...clearReplDefaultState, [location]: { @@ -300,7 +300,7 @@ describe('CLEAR_REPL_OUTPUT', () => { actions.forEach(action => { const result = WorkspaceReducer(clearReplDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...clearReplDefaultState, [location]: { @@ -331,7 +331,7 @@ describe('CLEAR_REPL_OUTPUT_LAST', () => { actions.forEach(action => { const result = WorkspaceReducer(clearReplLastPriorState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...clearReplLastPriorState, [location]: { @@ -355,7 +355,7 @@ describe('DEBUG_RESET', () => { actions.forEach(action => { const result = WorkspaceReducer(debugResetDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...debugResetDefaultState, [location]: { @@ -378,7 +378,7 @@ describe('DEBUG_RESUME', () => { actions.forEach(action => { const result = WorkspaceReducer(debugResumeDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...debugResumeDefaultState, [location]: { @@ -416,7 +416,7 @@ describe('END_CLEAR_CONTEXT', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceManager, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; const context = createContext( library.chapter, library.external.symbols, @@ -449,7 +449,7 @@ describe('END_DEBUG_PAUSE', () => { actions.forEach(action => { const result = WorkspaceReducer(debugPauseDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...debugPauseDefaultState, [location]: { @@ -474,7 +474,7 @@ describe('END_INTERRUPT_EXECUTION', () => { actions.forEach(action => { const result = WorkspaceReducer(interruptExecutionDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...interruptExecutionDefaultState, [location]: { @@ -497,7 +497,7 @@ describe('EVAL_EDITOR', () => { actions.forEach(action => { const result = WorkspaceReducer(evalEditorDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...evalEditorDefaultState, [location]: { @@ -535,7 +535,7 @@ describe('EVAL_INTERPRETER_ERROR', () => { actions.forEach(action => { const result = WorkspaceReducer(evalEditorDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...evalEditorDefaultState, [location]: { @@ -561,7 +561,7 @@ describe('EVAL_INTERPRETER_ERROR', () => { actions.forEach(action => { const result = WorkspaceReducer(evalEditorDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...evalEditorDefaultState, [location]: { @@ -595,7 +595,7 @@ describe('EVAL_INTERPRETER_SUCCESS', () => { actions.forEach(action => { const result = WorkspaceReducer(evalEditorDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...evalEditorDefaultState, [location]: { @@ -625,7 +625,7 @@ describe('EVAL_INTERPRETER_SUCCESS', () => { actions.forEach(action => { const result = WorkspaceReducer(evalEditorDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...evalEditorDefaultState, [location]: { @@ -648,7 +648,7 @@ describe('EVAL_REPL', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceManager, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...defaultWorkspaceManager, [location]: { @@ -696,7 +696,7 @@ describe('EVAL_TESTCASE_FAILURE', () => { actions.forEach(action => { const result = WorkspaceReducer(evalFailureDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...evalFailureDefaultState, [location]: { @@ -725,7 +725,7 @@ describe('EVAL_TESTCASE_SUCCESS', () => { actions.forEach(action => { const result = WorkspaceReducer(testcaseSuccessDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...testcaseSuccessDefaultState, [location]: { @@ -754,7 +754,7 @@ describe('EVAL_TESTCASE_SUCCESS', () => { actions.forEach(action => { const result = WorkspaceReducer(testcaseSuccessDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...testcaseSuccessDefaultState, [location]: { @@ -778,7 +778,7 @@ describe('HANDLE_CONSOLE_LOG', () => { const actions = generateActions(HANDLE_CONSOLE_LOG, { logString: [logString] }); actions.forEach(action => { const result = WorkspaceReducer(cloneDeep(consoleLogDefaultState), action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...consoleLogDefaultState, @@ -805,7 +805,7 @@ describe('HANDLE_CONSOLE_LOG', () => { actions.forEach(action => { const result = WorkspaceReducer(consoleLogDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...consoleLogDefaultState, [location]: { @@ -827,7 +827,7 @@ describe('HANDLE_CONSOLE_LOG', () => { actions.forEach(action => { const result = WorkspaceReducer(consoleLogDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...consoleLogDefaultState, [location]: { @@ -895,7 +895,7 @@ describe('RESET_TESTCASE', () => { actions.forEach(action => { const result = WorkspaceReducer(resetTestcaseDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...resetTestcaseDefaultState, [location]: { @@ -928,7 +928,7 @@ describe('RESET_WORKSPACE', () => { actions.forEach(action => { const result = WorkspaceReducer(resetWorkspaceDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; const newContext = createDefaultWorkspace(location); // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions @@ -969,7 +969,7 @@ describe('SEND_REPL_INPUT_TO_OUTPUT', () => { actions.forEach(action => { const result = WorkspaceReducer(inputToOutputDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...inputToOutputDefaultState, [location]: { @@ -1004,7 +1004,7 @@ describe('SEND_REPL_INPUT_TO_OUTPUT', () => { actions.forEach(action => { const result = WorkspaceReducer(inputToOutputDefaultState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...inputToOutputDefaultState, [location]: { @@ -1024,7 +1024,7 @@ describe('SET_EDITOR_SESSION_ID', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceManager, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...defaultWorkspaceManager, [location]: { @@ -1043,7 +1043,7 @@ describe('SET_SHAREDB_CONNECTED', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceManager, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...defaultWorkspaceManager, [location]: { @@ -1061,7 +1061,7 @@ describe('TOGGLE_EDITOR_AUTORUN', () => { actions.forEach(action => { let result = WorkspaceReducer(defaultWorkspaceManager, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...defaultWorkspaceManager, [location]: { @@ -1131,7 +1131,7 @@ describe('SET_FOLDER_MODE', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceManager, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...defaultWorkspaceManager, [location]: { @@ -1183,7 +1183,7 @@ describe('UPDATE_ACTIVE_EDITOR_TAB_INDEX', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -1241,7 +1241,7 @@ describe('UPDATE_ACTIVE_EDITOR_TAB', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; const newContext = createDefaultWorkspace(location); // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions @@ -1341,7 +1341,7 @@ describe('UPDATE_EDITOR_VALUE', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...defaultWorkspaceState, [location]: { @@ -1408,7 +1408,7 @@ describe('UPDATE_EDITOR_BREAKPOINTS', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...defaultWorkspaceState, [location]: { @@ -1487,7 +1487,7 @@ describe('UPDATE_EDITOR_HIGHLIGHTED_LINES', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...defaultWorkspaceState, [location]: { @@ -1569,7 +1569,7 @@ describe('MOVE_CURSOR', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...defaultWorkspaceState, [location]: { @@ -1608,7 +1608,7 @@ describe('ADD_EDITOR_TAB', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -1640,7 +1640,7 @@ describe('ADD_EDITOR_TAB', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -1777,7 +1777,7 @@ describe('SHIFT_EDITOR_TAB', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -1807,7 +1807,7 @@ describe('SHIFT_EDITOR_TAB', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -1878,7 +1878,7 @@ describe('REMOVE_EDITOR_TAB', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -1905,7 +1905,7 @@ describe('REMOVE_EDITOR_TAB', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -1932,7 +1932,7 @@ describe('REMOVE_EDITOR_TAB', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -1959,7 +1959,7 @@ describe('REMOVE_EDITOR_TAB', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -1986,7 +1986,7 @@ describe('REMOVE_EDITOR_TAB', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -2046,7 +2046,7 @@ describe('REMOVE_EDITOR_TAB_FOR_FILE', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -2073,7 +2073,7 @@ describe('REMOVE_EDITOR_TAB_FOR_FILE', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -2100,7 +2100,7 @@ describe('REMOVE_EDITOR_TAB_FOR_FILE', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -2127,7 +2127,7 @@ describe('REMOVE_EDITOR_TAB_FOR_FILE', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -2154,7 +2154,7 @@ describe('REMOVE_EDITOR_TAB_FOR_FILE', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -2231,7 +2231,7 @@ describe('REMOVE_EDITOR_TABS_FOR_DIRECTORY', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -2258,7 +2258,7 @@ describe('REMOVE_EDITOR_TABS_FOR_DIRECTORY', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -2285,7 +2285,7 @@ describe('REMOVE_EDITOR_TABS_FOR_DIRECTORY', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -2312,7 +2312,7 @@ describe('REMOVE_EDITOR_TABS_FOR_DIRECTORY', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -2339,7 +2339,7 @@ describe('REMOVE_EDITOR_TABS_FOR_DIRECTORY', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -2400,7 +2400,7 @@ describe('RENAME_EDITOR_TAB_FOR_FILE', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -2470,7 +2470,7 @@ describe('RENAME_EDITOR_TABS_FOR_DIRECTORY', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceState, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; // Note: we stringify because context contains functions which cause // the two to compare unequal; stringifying strips functions expect(JSON.stringify(result)).toEqual( @@ -2500,7 +2500,7 @@ describe('UPDATE_HAS_UNSAVED_CHANGES', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceManager, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...defaultWorkspaceManager, [location]: { @@ -2519,7 +2519,7 @@ describe('UPDATE_REPL_VALUE', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceManager, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; expect(result).toEqual({ ...defaultWorkspaceManager, [location]: { @@ -2538,7 +2538,7 @@ describe('TOGGLE_USING_SUBST', () => { actions.forEach(action => { const result = WorkspaceReducer(defaultWorkspaceManager, action); - const location = action.payload.workspaceLocation; + const location: WorkspaceLocation = action.payload.workspaceLocation; const expectedResult = location === playgroundWorkspace || location === sicpWorkspace diff --git a/src/features/cseMachine/CseMachineLayout.tsx b/src/features/cseMachine/CseMachineLayout.tsx index 311a4d94cf..eb7aa4c5e2 100644 --- a/src/features/cseMachine/CseMachineLayout.tsx +++ b/src/features/cseMachine/CseMachineLayout.tsx @@ -1,5 +1,6 @@ import Heap from 'js-slang/dist/cse-machine/heap'; import { Control, Stash } from 'js-slang/dist/cse-machine/interpreter'; +import { Frame } from 'js-slang/dist/types'; import { KonvaEventObject } from 'konva/lib/Node'; import React, { RefObject } from 'react'; import { Layer, Rect, Stage } from 'react-konva'; @@ -304,7 +305,7 @@ export class Layout { ); let i = 0; - const newHead = {}; + const newHead: Frame = {}; const newHeap = new Heap(); for (const fn of referencedFns) { if (isClosure(fn)) newHeap.add(fn); diff --git a/src/features/game/location/GameMap.ts b/src/features/game/location/GameMap.ts index d009abce86..412fd1fbbb 100644 --- a/src/features/game/location/GameMap.ts +++ b/src/features/game/location/GameMap.ts @@ -125,11 +125,14 @@ class GameMap { } public setItemInMap(gameItemType: GameItemType, itemId: string, item: any) { + // @ts-expect-error TS 5.0, violating abstraction of class and object using .set this[gameItemType].set(itemId, item); } public addItemToLocation(locId: LocationId, gameItemType: GameItemType, itemId: string) { - this.getLocationAtId(locId)[gameItemType].add(itemId); + const location = this.getLocationAtId(locId); + const items = location[gameItemType as keyof typeof location]; + (items as Set).add(itemId); } public setBGMusicAt(locId: LocationId, soundKey: AssetKey) { diff --git a/src/features/game/parser/LocationDetailsParser.ts b/src/features/game/parser/LocationDetailsParser.ts index 91a40012ff..a87b8649cb 100644 --- a/src/features/game/parser/LocationDetailsParser.ts +++ b/src/features/game/parser/LocationDetailsParser.ts @@ -30,7 +30,7 @@ export default class LocationDetailsParser { }); Parser.checkpoint.map.addMapAsset(this.locationAssetKey(shortPath), { - type: AssetType[type] || AssetType.Image, + type: AssetType[type as keyof typeof AssetType] || AssetType.Image, key: id, path: this.locationPath(shortPath), config: { diff --git a/src/features/game/parser/ObjectParser.ts b/src/features/game/parser/ObjectParser.ts index 1a23199201..2f2fecb0f4 100644 --- a/src/features/game/parser/ObjectParser.ts +++ b/src/features/game/parser/ObjectParser.ts @@ -83,7 +83,7 @@ export default class ObjectParser { }; Parser.checkpoint.map.addMapAsset(this.objectAssetKey(shortPath), { - type: AssetType[type] || AssetType.Image, + type: AssetType[type as keyof typeof AssetType] || AssetType.Image, key: objectId, path: this.objectPath(shortPath), config: { diff --git a/src/features/game/parser/ParserConverter.ts b/src/features/game/parser/ParserConverter.ts index 56b7dcb3bb..921cdb45ce 100644 --- a/src/features/game/parser/ParserConverter.ts +++ b/src/features/game/parser/ParserConverter.ts @@ -82,30 +82,44 @@ const stringToUserStateTypeMap = { * This also acts as a validity checker to ensure that * strings such as action types (eg 'show_dialogue') and * game modes (eg 'explore') are actually valid enums + * + * TODO: Investigate if the typing can be improved */ export default class ParserConverter { public static stringToSize(str: string) { - return stringToSizeMap[str] || GameSize.Medium; + return stringToSizeMap[str as keyof typeof stringToSizeMap] || GameSize.Medium; } public static stringToPosition(str: string) { - return stringToPositionMap[str] || GamePosition.Middle; + return stringToPositionMap[str as keyof typeof stringToPositionMap] || GamePosition.Middle; } public static stringToGameMode(str: string) { - return mandatory(stringToGameModeMap[str], `Invalid location mode, ${str}`); + return mandatory( + stringToGameModeMap[str as keyof typeof stringToGameModeMap], + `Invalid location mode, ${str}` + ); } public static stringToActionType(str: string) { - return mandatory(stringToActionTypeMap[str], `Invalid action type, ${str}`); + return mandatory( + stringToActionTypeMap[str as keyof typeof stringToActionTypeMap], + `Invalid action type, ${str}` + ); } public static stringToGameStateStorage(str: string) { - return mandatory(stringToGameStateStorageMap[str], `Invalid condition type, ${str}`); + return mandatory( + stringToGameStateStorageMap[str as keyof typeof stringToGameStateStorageMap], + `Invalid condition type, ${str}` + ); } public static stringToGameItemType(str: string) { - return mandatory(stringToGameItemMap[str], `Invalid entity type, ${str}`); + return mandatory( + stringToGameItemMap[str as keyof typeof stringToGameItemMap], + `Invalid entity type, ${str}` + ); } public static stringToBoolean(str: string) { @@ -113,6 +127,9 @@ export default class ParserConverter { } public static stringToUserStateType(str: string) { - return mandatory(stringToUserStateTypeMap[str], `Invalid user state type ${str}`); + return mandatory( + stringToUserStateTypeMap[str as keyof typeof stringToUserStateTypeMap], + `Invalid user state type ${str}` + ); } } diff --git a/src/features/game/parser/ParserValidator.ts b/src/features/game/parser/ParserValidator.ts index a58a106b0e..52bd8af204 100644 --- a/src/features/game/parser/ParserValidator.ts +++ b/src/features/game/parser/ParserValidator.ts @@ -121,6 +121,7 @@ export default class ParserValidator { (assertionDetails: AssertionDetail[], gameItemType: GameItemType) => { assertionDetails.forEach((assertionDetail: AssertionDetail) => { const { itemId, actionType } = assertionDetail; + // @ts-expect-error TS 5.0, violating abstraction of class and object using .has if (!Parser.checkpoint.map[gameItemType].has(itemId)) { if (actionType) { this.actionAssertionError(itemId, gameItemType, actionType); diff --git a/src/features/game/phase/GamePhaseManager.ts b/src/features/game/phase/GamePhaseManager.ts index 90d4477cc5..df7baa2b28 100644 --- a/src/features/game/phase/GamePhaseManager.ts +++ b/src/features/game/phase/GamePhaseManager.ts @@ -172,7 +172,9 @@ export default class GamePhaseManager { * @returns {boolean} */ public isCurrentPhaseTerminal(): boolean { - return Object.values(GameTerminalPhaseType).includes(this.getCurrentPhase()); + return Object.values(GameTerminalPhaseType).includes( + this.getCurrentPhase() as unknown as GameTerminalPhaseType + ); } /** diff --git a/src/features/game/state/GameStateManager.ts b/src/features/game/state/GameStateManager.ts index 5cb0ecc1ce..24c3264947 100644 --- a/src/features/game/state/GameStateManager.ts +++ b/src/features/game/state/GameStateManager.ts @@ -232,7 +232,10 @@ class GameStateManager { * @returns {ItemId[]} items IDS of all game items of that type in the location */ public getGameItemsInLocation(gameItemType: GameItemType, locationId: LocationId): ItemId[] { - return Array.from(this.gameMap.getLocationAtId(locationId)[gameItemType]) || []; + const location = this.gameMap.getLocationAtId(locationId); + const items = location[gameItemType as keyof typeof location]; + // Non-strict equality check to match both null and undefined + return items == undefined ? [] : Array.from(items); } /** @@ -245,7 +248,9 @@ class GameStateManager { * @param itemId item ID to be added */ public addItem(gameItemType: GameItemType, locationId: LocationId, itemId: ItemId) { - this.gameMap.getLocationAtId(locationId)[gameItemType]?.add(itemId); + const location = this.gameMap.getLocationAtId(locationId); + const items = location[gameItemType as keyof typeof location]; + (items as Set | undefined)?.add(itemId); this.isCurrentLocation(locationId) ? this.getSubscriberForItemType(gameItemType)?.handleAdd(itemId) @@ -263,7 +268,9 @@ class GameStateManager { * @param itemId item ID to be removed */ public removeItem(gameItemType: GameItemType, locationId: LocationId, itemId: string) { - this.gameMap.getLocationAtId(locationId)[gameItemType]?.delete(itemId); + const location = this.gameMap.getLocationAtId(locationId); + const items = location[gameItemType as keyof typeof location]; + (items as Set | undefined)?.delete(itemId); this.isCurrentLocation(locationId) ? this.getSubscriberForItemType(gameItemType)?.handleDelete(itemId) diff --git a/src/features/sicp/TableOfContentsHelper.ts b/src/features/sicp/TableOfContentsHelper.ts index 1616d6947c..7f2a850f5e 100644 --- a/src/features/sicp/TableOfContentsHelper.ts +++ b/src/features/sicp/TableOfContentsHelper.ts @@ -1,13 +1,20 @@ +import { DistributedKeyOf, Merge } from 'src/commons/utils/TypeHelper'; + import tocNavigation from './data/toc-navigation.json'; -export const getNext = (section: string): string | undefined => { - const node = tocNavigation[section]; +type MergeAll>> = { + [K in keyof T]: Merge, undefined>>; +}; - return node && node['next']; +type NodeTree = MergeAll; +type Node = NodeTree[keyof NodeTree]; + +export const getNext = (section: string): string | undefined => { + const node = tocNavigation[section as keyof typeof tocNavigation] as Node; + return node && node.next; }; export const getPrev = (section: string): string | undefined => { - const node = tocNavigation[section]; - - return node && node['prev']; + const node = tocNavigation[section as keyof typeof tocNavigation] as Node; + return node && node.prev; }; diff --git a/src/pages/academy/game/Game.tsx b/src/pages/academy/game/Game.tsx index f82c6264dd..f68a0aae78 100644 --- a/src/pages/academy/game/Game.tsx +++ b/src/pages/academy/game/Game.tsx @@ -69,6 +69,8 @@ function Game() { if (window.screen.orientation) { if (!isFullscreen) { + // @ts-expect-error: lock is not defined in the type definition + // as it is not suppored in some browsers (notably Firefox) window.screen.orientation.lock('landscape'); } else { window.screen.orientation.unlock(); diff --git a/src/pages/academy/gameSimulator/subcomponents/assetViewer/AssetViewerUtils.tsx b/src/pages/academy/gameSimulator/subcomponents/assetViewer/AssetViewerUtils.tsx index 3f05682c9a..b67e93ecfd 100644 --- a/src/pages/academy/gameSimulator/subcomponents/assetViewer/AssetViewerUtils.tsx +++ b/src/pages/academy/gameSimulator/subcomponents/assetViewer/AssetViewerUtils.tsx @@ -1,6 +1,8 @@ import { TreeNodeInfo } from '@blueprintjs/core'; import { set } from 'lodash'; +type Tree = Record | string[] | any; + /** * This function takes a list of filepaths and returns blueprint core tree nodes. * Each node represents a file / folder. @@ -21,7 +23,7 @@ export function convertAssetPathsToTree( iconRenderer: (pathName: string) => JSX.Element, rootFolders: string[] = [] ): TreeNodeInfo[] { - const assetObj = {}; + const assetObj: Tree = {}; assetPaths.forEach(assetPath => set(assetObj, assetPath.split('/'), 'FILE')); rootFolders.forEach(folder => { if (!assetObj[folder] || assetObj[folder] === 'FILE') { @@ -29,10 +31,7 @@ export function convertAssetPathsToTree( } }); - const convertAssetObjectsToTree = ( - parentFolders: string[], - assetObj: object | Array - ): TreeNodeInfo[] => { + const convertAssetObjectsToTree = (parentFolders: string[], assetObj: Tree): TreeNodeInfo[] => { return Object.keys(assetObj).map(file => { const shortPath = '/' + parentFolders.join('/') + '/' + file; return { diff --git a/src/pages/academy/grading/subcomponents/GradingBadges.tsx b/src/pages/academy/grading/subcomponents/GradingBadges.tsx index cae1f8e225..cdb85fc884 100644 --- a/src/pages/academy/grading/subcomponents/GradingBadges.tsx +++ b/src/pages/academy/grading/subcomponents/GradingBadges.tsx @@ -4,7 +4,7 @@ import { ColumnFilter } from '@tanstack/react-table'; import { Badge } from '@tremor/react'; import { ProgressStatus, ProgressStatuses } from 'src/commons/assessment/AssessmentTypes'; -const BADGE_COLORS = { +const BADGE_COLORS = Object.freeze({ // assessment types missions: 'indigo', quests: 'emerald', @@ -18,10 +18,11 @@ const BADGE_COLORS = { [ProgressStatuses.submitted]: 'yellow', [ProgressStatuses.graded]: 'green', [ProgressStatuses.published]: 'blue' -}; +}); export function getBadgeColorFromLabel(label: string) { - return BADGE_COLORS[label.toLowerCase()] || 'gray'; + const maybeKey = label.toLowerCase() as keyof typeof BADGE_COLORS; + return BADGE_COLORS[maybeKey] || 'gray'; } type AssessmentTypeBadgeProps = { diff --git a/src/pages/academy/teamFormation/subcomponents/TeamFormationTable.tsx b/src/pages/academy/teamFormation/subcomponents/TeamFormationTable.tsx index 6e5a30c124..3f255ae8ec 100644 --- a/src/pages/academy/teamFormation/subcomponents/TeamFormationTable.tsx +++ b/src/pages/academy/teamFormation/subcomponents/TeamFormationTable.tsx @@ -28,6 +28,7 @@ import { TextInput } from '@tremor/react'; import React, { useState } from 'react'; +import { objectKeys } from 'src/commons/utils/TypeHelper'; import { TeamFormationOverview } from 'src/features/teamFormation/TeamFormationTypes'; import { AssessmentTypeBadge } from '../../grading/subcomponents/GradingBadges'; @@ -60,9 +61,12 @@ const columns = [ {', '} )), - filterFn: (row: Row, id: string | number, filterValue: any): boolean => { - const rowValue = row.original[id]; - return Array.isArray(rowValue) && rowValue.includes(filterValue); + filterFn: (row, id: string | number, filterValue: any): boolean => { + const rowValue = row.original[id as keyof typeof row.original]; + if (typeof rowValue === 'string' || typeof rowValue === 'number') { + return rowValue === filterValue; + } + return rowValue.some(v => v === filterValue); } }), columnHelper.accessor(({ teamId }) => ({ teamId }), { @@ -97,7 +101,7 @@ const TeamFormationTable: React.FC = ({ group, teams }) columnId: string | number, filterValue: any ): boolean => { - for (const column of Object.keys(row.original)) { + for (const column of objectKeys(row.original)) { const rowValue = row.original[column]; if (Array.isArray(rowValue)) { diff --git a/src/pages/sicp/Sicp.tsx b/src/pages/sicp/Sicp.tsx index 1419ea43f1..aa093097ce 100644 --- a/src/pages/sicp/Sicp.tsx +++ b/src/pages/sicp/Sicp.tsx @@ -2,7 +2,7 @@ import 'katex/dist/katex.min.css'; import { Button, Classes, NonIdealState, Spinner } from '@blueprintjs/core'; import classNames from 'classnames'; -import React from 'react'; +import React, { useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; import { useLocation, useNavigate, useParams } from 'react-router'; import { Link } from 'react-router-dom'; @@ -37,12 +37,12 @@ export const CodeSnippetContext = React.createContext({ const loadingComponent = } />; const Sicp: React.FC = () => { - const [data, setData] = React.useState(<>); - const [loading, setLoading] = React.useState(false); - const [active, setActive] = React.useState('0'); + const [data, setData] = useState(<>); + const [loading, setLoading] = useState(false); + const [active, setActive] = useState('0'); const { section } = useParams<{ section: string }>(); - const parentRef = React.useRef(null); - const refs = React.useRef({}); + const parentRef = useRef(null); + const refs = useRef>({}); const navigate = useNavigate(); const location = useLocation(); const { isLoggedIn } = useSession(); @@ -73,7 +73,7 @@ const Sicp: React.FC = () => { return visibleParagraphs; }; - const scrollRefIntoView = (ref: HTMLDivElement | null) => { + const scrollRefIntoView = (ref: HTMLElement | null) => { if (!ref || !parentRef?.current) { return; } diff --git a/tsconfig.json b/tsconfig.json index 186f9134ca..c988d3f032 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,7 +18,6 @@ "noImplicitAny": true, "strictNullChecks": true, "skipLibCheck": true, - "suppressImplicitAnyIndexErrors": true, "noUnusedLocals": true, "esModuleInterop": false, "allowSyntheticDefaultImports": true, diff --git a/yarn.lock b/yarn.lock index 980c2abfb0..8b9593f2be 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz#bd9154aec9983f77b3a034ecaa015c2e4201f6cf" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + "@adobe/css-tools@^4.3.2": version "4.3.3" resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.3.3.tgz#90749bde8b89cd41764224f5aac29cd4138f75ff" @@ -1523,19 +1528,26 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.4.0": +"@eslint-community/eslint-utils@^4.4.0": version "4.4.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.4.0.tgz#3e61c564fcd6b921cb789838631c5ee44df09403" - integrity sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ== + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" -"@eslint/eslintrc@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.1.tgz#7888fe7ec8f21bc26d646dbd2c11cd776e21192d" - integrity sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw== +"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": + version "4.10.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== + +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.5.0" + espree "^9.6.0" globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -1543,10 +1555,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.36.0": - version "8.36.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe" - integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg== +"@eslint/js@8.57.0": + version "8.57.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f" + integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g== "@floating-ui/core@^1.0.0": version "1.6.0" @@ -1573,13 +1585,13 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== -"@humanwhocodes/config-array@^0.11.8": - version "0.11.8" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9" - integrity sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g== +"@humanwhocodes/config-array@^0.11.14": + version "0.11.14" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== dependencies: - "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" minimatch "^3.0.5" "@humanwhocodes/module-importer@^1.0.1": @@ -1587,10 +1599,10 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" + integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" @@ -2991,10 +3003,10 @@ resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2" integrity sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg== -"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": - version "7.0.11" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" - integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== +"@types/json-schema@*", "@types/json-schema@^7.0.12", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/json5@^0.0.29": version "0.0.29" @@ -3183,10 +3195,10 @@ resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== -"@types/semver@^7.3.12": - version "7.3.13" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" - integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== +"@types/semver@^7.3.12", "@types/semver@^7.5.0": + version "7.5.8" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" + integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== "@types/serve-index@^1.9.1": version "1.9.1" @@ -3289,6 +3301,23 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/eslint-plugin@^7.4.0": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.4.0.tgz#de61c3083842fc6ac889d2fc83c9a96b55ab8328" + integrity sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw== + dependencies: + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "7.4.0" + "@typescript-eslint/type-utils" "7.4.0" + "@typescript-eslint/utils" "7.4.0" + "@typescript-eslint/visitor-keys" "7.4.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" + "@typescript-eslint/experimental-utils@^5.0.0": version "5.54.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.54.1.tgz#a45609ce43fc6b24b4c4dde215446eaad7805223" @@ -3306,6 +3335,17 @@ "@typescript-eslint/typescript-estree" "5.54.1" debug "^4.3.4" +"@typescript-eslint/parser@^7.4.0": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-7.4.0.tgz#540f4321de1e52b886c0fa68628af1459954c1f1" + integrity sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ== + dependencies: + "@typescript-eslint/scope-manager" "7.4.0" + "@typescript-eslint/types" "7.4.0" + "@typescript-eslint/typescript-estree" "7.4.0" + "@typescript-eslint/visitor-keys" "7.4.0" + debug "^4.3.4" + "@typescript-eslint/scope-manager@5.54.1": version "5.54.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.54.1.tgz#6d864b4915741c608a58ce9912edf5a02bb58735" @@ -3314,6 +3354,14 @@ "@typescript-eslint/types" "5.54.1" "@typescript-eslint/visitor-keys" "5.54.1" +"@typescript-eslint/scope-manager@7.4.0": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.4.0.tgz#acfc69261f10ece7bf7ece1734f1713392c3655f" + integrity sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw== + dependencies: + "@typescript-eslint/types" "7.4.0" + "@typescript-eslint/visitor-keys" "7.4.0" + "@typescript-eslint/type-utils@5.54.1": version "5.54.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.54.1.tgz#4825918ec27e55da8bb99cd07ec2a8e5f50ab748" @@ -3324,11 +3372,26 @@ debug "^4.3.4" tsutils "^3.21.0" +"@typescript-eslint/type-utils@7.4.0": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.4.0.tgz#cfcaab21bcca441c57da5d3a1153555e39028cbd" + integrity sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw== + dependencies: + "@typescript-eslint/typescript-estree" "7.4.0" + "@typescript-eslint/utils" "7.4.0" + debug "^4.3.4" + ts-api-utils "^1.0.1" + "@typescript-eslint/types@5.54.1": version "5.54.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.54.1.tgz#29fbac29a716d0f08c62fe5de70c9b6735de215c" integrity sha512-G9+1vVazrfAfbtmCapJX8jRo2E4MDXxgm/IMOF4oGh3kq7XuK3JRkOg6y2Qu1VsTRmWETyTkWt1wxy7X7/yLkw== +"@typescript-eslint/types@7.4.0": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.4.0.tgz#ee9dafa75c99eaee49de6dcc9348b45d354419b6" + integrity sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw== + "@typescript-eslint/typescript-estree@5.54.1": version "5.54.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.54.1.tgz#df7b6ae05fd8fef724a87afa7e2f57fa4a599be1" @@ -3342,6 +3405,20 @@ semver "^7.3.7" tsutils "^3.21.0" +"@typescript-eslint/typescript-estree@7.4.0": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.4.0.tgz#12dbcb4624d952f72c10a9f4431284fca24624f4" + integrity sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg== + dependencies: + "@typescript-eslint/types" "7.4.0" + "@typescript-eslint/visitor-keys" "7.4.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + minimatch "9.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + "@typescript-eslint/utils@5.54.1", "@typescript-eslint/utils@^5.43.0": version "5.54.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.54.1.tgz#7a3ee47409285387b9d4609ea7e1020d1797ec34" @@ -3356,6 +3433,19 @@ eslint-utils "^3.0.0" semver "^7.3.7" +"@typescript-eslint/utils@7.4.0": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.4.0.tgz#d889a0630cab88bddedaf7c845c64a00576257bd" + integrity sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "7.4.0" + "@typescript-eslint/types" "7.4.0" + "@typescript-eslint/typescript-estree" "7.4.0" + semver "^7.5.4" + "@typescript-eslint/visitor-keys@5.54.1": version "5.54.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.54.1.tgz#d7a8a0f7181d6ac748f4d47b2306e0513b98bf8b" @@ -3364,7 +3454,15 @@ "@typescript-eslint/types" "5.54.1" eslint-visitor-keys "^3.3.0" -"@ungap/structured-clone@^1.0.0": +"@typescript-eslint/visitor-keys@7.4.0": + version "7.4.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.4.0.tgz#0c8ff2c1f8a6fe8d7d1a57ebbd4a638e86a60a94" + integrity sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA== + dependencies: + "@typescript-eslint/types" "7.4.0" + eslint-visitor-keys "^3.4.1" + +"@ungap/structured-clone@^1.0.0", "@ungap/structured-clone@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== @@ -3600,7 +3698,7 @@ acorn-walk@^8.0.0, acorn-walk@^8.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== -acorn@*, acorn@^8.0.4, acorn@^8.1, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0, acorn@^8.8.2, acorn@^8.9.0: +acorn@*, acorn@^8.0.4, acorn@^8.1, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: version "8.11.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== @@ -3679,7 +3777,7 @@ ajv-keywords@^5.0.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.12.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -6291,10 +6389,10 @@ eslint-scope@5.1.1, eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -6311,10 +6409,10 @@ eslint-visitor-keys@^2.0.0, eslint-visitor-keys@^2.1.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== eslint-webpack-plugin@^3.1.1: version "3.2.0" @@ -6327,27 +6425,28 @@ eslint-webpack-plugin@^3.1.1: normalize-path "^3.0.0" schema-utils "^4.0.0" -eslint@^8.3.0: - version "8.36.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.36.0.tgz#1bd72202200a5492f91803b113fb8a83b11285cf" - integrity sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw== +eslint@^8.3.0, eslint@^8.57.0: + version "8.57.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.0.tgz#c786a6fd0e0b68941aaf624596fb987089195668" + integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== dependencies: "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.0.1" - "@eslint/js" "8.36.0" - "@humanwhocodes/config-array" "^0.11.8" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.57.0" + "@humanwhocodes/config-array" "^0.11.14" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" - ajv "^6.10.0" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-visitor-keys "^3.3.0" - espree "^9.5.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -6355,32 +6454,29 @@ eslint@^8.3.0: find-up "^5.0.0" glob-parent "^6.0.2" globals "^13.19.0" - grapheme-splitter "^1.0.4" + graphemer "^1.4.0" ignore "^5.2.0" - import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" is-path-inside "^3.0.3" - js-sdsl "^4.1.4" js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" lodash.merge "^4.6.2" minimatch "^3.1.2" natural-compare "^1.4.0" - optionator "^0.9.1" + optionator "^0.9.3" strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" text-table "^0.2.0" -espree@^9.5.0: - version "9.5.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.0.tgz#3646d4e3f58907464edba852fa047e6a27bdf113" - integrity sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: - acorn "^8.8.0" + acorn "^8.9.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" + eslint-visitor-keys "^3.4.1" esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" @@ -7113,6 +7209,11 @@ grapheme-splitter@^1.0.4: resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + gzip-size@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" @@ -7528,10 +7629,10 @@ ieee754@^1.1.13, ieee754@^1.2.1: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== -ignore@^5.2.0: - version "5.2.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" - integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== +ignore@^5.2.0, ignore@^5.2.4: + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== immer@^9.0.21, immer@^9.0.7: version "9.0.21" @@ -7543,7 +7644,7 @@ immutable@^4.0.0: resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.5.tgz#f8b436e66d59f99760dc577f5c99a4fd2a5cc5a0" integrity sha512-8eabxkth9gZatlwl5TBuJnCsoTADlL6ftEr7A4qgdaTsPyreilDSnUk57SO+jfKcNtxPa22U5KK6DSeAYhpBJw== -import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.3.0: +import-fresh@^3.1.0, import-fresh@^3.2.1, import-fresh@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -8564,7 +8665,7 @@ js-base64@^3.7.5: resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.5.tgz#21e24cf6b886f76d6f5f165bfcd69cc55b9e3fca" integrity sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA== -js-sdsl@4.3.0, js-sdsl@^4.1.4: +js-sdsl@4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== @@ -9393,6 +9494,13 @@ minimalistic-assert@^1.0.0: resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== +minimatch@9.0.3, minimatch@^9.0.0: + version "9.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" + integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== + dependencies: + brace-expansion "^2.0.1" + minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -9407,13 +9515,6 @@ minimatch@^5.0.1, minimatch@^5.1.0: dependencies: brace-expansion "^2.0.1" -minimatch@^9.0.0: - version "9.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== - dependencies: - brace-expansion "^2.0.1" - minimist@^1.1.0, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" @@ -9905,17 +10006,17 @@ optionator@^0.8.1: type-check "~0.3.2" word-wrap "~1.2.3" -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== dependencies: + "@aashutoshrathi/word-wrap" "^1.2.3" deep-is "^0.1.3" fast-levenshtein "^2.0.6" levn "^0.4.1" prelude-ls "^1.2.1" type-check "^0.4.0" - word-wrap "^1.2.3" os-browserify@^0.3.0: version "0.3.0" @@ -11972,7 +12073,7 @@ selfsigned@^2.1.1: dependencies: node-forge "^1" -semver@7.6.0, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8: +semver@7.6.0, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.4: version "7.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d" integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg== @@ -12583,7 +12684,7 @@ strip-indent@^3.0.0: dependencies: min-indent "^1.0.0" -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== @@ -12989,6 +13090,11 @@ tryer@^1.0.1: resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== +ts-api-utils@^1.0.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1" + integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ== + ts-custom-error@^3.0.0: version "3.3.1" resolved "https://registry.yarnpkg.com/ts-custom-error/-/ts-custom-error-3.3.1.tgz#8bd3c8fc6b8dc8e1cb329267c45200f1e17a65d1" @@ -13134,10 +13240,10 @@ typescript-tuple@^2.2.1: dependencies: typescript-compare "^0.0.2" -typescript@~4.9.0: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@^5.4.3: + version "5.4.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.3.tgz#5c6fedd4c87bee01cd7a528a30145521f8e0feff" + integrity sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg== unbox-primitive@^1.0.2: version "1.0.2" @@ -13779,7 +13885,7 @@ wildcard@^2.0.0: resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== -word-wrap@^1.2.3, word-wrap@~1.2.3: +word-wrap@~1.2.3: version "1.2.4" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==