chore: add typo-checker skill and fix typos (#9963)

Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
Hiroshi Ogawa
2026-03-24 09:28:40 +01:00
committed by GitHub
co-authored by Hanzo Dev
parent f70bfe7adb
commit 558b705c41
26 changed files with 186 additions and 54 deletions
+100
View File
@@ -0,0 +1,100 @@
---
name: typo-checker
description: >
Run typos-cli on the codebase, review findings, fix real typos, and update
_typos.toml config to suppress false positives. Use this skill for routine
codebase typo maintenance. Invoke when doing periodic codebase hygiene,
when the user mentions typos or spelling checks, or as a scheduled
maintenance task.
---
# Typo Checker
Scan the codebase with `typos-cli`, classify findings, fix real typos, and maintain `_typos.toml` so false positives don't recur.
## Prerequisites
`typos-cli` must be installed. If not available, install via one of:
| Method | Command |
|---|---|
| cargo | `cargo install typos-cli` |
| brew | `brew install typos-cli` |
| pipx | `pipx install typos` |
| Binary | Download from https://github.com/crate-ci/typos/releases |
## Workflow
### 1. Scan
```bash
typos --format=brief
```
This outputs one finding per line: `file:line:col: \`typo\` -> \`suggestion\``. Compact and easy to parse.
`typos` respects `.gitignore` by default, so `node_modules/`, `dist/`, build outputs are already excluded.
To get an overview first:
```bash
# Count unique typo words by frequency
typos --format=brief 2>&1 | sed "s/.*\`//;s/\`.*//" | sort | uniq -c | sort -rn | head -20
```
If many findings come from minified/generated files, add those paths to `_typos.toml` `extend-exclude` first, then re-scan.
### 2. Classify each finding
For every finding, decide:
- **Real typo** — fix it
- **False positive** — add to `_typos.toml`
Common false positive patterns:
- Short variable names that happen to be words (`ba`, `fo`, `nd`)
- Domain abbreviations (`als` for AsyncLocalStorage, `PnP` for Plug'n'Play)
- File extensions in regexes (`.styl`, `.pcss`)
- Test fixture strings and test data
- Line truncation artifacts in inline snapshots (`afte...`, `wrapp...`)
- Product names and proper nouns
- Lorem ipsum text
When in doubt about whether a misspelled variable name is "intentional" — it's still a typo. Propagated typos are still typos. Fix them.
### 3. Fix real typos
- **Comments, docs, JSDoc**: fix the text directly
- **Variable/property names**: rename all occurrences consistently
- **Test names**: fix the name, update corresponding snapshot files
- **Filenames**: rename the file and update all imports/references — check for references before renaming
### 4. Update `_typos.toml`
Add false positives to `[default.extend-words]` with a comment explaining why:
```toml
[default.extend-words]
# AsyncLocalStorage abbreviation
als = "als"
```
For file-level exclusions, use `[files].extend-exclude`:
```toml
[files]
extend-exclude = [
"*.js.map",
"*.svg",
]
```
If `_typos.toml` doesn't exist yet, create it.
### 5. Commit
Commit fixes and config updates together:
```
chore: fix typos and update _typos.toml
```
+32
View File
@@ -0,0 +1,32 @@
[files]
extend-exclude = [
"*.js.map",
"*.svg",
"test/cli/test/fixtures/reporters/html/**",
]
[default.extend-words]
# AsyncLocalStorage abbreviation
als = "als"
# Stylus CSS preprocessor extension
styl = "styl"
# Yarn Plug'n'Play
Pn = "Pn"
PnP = "PnP"
# Variable prefix pattern (b-a annotation diff)
ba = "ba"
# Ordinal suffix
nd = "nd"
# Test string with backtick escaping: 'som`e`'
som = "som"
# Line truncation artifacts in test output
afte = "afte"
wrapp = "wrapp"
# Test data string
fo = "fo"
# Latin lorem ipsum
optio = "optio"
# Product name (evalite.dev)
Evalite = "Evalite"
# Typo in upstream tinyhighlight LICENSE (TODO: fix upstream)
Additionaly = "Additionaly"
+1 -1
View File
@@ -372,7 +372,7 @@ vi.mock('node:fs', async (importOriginal) => {
const fs = require('node:fs') // throws an error
```
This limitation exists because factories can be asynchronous. This should not be a problem because Vitest doesn't mock builtin modules inside `node_modules`, which is simillar to how Vitest works by default.
This limitation exists because factories can be asynchronous. This should not be a problem because Vitest doesn't mock builtin modules inside `node_modules`, which is similar to how Vitest works by default.
### TypeScript
+1 -1
View File
@@ -9,7 +9,7 @@
# and serve Web UI at http://localhost:16686
docker compose up -d
# Run tests (exports with OTLP HTTP by deafult)
# Run tests (exports with OTLP HTTP by default)
pnpm test --experimental.openTelemetry.enabled
# Use console exporter for quick debugging
+2 -2
View File
@@ -17,8 +17,8 @@ const provider = new WebTracerProvider({
provider.register({
// you can customize contextManager but browser support has limitation
// cf. https://github.com/open-telemetry/opentelemetry-js/discussions/2060
// contextManager: new StackContextManager(), // this is the default (avialable in sdk-trace-web)
// contextManager: new ZoneContextManager(), // doesn't seem to help (avialable in @opentelemetry/context-zone)
// contextManager: new StackContextManager(), // this is the default (available in sdk-trace-web)
// contextManager: new ZoneContextManager(), // doesn't seem to help (available in @opentelemetry/context-zone)
})
export default provider
+2 -2
View File
@@ -21,7 +21,7 @@ export { CDPSession };
export interface ScreenshotOptions extends SelectorOptions {
/**
* The HTML element to screeshot.
* The HTML element to screenshot.
*/
element?: Element | Locator
/**
@@ -905,7 +905,7 @@ export type PrettyDOMOptions = Omit<StringifyOptions, 'maxLength'>
export const utils: {
/**
* This is simillar to calling `page.elementLocator`, but it returns only
* This is similar to calling `page.elementLocator`, but it returns only
* locator selectors.
*/
getElementLocatorSelectors(element: Element): LocatorSelectors
@@ -31,7 +31,7 @@ function catchWindowErrors(errorEvent, prop, cb) {
cb(e)
}
else {
// `ErrorEvent` doesn't necessary have `ErrotEvent.error` defined
// `ErrorEvent` doesn't necessary have `ErrorEvent.error` defined
// but some has `ErrorEvent.message` defined, e.g. ResizeObserver error.
// https://developer.mozilla.org/en-US/docs/Web/API/ErrorEvent/error
// https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#observation_errors
@@ -26,7 +26,7 @@ function defineExternalModuleNodes(modules: string[]): ModuleNode[] {
createModuleLabelItem(module),
)
const map = calcExternalLabels(labels)
return labels.map(({ raw, id, splitted }) => {
return labels.map(({ raw, id, splitsCopy }) => {
return defineNode<ModuleType, ModuleNode>({
color: 'var(--color-node-external)',
label: {
@@ -34,7 +34,7 @@ function defineExternalModuleNodes(modules: string[]): ModuleNode[] {
fontSize: '0.875rem',
text: id.includes('node_modules')
? (map.get(raw) ?? raw)
: splitted[splitted.length - 1],
: splitsCopy[splitsCopy.length - 1],
},
isFocused: false,
id,
+2 -2
View File
@@ -37,7 +37,7 @@ export interface ModuleLabelItem {
id: string
raw: string
splits: string[]
readonly splitted: string[]
readonly splitsCopy: string[]
candidate: string
finished: boolean
}
@@ -105,7 +105,7 @@ export function createModuleLabelItem(module: string): ModuleLabelItem {
return {
raw,
splits,
splitted: [...splits],
splitsCopy: [...splits],
candidate: '',
finished: false,
id: module,
+1 -1
View File
@@ -34,7 +34,7 @@ export const bench: BenchmarkAPI = createBenchmark(function (
benchFns.set(task, fn)
benchOptsMap.set(task, options)
// vitest runner sets mode to `todo` if handler is not passed down
// but we store handler separetly
// but we store handler separately
if (!this.todo && task.mode === 'todo') {
task.mode = 'run'
}
@@ -23,7 +23,7 @@ test('.toBeEmptyDOMElement', () => {
const withElementAndComment = queryByTestId('with-element-and-comment')
const withWhitespace = queryByTestId('with-whitespace')
const withText = queryByTestId('with-whitespace')
const nonExistantElement = queryByTestId('not-exists')
const nonExistentElement = queryByTestId('not-exists')
const fakeElement = {thisIsNot: 'an html element'}
expect(empty).toBeEmptyDOMElement()
@@ -58,6 +58,6 @@ test('.toBeEmptyDOMElement', () => {
expect(() => expect(fakeElement).toBeEmptyDOMElement()).toThrow()
expect(() => {
expect(nonExistantElement).toBeEmptyDOMElement()
expect(nonExistentElement).toBeEmptyDOMElement()
}).toThrow()
})
@@ -14,7 +14,7 @@ const grandparent = queryByTestId('grandparent')
const parent = queryByTestId('parent')
const child = queryByTestId('child')
const svgElement = queryByTestId('svg-element')
const nonExistantElement = queryByTestId('not-exists')
const nonExistentElement = queryByTestId('not-exists')
const fakeElement = {thisIsNot: 'an html element'}
test('.toContainElement positive test cases', () => {
@@ -27,32 +27,32 @@ test('.toContainElement positive test cases', () => {
expect(child).not.toContainElement(parent)
expect(child).not.toContainElement(grandparent)
expect(child).not.toContainElement(svgElement)
expect(grandparent).not.toContainElement(nonExistantElement)
expect(grandparent).not.toContainElement(nonExistentElement)
})
test('.toContainElement negative test cases', () => {
expect(() =>
expect(nonExistantElement).not.toContainElement(child),
expect(nonExistentElement).not.toContainElement(child),
).toThrow()
expect(() => expect(parent).toContainElement(grandparent)).toThrow()
expect(() =>
expect(nonExistantElement).toContainElement(grandparent),
expect(nonExistentElement).toContainElement(grandparent),
).toThrow()
expect(() =>
expect(grandparent).toContainElement(nonExistantElement),
expect(grandparent).toContainElement(nonExistentElement),
).toThrow()
expect(() =>
expect(nonExistantElement).toContainElement(nonExistantElement),
expect(nonExistentElement).toContainElement(nonExistentElement),
).toThrow()
expect(() =>
// @ts-expect-error testing invalid assertion
expect(nonExistantElement).toContainElement(fakeElement),
expect(nonExistentElement).toContainElement(fakeElement),
).toThrow()
expect(() =>
expect(fakeElement).toContainElement(nonExistantElement),
expect(fakeElement).toContainElement(nonExistentElement),
).toThrow()
expect(() =>
expect(fakeElement).not.toContainElement(nonExistantElement),
expect(fakeElement).not.toContainElement(nonExistentElement),
).toThrow()
expect(() => expect(fakeElement).toContainElement(grandparent)).toThrow()
// @ts-expect-error testing invalid assertion
@@ -16,33 +16,33 @@ describe('.toContainHTML', () => {
const grandparent = queryByTestId('grandparent')
const parent = queryByTestId('parent')
const child = queryByTestId('child')
const nonExistantElement = queryByTestId('not-exists')
const nonExistentElement = queryByTestId('not-exists')
const fakeElement = {thisIsNot: 'an html element'}
const stringChildElement = '<span data-testid="child"></span>'
const stringChildElementSelfClosing = '<span data-testid="child" />'
const incorrectStringHtml = '<span data-testid="child"></div>'
const nonExistantString = '<span> Does not exists </span>'
const nonExistentString = '<span> Does not exists </span>'
const svgElement = queryByTestId('svg-element')
expect(grandparent).toContainHTML(stringChildElement)
expect(parent).toContainHTML(stringChildElement)
expect(child).toContainHTML(stringChildElement)
expect(child).toContainHTML(stringChildElementSelfClosing)
expect(grandparent).not.toContainHTML(nonExistantString)
expect(parent).not.toContainHTML(nonExistantString)
expect(child).not.toContainHTML(nonExistantString)
expect(child).not.toContainHTML(nonExistantString)
expect(grandparent).not.toContainHTML(nonExistentString)
expect(parent).not.toContainHTML(nonExistentString)
expect(child).not.toContainHTML(nonExistentString)
expect(child).not.toContainHTML(nonExistentString)
expect(grandparent).toContainHTML(incorrectStringHtml)
expect(parent).toContainHTML(incorrectStringHtml)
expect(child).toContainHTML(incorrectStringHtml)
// negative test cases wrapped in throwError assertions for coverage.
expect(() =>
expect(nonExistantElement).not.toContainHTML(stringChildElement),
expect(nonExistentElement).not.toContainHTML(stringChildElement),
).toThrow()
expect(() =>
// @ts-expect-error testing invalid input
expect(nonExistantElement).not.toContainHTML(nonExistantElement),
expect(nonExistentElement).not.toContainHTML(nonExistentElement),
).toThrow()
expect(() =>
// @ts-expect-error testing invalid input
@@ -66,23 +66,23 @@ describe('.toContainHTML', () => {
expect(() =>
expect(child).not.toContainHTML(stringChildElementSelfClosing),
).toThrow()
expect(() => expect(child).toContainHTML(nonExistantString)).toThrow()
expect(() => expect(parent).toContainHTML(nonExistantString)).toThrow()
expect(() => expect(child).toContainHTML(nonExistentString)).toThrow()
expect(() => expect(parent).toContainHTML(nonExistentString)).toThrow()
expect(() =>
expect(grandparent).toContainHTML(nonExistantString),
expect(grandparent).toContainHTML(nonExistentString),
).toThrow()
// @ts-expect-error testing invalid input
expect(() => expect(child).toContainHTML(nonExistantElement)).toThrow()
expect(() => expect(child).toContainHTML(nonExistentElement)).toThrow()
expect(() =>
// @ts-expect-error testing invalid input
expect(parent).toContainHTML(nonExistantElement),
expect(parent).toContainHTML(nonExistentElement),
).toThrow()
expect(() =>
// @ts-expect-error testing invalid input
expect(grandparent).toContainHTML(nonExistantElement),
expect(grandparent).toContainHTML(nonExistentElement),
).toThrow()
expect(() =>
expect(nonExistantElement).not.toContainHTML(incorrectStringHtml),
expect(nonExistentElement).not.toContainHTML(incorrectStringHtml),
).toThrow()
expect(() =>
expect(grandparent).not.toContainHTML(incorrectStringHtml),
@@ -98,11 +98,11 @@ describe('.toContainHTML', () => {
test('throws with an expected text', async () => {
const {queryByTestId} = render('<span data-testid="child"></span>')
const htmlElement = queryByTestId('child')
const nonExistantString = '<div> non-existent element </div>'
const nonExistentString = '<div> non-existent element </div>'
let errorMessage
try {
expect(htmlElement).toContainHTML(nonExistantString)
expect(htmlElement).toContainHTML(nonExistentString)
} catch (error) {
errorMessage = error.message
}
@@ -237,7 +237,7 @@ describe('.toHaveStyle', () => {
`)
expect(queryByTestId('color-example')).toHaveStyle({
// in jest-dom '12' is converted to 12px
// but in the browser setting the style to 12 wil have no effect,
// but in the browser setting the style to 12 will have no effect,
// so Vitest prioritizes the browser behavior
// fontSize: 12,
fontSize: '12px',
+3 -3
View File
@@ -18,12 +18,12 @@ function modeToConfig(mode: string): RunVitestConfig {
return {}
}
it.for(['node', 'playwright'])('test.retry.condition is corrrectly serialized %s', async (mode) => {
it.for(['node', 'playwright'])('test.retry.condition is correctly serialized %s', async (mode) => {
const { stderr, errorTree } = await runInlineTests({
'basic.test.js': /* js */`
import { expect, test } from 'vitest'
test('task.retry.condition is corrrectly deserialized', ({ task }) => {
test('task.retry.condition is correctly deserialized', ({ task }) => {
expect(task.retry.condition).toBeInstanceOf(RegExp)
expect(task.retry.condition).toStrictEqual(/retry_this/)
})
@@ -98,7 +98,7 @@ it.for(['node', 'playwright'])('test.retry.condition is corrrectly serialized %s
"retry_that",
],
"retry": "passed",
"task.retry.condition is corrrectly deserialized": "passed",
"task.retry.condition is correctly deserialized": "passed",
},
}
`)
@@ -2,12 +2,12 @@ import conditionCustom from 'test-dep-conditions-indirect/custom'
import conditionDevelopment from 'test-dep-conditions-indirect/development'
import conditionModule from 'test-dep-conditions-indirect/module'
import conditionNode from 'test-dep-conditions-indirect/node'
import conditionProductioin from 'test-dep-conditions-indirect/production'
import conditionProduction from 'test-dep-conditions-indirect/production'
export default {
conditionCustom,
conditionModule,
conditionNode,
conditionDevelopment,
conditionProductioin,
conditionProduction,
}
@@ -31,7 +31,7 @@ test("conditions", () => {
conditionDevelopment: true,
conditionModule: viteMajor <= 5 && inline,
conditionNode: true,
conditionProductioin: false,
conditionProduction: false,
},
});
});
@@ -31,7 +31,7 @@ test("conditions", () => {
conditionDevelopment: true,
conditionModule: viteMajor <= 5 && inline,
conditionNode: true,
conditionProductioin: false,
conditionProduction: false,
},
});
});
@@ -30,7 +30,7 @@ test('conditions', () => {
"conditionDevelopment": true,
"conditionModule": viteMajor <= 5 && inline,
"conditionNode": true,
"conditionProductioin": false,
"conditionProduction": false,
},
}
)
@@ -40,7 +40,7 @@ Number of calls: 3
"
`;
exports[`mocked function which fails on toReturnWith > oject type 1`] = `
exports[`mocked function which fails on toReturnWith > object type 1`] = `
"expected "vi.fn()" to return with: { a: '4' } at least once
Received:
+1 -1
View File
@@ -170,7 +170,7 @@ describe('mocked function which fails on toReturnWith', () => {
expect(getError(() => expect(mock).toReturnWith(2))).toMatchSnapshot()
})
test('oject type', () => {
test('object type', () => {
const mock = vi.fn(() => {
return { a: '1' }
})
@@ -1,8 +1,8 @@
// bar.spec.ts
import { describe, expect, it, vi } from 'vitest'
import { Bar } from '../../src/class-inheritence/bar'
import { Bar } from '../../src/class-inheritance/bar'
vi.mock(import('./../../src/class-inheritence/foo'))
vi.mock(import('./../../src/class-inheritance/foo'))
describe('not mocking class when parent is mocked', () => {
describe('doSomething', () => {
+1 -1
View File
@@ -46,7 +46,7 @@ export interface RunVitestConfig extends TestUserConfig {
const process_ = process
/**
* The config is assumed to be the config on the fille system, not CLI options
* The config is assumed to be the config on the file system, not CLI options
* (Note that CLI only options like "standalone" are passed as CLI options, not config options)
* - To pass options as CLI, provide `$cliOptions` in the config object.
* - To pass other Vite config properties, provide `$viteConfig` in the config object.