Skip to content

Commit

Permalink
feat: Support spell checking more document types (#5741)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jason3S authored Jun 13, 2024
1 parent acf59a4 commit 6e200f9
Show file tree
Hide file tree
Showing 32 changed files with 1,707 additions and 1,481 deletions.
48 changes: 48 additions & 0 deletions packages/cspell-eslint-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ interface Options {
/** Define dictionaries. */
dictionaryDefinitions?: DictionaryDefinition[];
};

/**
* Specify a path to a custom word list file.
*
Expand All @@ -201,6 +202,35 @@ interface Options {
* ```
*/
customWordListFile?: string | { path: string };

/**
* Scope selectors to spell check.
* This is a list of scope selectors to spell check.
*
* Example:
* ```js
* checkScope: [
* ['YAMLPair[key] YAMLScalar', true],
* ['YAMLPair[value] YAMLScalar', true],
* ['YAMLSequence[entries] YAMLScalar', true],
* ['JSONProperty[key] JSONLiteral', true],
* ['JSONProperty[value] JSONLiteral', true],
* ['JSONArrayExpression JSONLiteral', true],
* ],
* ```
*
* To turn off checking JSON keys, use the following:
*
* ```js
* checkScope: [
* ['JSONProperty[key] JSONLiteral', false],
* ],
* ```
*
* @since 8.9.0
*/
checkScope?: ScopeSelectorList;

/**
* Output debug logs
* @default false
Expand Down Expand Up @@ -294,6 +324,24 @@ When spell checking, if `colour` is not in one of the dictionaries, then `color`
CSpell will match case, but not word stems. `blacklist` and `Blacklist` will get replaced, but not `blacklists`.
## Checking Custom AST Nodes
The `checkScope` setting is used to enable / disable checking AST Nodes. ESLint uses parsers to generate the AST (Abstract Syntax Tree) to evaluate a document. Each PlugIn gets access to the AST. `checkScope` can be used to handle new AST nodes when a custom parser is added. Some knowledge of the AST output by the parser is necessary.
```js
rules: {
'@cspell/spellchecker': ['warn', { checkScope: [
['JSONLiteral': true], // will match AST Nodes of type `JSONLiteral` and spell check the value.
['JSONProperty[key] JSONLiteral', false] // will turn off checking the JSON Property keys.
['JSONProperty JSONLiteral', false] // will turn off checking the JSON Property keys and values.
['JSONProperty[value] JSONLiteral', true] // will turn on checking the JSON Property values.
['YAMLPair[key] YAMLScalar', true],
['YAMLPair[value] YAMLScalar', true],
['YAMLSequence YAMLScalar', true],
] }],
},
```
## In Combination with CSpell
Due to the nature of how files are parsed, the `cspell` command line tool and this ESLint plugin will give different results.
Expand Down
19 changes: 19 additions & 0 deletions packages/cspell-eslint-plugin/assets/options.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,25 @@
"description": "Spell check JSX Text",
"type": "boolean"
},
"checkScope": {
"description": "Scope selectors to spell check. This is a list of scope selectors to spell check.\n\nExample: ```js checkScope: [ ['YAMLPair[key] YAMLScalar', true], ['YAMLPair[value] YAMLScalar', true], ['YAMLSequence[entries] YAMLScalar', true], ['JSONProperty[key] JSONLiteral', true], ['JSONProperty[value] JSONLiteral', true], ['JSONArrayExpression JSONLiteral', true], ], ```",
"items": {
"description": "A scope selector entry is a tuple that defines a scope selector and whether to spell check it.",
"items": [
{
"description": "The scope selector is a string that defines the context in which a rule applies. Examples:\n- `YAMLPair[value] YAMLScalar` - check the value of a YAML pair.\n- `YAMLPair[key] YAMLScalar` - check the key of a YAML pair.",
"type": "string"
},
{
"type": "boolean"
}
],
"maxItems": 2,
"minItems": 2,
"type": "array"
},
"type": "array"
},
"checkStringTemplates": {
"default": true,
"description": "Spell check template strings",
Expand Down
3 changes: 3 additions & 0 deletions packages/cspell-eslint-plugin/fixtures/json-support/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# JSON Support

This directory contains sample to test JSON support.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// @ts-check
import eslint from '@eslint/js';
import cspellRecommended from '@cspell/eslint-plugin/recommended';
import eslintPluginJsonc from 'eslint-plugin-jsonc';

/**
* @type { import("eslint").Linter.FlatConfig[] }
*/
const config = [
eslint.configs.recommended,
cspellRecommended,
...eslintPluginJsonc.configs['flat/recommended-with-jsonc'],
{
ignores: ['eslint.config.mjs'],
},
{
files: ['**/*.json', '**/*.jsonc'],
rules: {
'@cspell/spellchecker': ['warn', { debugMode: true }],
},
},
];

export default config;
16 changes: 16 additions & 0 deletions packages/cspell-eslint-plugin/fixtures/json-support/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "markdown-support",
"version": "1.0.0",
"description": "",
"scripts": {
"test": "eslint ."
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@cspell/eslint-plugin": "workspace:*",
"eslint-plugin-mdx": "^3.1.5",
"eslint": "^8.50.0"
}
}
17 changes: 17 additions & 0 deletions packages/cspell-eslint-plugin/fixtures/json-support/sample.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"address": {
"street": "123 Main St",
"city": "Metropolis",
"state": "AA",
"zip": "12345"
},
"phone": "123-456-7890",
// This is a line commment
"email": "[email protected]",

