ecmarkup
Version:
Custom element definitions and core utilities for markup that specifies ECMAScript and related technologies.
174 lines (173 loc) • 6.52 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.collectSpellingDiagnostics = collectSpellingDiagnostics;
const utils_1 = require("../utils");
const ruleId = 'spelling';
// Note that these will be composed, so cannot contain backreferences
const matchers = [
{
pattern: /\*this\* object/gu,
message: 'prefer "*this* value"',
},
{
pattern: /1's complement/gu,
message: 'prefer "one\'s complement"',
},
{
pattern: /2's complement/gu,
message: 'prefer "two\'s complement"',
},
{
pattern: /\*0\*(?!<sub>ℤ<\/sub>)/gu,
message: 'the Number value 0 should be written "*+0*", to unambiguously exclude "*-0*"',
},
{
pattern: /[+-]?(?:&[a-z]+;|0x[0-9A-Fa-f]+|[0-9]+(?:\.[0-9]+)?)<sub>𝔽<\/sub>/gu,
message: 'literal Number values should be bolded',
},
{
pattern: /[+-]?[0-9]+<sub>ℤ<\/sub>/gu,
message: 'literal BigInt values should be bolded',
},
{
pattern: /\*[+-]?(?:&[a-z]+;|0x[0-9A-Fa-f]+|[0-9]+(?:\.[0-9]+)?)\*(?!<sub>[𝔽ℤ]<\/sub>)/gu,
message: 'literal Number or BigInt values should be followed by <sub>𝔽</sub> or <sub>ℤ</sub> respectively',
},
{
pattern: /(?<=\*)\+(?:0x[1-9A-Fa-f]|[1-9])/gu,
message: 'positive numeric values other than 0 should not have a leading plus sign (+)',
},
{
// this needs its own rule to catch +0 as a real number
// (but not similar text such as expanded-year dates like +000000-01-01
// or UTC offsets like +00:00)
pattern: /(?<=^|\s)\+[0-9](?![0-9]*[-:])/gu,
message: 'positive real numbers should not have a leading plus sign (+)',
},
{
pattern: /(?<![+-])∞/gu,
message: '∞ should always be written with a leading + or -',
},
{
pattern: /(?<= )[+-]\*(?:&[a-z]+;|0x[0-9A-Fa-f]+|[0-9]+(?:\.[0-9]+)?)\*/gu,
message: 'the sign character for a numeric literal should be within the `*`s',
},
{
pattern: /(?<=\bmathematical value )for\b/gu,
message: 'the mathematical value "of", not "for"',
},
{
pattern: /(?<=\b[Nn]umber value )of\b/gu,
message: 'the Number value "for", not "of"',
},
{
pattern: /\bnumber value\b/gu,
message: '"Number value", not "number value"',
},
{
// it would be best to somehow literally check against en-GB-oxendict,
// but absent that we use the sample list from
// https://en.wikipedia.org/wiki/American_and_British_English_spelling_differences#-our%2C_-or
pattern: /\b(?:[Bb]ehaviors?|[Ff]lavors?|[Hh]arbors?|[Hh]onors?|[Hh]umors?|[Ll]abors?|[Nn]eighbors?|[Rr]umors?|[Ss]plendors?)\b/gu,
message: 'ECMA-262 uses Oxford spelling ("behaviour", etc.)',
},
{
pattern: /\b[Ii]ndexes\b/gu,
message: 'prefer "indices"',
},
{
pattern: /\b[Nn]onnegative\b/gu,
message: 'prefer "non-negative"',
},
{
pattern: /\b[Nn]onempty\b/gu,
message: 'prefer "non-empty"',
},
{
pattern: /\b[Nn]onzero\b/gu,
message: 'prefer "non-zero"',
},
{
pattern: /\b[Tt]he empty string\b/gu,
message: 'prefer "the empty String"',
},
{
pattern: /[ \t]+\n/gu,
message: 'trailing spaces are not allowed',
},
{
pattern: /(?<=(^|[^\n])\n\n)\n+/gu,
message: 'no more than one blank line is allowed',
},
{
pattern: /(?<=<emu-clause.*>\n)\n\s*<h1>/gu,
message: "there should not be a blank line between a clause's opening tag and its header",
},
{
pattern: /(?<=(^|[^\n])\n)\n+[ \t]*<\/emu-clause>/gu,
message: 'there should not be a blank line between the last line of a clause and its closing tag',
},
{
pattern: /\r/gu,
message: 'only Unix-style (LF) linebreaks are allowed',
},
{
pattern: /(?<=\b[Ss]teps? )\d/gu,
message: 'prefer using labeled steps and <emu-xref> tags over hardcoding step numbers',
},
{
pattern: /(?<=\b[Cc]lauses? )\d/gu,
message: 'clauses should be referenced using <emu-xref> tags rather than hardcoding clause numbers',
},
{
pattern: /(?<=\S) +(?! |<\/(td|th|dd|dt)>)/gu,
message: 'multiple consecutive spaces are not allowed',
},
{
pattern: /(?<=< ?\*)\+0\*/gu,
message: '"less than" comparisons against floating-point zero should use negative zero',
},
{
pattern: /(?<=> ?\*)-0\*/gu,
message: '"greater than" comparisons against floating-point zero should use positive zero',
},
{
pattern: /(?<=&[lg]e; ?\*)[+-]0\*/gu,
message: 'comparisons against floating-point zero should use strict comparisons (< or >); guard the equals case with "is"',
},
{
pattern: /(÷|÷)/gu,
message: 'division should be written as "/", not "÷", per ISO 80000-2',
},
];
function collectSpellingDiagnostics(report, mainSource, imports) {
const composed = new RegExp(matchers.map(m => `(?:${m.pattern.source})`).join('|'), 'u');
const toTest = [{ source: mainSource }].concat(imports);
for (const { source, importLocation } of toTest) {
// The usual case will be to have no errors, so we have a fast path for that case.
// We only fall back to slower individual tests if there is at least one error.
if (composed.test(source)) {
let reported = false;
for (const { pattern, message } of matchers) {
let match = pattern.exec(source);
while (match !== null) {
reported = true;
const { line, column } = (0, utils_1.offsetToLineAndColumn)(source, match.index);
report({
type: 'raw',
ruleId,
line,
column,
message,
source,
file: importLocation,
});
match = pattern.exec(source);
}
}
if (!reported) {
throw new Error('Ecmarkup has a bug: the spell checker reported an error, but could not find one. Please report this at https://github.com/tc39/ecmarkup/issues/new.');
}
}
}
}
;