From e0f1502952579f590792f24a324c6b9bf5321404 Mon Sep 17 00:00:00 2001 From: Kevin Gibbons Date: Wed, 7 Sep 2022 19:51:02 -0700 Subject: [PATCH] lint against unknown step attributes (#482) --- package-lock.json | 14 +++++------ package.json | 2 +- src/lint/collect-algorithm-diagnostics.ts | 2 ++ src/lint/rules/algorithm-step-labels.ts | 10 ++++---- src/lint/rules/step-attributes.ts | 29 +++++++++++++++++++++++ test/lint-algorithms.js | 16 +++++++++++++ 6 files changed, 60 insertions(+), 13 deletions(-) create mode 100644 src/lint/rules/step-attributes.ts diff --git a/package-lock.json b/package-lock.json index 0dd75a7f..fd858291 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "command-line-args": "^5.2.0", "command-line-usage": "^6.1.1", "dedent-js": "^1.0.1", - "ecmarkdown": "^7.1.0", + "ecmarkdown": "^7.2.0", "eslint-formatter-codeframe": "^7.32.1", "fast-glob": "^3.2.7", "grammarkdown": "^3.2.0", @@ -1116,9 +1116,9 @@ } }, "node_modules/ecmarkdown": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/ecmarkdown/-/ecmarkdown-7.1.0.tgz", - "integrity": "sha512-xTrf1Qj6nCsHGSHaOrAPfALoEH2nPSs+wclpzXEsozVJbEYcTkims59rJiJQB6TxAW2J4oFfoaB2up1wbb9k4w==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ecmarkdown/-/ecmarkdown-7.2.0.tgz", + "integrity": "sha512-p0C4SJCvnvtm0y9gPhXBb5DlNbHsNS44ihVKBw3MXviZG2QQpZNH4z/3PbkpgECOjKOeZI+m84ISHVV9WLECFQ==", "dependencies": { "escape-html": "^1.0.1" } @@ -4371,9 +4371,9 @@ } }, "ecmarkdown": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/ecmarkdown/-/ecmarkdown-7.1.0.tgz", - "integrity": "sha512-xTrf1Qj6nCsHGSHaOrAPfALoEH2nPSs+wclpzXEsozVJbEYcTkims59rJiJQB6TxAW2J4oFfoaB2up1wbb9k4w==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ecmarkdown/-/ecmarkdown-7.2.0.tgz", + "integrity": "sha512-p0C4SJCvnvtm0y9gPhXBb5DlNbHsNS44ihVKBw3MXviZG2QQpZNH4z/3PbkpgECOjKOeZI+m84ISHVV9WLECFQ==", "requires": { "escape-html": "^1.0.1" } diff --git a/package.json b/package.json index b2e2bbf5..9ccdb49a 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,7 @@ "command-line-args": "^5.2.0", "command-line-usage": "^6.1.1", "dedent-js": "^1.0.1", - "ecmarkdown": "^7.1.0", + "ecmarkdown": "^7.2.0", "eslint-formatter-codeframe": "^7.32.1", "fast-glob": "^3.2.7", "grammarkdown": "^3.2.0", diff --git a/src/lint/collect-algorithm-diagnostics.ts b/src/lint/collect-algorithm-diagnostics.ts index d0011e8e..3be1a055 100644 --- a/src/lint/collect-algorithm-diagnostics.ts +++ b/src/lint/collect-algorithm-diagnostics.ts @@ -10,12 +10,14 @@ import lintAlgorithmLineStyle from './rules/algorithm-line-style'; import lintAlgorithmStepNumbering from './rules/algorithm-step-numbering'; import lintAlgorithmStepLabels from './rules/algorithm-step-labels'; import lintForEachElement from './rules/for-each-element'; +import lintStepAttributes from './rules/step-attributes'; const algorithmRules = [ lintAlgorithmLineStyle, lintAlgorithmStepNumbering, lintAlgorithmStepLabels, lintForEachElement, + lintStepAttributes, ]; function composeObservers(...observers: Observer[]): Observer { diff --git a/src/lint/rules/algorithm-step-labels.ts b/src/lint/rules/algorithm-step-labels.ts index 5107eb45..44533a51 100644 --- a/src/lint/rules/algorithm-step-labels.ts +++ b/src/lint/rules/algorithm-step-labels.ts @@ -15,14 +15,14 @@ export default function (report: Reporter, node: Element, algorithmSource: strin const idAttr = node.attrs.find(({ key }) => key === 'id'); if (idAttr != null && !/^step-/.test(idAttr.value)) { const itemSource = algorithmSource.slice( - node.location.start.offset, - node.location.end.offset + idAttr.location.start.offset, + idAttr.location.end.offset ); - const offset = itemSource.match(/^\s*\d+\. \[ *id *= *"/)![0].length; + const offset = itemSource.match(/^id *= *"/)![0].length; report({ ruleId, - line: node.location.start.line, - column: node.location.start.column + offset, + line: idAttr.location.start.line, + column: idAttr.location.start.column + offset, message: `step labels should start with "step-"`, }); } diff --git a/src/lint/rules/step-attributes.ts b/src/lint/rules/step-attributes.ts new file mode 100644 index 00000000..b6486367 --- /dev/null +++ b/src/lint/rules/step-attributes.ts @@ -0,0 +1,29 @@ +import type { Node as EcmarkdownNode, Observer } from 'ecmarkdown'; +import type { Reporter } from '../algorithm-error-reporter-type'; + +const ruleId = 'unknown-step-attribute'; + +const KNOWN_ATTRIBUTES = ['id', 'fence-effects']; + +/* +Checks for unknown attributes on steps. +*/ +export default function (report: Reporter): Observer { + return { + enter(node: EcmarkdownNode) { + if (node.name !== 'ordered-list-item') { + return; + } + for (const attr of node.attrs) { + if (!KNOWN_ATTRIBUTES.includes(attr.key)) { + report({ + ruleId, + message: `unknown step attribute ${JSON.stringify(attr.key)}`, + line: attr.location.start.line, + column: attr.location.start.column, + }); + } + } + }, + }; +} diff --git a/test/lint-algorithms.js b/test/lint-algorithms.js index 8744b271..60f2f8c0 100644 --- a/test/lint-algorithms.js +++ b/test/lint-algorithms.js @@ -391,6 +391,22 @@ describe('linting algorithms', () => { }); }); + describe('step attributes', () => { + const ruleId = 'unknown-step-attribute'; + it('rejects unknown attributes', async () => { + await assertLint( + positioned` + 1. [id="step-id",${M}unknown="foo"] Step. + `, + { + ruleId, + nodeType, + message: 'unknown step attribute "unknown"', + } + ); + }); + }); + describe('for each element', () => { const ruleId = 'for-each-element'; it('rejects loops without types', async () => {