"places": ["Home", "Work", "Vacation", "Schooll", "Park", "Library", "Store"]

/*
This is a block comment
*/
}
12 changes: 12 additions & 0 deletions packages/cspell-eslint-plugin/fixtures/mdx-support/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Markdown Support

**NOTE:** Markdown is not yet supported. See [#3464](https://github.com/streetsidesoftware/cspell/issues/3464)

This is a sample file to test out support for spell checking Markdown files.

## Table

| col 1 | col 2 | col 3 |
| ----- | ----- | ------ |
| a | 22 | b |
| b | 42 | `null` |
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import eslint from '@eslint/js';
import cspellRecommended from '@cspell/eslint-plugin/recommended';
import * as mdx from 'eslint-plugin-mdx';

/**
* @type { import("eslint").Linter.FlatConfig[] }
*/
const config = [
eslint.configs.recommended,
cspellRecommended,
mdx.configs.flat,
{
ignores: ['eslint.config.mjs'],
},
{
files: ['**/*.mdx', '**/*.md'],
rules: {
'@cspell/spellchecker': ['warn', { debugMode: true }],
},
},
];

export default config;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Markdown Support with Code

This is a sample file to test out support for spell checking Markdown files.

```js
/* Example Function */
function sampleFn() {}
```

```ts
const msg = 'A bit of typescript.';
```
16 changes: 16 additions & 0 deletions packages/cspell-eslint-plugin/fixtures/mdx-support/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "markdown-support",
"version": "1.0.0",
"description": "",
"scripts": {
"test": "eslint ."
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@cspell/eslint-plugin": "workspace:*",
"eslint-plugin-mdx": "^3.1.5",
"eslint": "^8.50.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import eslint from '@eslint/js';
import cspellRecommended from '@cspell/eslint-plugin/recommended';
import parserYml from 'yaml-eslint-parser';
import pluginYml from 'eslint-plugin-yml';

/**
* @type { import("eslint").Linter.FlatConfig[] }
*/
const config = [
eslint.configs.recommended,
cspellRecommended,
...pluginYml.configs['flat/standard'],
{
files: ['**/*.yaml', '**/*.yml'],
languageOptions: {
parser: parserYml,
},
// plugins: {
// yml: pluginYml,
// },
rules: {
'@cspell/spellchecker': 'warn',
},
},
];

export default config;
24 changes: 24 additions & 0 deletions packages/cspell-eslint-plugin/fixtures/yaml-support/sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
# Starting comment
list:
- one
- two
- three
- 4
- 5
- true
- false
obj:
key: value
key2: value2 # comment after value
key3: value3
command: |
echo "Hello, World!"
echo "Goodbye, World!"
command2: >-
line 1
line 2
'command three': >
line 3
line 4
# comment
14 changes: 10 additions & 4 deletions packages/cspell-eslint-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,10 @@
"assets",
"dist",
"!**/__mocks__",
"!**/*.tsbuildInfo",
"!**/*.test.*",
"!**/*.spec.*",
"!**/*.test.*",
"!**/test*/**",
"!**/*.tsbuildInfo",
"!**/*.map"
],
"scripts": {
Expand All @@ -60,6 +61,7 @@
"clean-build": "pnpm run clean && pnpm run build",
"coverage": "echo coverage",
"test-watch": "pnpm run test -- --watch",
"test-yaml": "npx mocha --timeout 10000 \"dist/**/yaml.test.mjs\"",
"test": "npx mocha --timeout 10000 \"dist/**/*.test.mjs\""
},
"repository": {
Expand All @@ -81,19 +83,23 @@
"@typescript-eslint/parser": "^7.13.0",
"@typescript-eslint/types": "^7.13.0",
"eslint": "^9.4.0",
"eslint-plugin-jsonc": "^2.16.0",
"eslint-plugin-mdx": "^3.1.5",
"eslint-plugin-n": "^17.8.1",
"eslint-plugin-react": "^7.34.2",
"eslint-plugin-simple-import-sort": "^12.1.0",
"eslint-plugin-yml": "^1.14.0",
"globals": "^15.4.0",
"jsonc-eslint-parser": "^2.4.0",
"mocha": "^10.4.0",
"ts-json-schema-generator": "^2.3.0",
"typescript": "^5.4.5",
"typescript-eslint": "^7.13.0"
"typescript-eslint": "^7.13.0",
"yaml-eslint-parser": "^1.2.3"
},
"dependencies": {
"@cspell/cspell-types": "workspace:*",
"cspell-lib": "workspace:*",
"estree-walker": "^3.0.3",
"synckit": "^0.9.0"
},
"peerDependencies": {
Expand Down
38 changes: 38 additions & 0 deletions packages/cspell-eslint-plugin/src/common/options.cts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,26 @@ export interface Check {
* ```
*/
customWordListFile?: CustomWordListFilePath | CustomWordListFile | undefined;

/**
* Scope selectors to spell check.
* This is a list of scope selectors to spell check.
*
* Example:
* ```js
* checkScope: [
* ['YAMLPair[key] YAMLScalar', true],
* ['YAMLPair[value] YAMLScalar', true],
* ['YAMLSequence[entries] YAMLScalar', true],
* ['JSONProperty[key] JSONLiteral', true],
* ['JSONProperty[value] JSONLiteral', true],
* ['JSONArrayExpression JSONLiteral', true],
* ],
* ```
*
* @since 8.9.0
*/
checkScope?: ScopeSelectorList;
}

/**
Expand All @@ -134,3 +154,21 @@ export const defaultOptions: Options = {
generateSuggestions: true,
autoFix: false,
};

/**
* The scope selector is a string that defines the context in which a rule applies.
* Examples:
* - `YAMLPair[value] YAMLScalar` - check the value of a YAML pair.
* - `YAMLPair[key] YAMLScalar` - check the key of a YAML pair.
*/
export type ScopeSelector = string;

/**
* A scope selector entry is a tuple that defines a scope selector and whether to spell check it.
*/
export type ScopeSelectorEntry = [ScopeSelector, boolean];

/**
* A list of scope selectors.
*/
export type ScopeSelectorList = ScopeSelectorEntry[];
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { readFileSync } from 'node:fs';
import { join as pathJoin } from 'node:path';

import type { ESLint, Rule } from 'eslint';
import type { Program } from 'estree';
import { createSyncFn } from 'synckit';

import { getDefaultLogger } from '../common/logger.cjs';
Expand Down Expand Up @@ -120,10 +121,11 @@ function create(context: Rule.RuleContext): Rule.RuleListener {
context.report(des);
}

function checkProgram() {
function checkProgram(_node: Program) {
const filename = context.filename || context.getFilename();
const sc = context.sourceCode || context.getSourceCode();
if (!sc) return;
const { issues, errors } = spellCheck(context.filename || context.getFilename(), sc.text, sc.ast, options);
const { issues, errors } = spellCheck(filename, sc.text, sc.ast, options);
if (errors && errors.length) {
log(
'errors: %o',
Expand Down
Loading

0 comments on commit 6e200f9

Please sign in to comment.