Add more TS lints and fix types (#6037)

* Add as const lint

* Add lint for no implied eval

* Fix incorrect type and add lints

* Add more type lints

* Remove redundant type assertions and add lint

* Fix to turn off incorrect base rules

* Fix yarn lint workflow to wait for build:wasm

* Change so that we don't build:wasm more than once in the workflow
This commit is contained in:
Jonathan Tran
2025-03-28 00:24:24 -04:00
committed by GitHub
parent d1f811f91d
commit cc2efd316c
20 changed files with 163 additions and 65 deletions

View File

@ -20,9 +20,20 @@
"plugin:react-hooks/recommended" "plugin:react-hooks/recommended"
], ],
"rules": { "rules": {
"no-array-constructor": "off", // This is wrong; use the @typescript-eslint one instead.
"@typescript-eslint/no-array-constructor": "error",
"@typescript-eslint/no-array-delete": "error",
"@typescript-eslint/no-duplicate-enum-values": "error",
"@typescript-eslint/no-duplicate-type-constituents": "error",
"@typescript-eslint/no-empty-object-type": "error", "@typescript-eslint/no-empty-object-type": "error",
"@typescript-eslint/no-floating-promises": "error", "@typescript-eslint/no-floating-promises": "error",
"no-implied-eval": "off", // This is wrong; use the @typescript-eslint one instead.
"@typescript-eslint/no-implied-eval": "error",
"@typescript-eslint/no-misused-promises": "error", "@typescript-eslint/no-misused-promises": "error",
"@typescript-eslint/no-namespace": "error",
"@typescript-eslint/no-unnecessary-type-assertion": "error",
"@typescript-eslint/no-unnecessary-type-constraint": "error",
"no-unused-vars": "off", // This is wrong; use the @typescript-eslint one instead.
"@typescript-eslint/no-unused-vars": ["error", { "@typescript-eslint/no-unused-vars": ["error", {
"varsIgnorePattern": "^_", "varsIgnorePattern": "^_",
"argsIgnorePattern": "^_", "argsIgnorePattern": "^_",
@ -30,6 +41,7 @@
"vars": "all", "vars": "all",
"args": "none" "args": "none"
}], }],
"@typescript-eslint/prefer-as-const": "warn",
"jsx-a11y/click-events-have-key-events": "off", "jsx-a11y/click-events-have-key-events": "off",
"jsx-a11y/no-autofocus": "off", "jsx-a11y/no-autofocus": "off",
"jsx-a11y/no-noninteractive-element-interactions": "off", "jsx-a11y/no-noninteractive-element-interactions": "off",

View File

@ -28,43 +28,57 @@ jobs:
- run: yarn fmt-check - run: yarn fmt-check
yarn-build-wasm: yarn-build-wasm:
runs-on: ubuntu-22.04 # Build the wasm blob once on the fastest runner.
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
cache: 'yarn' cache: 'yarn'
- run: yarn install
- name: Install dependencies
run: yarn install
- name: Use correct Rust toolchain
shell: bash
run: |
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
- name: Install rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
cache: false # Configured below.
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b - uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
with: with:
tool: wasm-pack tool: wasm-pack
- run: yarn build:wasm
yarn-tsc: - name: Rust Cache
runs-on: ubuntu-22.04 uses: Swatinem/rust-cache@v2
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- run: yarn install
- run: yarn --cwd ./rust/kcl-language-server --modules-folder node_modules install
- uses: Swatinem/rust-cache@v2
with: with:
workspaces: './rust' workspaces: './rust'
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b - name: Build Wasm
with: shell: bash
tool: wasm-pack run: yarn build:wasm
- run: yarn build:wasm
- run: yarn tsc
yarn-lint: - uses: actions/upload-artifact@v4
runs-on: ubuntu-22.04 with:
name: prepared-wasm
path: |
rust/kcl-wasm-lib/pkg/kcl_wasm_lib*
- uses: actions/upload-artifact@v4
with:
name: prepared-ts-rs-bindings
path: |
rust/kcl-lib/bindings/*
yarn-tsc:
runs-on: ubuntu-latest
needs: yarn-build-wasm
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -73,7 +87,53 @@ jobs:
node-version-file: '.nvmrc' node-version-file: '.nvmrc'
cache: 'yarn' cache: 'yarn'
- run: yarn install - run: yarn install
- run: yarn --cwd ./rust/kcl-language-server --modules-folder node_modules install
- name: Download all artifacts
uses: actions/download-artifact@v4
- name: Copy prepared wasm
run: |
ls -R prepared-wasm
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
mkdir rust/kcl-wasm-lib/pkg
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
- name: Copy prepared ts-rs bindings
run: |
ls -R prepared-ts-rs-bindings
mkdir rust/kcl-lib/bindings
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
- run: yarn tsc
yarn-lint:
runs-on: ubuntu-latest
needs: yarn-build-wasm
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- run: yarn install
- name: Download all artifacts
uses: actions/download-artifact@v4
- name: Copy prepared wasm
run: |
ls -R prepared-wasm
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
mkdir rust/kcl-wasm-lib/pkg
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
- name: Copy prepared ts-rs bindings
run: |
ls -R prepared-ts-rs-bindings
mkdir rust/kcl-lib/bindings
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
- run: yarn lint - run: yarn lint
python-codespell: python-codespell:
@ -91,6 +151,7 @@ jobs:
yarn-unit-test-kcl-samples: yarn-unit-test-kcl-samples:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: yarn-build-wasm
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -103,7 +164,22 @@ jobs:
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b - uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
with: with:
tool: wasm-pack tool: wasm-pack
- run: yarn build:wasm
- name: Download all artifacts
uses: actions/download-artifact@v4
- name: Copy prepared wasm
run: |
ls -R prepared-wasm
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
mkdir rust/kcl-wasm-lib/pkg
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
- name: Copy prepared ts-rs bindings
run: |
ls -R prepared-ts-rs-bindings
mkdir rust/kcl-lib/bindings
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
- run: yarn simpleserver:bg - run: yarn simpleserver:bg
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }} if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
@ -120,6 +196,7 @@ jobs:
yarn-unit-test: yarn-unit-test:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: yarn-build-wasm
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
@ -132,7 +209,22 @@ jobs:
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b - uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
with: with:
tool: wasm-pack tool: wasm-pack
- run: yarn build:wasm
- name: Download all artifacts
uses: actions/download-artifact@v4
- name: Copy prepared wasm
run: |
ls -R prepared-wasm
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
mkdir rust/kcl-wasm-lib/pkg
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
- name: Copy prepared ts-rs bindings
run: |
ls -R prepared-ts-rs-bindings
mkdir rust/kcl-lib/bindings
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
- run: yarn simpleserver:bg - run: yarn simpleserver:bg
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }} if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}

View File

@ -90,7 +90,7 @@ export default class StreamDemuxer extends Queue<Uint8Array> {
} }
add(bytes: Uint8Array): void { add(bytes: Uint8Array): void {
const message = Codec.decode(bytes) as vsrpc.Message const message = Codec.decode<vsrpc.Message>(bytes)
if (this.trace) { if (this.trace) {
Tracer.server(message) Tracer.server(message)
} }

View File

@ -669,7 +669,7 @@ const ConstraintSymbol = ({
return Promise.reject(pResult) return Promise.reject(pResult)
const _node1 = getNodeFromPath<CallExpression | CallExpressionKw>( const _node1 = getNodeFromPath<CallExpression | CallExpressionKw>(
pResult.program!, pResult.program,
pathToNode, pathToNode,
['CallExpression', 'CallExpressionKw'], ['CallExpression', 'CallExpressionKw'],
true true

View File

@ -43,8 +43,8 @@ export const INTERSECTION_PLANE_LAYER = 1
export const SKETCH_LAYER = 2 export const SKETCH_LAYER = 2
// redundant types so that it can be changed temporarily but CI will catch the wrong type // redundant types so that it can be changed temporarily but CI will catch the wrong type
export const DEBUG_SHOW_INTERSECTION_PLANE: false = false export const DEBUG_SHOW_INTERSECTION_PLANE = false
export const DEBUG_SHOW_BOTH_SCENES: false = false export const DEBUG_SHOW_BOTH_SCENES = false
export const RAYCASTABLE_PLANE = 'raycastable-plane' export const RAYCASTABLE_PLANE = 'raycastable-plane'

View File

@ -1037,7 +1037,7 @@ class ArcSegment implements SegmentUtils {
endAngle, endAngle,
scale, scale,
color: grey, // Red color for the angle indicator color: grey, // Red color for the angle indicator
}) as Line })
angleIndicator.name = 'angleIndicator' angleIndicator.name = 'angleIndicator'
// Create a new angle indicator for the end angle // Create a new angle indicator for the end angle
@ -1048,7 +1048,7 @@ class ArcSegment implements SegmentUtils {
endAngle: (endAngle * Math.PI) / 180, endAngle: (endAngle * Math.PI) / 180,
scale, scale,
color: grey, // Green color for the end angle indicator color: grey, // Green color for the end angle indicator
}) as Line })
endAngleIndicator.name = 'endAngleIndicator' endAngleIndicator.name = 'endAngleIndicator'
// Create a length indicator for the end angle // Create a length indicator for the end angle

View File

@ -1,8 +1,7 @@
import { import {
IconDefinition as SolidIconDefinition, IconDefinition,
faCircleExclamation, faCircleExclamation,
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { IconDefinition as BrandIconDefinition } from '@fortawesome/free-brands-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { CustomIcon, CustomIconName } from './CustomIcon' import { CustomIcon, CustomIconName } from './CustomIcon'
@ -14,7 +13,7 @@ const iconSizes = {
} }
export interface ActionIconProps extends React.PropsWithChildren { export interface ActionIconProps extends React.PropsWithChildren {
icon?: SolidIconDefinition | BrandIconDefinition | CustomIconName icon?: IconDefinition | CustomIconName
iconColor?: string iconColor?: string
className?: string className?: string
bgClassName?: string bgClassName?: string

View File

@ -98,9 +98,7 @@ function CommandBarHeader({ children }: React.PropsWithChildren<object>) {
.map(([argName, arg], i) => { .map(([argName, arg], i) => {
const argValue = const argValue =
(typeof argumentsToSubmit[argName] === 'function' (typeof argumentsToSubmit[argName] === 'function'
? (argumentsToSubmit[argName] as Function)( ? argumentsToSubmit[argName](commandBarState.context)
commandBarState.context
)
: argumentsToSubmit[argName]) || '' : argumentsToSubmit[argName]) || ''
return ( return (

View File

@ -517,10 +517,7 @@ interface FileTreeProps {
className?: string className?: string
file?: IndexLoaderData['file'] file?: IndexLoaderData['file']
onNavigateToFile: ( onNavigateToFile: (
focusableElement?: focusableElement?: HTMLElement | React.MutableRefObject<HTMLElement | null>
| HTMLElement
| React.MutableRefObject<HTMLElement | null>
| undefined
) => void ) => void
} }

View File

@ -1431,7 +1431,6 @@ export const ModelingMachineProvider = ({
parsed = pResult.program parsed = pResult.program
if (trap(parsed)) return Promise.reject(parsed) if (trap(parsed)) return Promise.reject(parsed)
parsed = parsed as Node<Program>
if (!result.pathToReplaced) if (!result.pathToReplaced)
return Promise.reject(new Error('No path to replaced node')) return Promise.reject(new Error('No path to replaced node'))
const { const {

View File

@ -8,9 +8,9 @@ export const NODE_ENV = env.NODE_ENV as string | undefined
export const VITE_KC_API_WS_MODELING_URL = env.VITE_KC_API_WS_MODELING_URL as export const VITE_KC_API_WS_MODELING_URL = env.VITE_KC_API_WS_MODELING_URL as
| string | string
| undefined | undefined
export const VITE_KC_API_BASE_URL = env.VITE_KC_API_BASE_URL as string export const VITE_KC_API_BASE_URL = env.VITE_KC_API_BASE_URL
export const VITE_KC_SITE_BASE_URL = env.VITE_KC_SITE_BASE_URL as string export const VITE_KC_SITE_BASE_URL = env.VITE_KC_SITE_BASE_URL
export const VITE_KC_SITE_APP_URL = env.VITE_KC_SITE_APP_URL as string export const VITE_KC_SITE_APP_URL = env.VITE_KC_SITE_APP_URL
export const VITE_KC_SKIP_AUTH = env.VITE_KC_SKIP_AUTH as string | undefined export const VITE_KC_SKIP_AUTH = env.VITE_KC_SKIP_AUTH as string | undefined
export const VITE_KC_CONNECTION_TIMEOUT_MS = export const VITE_KC_CONNECTION_TIMEOUT_MS =
env.VITE_KC_CONNECTION_TIMEOUT_MS as string | undefined env.VITE_KC_CONNECTION_TIMEOUT_MS as string | undefined

View File

@ -340,7 +340,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
defaultValue: (commandBarContext) => { defaultValue: (commandBarContext) => {
return Object.values( return Object.values(
commandBarContext.machineManager.machines || [] commandBarContext.machineManager.machines || []
)[0] as components['schemas']['MachineInfoResponse'] )[0]
}, },
}, },
}, },

View File

@ -74,8 +74,8 @@ export const projectsCommandBarConfig: StateMachineCommandSetConfig<
required: true, required: true,
options: (_, context) => options: (_, context) =>
context?.projects.map((p) => ({ context?.projects.map((p) => ({
name: p.name!, name: p.name,
value: p.name!, value: p.name,
})) || [], })) || [],
}, },
}, },
@ -91,8 +91,8 @@ export const projectsCommandBarConfig: StateMachineCommandSetConfig<
required: true, required: true,
options: (_, context) => options: (_, context) =>
context?.projects.map((p) => ({ context?.projects.map((p) => ({
name: p.name!, name: p.name,
value: p.name!, value: p.name,
})) || [], })) || [],
}, },
newName: { newName: {
@ -136,8 +136,8 @@ export const projectsCommandBarConfig: StateMachineCommandSetConfig<
skip: true, skip: true,
options: (_, context) => options: (_, context) =>
context?.projects.map((p) => ({ context?.projects.map((p) => ({
name: p.name!, name: p.name,
value: p.name!, value: p.name,
})) || [], })) || [],
}, },
name: { name: {

View File

@ -28,7 +28,7 @@ export const FILE_EXT = '.kcl'
/** Default file to open when a project is opened */ /** Default file to open when a project is opened */
export const PROJECT_ENTRYPOINT = `main${FILE_EXT}` as const export const PROJECT_ENTRYPOINT = `main${FILE_EXT}` as const
/** Thumbnail file name */ /** Thumbnail file name */
export const PROJECT_IMAGE_NAME = `thumbnail.png` as const export const PROJECT_IMAGE_NAME = `thumbnail.png`
/** The localStorage key for last-opened projects */ /** The localStorage key for last-opened projects */
export const FILE_PERSIST_KEY = `${PROJECT_FOLDER}-last-opened` as const export const FILE_PERSIST_KEY = `${PROJECT_FOLDER}-last-opened` as const
/** The default name given to new kcl files in a project */ /** The default name given to new kcl files in a project */
@ -169,11 +169,11 @@ export const ZOO_STUDIO_PROTOCOL = 'zoo-studio'
export const ASK_TO_OPEN_QUERY_PARAM = 'ask-open-desktop' export const ASK_TO_OPEN_QUERY_PARAM = 'ask-open-desktop'
/** Real execution. */ /** Real execution. */
export const EXECUTION_TYPE_REAL = 'real' as const export const EXECUTION_TYPE_REAL = 'real'
/** Mock execution. */ /** Mock execution. */
export const EXECUTION_TYPE_MOCK = 'mock' as const export const EXECUTION_TYPE_MOCK = 'mock'
/** No execution. */ /** No execution. */
export const EXECUTION_TYPE_NONE = 'none' as const export const EXECUTION_TYPE_NONE = 'none'
/** /**
* Enum of engine execution kinds. * Enum of engine execution kinds.
*/ */

View File

@ -23,7 +23,7 @@ type OnboardingPaths = {
[K in keyof typeof onboardingPaths]: `/onboarding${(typeof onboardingPaths)[K]}` [K in keyof typeof onboardingPaths]: `/onboarding${(typeof onboardingPaths)[K]}`
} }
const SETTINGS = '/settings' as const const SETTINGS = '/settings'
export type ProjectRoute = { export type ProjectRoute = {
projectName: string | null projectName: string | null

View File

@ -12,6 +12,7 @@ import {
defaultSourceRange, defaultSourceRange,
topLevelRange, topLevelRange,
ArtifactGraph, ArtifactGraph,
CallExpressionKw,
} from 'lang/wasm' } from 'lang/wasm'
import { ModelingMachineEvent } from 'machines/modelingMachine' import { ModelingMachineEvent } from 'machines/modelingMachine'
import { isNonNullable, uuidv4 } from 'lib/utils' import { isNonNullable, uuidv4 } from 'lib/utils'
@ -337,7 +338,7 @@ function updateSceneObjectColors(codeBasedSelections: Selection[]) {
Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => { Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => {
if (!SEGMENT_BODIES_PLUS_PROFILE_START.includes(segmentGroup?.name)) return if (!SEGMENT_BODIES_PLUS_PROFILE_START.includes(segmentGroup?.name)) return
const nodeMeta = getNodeFromPath<Node<CallExpression | CallExpression>>( const nodeMeta = getNodeFromPath<Node<CallExpression | CallExpressionKw>>(
updated, updated,
segmentGroup.userData.pathToNode, segmentGroup.userData.pathToNode,
['CallExpression', 'CallExpressionKw'] ['CallExpression', 'CallExpressionKw']
@ -716,7 +717,7 @@ export function updateSelections(
}, },
} }
}) })
.filter((x?: Selection) => x !== undefined) as Selection[] .filter((x?: Selection) => x !== undefined)
// for when there is no artifact (sketch mode since mock execute does not update artifactGraph) // for when there is no artifact (sketch mode since mock execute does not update artifactGraph)
const pathToNodeBasedSelections: Selections['graphSelections'] = [] const pathToNodeBasedSelections: Selections['graphSelections'] = []

