From 12ba022fb548c70645380acfac9ec52012a7c17a Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Tue, 3 May 2022 22:13:27 +0800 Subject: [PATCH] [repo] Add markdown lint rule to convert directional quotes --- .eslintrc.js | 1 + .lintstagedrc.json | 2 +- .../no-directional-quotation-marks.js | 97 +++++++++++++++++++ .vscode/settings.json | 3 + package.json | 5 +- yarn.lock | 2 +- 6 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 .markdownlint/no-directional-quotation-marks.js diff --git a/.eslintrc.js b/.eslintrc.js index e50d4da554..4e2deaadc6 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -27,6 +27,7 @@ module.exports = { // Ignore code examples. 'docs/**/_examples/*', 'general/**/_examples/*', + '!.markdownlint', ], env: { browser: true, diff --git a/.lintstagedrc.json b/.lintstagedrc.json index 7956f9d5fd..af590e78d0 100644 --- a/.lintstagedrc.json +++ b/.lintstagedrc.json @@ -2,7 +2,7 @@ "*.css": ["stylelint --allow-empty-input --fix"], "*.{js,jsx,ts,tsx,mjs}": ["eslint --fix"], "*.md": [ - "markdownlint --fix", + "markdownlint --rules .markdownlint/no-directional-quotation-marks --fix", "cspell --no-must-find-files --no-progress" ], "src/**/*.{js,jsx,ts,tsx,mjs}": [ diff --git a/.markdownlint/no-directional-quotation-marks.js b/.markdownlint/no-directional-quotation-marks.js new file mode 100644 index 0000000000..4dc795a680 --- /dev/null +++ b/.markdownlint/no-directional-quotation-marks.js @@ -0,0 +1,97 @@ +/** + * Copyright (c) Moodle Pty Ltd. + * + * Moodle is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Moodle is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moodle. If not, see . + */ + +/* eslint-disable import/no-extraneous-dependencies */ + +const { + addErrorDetailIf, + forEachLine, + getLineMetadata, +} = require('markdownlint-rule-helpers'); + +/** + * Get the whole word at the position, given a position within the word. + * + * @param {string} line The full line of texct + * @param {number} position The position within the line that the word is found + * @returns {string} + */ +const getWord = (line, position) => { + const left = line.slice(0, position + 1).search(/\S+$/); + const right = line.slice(left).search(/\s/); + + if (right < 0) { + // This is the last word in the line. + // Return from the start of the word to the end of the line. + return line.slice(left); + } + + return line.slice(left, left + right); +}; + +const characterReplacementMap = { + '“': '"', + '”': '"', + '‘': "'", + '’': "'", +}; + +const punctuation = /(?[“‘’”])/g; +module.exports = { + names: ['MDLDOC001', 'no-directional-quotation-marks'], + description: 'Do not use directional quotations (use apostrophe or double-quote)', + tags: ['punctuation'], + function: function MDLDOC001(params, onError) { + forEachLine(getLineMetadata(params), (line, lineIndex, inCode) => { + if (inCode) { + // Do not make changes to code stanzas. + return; + } + + const lineNumber = lineIndex + 1; + let matches = punctuation.exec(line); + if (!matches) { + return; + } + do { + const foundCharacter = matches.groups.character; + const replacementCharacter = characterReplacementMap[foundCharacter]; + const column = matches.index + 1; + const fixInfo = { + editColumn: column, + deleteCount: 1, + insertText: replacementCharacter, + }; + + const word = getWord(line, matches.index); + const expectedWord = word.replace(foundCharacter, replacementCharacter); + + addErrorDetailIf( + onError, + lineNumber, + expectedWord, + word, + null, + line, + [matches.index + 1, 1], + fixInfo, + ); + matches = punctuation.exec(line); + } while (matches !== null); + }); + }, +}; diff --git a/.vscode/settings.json b/.vscode/settings.json index f212d0c7da..1b273c003e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,6 @@ { + "markdownlint.customRules": [ + "./.markdownlint/no-directional-quotation-marks" + ], "stylelint.packageManager": "yarn" } diff --git a/package.json b/package.json index ede2e10dcc..6d0772a3af 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "wikimedia-sync": "scripts/wikimedia-sync.js", "component-spellings": "scripts/component-helper.js spelling", "test": "jest", - "lint": "markdownlint 'docs/**/*.md' 'general/**/*.md' '*.md'", + "lint": "markdownlint --rules .markdownlint/no-directional-quotation-marks 'docs/**/*.md' 'general/**/*.md' '*.md'", "prepare": "husky install", "spell": "cspell '*.md' '**/*.md' 'docs/*.md' 'docs/**/*.md' 'general/*.md' 'general/**/*.md'" }, @@ -82,9 +82,10 @@ "jest": "^27.5.1", "lint-staged": "^12.3.7", "markdownlint-cli": "^0.31.1", + "markdownlint-rule-helpers": "^0.16.0", "stylelint-config-standard": "^25.0.0", "ts-jest": "^27.1.4", "typescript": "^4.6.3", "unist-util-inspect": "6.0.0" } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index cde635749f..8a507f4c9a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8853,7 +8853,7 @@ markdownlint-cli@^0.31.1: minimatch "~3.0.5" run-con "~1.2.10" -markdownlint-rule-helpers@~0.16.0: +markdownlint-rule-helpers@^0.16.0, markdownlint-rule-helpers@~0.16.0: version "0.16.0" resolved "https://registry.yarnpkg.com/markdownlint-rule-helpers/-/markdownlint-rule-helpers-0.16.0.tgz#c327f72782bd2b9475127a240508231f0413a25e" integrity sha512-oEacRUVeTJ5D5hW1UYd2qExYI0oELdYK72k1TKGvIeYJIbqQWAz476NAc7LNixSySUhcNl++d02DvX0ccDk9/w==