From 2a9836e0c10a562bfc7325e15731b8d75d120a6d Mon Sep 17 00:00:00 2001 From: Jason Dent Date: Wed, 9 Mar 2022 11:54:39 +0100 Subject: [PATCH] dev: Prepare for alpha release of ESLint Plugin. (#2560) * dev: eslint-plugin - a bit of clean up and more testing * dev: Prepare to publish the eslint plugin. --- .../cspell-eslint-plugin/.vscode/launch.json | 2 +- .../with-errors/sampleTemplateString.mjs | 18 +++++++++ packages/cspell-eslint-plugin/package.json | 1 - .../cspell-eslint-plugin/src/index.test.ts | 25 ++++++------- packages/cspell-eslint-plugin/src/index.ts | 37 ++++++++++++++----- 5 files changed, 58 insertions(+), 25 deletions(-) create mode 100644 packages/cspell-eslint-plugin/fixtures/with-errors/sampleTemplateString.mjs diff --git a/packages/cspell-eslint-plugin/.vscode/launch.json b/packages/cspell-eslint-plugin/.vscode/launch.json index 5234a6edc218..a3b17eb7ade3 100644 --- a/packages/cspell-eslint-plugin/.vscode/launch.json +++ b/packages/cspell-eslint-plugin/.vscode/launch.json @@ -26,7 +26,7 @@ "skipFiles": [ "/**" ], - "stopOnEntry": true, + "stopOnEntry": false, "type": "pwa-node" } ] diff --git a/packages/cspell-eslint-plugin/fixtures/with-errors/sampleTemplateString.mjs b/packages/cspell-eslint-plugin/fixtures/with-errors/sampleTemplateString.mjs new file mode 100644 index 000000000000..df532cfbc1ac --- /dev/null +++ b/packages/cspell-eslint-plugin/fixtures/with-errors/sampleTemplateString.mjs @@ -0,0 +1,18 @@ +/** + * This is a sample with spelling errors in template strings. + */ + +const now = new Date(); + +export const messageToday = ` +Todayy is ${now.toLocaleDateString(undefined, { weekday: 'long' })}. +Day ${now.toLocaleDateString(undefined, { day: 'numeric' })} of the Montj of ${now.toLocaleDateString(undefined, { month: 'long' })} +in the Yaar ${now.toLocaleDateString(undefined, { year: 'numeric' })}. +` + +export const months = 'January\u00eb, Februarry, March, Aprill, May, June'; + +export const templateWithUnicodeEscapeSequences = ` +Lets gooo to a caf\u00e9 or a cafe\u0301? ${now.getDay() === 0 ? 'Yes' : 'No'} +Only if Today is ${now.toLocaleDateString(undefined, { weekday: 'long' })} it is the start of the weeek. +`; diff --git a/packages/cspell-eslint-plugin/package.json b/packages/cspell-eslint-plugin/package.json index 4050f8678420..6deec4ff45b6 100644 --- a/packages/cspell-eslint-plugin/package.json +++ b/packages/cspell-eslint-plugin/package.json @@ -1,6 +1,5 @@ { "name": "@cspell/eslint-plugin-cspell", - "private": true, "publishConfig": { "access": "public" }, diff --git a/packages/cspell-eslint-plugin/src/index.test.ts b/packages/cspell-eslint-plugin/src/index.test.ts index b43c8a4ec37d..673f894a890b 100644 --- a/packages/cspell-eslint-plugin/src/index.test.ts +++ b/packages/cspell-eslint-plugin/src/index.test.ts @@ -36,26 +36,25 @@ const ruleTester = new RuleTester({ }); ruleTester.run('cspell', rule.rules.cspell, { - valid: [ - // "import * as jest from 'jest'", - // "import { jestFn as jestFunc} from 'jest'", - // 'const mocha = require("mocha");', - // 'async function* values(iter) { yield* iter; }', - // 'var foo = true', - // 'const x = `It is now time to add everything up: \\` ${y} + ${x}`', - readSample('sample.js'), - readSample('sample.ts'), - readSample('sampleESM.mjs'), - // readSample('sample.json'), - ], - // cspell:ignore Guuide Gallaxy BADD functionn + valid: [readSample('sample.js'), readSample('sample.ts'), readSample('sampleESM.mjs')], invalid: [ + // cspell:ignore Guuide Gallaxy BADD functionn readInvalid('with-errors/sampleESM.mjs', [ 'Unknown word: "Guuide"', 'Unknown word: "Gallaxy"', 'Unknown word: "BADD"', 'Unknown word: "functionn"', ]), + // cspell:ignore Montj Todayy Yaar Aprill Februarry gooo weeek + readInvalid('with-errors/sampleTemplateString.mjs', [ + { message: 'Unknown word: "Todayy"' }, + 'Unknown word: "Montj"', + 'Unknown word: "Yaar"', + 'Unknown word: "Februarry"', + 'Unknown word: "Aprill"', + 'Unknown word: "gooo"', + 'Unknown word: "weeek"', + ]), ], }); diff --git a/packages/cspell-eslint-plugin/src/index.ts b/packages/cspell-eslint-plugin/src/index.ts index c4e98ee96857..9174009d8dd7 100644 --- a/packages/cspell-eslint-plugin/src/index.ts +++ b/packages/cspell-eslint-plugin/src/index.ts @@ -5,7 +5,7 @@ import type { Rule } from 'eslint'; // eslint-disable-next-line node/no-missing-import import type { Comment, Identifier, Literal, Node, TemplateElement } from 'estree'; import { format } from 'util'; -import { createTextDocument, DocumentValidator, ValidationIssue } from 'cspell-lib'; +import { createTextDocument, DocumentValidator, ValidationIssue, CSpellSettings } from 'cspell-lib'; interface PluginRules { ['cspell']: Rule.RuleModule; @@ -17,13 +17,26 @@ const meta: Rule.RuleMetaData = { }, }; +type ASTNode = Node | Comment; + +const defaultSettings: CSpellSettings = { + patterns: [ + // @todo: be able to use cooked / transformed strings. + // { + // // Do not block unicode escape sequences. + // name: 'js-unicode-escape', + // pattern: /$^/g, + // }, + ], +}; + const isDebugMode = false; const log: typeof console.log = isDebugMode ? console.log : () => undefined; function create(context: Rule.RuleContext): Rule.RuleListener { const doc = createTextDocument({ uri: context.getFilename(), content: context.getSourceCode().getText() }); - const validator = new DocumentValidator(doc, {}, {}); + const validator = new DocumentValidator(doc, {}, defaultSettings); validator.prepareSync(); log('Source code: \n ************************ \n\n'); @@ -46,6 +59,7 @@ scope: ${context.getScope().type} function checkTemplateElement(node: TemplateElement & Rule.NodeParentExtension) { debugNode(node, node.value); + // console.log('Template: %o', node.value); checkNodeText(node, node.value.cooked || node.value.raw); } @@ -60,13 +74,16 @@ scope: ${context.getScope().type} log(`Comment: ${val}`); } - function checkNodeText(node: Node | Comment, text: string) { + function checkNodeText(node: ASTNode, text: string) { if (!node.range) return; + const adj = node.type === 'Literal' ? 1 : 0; + const range = [node.range[0] + adj, node.range[1] - adj] as const; + const scope = inheritance(node); - const result = validator.checkText(node.range, text, scope); + const result = validator.checkText(range, text, scope); if (result.length) { - console.error('%o', result); + log('%o', result); } result.forEach((issue) => reportIssue(issue)); } @@ -100,7 +117,7 @@ scope: ${context.getScope().type} Identifier: checkIdentifier, }; - function mapNode(node: Node | TSESTree.Node | Comment, index: number, nodes: (Node | Comment)[]): string { + function mapNode(node: ASTNode | TSESTree.Node, index: number, nodes: ASTNode[]): string { const child = nodes[index + 1]; if (node.type === 'ImportSpecifier') { const extra = node.imported === child ? '.imported' : node.local === child ? '.local' : ''; @@ -136,23 +153,23 @@ scope: ${context.getScope().type} return node.type; } - function inheritance(node: Node | Comment) { + function inheritance(node: ASTNode) { const a = [...context.getAncestors(), node]; return a.map(mapNode); } - function inheritanceSummary(node: Node | Comment) { + function inheritanceSummary(node: ASTNode) { return inheritance(node).join(' '); } - function debugNode(node: Node | Comment, value: unknown) { + function debugNode(node: ASTNode, value: unknown) { if (!isDebugMode) return; const val = format('%o', value); log(`${inheritanceSummary(node)}: ${val}`); } } -function tagLiteral(node: Node | TSESTree.Node): string { +function tagLiteral(node: ASTNode | TSESTree.Node): string { assert(node.type === 'Literal'); const kind = typeof node.value; const extra =