View File

@ -302,7 +302,7 @@ export function createSettings() {
defaultUnit: new Setting<BaseUnit>({ defaultUnit: new Setting<BaseUnit>({
defaultValue: 'mm', defaultValue: 'mm',
description: 'The default unit to use in modeling dimensions', description: 'The default unit to use in modeling dimensions',
validate: (v) => baseUnitsUnion.includes(v as BaseUnit), validate: (v) => baseUnitsUnion.includes(v),
commandConfig: { commandConfig: {
inputType: 'options', inputType: 'options',
defaultValueFromContext: (context) => defaultValueFromContext: (context) =>
@ -332,7 +332,7 @@ export function createSettings() {
mouseControls: new Setting<CameraSystem>({ mouseControls: new Setting<CameraSystem>({
defaultValue: 'Zoo', defaultValue: 'Zoo',
description: 'The controls for how to navigate the 3D view', description: 'The controls for how to navigate the 3D view',
validate: (v) => cameraSystems.includes(v as CameraSystem), validate: (v) => cameraSystems.includes(v),
hideOnLevel: 'project', hideOnLevel: 'project',
commandConfig: { commandConfig: {
inputType: 'options', inputType: 'options',

View File

@ -17,7 +17,7 @@ export function usePreviousVarMentions(context: CompletionContext) {
return null return null
} }
return { return {
from: word?.from!, from: word?.from,
options: [...data], options: [...data],
} }
} }

View File

@ -17,7 +17,7 @@ export function varMentions(data: Completion[] = []): Extension {
return null return null
} }
return { return {
from: word?.from!, from: word?.from,
options: [...data], options: [...data],
} }
}, },

View File

@ -146,7 +146,7 @@ export const settingsMachine = setup({
actor: input.actor, actor: input.actor,
}) })
) )
.filter((c) => c !== null) as Command[] .filter((c) => c !== null)
const addCommands = () => const addCommands = () =>
commandBarActor.send({ commandBarActor.send({
type: 'Add commands', type: 'Add commands',