UNPKG

@cspell/eslint-plugin

Version:
358 lines 13.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.spellCheckAST = spellCheckAST; // cspell:ignore TSESTree const node_assert_1 = __importDefault(require("node:assert")); const synckit_1 = require("synckit"); const logger_cjs_1 = require("../common/logger.cjs"); const customScopes_cjs_1 = require("./customScopes.cjs"); const scope_cjs_1 = require("./scope.cjs"); const walkTree_cjs_1 = require("./walkTree.cjs"); const spellCheck = (0, synckit_1.createSyncFn)(require.resolve('./worker.mjs')); const isDebugModeExtended = false; const forceLogging = false; const skipCheck = process.env.CSPELL_ESLINT_SKIP_CHECK || false; function spellCheckAST(filename, text, root, options) { const logger = (0, logger_cjs_1.getDefaultLogger)(); const debugMode = forceLogging || options.debugMode || false; logger.enabled = forceLogging || (options.debugMode ?? (logger.enabled || isDebugModeExtended)); const log = logger.log; const mapScopes = groupScopes([...customScopes_cjs_1.defaultCheckedScopes, ...(options.checkScope || [])]); log('options: %o', options); const toIgnore = new Set(); const importedIdentifiers = new Set(); const toBeChecked = []; function checkLiteral(path) { const node = path.node; if (node.type !== 'Literal') return; if (!options.checkStrings) return; if (typeof node.value === 'string') { debugNode(path, node.value); if (options.ignoreImports && (isImportOrRequired(node) || isExportNamedDeclaration(node))) return; if (options.ignoreImportProperties && isImportedProperty(node)) return; checkNodeText(path, node.value); } } function checkJSXText(path) { const node = path.node; if (node.type !== 'JSXText') return; if (!options.checkJSXText) return; if (typeof node.value === 'string') { debugNode(path, node.value); checkNodeText(path, node.value); } } function checkTemplateElement(path) { const node = path.node; if (node.type !== 'TemplateElement') return; if (!options.checkStringTemplates) return; debugNode(path, node.value); checkNodeText(path, node.value.cooked || node.value.raw); } function checkIdentifier(path) { const node = path.node; if (node.type !== 'Identifier') return; debugNode(path, node.name); if (options.ignoreImports) { if (isRawImportIdentifier(node)) { toIgnore.add(node.name); return; } if (isImportIdentifier(node)) { importedIdentifiers.add(node.name); if (isLocalImportIdentifierUnique(node)) { checkNodeText(path, node.name); } return; } else if (options.ignoreImportProperties && isImportedProperty(node)) { return; } if (isExportIdentifier(node)) { importedIdentifiers.add(node.name); if (isLocalExportIdentifierUnique(node)) { checkNodeText(path, node.name); } return; } } if (!options.checkIdentifiers) return; if (toIgnore.has(node.name) && !isObjectProperty(node)) return; if (skipCheckForRawImportIdentifiers(node)) return; checkNodeText(path, node.name); } function checkComment(path) { const node = path.node; if (node.type !== 'Line' && node.type !== 'Block') return; if (!options.checkComments) return; debugNode(path, node.value); checkNodeText(path, node.value); } function checkNodeText(path, _text) { const node = path.node; if (!node.range) return; const adj = node.type === 'Literal' ? 1 : 0; const range = [node.range[0] + adj, node.range[1] - adj]; toBeChecked.push({ range, node }); } function isImportIdentifier(node) { const parent = node.parent; if (node.type !== 'Identifier' || !parent) return false; return ((parent.type === 'ImportSpecifier' || parent.type === 'ImportNamespaceSpecifier' || parent.type === 'ImportDefaultSpecifier') && parent.local === node); } function isExportIdentifier(node) { const parent = getExportParent(node); if (node.type !== 'Identifier' || !parent) return false; return parent.type === 'ExportSpecifier' && parent.exported === node; } function isRawImportIdentifier(node) { const parent = node.parent; if (node.type !== 'Identifier' || !parent) return false; return ((parent.type === 'ImportSpecifier' && parent.imported === node) || (parent.type === 'ExportSpecifier' && parent.local === node)); } function isLocalImportIdentifierUnique(node) { const parent = getImportParent(node); if (!parent) return true; const { imported, local } = parent; if (imported.type === 'Identifier' && imported.name !== local.name) return true; return imported.range?.[0] !== local.range?.[0] && imported.range?.[1] !== local.range?.[1]; } function isLocalExportIdentifierUnique(node) { const parent = getExportParent(node); if (!parent) return true; const { exported, local } = parent; if (exported.type === 'Identifier' && exported.name !== local.name) return true; return exported.range?.[0] !== local.range?.[0] && exported.range?.[1] !== local.range?.[1]; } function getImportParent(node) { const parent = node.parent; return parent?.type === 'ImportSpecifier' ? parent : undefined; } function getExportParent(node) { const parent = node.parent; return parent?.type === 'ExportSpecifier' ? parent : undefined; } function skipCheckForRawImportIdentifiers(node) { if (options.ignoreImports) return false; const parent = getImportParent(node); return !!parent && parent.imported === node && !isLocalImportIdentifierUnique(node); } function isImportedProperty(node) { const obj = findOriginObject(node); return !!obj && obj.type === 'Identifier' && importedIdentifiers.has(obj.name); } function isObjectProperty(node) { return node.parent?.type === 'MemberExpression'; } function reportIssue(issue) { const { word, start, end, severity } = issue; const node = toBeChecked[issue.rangeIdx].node; const nodeType = node.type; const suggestions = normalizeSuggestions(issue.suggestions, nodeType); return { word, start, end, nodeType, node, suggestions, severity }; } const processors = { Line: checkComment, Block: checkComment, Literal: checkLiteral, TemplateElement: checkTemplateElement, Identifier: checkIdentifier, JSXText: checkJSXText, }; function needToCheckFields(path) { const possibleScopes = mapScopes.get(path.node.type); if (!possibleScopes) { if (debugMode) _dumpNode(path); return undefined; } const scopePath = new scope_cjs_1.AstPathScope(path); const scores = possibleScopes .map(({ scope, check }) => ({ score: scopePath.score(scope), check, scope })) .filter((s) => s.score > 0); const maxScore = Math.max(0, ...scores.map((s) => s.score)); const topScopes = scores.filter((s) => s.score === maxScore); if (!topScopes.length) return undefined; return Object.fromEntries(topScopes.map((s) => [s.scope.scopeField(), s.check])); } function defaultHandler(path) { const fields = needToCheckFields(path); if (!fields) return; for (const [field, check] of Object.entries(fields)) { if (!check) continue; const node = path.node; const value = node[field]; if (typeof value !== 'string') continue; debugNode(path, value); checkNodeText(path, value); } } function checkNode(path) { // _dumpNode(path); const handler = processors[path.node.type] ?? defaultHandler; handler(path); } function _dumpNode(path) { function value(v) { if (['string', 'number', 'boolean'].includes(typeof v)) return v; if (v && typeof v === 'object' && 'type' in v) return `{ type: ${v.type} }`; return `<${v}>`; } function dotValue(v) { if (typeof v === 'object' && v) { return Object.fromEntries(Object.entries(v).map(([k, v]) => [k, value(v)])); } return `<${typeof v}>`; } const { parent: _, ...n } = path.node; const warn = log; warn('Node: %o', { key: path.key, type: n.type, path: inheritanceSummary(path), node: dotValue(n), }); } /** * find the origin of a member expression */ function findOriginObject(node) { const parent = node.parent; if (parent?.type !== 'MemberExpression' || parent.property !== node) return undefined; let obj = parent.object; while (obj.type === 'MemberExpression') { obj = obj.object; } return obj; } function isFunctionCall(node, name) { if (!node) return false; return node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === name; } function isRequireCall(node) { return isFunctionCall(node, 'require'); } function isImportOrRequired(node) { return isRequireCall(node.parent) || (node.parent?.type === 'ImportDeclaration' && node.parent.source === node); } function isExportNamedDeclaration(node) { return node.parent?.type === 'ExportNamedDeclaration' && node.parent.source === node; } function debugNode(path, value) { if (!debugMode) return; log(`${inheritanceSummary(path)}: %o`, value); _dumpNode(path); } // console.warn('root: %o', root); (0, walkTree_cjs_1.walkTree)(root, checkNode); const result = skipCheck ? { issues: [] } : spellCheck(filename, text, toBeChecked.map((t) => t.range), options); const issues = result.issues.map((issue) => reportIssue(issue)); return { issues, errors: result.errors || [] }; } function mapNode(path, key) { const node = path.node; if (node.type === 'Literal') { return (0, scope_cjs_1.scopeItem)(tagLiteral(node)); } if (node.type === 'Block') { const value = typeof node.value === 'string' ? node.value : ''; return (0, scope_cjs_1.scopeItem)(value[0] === '*' ? 'Comment.docBlock' : 'Comment.block'); } if (node.type === 'Line') { return (0, scope_cjs_1.scopeItem)('Comment.line'); } return (0, scope_cjs_1.mapNodeToScope)(path, key); } function inheritanceSummary(path) { return (0, scope_cjs_1.astScopeToString)(path, ' ', mapNode); } function tagLiteral(node) { (0, node_assert_1.default)(node.type === 'Literal'); const kind = typeof node.value; const extra = kind === 'string' ? asStr(node.raw)?.[0] === '"' ? 'string.double' : 'string.single' : node.value === null ? 'null' : kind; return node.type + '.' + extra; } const needToAdjustSpace = { Identifier: true, }; const isSpecial = /[^\p{L}_0-9]/u; const allSpecial = /[^\p{L}_0-9]/gu; function normalizeSuggestions(suggestions, nodeType) { if (!suggestions) return undefined; if (!(nodeType in needToAdjustSpace)) return suggestions; return suggestions.map((sug) => { if (!isSpecial.test(sug.word)) return sug; const s = { ...sug }; s.word = s.word.replaceAll(allSpecial, '_'); if (s.wordAdjustedToMatchCase) { s.wordAdjustedToMatchCase = s.wordAdjustedToMatchCase.replaceAll(allSpecial, '_'); } return s; }); } function groupScopes(scopes) { const objScopes = Object.fromEntries(scopes); const map = new Map(); for (const [selector, check] of Object.entries(objScopes)) { const scope = scope_cjs_1.AstScopeMatcher.fromScopeSelector(selector); const key = scope.scopeType(); const list = map.get(key) || []; list.push({ scope, check }); map.set(key, list); } return map; } function asStr(v) { return typeof v === 'string' ? v : undefined; } //# sourceMappingURL=spellCheckAST.cjs.map