@redocly/recheck 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1023 -56
- package/dist/cli.js +40 -7
- package/dist/cli.js.map +1 -1
- package/dist/commands/markdoc-schema.d.ts +17 -0
- package/dist/commands/markdoc-schema.d.ts.map +1 -0
- package/dist/commands/markdoc-schema.js +127 -0
- package/dist/commands/markdoc-schema.js.map +1 -0
- package/dist/commands/run.d.ts +2 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +87 -10
- package/dist/commands/run.js.map +1 -1
- package/dist/config/load.d.ts +11 -0
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +12 -2
- package/dist/config/load.js.map +1 -1
- package/dist/config/presets/google.d.ts +3 -0
- package/dist/config/presets/google.d.ts.map +1 -0
- package/dist/config/presets/google.js +1671 -0
- package/dist/config/presets/google.js.map +1 -0
- package/dist/config/presets/inclusive-language.d.ts +3 -0
- package/dist/config/presets/inclusive-language.d.ts.map +1 -0
- package/dist/config/presets/inclusive-language.js +321 -0
- package/dist/config/presets/inclusive-language.js.map +1 -0
- package/dist/config/presets/index.d.ts +67 -0
- package/dist/config/presets/index.d.ts.map +1 -0
- package/dist/config/presets/index.js +137 -0
- package/dist/config/presets/index.js.map +1 -0
- package/dist/config/presets/markdoc.d.ts +21 -0
- package/dist/config/presets/markdoc.d.ts.map +1 -0
- package/dist/config/presets/markdoc.js +101 -0
- package/dist/config/presets/markdoc.js.map +1 -0
- package/dist/config/presets/markdown-relaxed.d.ts +3 -0
- package/dist/config/presets/markdown-relaxed.d.ts.map +1 -0
- package/dist/config/presets/markdown-relaxed.js +98 -0
- package/dist/config/presets/markdown-relaxed.js.map +1 -0
- package/dist/config/presets/markdown.d.ts +43 -0
- package/dist/config/presets/markdown.d.ts.map +1 -0
- package/dist/config/presets/markdown.js +132 -0
- package/dist/config/presets/markdown.js.map +1 -0
- package/dist/config/presets/microsoft.d.ts +3 -0
- package/dist/config/presets/microsoft.d.ts.map +1 -0
- package/dist/config/presets/microsoft.js +2268 -0
- package/dist/config/presets/microsoft.js.map +1 -0
- package/dist/config/presets/minimal.d.ts +3 -0
- package/dist/config/presets/minimal.d.ts.map +1 -0
- package/dist/config/presets/minimal.js +21 -0
- package/dist/config/presets/minimal.js.map +1 -0
- package/dist/config/presets/plain-language.d.ts +3 -0
- package/dist/config/presets/plain-language.d.ts.map +1 -0
- package/dist/config/presets/plain-language.js +351 -0
- package/dist/config/presets/plain-language.js.map +1 -0
- package/dist/config/presets/prose.d.ts +52 -0
- package/dist/config/presets/prose.d.ts.map +1 -0
- package/dist/config/presets/prose.js +138 -0
- package/dist/config/presets/prose.js.map +1 -0
- package/dist/config/schema.d.ts +128 -22
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +105 -21
- package/dist/config/schema.js.map +1 -1
- package/dist/config/validate.d.ts +12 -2
- package/dist/config/validate.d.ts.map +1 -1
- package/dist/config/validate.js +1200 -44
- package/dist/config/validate.js.map +1 -1
- package/dist/core/auto-fix.d.ts +8 -13
- package/dist/core/auto-fix.d.ts.map +1 -1
- package/dist/core/auto-fix.js +94 -75
- package/dist/core/auto-fix.js.map +1 -1
- package/dist/core/case-preserve.d.ts +46 -0
- package/dist/core/case-preserve.d.ts.map +1 -0
- package/dist/core/case-preserve.js +57 -0
- package/dist/core/case-preserve.js.map +1 -0
- package/dist/core/directives.d.ts +9 -0
- package/dist/core/directives.d.ts.map +1 -0
- package/dist/core/directives.js +73 -0
- package/dist/core/directives.js.map +1 -0
- package/dist/core/files.d.ts +63 -0
- package/dist/core/files.d.ts.map +1 -1
- package/dist/core/files.js +185 -0
- package/dist/core/files.js.map +1 -1
- package/dist/core/inline-code.d.ts +87 -0
- package/dist/core/inline-code.d.ts.map +1 -0
- package/dist/core/inline-code.js +104 -0
- package/dist/core/inline-code.js.map +1 -0
- package/dist/core/line-endings.d.ts +32 -0
- package/dist/core/line-endings.d.ts.map +1 -0
- package/dist/core/line-endings.js +65 -0
- package/dist/core/line-endings.js.map +1 -0
- package/dist/core/markdoc-tags.d.ts +79 -0
- package/dist/core/markdoc-tags.d.ts.map +1 -0
- package/dist/core/markdoc-tags.js +131 -0
- package/dist/core/markdoc-tags.js.map +1 -0
- package/dist/core/rule-filters.d.ts +17 -0
- package/dist/core/rule-filters.d.ts.map +1 -1
- package/dist/core/rule-filters.js +64 -0
- package/dist/core/rule-filters.js.map +1 -1
- package/dist/core/runner.d.ts +92 -3
- package/dist/core/runner.d.ts.map +1 -1
- package/dist/core/runner.js +348 -110
- package/dist/core/runner.js.map +1 -1
- package/dist/core/timing.d.ts.map +1 -1
- package/dist/data/markdoc-realm-schema.d.ts +3 -0
- package/dist/data/markdoc-realm-schema.d.ts.map +1 -0
- package/dist/data/markdoc-realm-schema.js +760 -0
- package/dist/data/markdoc-realm-schema.js.map +1 -0
- package/dist/data/proper-nouns.d.ts +2 -0
- package/dist/data/proper-nouns.d.ts.map +1 -0
- package/dist/data/proper-nouns.js +47 -0
- package/dist/data/proper-nouns.js.map +1 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +152 -0
- package/dist/index.js.map +1 -0
- package/dist/metrics/formulas.d.ts +17 -0
- package/dist/metrics/formulas.d.ts.map +1 -0
- package/dist/metrics/formulas.js +70 -0
- package/dist/metrics/formulas.js.map +1 -0
- package/dist/metrics/index.d.ts +5 -0
- package/dist/metrics/index.d.ts.map +1 -0
- package/dist/metrics/index.js +3 -0
- package/dist/metrics/index.js.map +1 -0
- package/dist/metrics/statistics.d.ts +27 -0
- package/dist/metrics/statistics.d.ts.map +1 -0
- package/dist/metrics/statistics.js +56 -0
- package/dist/metrics/statistics.js.map +1 -0
- package/dist/parser/index.d.ts +18 -0
- package/dist/parser/index.d.ts.map +1 -0
- package/dist/parser/index.js +168 -0
- package/dist/parser/index.js.map +1 -0
- package/dist/parser/markdoc/extract-statics.d.ts +45 -0
- package/dist/parser/markdoc/extract-statics.d.ts.map +1 -0
- package/dist/parser/markdoc/extract-statics.js +139 -0
- package/dist/parser/markdoc/extract-statics.js.map +1 -0
- package/dist/parser/markdoc/pairing.d.ts +63 -0
- package/dist/parser/markdoc/pairing.d.ts.map +1 -0
- package/dist/parser/markdoc/pairing.js +94 -0
- package/dist/parser/markdoc/pairing.js.map +1 -0
- package/dist/parser/markdoc/schema.d.ts +85 -0
- package/dist/parser/markdoc/schema.d.ts.map +1 -0
- package/dist/parser/markdoc/schema.js +86 -0
- package/dist/parser/markdoc/schema.js.map +1 -0
- package/dist/parser/markdoc/span.d.ts +64 -0
- package/dist/parser/markdoc/span.d.ts.map +1 -0
- package/dist/parser/markdoc/span.js +729 -0
- package/dist/parser/markdoc/span.js.map +1 -0
- package/dist/parser/markdoc/structure.d.ts +28 -0
- package/dist/parser/markdoc/structure.d.ts.map +1 -0
- package/dist/parser/markdoc/structure.js +153 -0
- package/dist/parser/markdoc/structure.js.map +1 -0
- package/dist/parser/markdoc/syntax.d.ts +44 -0
- package/dist/parser/markdoc/syntax.d.ts.map +1 -0
- package/dist/parser/markdoc/syntax.js +317 -0
- package/dist/parser/markdoc/syntax.js.map +1 -0
- package/dist/parser/types.d.ts +18 -0
- package/dist/parser/types.d.ts.map +1 -0
- package/dist/parser/types.js.map +1 -0
- package/dist/reporter/fixes.d.ts.map +1 -1
- package/dist/reporter/fixes.js +22 -1
- package/dist/reporter/fixes.js.map +1 -1
- package/dist/reporter/statistics.d.ts +1 -8
- package/dist/reporter/statistics.d.ts.map +1 -1
- package/dist/reporter/statistics.js.map +1 -1
- package/dist/rules/registry.d.ts +15 -0
- package/dist/rules/registry.d.ts.map +1 -0
- package/dist/rules/registry.js +81 -0
- package/dist/rules/registry.js.map +1 -0
- package/dist/rules/scope/capitalization.d.ts +3 -0
- package/dist/rules/scope/capitalization.d.ts.map +1 -0
- package/dist/rules/scope/capitalization.js +163 -0
- package/dist/rules/scope/capitalization.js.map +1 -0
- package/dist/rules/scope/conditional.d.ts +3 -0
- package/dist/rules/scope/conditional.d.ts.map +1 -0
- package/dist/rules/scope/conditional.js +112 -0
- package/dist/rules/scope/conditional.js.map +1 -0
- package/dist/rules/scope/consistency.d.ts +3 -0
- package/dist/rules/scope/consistency.d.ts.map +1 -0
- package/dist/rules/scope/consistency.js +178 -0
- package/dist/rules/scope/consistency.js.map +1 -0
- package/dist/rules/scope/length.d.ts +3 -0
- package/dist/rules/scope/length.d.ts.map +1 -0
- package/dist/rules/scope/length.js +69 -0
- package/dist/rules/scope/length.js.map +1 -0
- package/dist/rules/scope/max-image-size.d.ts +3 -0
- package/dist/rules/scope/max-image-size.d.ts.map +1 -0
- package/dist/rules/scope/max-image-size.js +65 -0
- package/dist/rules/scope/max-image-size.js.map +1 -0
- package/dist/rules/scope/metric.d.ts +17 -0
- package/dist/rules/scope/metric.d.ts.map +1 -0
- package/dist/rules/scope/metric.js +216 -0
- package/dist/rules/scope/metric.js.map +1 -0
- package/dist/rules/scope/occurrence.d.ts +3 -0
- package/dist/rules/scope/occurrence.d.ts.map +1 -0
- package/dist/rules/scope/occurrence.js +48 -0
- package/dist/rules/scope/occurrence.js.map +1 -0
- package/dist/rules/scope/pattern.d.ts +3 -0
- package/dist/rules/scope/pattern.d.ts.map +1 -0
- package/dist/rules/scope/pattern.js +75 -0
- package/dist/rules/scope/pattern.js.map +1 -0
- package/dist/rules/scope/repetition.d.ts +3 -0
- package/dist/rules/scope/repetition.d.ts.map +1 -0
- package/dist/rules/scope/repetition.js +139 -0
- package/dist/rules/scope/repetition.js.map +1 -0
- package/dist/rules/scope/semantic-line-breaks.d.ts +3 -0
- package/dist/rules/scope/semantic-line-breaks.d.ts.map +1 -0
- package/dist/rules/scope/semantic-line-breaks.js +213 -0
- package/dist/rules/scope/semantic-line-breaks.js.map +1 -0
- package/dist/rules/scope/spelling.d.ts +27 -0
- package/dist/rules/scope/spelling.d.ts.map +1 -0
- package/dist/rules/scope/spelling.js +227 -0
- package/dist/rules/scope/spelling.js.map +1 -0
- package/dist/rules/scope/swap.d.ts +3 -0
- package/dist/rules/scope/swap.d.ts.map +1 -0
- package/dist/rules/scope/swap.js +149 -0
- package/dist/rules/scope/swap.js.map +1 -0
- package/dist/rules/scope/title-case.d.ts +46 -0
- package/dist/rules/scope/title-case.d.ts.map +1 -0
- package/dist/rules/scope/title-case.js +301 -0
- package/dist/rules/scope/title-case.js.map +1 -0
- package/dist/rules/token/blanks-around-fences.d.ts +3 -0
- package/dist/rules/token/blanks-around-fences.d.ts.map +1 -0
- package/dist/rules/token/blanks-around-fences.js +44 -0
- package/dist/rules/token/blanks-around-fences.js.map +1 -0
- package/dist/rules/token/blanks-around-headings.d.ts +3 -0
- package/dist/rules/token/blanks-around-headings.d.ts.map +1 -0
- package/dist/rules/token/blanks-around-headings.js +108 -0
- package/dist/rules/token/blanks-around-headings.js.map +1 -0
- package/dist/rules/token/blanks-around-lists.d.ts +3 -0
- package/dist/rules/token/blanks-around-lists.d.ts.map +1 -0
- package/dist/rules/token/blanks-around-lists.js +56 -0
- package/dist/rules/token/blanks-around-lists.js.map +1 -0
- package/dist/rules/token/blanks-around-tables.d.ts +3 -0
- package/dist/rules/token/blanks-around-tables.d.ts.map +1 -0
- package/dist/rules/token/blanks-around-tables.js +42 -0
- package/dist/rules/token/blanks-around-tables.js.map +1 -0
- package/dist/rules/token/code-block-style.d.ts +3 -0
- package/dist/rules/token/code-block-style.d.ts.map +1 -0
- package/dist/rules/token/code-block-style.js +30 -0
- package/dist/rules/token/code-block-style.js.map +1 -0
- package/dist/rules/token/code-fence-style.d.ts +3 -0
- package/dist/rules/token/code-fence-style.d.ts.map +1 -0
- package/dist/rules/token/code-fence-style.js +35 -0
- package/dist/rules/token/code-fence-style.js.map +1 -0
- package/dist/rules/token/commands-show-output.d.ts +3 -0
- package/dist/rules/token/commands-show-output.d.ts.map +1 -0
- package/dist/rules/token/commands-show-output.js +38 -0
- package/dist/rules/token/commands-show-output.js.map +1 -0
- package/dist/rules/token/descriptive-link-text.d.ts +3 -0
- package/dist/rules/token/descriptive-link-text.d.ts.map +1 -0
- package/dist/rules/token/descriptive-link-text.js +53 -0
- package/dist/rules/token/descriptive-link-text.js.map +1 -0
- package/dist/rules/token/emphasis-style.d.ts +3 -0
- package/dist/rules/token/emphasis-style.d.ts.map +1 -0
- package/dist/rules/token/emphasis-style.js +51 -0
- package/dist/rules/token/emphasis-style.js.map +1 -0
- package/dist/rules/token/fenced-code-language.d.ts +3 -0
- package/dist/rules/token/fenced-code-language.d.ts.map +1 -0
- package/dist/rules/token/fenced-code-language.js +35 -0
- package/dist/rules/token/fenced-code-language.js.map +1 -0
- package/dist/rules/token/first-line-h1.d.ts +3 -0
- package/dist/rules/token/first-line-h1.d.ts.map +1 -0
- package/dist/rules/token/first-line-h1.js +107 -0
- package/dist/rules/token/first-line-h1.js.map +1 -0
- package/dist/rules/token/heading-increment.d.ts +3 -0
- package/dist/rules/token/heading-increment.d.ts.map +1 -0
- package/dist/rules/token/heading-increment.js +27 -0
- package/dist/rules/token/heading-increment.js.map +1 -0
- package/dist/rules/token/heading-start-left.d.ts +3 -0
- package/dist/rules/token/heading-start-left.d.ts.map +1 -0
- package/dist/rules/token/heading-start-left.js +31 -0
- package/dist/rules/token/heading-start-left.js.map +1 -0
- package/dist/rules/token/heading-style.d.ts +3 -0
- package/dist/rules/token/heading-style.d.ts.map +1 -0
- package/dist/rules/token/heading-style.js +41 -0
- package/dist/rules/token/heading-style.js.map +1 -0
- package/dist/rules/token/helpers.d.ts +313 -0
- package/dist/rules/token/helpers.d.ts.map +1 -0
- package/dist/rules/token/helpers.js +746 -0
- package/dist/rules/token/helpers.js.map +1 -0
- package/dist/rules/token/hr-style.d.ts +3 -0
- package/dist/rules/token/hr-style.d.ts.map +1 -0
- package/dist/rules/token/hr-style.js +28 -0
- package/dist/rules/token/hr-style.js.map +1 -0
- package/dist/rules/token/index.d.ts +75 -0
- package/dist/rules/token/index.d.ts.map +1 -0
- package/dist/rules/token/index.js +226 -0
- package/dist/rules/token/index.js.map +1 -0
- package/dist/rules/token/line-length.d.ts +3 -0
- package/dist/rules/token/line-length.d.ts.map +1 -0
- package/dist/rules/token/line-length.js +120 -0
- package/dist/rules/token/line-length.js.map +1 -0
- package/dist/rules/token/link-fragments.d.ts +3 -0
- package/dist/rules/token/link-fragments.d.ts.map +1 -0
- package/dist/rules/token/link-fragments.js +145 -0
- package/dist/rules/token/link-fragments.js.map +1 -0
- package/dist/rules/token/link-image-reference-definitions.d.ts +3 -0
- package/dist/rules/token/link-image-reference-definitions.d.ts.map +1 -0
- package/dist/rules/token/link-image-reference-definitions.js +50 -0
- package/dist/rules/token/link-image-reference-definitions.js.map +1 -0
- package/dist/rules/token/link-image-style.d.ts +3 -0
- package/dist/rules/token/link-image-style.d.ts.map +1 -0
- package/dist/rules/token/link-image-style.js +131 -0
- package/dist/rules/token/link-image-style.js.map +1 -0
- package/dist/rules/token/list-indent.d.ts +3 -0
- package/dist/rules/token/list-indent.d.ts.map +1 -0
- package/dist/rules/token/list-indent.js +60 -0
- package/dist/rules/token/list-indent.js.map +1 -0
- package/dist/rules/token/list-length.d.ts +3 -0
- package/dist/rules/token/list-length.d.ts.map +1 -0
- package/dist/rules/token/list-length.js +55 -0
- package/dist/rules/token/list-length.js.map +1 -0
- package/dist/rules/token/list-marker-space.d.ts +3 -0
- package/dist/rules/token/list-marker-space.d.ts.map +1 -0
- package/dist/rules/token/list-marker-space.js +52 -0
- package/dist/rules/token/list-marker-space.js.map +1 -0
- package/dist/rules/token/markdoc-attributes.d.ts +3 -0
- package/dist/rules/token/markdoc-attributes.d.ts.map +1 -0
- package/dist/rules/token/markdoc-attributes.js +269 -0
- package/dist/rules/token/markdoc-attributes.js.map +1 -0
- package/dist/rules/token/markdoc-pairing.d.ts +3 -0
- package/dist/rules/token/markdoc-pairing.d.ts.map +1 -0
- package/dist/rules/token/markdoc-pairing.js +73 -0
- package/dist/rules/token/markdoc-pairing.js.map +1 -0
- package/dist/rules/token/markdoc-syntax.d.ts +3 -0
- package/dist/rules/token/markdoc-syntax.d.ts.map +1 -0
- package/dist/rules/token/markdoc-syntax.js +119 -0
- package/dist/rules/token/markdoc-syntax.js.map +1 -0
- package/dist/rules/token/markdoc-unknown-tag.d.ts +3 -0
- package/dist/rules/token/markdoc-unknown-tag.d.ts.map +1 -0
- package/dist/rules/token/markdoc-unknown-tag.js +64 -0
- package/dist/rules/token/markdoc-unknown-tag.js.map +1 -0
- package/dist/rules/token/messages.d.ts +4 -0
- package/dist/rules/token/messages.d.ts.map +1 -0
- package/dist/rules/token/messages.js +20 -0
- package/dist/rules/token/messages.js.map +1 -0
- package/dist/rules/token/no-alt-text.d.ts +3 -0
- package/dist/rules/token/no-alt-text.d.ts.map +1 -0
- package/dist/rules/token/no-alt-text.js +47 -0
- package/dist/rules/token/no-alt-text.js.map +1 -0
- package/dist/rules/token/no-bare-urls.d.ts +3 -0
- package/dist/rules/token/no-bare-urls.d.ts.map +1 -0
- package/dist/rules/token/no-bare-urls.js +88 -0
- package/dist/rules/token/no-bare-urls.js.map +1 -0
- package/dist/rules/token/no-blanks-blockquote.d.ts +3 -0
- package/dist/rules/token/no-blanks-blockquote.d.ts.map +1 -0
- package/dist/rules/token/no-blanks-blockquote.js +39 -0
- package/dist/rules/token/no-blanks-blockquote.js.map +1 -0
- package/dist/rules/token/no-duplicate-heading.d.ts +3 -0
- package/dist/rules/token/no-duplicate-heading.d.ts.map +1 -0
- package/dist/rules/token/no-duplicate-heading.js +101 -0
- package/dist/rules/token/no-duplicate-heading.js.map +1 -0
- package/dist/rules/token/no-duplicate-link-destinations.d.ts +3 -0
- package/dist/rules/token/no-duplicate-link-destinations.d.ts.map +1 -0
- package/dist/rules/token/no-duplicate-link-destinations.js +65 -0
- package/dist/rules/token/no-duplicate-link-destinations.js.map +1 -0
- package/dist/rules/token/no-emphasis-as-heading.d.ts +3 -0
- package/dist/rules/token/no-emphasis-as-heading.d.ts.map +1 -0
- package/dist/rules/token/no-emphasis-as-heading.js +44 -0
- package/dist/rules/token/no-emphasis-as-heading.js.map +1 -0
- package/dist/rules/token/no-empty-headings.d.ts +3 -0
- package/dist/rules/token/no-empty-headings.d.ts.map +1 -0
- package/dist/rules/token/no-empty-headings.js +28 -0
- package/dist/rules/token/no-empty-headings.js.map +1 -0
- package/dist/rules/token/no-empty-links.d.ts +3 -0
- package/dist/rules/token/no-empty-links.d.ts.map +1 -0
- package/dist/rules/token/no-empty-links.js +67 -0
- package/dist/rules/token/no-empty-links.js.map +1 -0
- package/dist/rules/token/no-hard-tabs.d.ts +3 -0
- package/dist/rules/token/no-hard-tabs.d.ts.map +1 -0
- package/dist/rules/token/no-hard-tabs.js +76 -0
- package/dist/rules/token/no-hard-tabs.js.map +1 -0
- package/dist/rules/token/no-inline-html.d.ts +3 -0
- package/dist/rules/token/no-inline-html.d.ts.map +1 -0
- package/dist/rules/token/no-inline-html.js +45 -0
- package/dist/rules/token/no-inline-html.js.map +1 -0
- package/dist/rules/token/no-missing-space-atx.d.ts +3 -0
- package/dist/rules/token/no-missing-space-atx.d.ts.map +1 -0
- package/dist/rules/token/no-missing-space-atx.js +36 -0
- package/dist/rules/token/no-missing-space-atx.js.map +1 -0
- package/dist/rules/token/no-missing-space-closed-atx.d.ts +3 -0
- package/dist/rules/token/no-missing-space-closed-atx.d.ts.map +1 -0
- package/dist/rules/token/no-missing-space-closed-atx.js +45 -0
- package/dist/rules/token/no-missing-space-closed-atx.js.map +1 -0
- package/dist/rules/token/no-multiple-blanks.d.ts +3 -0
- package/dist/rules/token/no-multiple-blanks.d.ts.map +1 -0
- package/dist/rules/token/no-multiple-blanks.js +35 -0
- package/dist/rules/token/no-multiple-blanks.js.map +1 -0
- package/dist/rules/token/no-multiple-space-atx.d.ts +13 -0
- package/dist/rules/token/no-multiple-space-atx.d.ts.map +1 -0
- package/dist/rules/token/no-multiple-space-atx.js +50 -0
- package/dist/rules/token/no-multiple-space-atx.js.map +1 -0
- package/dist/rules/token/no-multiple-space-blockquote.d.ts +3 -0
- package/dist/rules/token/no-multiple-space-blockquote.d.ts.map +1 -0
- package/dist/rules/token/no-multiple-space-blockquote.js +43 -0
- package/dist/rules/token/no-multiple-space-blockquote.js.map +1 -0
- package/dist/rules/token/no-multiple-space-closed-atx.d.ts +3 -0
- package/dist/rules/token/no-multiple-space-closed-atx.d.ts.map +1 -0
- package/dist/rules/token/no-multiple-space-closed-atx.js +19 -0
- package/dist/rules/token/no-multiple-space-closed-atx.js.map +1 -0
- package/dist/rules/token/no-reversed-links.d.ts +3 -0
- package/dist/rules/token/no-reversed-links.d.ts.map +1 -0
- package/dist/rules/token/no-reversed-links.js +52 -0
- package/dist/rules/token/no-reversed-links.js.map +1 -0
- package/dist/rules/token/no-space-in-code.d.ts +3 -0
- package/dist/rules/token/no-space-in-code.d.ts.map +1 -0
- package/dist/rules/token/no-space-in-code.js +75 -0
- package/dist/rules/token/no-space-in-code.js.map +1 -0
- package/dist/rules/token/no-space-in-emphasis.d.ts +3 -0
- package/dist/rules/token/no-space-in-emphasis.d.ts.map +1 -0
- package/dist/rules/token/no-space-in-emphasis.js +79 -0
- package/dist/rules/token/no-space-in-emphasis.js.map +1 -0
- package/dist/rules/token/no-space-in-links.d.ts +3 -0
- package/dist/rules/token/no-space-in-links.d.ts.map +1 -0
- package/dist/rules/token/no-space-in-links.js +48 -0
- package/dist/rules/token/no-space-in-links.js.map +1 -0
- package/dist/rules/token/no-trailing-punctuation.d.ts +3 -0
- package/dist/rules/token/no-trailing-punctuation.d.ts.map +1 -0
- package/dist/rules/token/no-trailing-punctuation.js +38 -0
- package/dist/rules/token/no-trailing-punctuation.js.map +1 -0
- package/dist/rules/token/no-trailing-spaces.d.ts +3 -0
- package/dist/rules/token/no-trailing-spaces.d.ts.map +1 -0
- package/dist/rules/token/no-trailing-spaces.js +89 -0
- package/dist/rules/token/no-trailing-spaces.js.map +1 -0
- package/dist/rules/token/ol-prefix.d.ts +3 -0
- package/dist/rules/token/ol-prefix.d.ts.map +1 -0
- package/dist/rules/token/ol-prefix.js +70 -0
- package/dist/rules/token/ol-prefix.js.map +1 -0
- package/dist/rules/token/proper-names.d.ts +3 -0
- package/dist/rules/token/proper-names.d.ts.map +1 -0
- package/dist/rules/token/proper-names.js +93 -0
- package/dist/rules/token/proper-names.js.map +1 -0
- package/dist/rules/token/reference-links-images.d.ts +3 -0
- package/dist/rules/token/reference-links-images.d.ts.map +1 -0
- package/dist/rules/token/reference-links-images.js +36 -0
- package/dist/rules/token/reference-links-images.js.map +1 -0
- package/dist/rules/token/required-headings.d.ts +3 -0
- package/dist/rules/token/required-headings.d.ts.map +1 -0
- package/dist/rules/token/required-headings.js +83 -0
- package/dist/rules/token/required-headings.js.map +1 -0
- package/dist/rules/token/single-h1.d.ts +3 -0
- package/dist/rules/token/single-h1.d.ts.map +1 -0
- package/dist/rules/token/single-h1.js +56 -0
- package/dist/rules/token/single-h1.js.map +1 -0
- package/dist/rules/token/single-trailing-newline.d.ts +3 -0
- package/dist/rules/token/single-trailing-newline.d.ts.map +1 -0
- package/dist/rules/token/single-trailing-newline.js +25 -0
- package/dist/rules/token/single-trailing-newline.js.map +1 -0
- package/dist/rules/token/strong-style.d.ts +3 -0
- package/dist/rules/token/strong-style.d.ts.map +1 -0
- package/dist/rules/token/strong-style.js +51 -0
- package/dist/rules/token/strong-style.js.map +1 -0
- package/dist/rules/token/table-column-count.d.ts +3 -0
- package/dist/rules/token/table-column-count.d.ts.map +1 -0
- package/dist/rules/token/table-column-count.js +44 -0
- package/dist/rules/token/table-column-count.js.map +1 -0
- package/dist/rules/token/table-column-style.d.ts +3 -0
- package/dist/rules/token/table-column-style.d.ts.map +1 -0
- package/dist/rules/token/table-column-style.js +179 -0
- package/dist/rules/token/table-column-style.js.map +1 -0
- package/dist/rules/token/table-pipe-style.d.ts +3 -0
- package/dist/rules/token/table-pipe-style.d.ts.map +1 -0
- package/dist/rules/token/table-pipe-style.js +54 -0
- package/dist/rules/token/table-pipe-style.js.map +1 -0
- package/dist/rules/token/ul-indent.d.ts +3 -0
- package/dist/rules/token/ul-indent.d.ts.map +1 -0
- package/dist/rules/token/ul-indent.js +72 -0
- package/dist/rules/token/ul-indent.js.map +1 -0
- package/dist/rules/token/ul-style.d.ts +3 -0
- package/dist/rules/token/ul-style.d.ts.map +1 -0
- package/dist/rules/token/ul-style.js +80 -0
- package/dist/rules/token/ul-style.js.map +1 -0
- package/dist/rules/types.d.ts +81 -0
- package/dist/rules/types.d.ts.map +1 -0
- package/dist/rules/types.js +2 -0
- package/dist/rules/types.js.map +1 -0
- package/dist/rules/utils.d.ts +29 -0
- package/dist/rules/utils.d.ts.map +1 -0
- package/dist/{assertions → rules}/utils.js +27 -0
- package/dist/rules/utils.js.map +1 -0
- package/dist/scopes/extractor.d.ts +7 -0
- package/dist/scopes/extractor.d.ts.map +1 -0
- package/dist/scopes/extractor.js +475 -0
- package/dist/scopes/extractor.js.map +1 -0
- package/dist/scopes/selector.d.ts +51 -0
- package/dist/scopes/selector.d.ts.map +1 -0
- package/dist/scopes/selector.js +121 -0
- package/dist/scopes/selector.js.map +1 -0
- package/dist/scopes/sentences.d.ts +15 -0
- package/dist/scopes/sentences.d.ts.map +1 -0
- package/dist/scopes/sentences.js +124 -0
- package/dist/scopes/sentences.js.map +1 -0
- package/dist/scopes/types.d.ts +44 -0
- package/dist/scopes/types.d.ts.map +1 -0
- package/dist/scopes/types.js +2 -0
- package/dist/scopes/types.js.map +1 -0
- package/dist/scopes/vocabulary.d.ts +16 -0
- package/dist/scopes/vocabulary.d.ts.map +1 -0
- package/dist/scopes/vocabulary.js +70 -0
- package/dist/scopes/vocabulary.js.map +1 -0
- package/dist/types/assertions.d.ts +95 -32
- package/dist/types/assertions.d.ts.map +1 -1
- package/dist/types/problems.d.ts +4 -5
- package/dist/types/problems.d.ts.map +1 -1
- package/dist/types/rules.d.ts +4 -6
- package/dist/types/rules.d.ts.map +1 -1
- package/examples/appendices/google.appendix.yaml +91 -0
- package/examples/appendices/inclusive-language.appendix.yaml +61 -0
- package/examples/appendices/microsoft.appendix.yaml +99 -0
- package/examples/appendices/plain-language.appendix.yaml +88 -0
- package/examples/google.yaml +1525 -0
- package/examples/inclusive-language.yaml +304 -0
- package/examples/microsoft.yaml +1542 -0
- package/examples/plain-language.yaml +325 -0
- package/package.json +49 -16
- package/presets/google/PROVENANCE.md +1022 -0
- package/presets/google/sources.json +192 -0
- package/presets/inclusive-language/PROVENANCE.md +174 -0
- package/presets/inclusive-language/sources.json +107 -0
- package/presets/microsoft/PROVENANCE.md +1555 -0
- package/presets/microsoft/sources.json +494 -0
- package/presets/plain-language/PROVENANCE.md +364 -0
- package/presets/plain-language/sources.json +108 -0
- package/dist/assertions/bullet-style.d.ts +0 -3
- package/dist/assertions/bullet-style.d.ts.map +0 -1
- package/dist/assertions/bullet-style.js +0 -60
- package/dist/assertions/bullet-style.js.map +0 -1
- package/dist/assertions/index.d.ts +0 -21
- package/dist/assertions/index.d.ts.map +0 -1
- package/dist/assertions/index.js +0 -30
- package/dist/assertions/index.js.map +0 -1
- package/dist/assertions/max-image-size.d.ts +0 -3
- package/dist/assertions/max-image-size.d.ts.map +0 -1
- package/dist/assertions/max-image-size.js +0 -73
- package/dist/assertions/max-image-size.js.map +0 -1
- package/dist/assertions/max-line-length.d.ts +0 -3
- package/dist/assertions/max-line-length.d.ts.map +0 -1
- package/dist/assertions/max-line-length.js +0 -68
- package/dist/assertions/max-line-length.js.map +0 -1
- package/dist/assertions/no-broken-fragment-links.d.ts +0 -3
- package/dist/assertions/no-broken-fragment-links.d.ts.map +0 -1
- package/dist/assertions/no-broken-fragment-links.js +0 -79
- package/dist/assertions/no-broken-fragment-links.js.map +0 -1
- package/dist/assertions/no-duplicate-headings.d.ts +0 -3
- package/dist/assertions/no-duplicate-headings.d.ts.map +0 -1
- package/dist/assertions/no-duplicate-headings.js +0 -66
- package/dist/assertions/no-duplicate-headings.js.map +0 -1
- package/dist/assertions/no-hard-tabs.d.ts +0 -3
- package/dist/assertions/no-hard-tabs.d.ts.map +0 -1
- package/dist/assertions/no-hard-tabs.js +0 -63
- package/dist/assertions/no-hard-tabs.js.map +0 -1
- package/dist/assertions/no-trailing-spaces.d.ts +0 -3
- package/dist/assertions/no-trailing-spaces.d.ts.map +0 -1
- package/dist/assertions/no-trailing-spaces.js +0 -72
- package/dist/assertions/no-trailing-spaces.js.map +0 -1
- package/dist/assertions/pattern.d.ts +0 -3
- package/dist/assertions/pattern.d.ts.map +0 -1
- package/dist/assertions/pattern.js +0 -39
- package/dist/assertions/pattern.js.map +0 -1
- package/dist/assertions/semantic-line-breaks.d.ts +0 -3
- package/dist/assertions/semantic-line-breaks.d.ts.map +0 -1
- package/dist/assertions/semantic-line-breaks.js +0 -152
- package/dist/assertions/semantic-line-breaks.js.map +0 -1
- package/dist/assertions/swap.d.ts +0 -3
- package/dist/assertions/swap.d.ts.map +0 -1
- package/dist/assertions/swap.js +0 -39
- package/dist/assertions/swap.js.map +0 -1
- package/dist/assertions/utils.d.ts +0 -8
- package/dist/assertions/utils.d.ts.map +0 -1
- package/dist/assertions/utils.js.map +0 -1
- package/dist/core/scope-parser.d.ts +0 -26
- package/dist/core/scope-parser.d.ts.map +0 -1
- package/dist/core/scope-parser.js +0 -110
- package/dist/core/scope-parser.js.map +0 -1
- package/dist/files.d.ts +0 -2
- package/dist/files.d.ts.map +0 -1
- package/dist/files.js +0 -39
- package/dist/files.js.map +0 -1
- package/dist/load-config.d.ts +0 -25
- package/dist/load-config.d.ts.map +0 -1
- package/dist/load-config.js +0 -104
- package/dist/load-config.js.map +0 -1
- package/dist/load.d.ts +0 -25
- package/dist/load.d.ts.map +0 -1
- package/dist/load.js +0 -112
- package/dist/load.js.map +0 -1
- package/dist/scope.d.ts +0 -26
- package/dist/scope.d.ts.map +0 -1
- package/dist/scope.js +0 -110
- package/dist/scope.js.map +0 -1
- package/dist/types.d.ts +0 -109
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/validate.d.ts +0 -31
- package/dist/validate.d.ts.map +0 -1
- package/dist/validate.js +0 -154
- package/dist/validate.js.map +0 -1
- /package/dist/{types.js → parser/types.js} +0 -0
|
@@ -0,0 +1,2268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `recheck/microsoft` — the Microsoft Writing Style Guide
|
|
3
|
+
* (https://learn.microsoft.com/en-us/style-guide/welcome/), adapted to
|
|
4
|
+
* Recheck assertions.
|
|
5
|
+
*
|
|
6
|
+
* Source: Microsoft Writing Style Guide
|
|
7
|
+
* Canonical URL: https://learn.microsoft.com/en-us/style-guide/welcome/
|
|
8
|
+
* Upstream status: **archived** as of 2024-11-13 (Microsoft stopped actively
|
|
9
|
+
* updating the guide on that date; the content remains published and is
|
|
10
|
+
* still the guide Microsoft itself links to). The pages continue to receive
|
|
11
|
+
* occasional copyedits after that date (see sources.json fetch dates), so
|
|
12
|
+
* "archived" describes editorial status, not staleness of the text quoted
|
|
13
|
+
* here.
|
|
14
|
+
* License: CC BY 4.0 (https://creativecommons.org/licenses/by/4.0/) — see
|
|
15
|
+
* the licence note below; the grant is NOT stated on any learn.microsoft.com
|
|
16
|
+
* page.
|
|
17
|
+
* Sync date: 2026-07-30.
|
|
18
|
+
*
|
|
19
|
+
* LICENCE, READ BEFORE COPYING A LINK FROM THIS FILE: no `learn.microsoft.com`
|
|
20
|
+
* page states a licence anywhere (verified: zero hits for "creative commons"/
|
|
21
|
+
* "cc-by"/"licensed under" across every fetched page; the only copyright text
|
|
22
|
+
* on the site is a sitewide "(c) Microsoft. All rights reserved." footer).
|
|
23
|
+
* The CC-BY-4.0 grant lives one hop away, in the backing GitHub repository
|
|
24
|
+
* every style-guide page's `content_git_url` metadata points at:
|
|
25
|
+
* https://github.com/MicrosoftDocs/microsoft-style-guide/blob/main/LICENSE
|
|
26
|
+
* (confirmed via GitHub's license-detection API and the raw LICENSE file
|
|
27
|
+
* itself — both independently agree: CC BY 4.0). The site's Terms of Use
|
|
28
|
+
* (https://learn.microsoft.com/en-us/legal/termsofuse) explicitly defers to
|
|
29
|
+
* this: "Certain documentation may be subject to explicit license terms
|
|
30
|
+
* separate from the terms contained here. To the extent the terms conflict,
|
|
31
|
+
* the explicit license terms control." So: CC-BY-4.0 attribution is correct
|
|
32
|
+
* to ship, but every `link:` below points at the learn.microsoft.com page the
|
|
33
|
+
* rule's TEXT comes from (per spec §3), never at a page claiming to state the
|
|
34
|
+
* licence — because none does. See PROVENANCE.md's licence section for the
|
|
35
|
+
* full citation chain.
|
|
36
|
+
*
|
|
37
|
+
* PROVENANCE DISCIPLINE (why this file looks the way it does): every rule
|
|
38
|
+
* below was confirmed against a LIVE fetch of its cited page by one of four
|
|
39
|
+
* independent verification passes (task-10-verify-{E,F,G,H}.md) — ~490
|
|
40
|
+
* rules/entries checked across ~340 page fetches. The word list produced 7
|
|
41
|
+
* contradictions, 1 fabrication, 3 crossed pairings, and 1 inverted verdict
|
|
42
|
+
* in the research draft this preset's candidate list started from; none of
|
|
43
|
+
* those defects are shipped here. See `packages/recheck/presets/microsoft/
|
|
44
|
+
* PROVENANCE.md` for the full rule -> source page -> quote -> verdict table,
|
|
45
|
+
* including every candidate rule considered and NOT shipped, and why, and
|
|
46
|
+
* `packages/recheck/presets/microsoft/sources.json` for the fetched-page
|
|
47
|
+
* hashes drift detection needs.
|
|
48
|
+
*
|
|
49
|
+
* SEVERITY POLICY: structural/document-mechanics rules (heading, list, table,
|
|
50
|
+
* alt-text, link-text mechanics) are `error`, matching `recheck/google`'s
|
|
51
|
+
* convention. The A-Z word list's three "unconditional" tiers (Tier 1 general
|
|
52
|
+
* terminology, Tier 2 accessibility/people-first language, Tier 3 spelling
|
|
53
|
+
* and hyphenation normalization) also ship at `error`, per the research's own
|
|
54
|
+
* "ship at error" framing for those tiers (§5a/§5b/§5c) — these are
|
|
55
|
+
* "never use" rules, not softer style preferences, and every verifier
|
|
56
|
+
* confirmed them as unconditional (once Tier 4's audience-conditional
|
|
57
|
+
* entries are removed — see below). Everything else — voice, contractions,
|
|
58
|
+
* punctuation conventions, UI-verb terminology, and any rule whose detection
|
|
59
|
+
* mechanism is a narrowed heuristic rather than a complete test of the
|
|
60
|
+
* guide's stated rule — is `warn`.
|
|
61
|
+
*
|
|
62
|
+
* DETECTION-ONLY BY DESIGN: this preset never auto-fixes any rule, full
|
|
63
|
+
* stop. `buildMicrosoftPreset()` forces `fix: false` onto every rule it
|
|
64
|
+
* returns, structurally, regardless of what an individual rule's own builder
|
|
65
|
+
* call sets — see the loop at the end of that function. Adversarial testing
|
|
66
|
+
* of auto-fix on this preset's (and `recheck/google`'s) swap pairs found
|
|
67
|
+
* corruption spanning every category once assumed safe — including
|
|
68
|
+
* spelling and hyphenation — under the "same word normalized" criterion
|
|
69
|
+
* described below. Concrete failures: Hemingway's *A Moveable Feast* → "A
|
|
70
|
+
* Movable Feast" (spelling), "read only the introduction" → "read-only the
|
|
71
|
+
* introduction" (hyphenation), "No SQL is used here" → "NoSQL is used here"
|
|
72
|
+
* (meaning inverted outright). A rule's category does not predict
|
|
73
|
+
* fix-safety at this scale — a style guide describes intent,
|
|
74
|
+
* `swap`/`consistency`/`pattern` match tokens, and that gap does not close
|
|
75
|
+
* by further narrowing which categories are "safe." Detection is
|
|
76
|
+
* unaffected and is this preset's entire product: every rule still runs
|
|
77
|
+
* `execute()` and reports; only `fix()` is gated off. See
|
|
78
|
+
* `presets/microsoft/PROVENANCE.md`'s "Detection-only" section for the full
|
|
79
|
+
* corruption examples, and `preset-microsoft.test.ts`'s preset-derived "no
|
|
80
|
+
* rule is fixable" test for the permanent guarantee.
|
|
81
|
+
*
|
|
82
|
+
* The reasoning from "FIX SAFETY, READ BEFORE ADDING A PAIR" through
|
|
83
|
+
* "FIX-POSTURE" below remains accurate even though the blanket override
|
|
84
|
+
* above makes it redundant in practice: it records which pairs
|
|
85
|
+
* are same-word normalizations versus different-word substitutions, and
|
|
86
|
+
* which carry a case-only or verb-able hazard — the criteria that would
|
|
87
|
+
* matter again if this preset's auto-fix were ever reconsidered. As of the
|
|
88
|
+
* override above, no pair in this file is currently fixable.
|
|
89
|
+
*
|
|
90
|
+
* FIX SAFETY, READ BEFORE ADDING A PAIR: three hazards are handled per pair
|
|
91
|
+
* below rather than by a single blanket rule:
|
|
92
|
+
* - CASE-ONLY (`fix: false`): a pair whose replacement differs from the
|
|
93
|
+
* avoid-term ONLY by case (Web -> web, Registry -> registry, ...).
|
|
94
|
+
* `applyMatchCase` (src/core/case-preserve.ts) reapplies the MATCH's
|
|
95
|
+
* casing to the replacement, so fixing "Web" reproduces "Web" — a
|
|
96
|
+
* silent, permanent no-op. `az-case-only` below.
|
|
97
|
+
* - VERB-ABLE (`fix: false`, message says "rewrite" not "replace"): a pair
|
|
98
|
+
* whose avoid-term can be used as a verb but whose replacement is a noun
|
|
99
|
+
* phrase (whitelist an IP -> allow list an IP is ungrammatical).
|
|
100
|
+
* `az-verb-able` below.
|
|
101
|
+
* - SUBSTRING/HOMOGRAPH risk: bare single letters/punctuation (x, +),
|
|
102
|
+
* hyphen-joined compounds that share a bare token with the avoid-term
|
|
103
|
+
* (double-click, x-axis), and whole-word homographs with an unrelated
|
|
104
|
+
* common meaning (may/May the month, that, star) are excluded from this
|
|
105
|
+
* preset entirely rather than shipped with a marginal regex guard — see
|
|
106
|
+
* PROVENANCE.md's excluded-candidates table.
|
|
107
|
+
* `applyMatchCase` itself is out of scope for this task (shared by every
|
|
108
|
+
* swap rule across every preset); its documented limitations are recorded in
|
|
109
|
+
* the whole-branch review, not patched here.
|
|
110
|
+
*
|
|
111
|
+
* DEVELOPER-AUDIENCE CARVE-OUTS: `header`, `context menu`, `disk`, and
|
|
112
|
+
* `directory` all carry a Microsoft-documented exception for developer/API
|
|
113
|
+
* content — precisely Redocly's own audience. None of the four ship as
|
|
114
|
+
* unconditional Tier 1 rules; running this preset on Redocly's own docs must
|
|
115
|
+
* not flag "response header", "context menu (developer content)", "managed
|
|
116
|
+
* disk", or "working directory". See PROVENANCE.md §5.
|
|
117
|
+
*
|
|
118
|
+
* TIER 4 (audience-conditional / UI-conditional) NEVER SHIPS. Five Tier-1
|
|
119
|
+
* candidates in the original research draft also appeared, verbatim, on
|
|
120
|
+
* Tier 4's own conditional list (`execute`, `reboot`, `navigate`, `ZIP Code`,
|
|
121
|
+
* `disjoint selection`); five more were found to carry the identical
|
|
122
|
+
* carve-out pattern on closer reading (`shortcut key`, `radio button`,
|
|
123
|
+
* `scroll`, the x-multiplication UI exception, `Microsoft's`). None of the
|
|
124
|
+
* ten ship. Additional conditional entries surfaced during authoring (not
|
|
125
|
+
* pre-identified by the verifiers, but sharing the exact same
|
|
126
|
+
* "unless...technical audience"/"unless it's in the UI" shape) are also
|
|
127
|
+
* excluded — see PROVENANCE.md's "Additional tier-boundary findings".
|
|
128
|
+
*
|
|
129
|
+
* SELF-CONTRADICTIONS: three cases where two live Microsoft pages give
|
|
130
|
+
* opposite guidance are enforced in NEITHER direction (no rule ships for
|
|
131
|
+
* either side): `%` vs. spelled-out "percent", `etc.`, and forced line
|
|
132
|
+
* breaks in paragraphs vs. the heading line-break exception. Both citations
|
|
133
|
+
* per case are in PROVENANCE.md.
|
|
134
|
+
*
|
|
135
|
+
* `master/slave` is a fourth self-contradiction case, but unlike the three
|
|
136
|
+
* above it does not ship silently permitted: both pages agree the TERM
|
|
137
|
+
* should be avoided and disagree only on the REPLACEMENT, so
|
|
138
|
+
* `microsoft/master-slave` ships as a `pattern` rule (no swap) naming both
|
|
139
|
+
* candidates in its message rather than guessing which one the guide
|
|
140
|
+
* intends. See PROVENANCE.md's "Self-contradictions" section.
|
|
141
|
+
*
|
|
142
|
+
* NO `metric` RULE: Microsoft's style guide publishes no readability
|
|
143
|
+
* formula or grade-level target anywhere — the 7-8 grade / 60-70 Flesch
|
|
144
|
+
* figures some contributors cite come from Word's Editor feature Q&A pages,
|
|
145
|
+
* not the Writing Style Guide. Shipping a `metric` rule under a Microsoft
|
|
146
|
+
* citation would misattribute a number the guide never states. This preset
|
|
147
|
+
* instead ships the guide's own real structural numbers via `length`,
|
|
148
|
+
* `list-length`, and `occurrence` — see the "Structural numbers" section.
|
|
149
|
+
*
|
|
150
|
+
* TIER-1 GATE COVERAGE: the clean fixture and the per-token gate both now
|
|
151
|
+
* cover A-Z near-miss cases and read the live preset directly (see
|
|
152
|
+
* `preset-microsoft.test.ts`) — every pair those gates can expose is either
|
|
153
|
+
* anchored against its colliding sense (`exit`/`launch`/`boot`/`hang`/
|
|
154
|
+
* `hangs`/`roman`/`blade`/`beta`/`visit`/`in addition`/`print out`/`click`/
|
|
155
|
+
* `clicks`), moved to a detection-only `*-detect` pattern sibling
|
|
156
|
+
* (`crash`/`lock up`, `quit`/`deinstall`/`reinitialize`, `italics`/
|
|
157
|
+
* `italicized`, `bottom left`/`bottom right`, `thank you`,
|
|
158
|
+
* `hierarchical menu`/`secondary menu`/`running head`/`running foot`,
|
|
159
|
+
* `pound sign`, `as well as`, `or greater`/`or higher`/`or lower`, `visit`),
|
|
160
|
+
* given `fix: false` (`left-hand`/`right-hand`, `leverage`/`leveraging`/
|
|
161
|
+
* `leveraged`, `glyph`), or dropped where the guide's own carve-out is a
|
|
162
|
+
* genuine meaning change in Redocly's own domain (`SMB`, `SKU`, `terminate`,
|
|
163
|
+
* `de facto`/`ad hoc`/`vis-a-vis`, no replacement stated for any of the
|
|
164
|
+
* three). `us-spelling`'s keys are one literal pair per inflection (not an
|
|
165
|
+
* alternation group mapped to a single replacement), so every inflection
|
|
166
|
+
* maps to its own correct output instead of collapsing onto whichever form
|
|
167
|
+
* the config happened to name. A same-to-same self-mapping defect
|
|
168
|
+
* (`'a SQL': 'a SQL'`, flagging already-correct text) is also fixed in
|
|
169
|
+
* `article-before-acronym`. See PROVENANCE.md's "Fix wave B"/"Fix wave C"
|
|
170
|
+
* sections for the complete per-pair disposition and reasoning, and each
|
|
171
|
+
* rule's own comment below for the specific anchor/exclusion it carries.
|
|
172
|
+
*
|
|
173
|
+
* FIX-POSTURE: a `swap` pair keeps `fix: true` only if it is the SAME WORD
|
|
174
|
+
* normalized (spelling, hyphenation, casing, or a non-standard form) —
|
|
175
|
+
* never a different word or phrase substituted, however safe-looking.
|
|
176
|
+
* `DMZ`, `the ask`, `home directory`, and `spec` are also anchored against
|
|
177
|
+
* their most common unrelated sense so the clean fixture doesn't visibly
|
|
178
|
+
* misfire on them, even though detection-only rules can no longer corrupt
|
|
179
|
+
* anything. This criterion is now redundant with the blanket
|
|
180
|
+
* `fix: false` override above, but still explains the "same word
|
|
181
|
+
* normalized" vs. "different word/phrase substituted" reasoning attached
|
|
182
|
+
* to individual rules throughout this file. See PROVENANCE.md's
|
|
183
|
+
* "Fix-posture change" section for the two-axis rationale (guidance-shape
|
|
184
|
+
* vs. homograph) and the complete rule-by-rule table.
|
|
185
|
+
*/
|
|
186
|
+
// -- link constants (one per distinct source page cited below) -----------
|
|
187
|
+
const CAPITALIZATION = 'https://learn.microsoft.com/en-us/style-guide/capitalization';
|
|
188
|
+
const HEADINGS = 'https://learn.microsoft.com/en-us/style-guide/scannable-content/headings';
|
|
189
|
+
const COLONS = 'https://learn.microsoft.com/en-us/style-guide/punctuation/colons';
|
|
190
|
+
const VERSUS_VS = 'https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/v/versus-vs';
|
|
191
|
+
const ACCESSIBILITY_WRITING = 'https://learn.microsoft.com/en-us/style-guide/accessibility/writing-all-abilities';
|
|
192
|
+
const APOSTROPHES = 'https://learn.microsoft.com/en-us/style-guide/punctuation/apostrophes';
|
|
193
|
+
const ACRONYMS = 'https://learn.microsoft.com/en-us/style-guide/acronyms';
|
|
194
|
+
const LISTS = 'https://learn.microsoft.com/en-us/style-guide/scannable-content/lists';
|
|
195
|
+
const TABLES = 'https://learn.microsoft.com/en-us/style-guide/scannable-content/tables';
|
|
196
|
+
const PERIODS = 'https://learn.microsoft.com/en-us/style-guide/punctuation/periods';
|
|
197
|
+
const TOP_10_TIPS = 'https://learn.microsoft.com/en-us/style-guide/top-10-tips-style-voice';
|
|
198
|
+
const DASHES_HYPHENS = 'https://learn.microsoft.com/en-us/style-guide/punctuation/dashes-hyphens/';
|
|
199
|
+
const NUMBERS = 'https://learn.microsoft.com/en-us/style-guide/numbers';
|
|
200
|
+
const QUOTATION_MARKS = 'https://learn.microsoft.com/en-us/style-guide/punctuation/quotation-marks';
|
|
201
|
+
const ALTERNATIVE_TEXT = 'https://learn.microsoft.com/en-us/style-guide/accessibility/alternative-text';
|
|
202
|
+
const URLS_WEB_ADDRESSES = 'https://learn.microsoft.com/en-us/style-guide/urls-web-addresses';
|
|
203
|
+
const USE_CONTRACTIONS = 'https://learn.microsoft.com/en-us/style-guide/word-choice/use-contractions';
|
|
204
|
+
const USE_US_SPELLING = 'https://learn.microsoft.com/en-us/style-guide/word-choice/use-us-spelling-avoid-non-english-words';
|
|
205
|
+
const USE_SIMPLE_WORDS = 'https://learn.microsoft.com/en-us/style-guide/word-choice/use-simple-words-concise-sentences';
|
|
206
|
+
const DONT_USE_COMMON_WORDS = 'https://learn.microsoft.com/en-us/style-guide/word-choice/dont-use-common-words-in-new-ways';
|
|
207
|
+
const AVOID_JARGON = 'https://learn.microsoft.com/en-us/style-guide/word-choice/avoid-jargon';
|
|
208
|
+
const BIAS_FREE = 'https://learn.microsoft.com/en-us/style-guide/bias-free-communication';
|
|
209
|
+
const MILITARISTIC_LANGUAGE = 'https://learn.microsoft.com/en-us/style-guide/militaristic-language';
|
|
210
|
+
const ACCESSIBILITY_TERMS = 'https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/accessibility-terms';
|
|
211
|
+
const AZ_BASE = 'https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/';
|
|
212
|
+
const DESCRIBING_UI = 'https://learn.microsoft.com/en-us/style-guide/procedures-instructions/describing-interactions-with-ui';
|
|
213
|
+
const FORMATTING_TEXT_IN_INSTRUCTIONS = 'https://learn.microsoft.com/en-us/style-guide/procedures-instructions/formatting-text-in-instructions';
|
|
214
|
+
const MASTER_SLAVE = 'https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/m/master-slave';
|
|
215
|
+
// -- small builders, mirroring recheck/google's identical shape (see
|
|
216
|
+
// google.ts) so the two flagship presets stay structurally interchangeable
|
|
217
|
+
// for anyone reading both. ----------------------------------------------
|
|
218
|
+
function swapRule(opts) {
|
|
219
|
+
return {
|
|
220
|
+
severity: opts.severity ?? 'warn',
|
|
221
|
+
scope: opts.scope ?? 'summary',
|
|
222
|
+
link: opts.link,
|
|
223
|
+
message: opts.message,
|
|
224
|
+
...(opts.fix === false ? { fix: false } : {}),
|
|
225
|
+
assertions: {
|
|
226
|
+
swap: {
|
|
227
|
+
pairs: opts.pairs,
|
|
228
|
+
ignoreCase: opts.ignoreCase,
|
|
229
|
+
wordBoundary: opts.wordBoundary,
|
|
230
|
+
keysAreRegex: opts.keysAreRegex,
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function patternRule(opts) {
|
|
236
|
+
return {
|
|
237
|
+
severity: opts.severity ?? 'warn',
|
|
238
|
+
scope: opts.scope ?? 'summary',
|
|
239
|
+
link: opts.link,
|
|
240
|
+
message: opts.message,
|
|
241
|
+
assertions: {
|
|
242
|
+
pattern: {
|
|
243
|
+
tokens: opts.tokens,
|
|
244
|
+
ignoreCase: opts.ignoreCase,
|
|
245
|
+
includeCode: opts.includeCode,
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function tokenRule(opts) {
|
|
251
|
+
return {
|
|
252
|
+
severity: opts.severity ?? 'error',
|
|
253
|
+
link: opts.link,
|
|
254
|
+
message: opts.message,
|
|
255
|
+
assertions: { [opts.name]: opts.options ?? {} },
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
export function buildMicrosoftPreset() {
|
|
259
|
+
const rules = {};
|
|
260
|
+
// ======================================================================
|
|
261
|
+
// STRUCTURAL — headings, lists, tables, alt text, links (`error`).
|
|
262
|
+
// ======================================================================
|
|
263
|
+
// "Microsoft style uses sentence-style capitalization." (C1)
|
|
264
|
+
// `fix: false`: same reasoning as recheck/google's identical rule — an
|
|
265
|
+
// autofix would lowercase any proper noun not covered by the built-in
|
|
266
|
+
// TECHNICAL_PROPER_NOUNS vocabulary or a user's own `exceptions`.
|
|
267
|
+
rules['microsoft/heading-sentence-case'] = {
|
|
268
|
+
severity: 'error',
|
|
269
|
+
scope: 'heading',
|
|
270
|
+
fix: false,
|
|
271
|
+
link: CAPITALIZATION,
|
|
272
|
+
message: '"%s" should use %s capitalization (Microsoft: sentence case for headings).',
|
|
273
|
+
assertions: { capitalization: { match: '$sentence' } },
|
|
274
|
+
};
|
|
275
|
+
// "Always capitalize the word after the colon." (C2) — scoped to headings
|
|
276
|
+
// only; Microsoft's colon rule is title/heading-specific, unlike the
|
|
277
|
+
// general mid-sentence-colon rule (which this preset does not ship: the
|
|
278
|
+
// guide's own table shows the mid-sentence form as merely "Acceptable",
|
|
279
|
+
// not mandatory — see PROVENANCE.md).
|
|
280
|
+
rules['microsoft/capitalize-after-heading-colon'] = patternRule({
|
|
281
|
+
tokens: [':\\s+[a-z]'],
|
|
282
|
+
message: 'Capitalize the first word after a colon in a heading (Microsoft).',
|
|
283
|
+
link: COLONS,
|
|
284
|
+
scope: 'heading',
|
|
285
|
+
});
|
|
286
|
+
// "Don't end headings with a period." / "Don't use a colon at the end of
|
|
287
|
+
// titles or headings." (C3, C4). Default `punctuation` (`.,;:` with the
|
|
288
|
+
// question mark stripped) already matches Microsoft's own stated
|
|
289
|
+
// exceptions (`?` allowed, `!` rarely) with no override needed.
|
|
290
|
+
rules['microsoft/no-trailing-punctuation'] = tokenRule({
|
|
291
|
+
name: 'no-trailing-punctuation',
|
|
292
|
+
message: "Don't end headings with punctuation (Microsoft).",
|
|
293
|
+
link: HEADINGS,
|
|
294
|
+
});
|
|
295
|
+
// "Don't use ampersands (&) or plus signs (+) in headings unless you're
|
|
296
|
+
// referring to UI that contains them or space is limited." (C5)
|
|
297
|
+
rules['microsoft/no-ampersand-in-headings'] = {
|
|
298
|
+
severity: 'warn',
|
|
299
|
+
scope: 'heading',
|
|
300
|
+
link: HEADINGS,
|
|
301
|
+
message: 'Spell out "and"; avoid & and + in headings (Microsoft).',
|
|
302
|
+
exceptions: { lines: ['C++', 'A+', '.NET'] },
|
|
303
|
+
assertions: {
|
|
304
|
+
pattern: { tokens: ['&(?!amp;|nbsp;|lt;|gt;|quot;|#)', '\\+'] },
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
// "In headings, use the abbreviation vs., all lowercase. In text, spell
|
|
308
|
+
// out as versus." (C6) — two scoped rules with opposite directions; a
|
|
309
|
+
// single unscoped rule would fight itself.
|
|
310
|
+
rules['microsoft/vs-in-headings'] = swapRule({
|
|
311
|
+
pairs: { versus: 'vs.' },
|
|
312
|
+
message: 'Use "%s" instead of "%s" in headings (Microsoft).',
|
|
313
|
+
link: VERSUS_VS,
|
|
314
|
+
scope: 'heading',
|
|
315
|
+
ignoreCase: true,
|
|
316
|
+
wordBoundary: true,
|
|
317
|
+
});
|
|
318
|
+
// `vs.` ends in a period, so a trailing `\b` right after "vs." followed by
|
|
319
|
+
// a space never matches (both non-word characters) -- same class of bug
|
|
320
|
+
// recheck/google's `no-latinisms` fixed for `i.e.`/`e.g.`/`vs.`. A LEADING
|
|
321
|
+
// `\b` is baked into the regex source instead (`keysAreRegex`), which
|
|
322
|
+
// still blocks a match inside "revs." (no boundary between "re" and "vs.")
|
|
323
|
+
// without needing the trailing anchor at all.
|
|
324
|
+
rules['microsoft/versus-in-text'] = swapRule({
|
|
325
|
+
pairs: { '\\bvs\\.': 'versus' },
|
|
326
|
+
message: 'Use "%s" instead of "%s" in body text (Microsoft).',
|
|
327
|
+
link: VERSUS_VS,
|
|
328
|
+
scope: ['paragraph', 'list-item', 'table.cell'],
|
|
329
|
+
ignoreCase: true,
|
|
330
|
+
wordBoundary: false,
|
|
331
|
+
keysAreRegex: true,
|
|
332
|
+
});
|
|
333
|
+
// "Don't use extra line breaks to increase heading spacing." (C8)
|
|
334
|
+
rules['microsoft/no-multiple-blanks'] = tokenRule({
|
|
335
|
+
name: 'no-multiple-blanks',
|
|
336
|
+
message: "Don't use extra blank lines to create heading spacing (Microsoft).",
|
|
337
|
+
link: HEADINGS,
|
|
338
|
+
});
|
|
339
|
+
// "Use heading levels instead of text formatting to communicate...
|
|
340
|
+
// hierarchy." (C9)
|
|
341
|
+
rules['microsoft/no-emphasis-as-heading'] = tokenRule({
|
|
342
|
+
name: 'no-emphasis-as-heading',
|
|
343
|
+
message: 'Use a heading level, not bold or italic text, to show hierarchy (Microsoft).',
|
|
344
|
+
link: ACCESSIBILITY_WRITING,
|
|
345
|
+
});
|
|
346
|
+
// "Don't use an apostrophe... to form the plural of a singular noun."
|
|
347
|
+
// (C17) — scoped to the unambiguous decade case (`1990's`); a bare
|
|
348
|
+
// `[A-Z]{2,}'s` (e.g. `API's`) is excluded on purpose: it is frequently a
|
|
349
|
+
// legitimate possessive ("the API's response"), not an attempted plural
|
|
350
|
+
// (see PROVENANCE.md's excluded-candidates table for C18/C20).
|
|
351
|
+
rules['microsoft/no-apostrophe-plural-decade'] = patternRule({
|
|
352
|
+
tokens: ["\\b(?:19|20)\\d0['\u2019]s\\b"],
|
|
353
|
+
message: 'Don\'t use an apostrophe to form a plural decade (Microsoft): "%s"',
|
|
354
|
+
link: APOSTROPHES,
|
|
355
|
+
});
|
|
356
|
+
// "a DLL / an ISP / a URL / a SQL database" (C19) — article choice
|
|
357
|
+
// follows pronunciation, not spelling. `applyMatchCase` handles a
|
|
358
|
+
// sentence-initial capitalized match correctly here (multi-word
|
|
359
|
+
// replacement, "a"/"an" + acronym): verified "An URL" -> "A URL".
|
|
360
|
+
//
|
|
361
|
+
// A same-to-same self-mapping (`'a SQL': 'a SQL'`) would be a defect
|
|
362
|
+
// here, not a wrong-to-right correction like its three siblings: since
|
|
363
|
+
// `swap`'s `execute()` reports every regex match as a violation
|
|
364
|
+
// regardless of whether match equals replacement, that would flag the
|
|
365
|
+
// ALREADY-CORRECT "a SQL" (e.g. "Write a SQL query") as if it were
|
|
366
|
+
// wrong, and `--fix` would reproduce identical text (a permanent, silent
|
|
367
|
+
// no-op on correct prose — a false-positive DETECTION, not a
|
|
368
|
+
// corruption). The pair below is `'an SQL': 'a SQL'` instead, mirroring
|
|
369
|
+
// `'an SQL database'`'s own pronunciation rule for the bare acronym
|
|
370
|
+
// without a following noun ("Write an SQL query" -> "Write a SQL
|
|
371
|
+
// query").
|
|
372
|
+
rules['microsoft/article-before-acronym'] = swapRule({
|
|
373
|
+
pairs: {
|
|
374
|
+
'an URL': 'a URL',
|
|
375
|
+
'a ISP': 'an ISP',
|
|
376
|
+
'an SQL database': 'a SQL database',
|
|
377
|
+
'an SQL': 'a SQL',
|
|
378
|
+
},
|
|
379
|
+
message: 'Use "%s" instead of "%s" (Microsoft: article choice follows pronunciation).',
|
|
380
|
+
link: ACRONYMS,
|
|
381
|
+
ignoreCase: true,
|
|
382
|
+
wordBoundary: true,
|
|
383
|
+
});
|
|
384
|
+
// "Begin each item in a list with a capital letter unless there's a
|
|
385
|
+
// reason not to." (L1) Custom regex (not `$sentence`): `$sentence` would
|
|
386
|
+
// also lowercase every later word in the item, overshooting the guide's
|
|
387
|
+
// rule, which only constrains the FIRST letter. Detection-only by
|
|
388
|
+
// construction (custom-regex `capitalization` never produces a fix).
|
|
389
|
+
rules['microsoft/list-item-capital'] = {
|
|
390
|
+
severity: 'error',
|
|
391
|
+
scope: 'list-item',
|
|
392
|
+
fix: false,
|
|
393
|
+
link: LISTS,
|
|
394
|
+
message: '"%s" should start with a capital letter (Microsoft).',
|
|
395
|
+
assertions: { capitalization: { match: '^[^a-z].*' } },
|
|
396
|
+
};
|
|
397
|
+
// "Don't use semicolons, commas, or conjunctions (like and or or) at the
|
|
398
|
+
// end of list items." (L2)
|
|
399
|
+
rules['microsoft/no-trailing-conjunction-list'] = patternRule({
|
|
400
|
+
tokens: ['[,;]$', '\\b(?:and|or)$'],
|
|
401
|
+
message: 'Don\'t end a list item with a semicolon, comma, or conjunction (Microsoft): "%s"',
|
|
402
|
+
link: LISTS,
|
|
403
|
+
scope: 'list-item',
|
|
404
|
+
ignoreCase: true,
|
|
405
|
+
});
|
|
406
|
+
// "Don't use ellipses at the end of column headers." (T2)
|
|
407
|
+
rules['microsoft/no-ellipsis-column-header'] = patternRule({
|
|
408
|
+
tokens: ['(?:\\.\\.\\.|\u2026)$'],
|
|
409
|
+
message: 'Don\'t end a table column header with an ellipsis (Microsoft): "%s"',
|
|
410
|
+
link: TABLES,
|
|
411
|
+
scope: 'table.header',
|
|
412
|
+
});
|
|
413
|
+
// "Don't leave a cell blank or use an em dash [for 'no entry']. Instead,
|
|
414
|
+
// use Not applicable or None." (T3) — NARROWED, deliberately, to the em
|
|
415
|
+
// dash case only; the guide's other half ("don't leave a cell blank") is
|
|
416
|
+
// NOT enforced. This is a `pattern`-assertion architectural limit, not a
|
|
417
|
+
// missed narrowing pass: a truly blank cell (no `tableContent` descendant
|
|
418
|
+
// at all -- an empty `| |` cell, or one that's pure whitespace, which the
|
|
419
|
+
// GFM table parser trims to nothing) reaches this rule as a segment whose
|
|
420
|
+
// `content` is the empty string (see scopes/extractor.ts's `tableRow`
|
|
421
|
+
// case). Every `pattern` token runs through `regex.exec(content)`, and
|
|
422
|
+
// matching ANY token -- including this one -- against `''` can only ever
|
|
423
|
+
// produce a ZERO-WIDTH match; pattern.ts explicitly skips zero-width
|
|
424
|
+
// matches (`match[0].length === 0`) because they carry no real text to
|
|
425
|
+
// report or fix, the same guard `swap`/`conditional`/`repetition` use for
|
|
426
|
+
// the same reason. So a blank cell structurally cannot be reported by a
|
|
427
|
+
// `pattern` token, no matter how the token is written -- reporting "this
|
|
428
|
+
// segment is empty" is an EXISTENCE check ("flag when a pattern is
|
|
429
|
+
// absent"), not a pattern MATCH, and `validate.ts`'s `PatternAssertion`
|
|
430
|
+
// doc comment already notes that shape is a planned-but-not-yet-built
|
|
431
|
+
// Vale-parity feature. Properly detecting a blank cell needs either a
|
|
432
|
+
// token rule (walking the table's AST cells directly, the way
|
|
433
|
+
// `table-column-count`/`table-column-style` do) or an extractor-level
|
|
434
|
+
// change (e.g. emitting a sentinel for an empty cell so a pattern has
|
|
435
|
+
// literal text to match) -- out of scope for a `pattern`-only rule, so
|
|
436
|
+
// this rule instead does the one thing a `pattern` token CAN actually
|
|
437
|
+
// detect (a cell containing exactly an em dash, non-empty content) and
|
|
438
|
+
// says so honestly, rather than shipping a message/PROVENANCE claim
|
|
439
|
+
// ("covers a blank cell") the implementation cannot back up. A project
|
|
440
|
+
// that wants the blank-cell half enforced needs a custom check outside
|
|
441
|
+
// this preset until that feature lands.
|
|
442
|
+
//
|
|
443
|
+
// Token: the pattern is `^\s*(?:<em-dash>\s*)?$`, not the old
|
|
444
|
+
// `^\s*(?:<em-dash>)?\s*$` -- the two separate `\s*` groups either side of
|
|
445
|
+
// the optional em dash let the engine try many different ways to split a
|
|
446
|
+
// run of whitespace between them before settling on the overall match,
|
|
447
|
+
// which is quadratic on a long whitespace-only cell (632ms measured on a
|
|
448
|
+
// 32KB one). Folding the trailing `\s*` INSIDE the optional group removes
|
|
449
|
+
// that ambiguity: for whitespace-only content the leading `\s*` alone
|
|
450
|
+
// consumes everything, linearly, and the inner group is never re-tried
|
|
451
|
+
// against the same run.
|
|
452
|
+
rules['microsoft/no-blank-table-cell'] = patternRule({
|
|
453
|
+
tokens: ['^\\s*(?:\u2014\\s*)?$'],
|
|
454
|
+
message: 'Use "Not applicable" or "None" instead of an em dash in a table cell (Microsoft).',
|
|
455
|
+
link: TABLES,
|
|
456
|
+
scope: 'table.cell',
|
|
457
|
+
});
|
|
458
|
+
// "Put one space, not two, after a period." / (top-10-tips broadens this
|
|
459
|
+
// to periods, question marks, AND colons.) (P1)
|
|
460
|
+
rules['microsoft/single-space-after-punctuation'] = patternRule({
|
|
461
|
+
tokens: ['[.!?:]\\s{2,}(?=[A-Z])'],
|
|
462
|
+
message: 'Use one space, not two, after end punctuation (Microsoft).',
|
|
463
|
+
link: PERIODS,
|
|
464
|
+
});
|
|
465
|
+
// "Don't use spaces around em dashes." (P2) — narrowed to the EM dash
|
|
466
|
+
// only. The guide's en-dash rule has an explicit, worked exception for UI
|
|
467
|
+
// timestamps and dual date/time ranges ("2:15 PM \u2013 4:45 PM"); a blind
|
|
468
|
+
// regex can't tell that case apart from the ordinary prohibited case, so
|
|
469
|
+
// en dash spacing is not enforced here at all (see PROVENANCE.md).
|
|
470
|
+
rules['microsoft/no-space-around-em-dash'] = patternRule({
|
|
471
|
+
tokens: ['\\s\u2014\\s'],
|
|
472
|
+
message: "Don't use spaces around an em dash (Microsoft).",
|
|
473
|
+
link: DASHES_HYPHENS,
|
|
474
|
+
});
|
|
475
|
+
// "Don't use from before a range indicated by an en dash." (P5)
|
|
476
|
+
rules['microsoft/no-from-before-en-dash-range'] = patternRule({
|
|
477
|
+
tokens: ['\\bfrom\\s+\\d+\\s*[\u2013\u2014]\\s*\\d+'],
|
|
478
|
+
message: 'Don\'t use "from" before an en-dash number range (Microsoft): "%s"',
|
|
479
|
+
link: NUMBERS,
|
|
480
|
+
});
|
|
481
|
+
// "Use straight quotation marks... Segoe Sans... does not have a curly
|
|
482
|
+
// quotation mark option." (P7) Mechanical, unambiguous, autofixable.
|
|
483
|
+
rules['microsoft/straight-quotes'] = swapRule({
|
|
484
|
+
pairs: {
|
|
485
|
+
'\u201c': '"',
|
|
486
|
+
'\u201d': '"',
|
|
487
|
+
'\u2018': "'",
|
|
488
|
+
'\u2019': "'",
|
|
489
|
+
},
|
|
490
|
+
message: 'Use straight quotation marks, not curly ones (Microsoft).',
|
|
491
|
+
link: QUOTATION_MARKS,
|
|
492
|
+
});
|
|
493
|
+
// "Always spell out ordinal numbers." (N3)
|
|
494
|
+
rules['microsoft/spell-out-ordinals'] = patternRule({
|
|
495
|
+
tokens: ['\\b\\d+(?:st|nd|rd|th)\\b'],
|
|
496
|
+
message: 'Spell out ordinal numbers; avoid "%s" (Microsoft).',
|
|
497
|
+
link: NUMBERS,
|
|
498
|
+
});
|
|
499
|
+
// "Don't add -ly to an ordinal number, as in firstly or secondly." (N4)
|
|
500
|
+
rules['microsoft/ordinal-no-ly'] = swapRule({
|
|
501
|
+
pairs: { firstly: 'first', secondly: 'second', thirdly: 'third' },
|
|
502
|
+
message: 'Use "%s" instead of "%s" (Microsoft).',
|
|
503
|
+
link: NUMBERS,
|
|
504
|
+
ignoreCase: true,
|
|
505
|
+
wordBoundary: true,
|
|
506
|
+
});
|
|
507
|
+
// "Don't use numerals for 12:00. Use noon or midnight instead." (N8)
|
|
508
|
+
rules['microsoft/noon-midnight'] = patternRule({
|
|
509
|
+
tokens: ['\\b12:00\\s*(?:AM|PM|am|pm)\\b'],
|
|
510
|
+
message: 'Use "noon" or "midnight" instead of "%s" (Microsoft).',
|
|
511
|
+
link: NUMBERS,
|
|
512
|
+
});
|
|
513
|
+
// "Add alt text to all images that convey important meaning." (A1)
|
|
514
|
+
rules['microsoft/no-alt-text'] = tokenRule({
|
|
515
|
+
name: 'no-alt-text',
|
|
516
|
+
message: 'Every image needs alt text (Microsoft: accessibility).',
|
|
517
|
+
link: ALTERNATIVE_TEXT,
|
|
518
|
+
});
|
|
519
|
+
// "Limit the length to 150 characters." (A2) — one of this preset's four
|
|
520
|
+
// guide-stated structural numbers (see "Structural numbers" note above
|
|
521
|
+
// the rules).
|
|
522
|
+
rules['microsoft/alt-text-length'] = {
|
|
523
|
+
severity: 'warn',
|
|
524
|
+
scope: 'alt',
|
|
525
|
+
link: ALTERNATIVE_TEXT,
|
|
526
|
+
message: 'Alt text is %s %s long; Microsoft limits it to 150 characters (max %s).',
|
|
527
|
+
assertions: { length: { unit: 'characters', max: 150 } },
|
|
528
|
+
};
|
|
529
|
+
// "Begin alt text with a capital letter. End it with a period." (A3)
|
|
530
|
+
// Detection-only by construction (custom-regex `capitalization`).
|
|
531
|
+
// Limitation: the guide's own carve-out ("even if it's just a fragment,
|
|
532
|
+
// if doing so is practical for the image type") is not modeled — see
|
|
533
|
+
// PROVENANCE.md.
|
|
534
|
+
rules['microsoft/alt-text-format'] = {
|
|
535
|
+
severity: 'warn',
|
|
536
|
+
scope: 'alt',
|
|
537
|
+
fix: false,
|
|
538
|
+
link: ALTERNATIVE_TEXT,
|
|
539
|
+
message: 'Alt text should start with a capital letter and end with a period (Microsoft).',
|
|
540
|
+
assertions: { capitalization: { match: '^[A-Z].*\\.$' } },
|
|
541
|
+
};
|
|
542
|
+
// "Don't start alt text with 'Image.'" / "Don't start... with 'Button' or
|
|
543
|
+
// 'Link.'" (A4) — Screenshot/Diagram/Photograph/Chart/Drawing are
|
|
544
|
+
// prescribed openers per the same page, so they are deliberately absent
|
|
545
|
+
// from this pattern.
|
|
546
|
+
rules['microsoft/alt-text-generic-opener'] = patternRule({
|
|
547
|
+
tokens: ['^(?:Image|Icon|Graphic|Button|Link)\\b'],
|
|
548
|
+
message: 'Don\'t start alt text with a generic word such as "%s" (Microsoft).',
|
|
549
|
+
link: ALTERNATIVE_TEXT,
|
|
550
|
+
scope: 'alt',
|
|
551
|
+
ignoreCase: true,
|
|
552
|
+
});
|
|
553
|
+
// "Don't use the file name of an image as alt text." (A5)
|
|
554
|
+
rules['microsoft/alt-text-no-filename'] = patternRule({
|
|
555
|
+
tokens: ['\\.(?:png|jpe?g|gif|svg|webp)$'],
|
|
556
|
+
message: "Don't use an image's file name as its alt text (Microsoft).",
|
|
557
|
+
link: ALTERNATIVE_TEXT,
|
|
558
|
+
scope: 'alt',
|
|
559
|
+
ignoreCase: true,
|
|
560
|
+
});
|
|
561
|
+
// "rather than a generic phrase like click here" (A6)
|
|
562
|
+
rules['microsoft/descriptive-link-text'] = tokenRule({
|
|
563
|
+
name: 'descriptive-link-text',
|
|
564
|
+
message: 'Link text should be descriptive, not a generic phrase (Microsoft).',
|
|
565
|
+
link: URLS_WEB_ADDRESSES,
|
|
566
|
+
});
|
|
567
|
+
// ======================================================================
|
|
568
|
+
// STRUCTURAL NUMBERS — the guide's own real numeric thresholds, in place
|
|
569
|
+
// of a fabricated readability metric (see the file header's note). All
|
|
570
|
+
// four map spec §5.6's "Microsoft-stated numbers" table onto the
|
|
571
|
+
// corresponding native assertion.
|
|
572
|
+
// ======================================================================
|
|
573
|
+
// "Three to seven lines is about the right length for a paragraph."
|
|
574
|
+
// DEVIATION #1: Recheck has no concept of a rendered "line" in Markdown —
|
|
575
|
+
// line length depends on viewport/font, which is meaningless for a
|
|
576
|
+
// structural check. `length`'s `sentences` unit is used as the closest
|
|
577
|
+
// countable proxy for the guide's stated range; this is an intentional
|
|
578
|
+
// substitution, not a literal reading of "lines".
|
|
579
|
+
// DEVIATION #2: only the UPPER bound (7) ships; the lower bound (3) does
|
|
580
|
+
// not. Discovered empirically while building this preset's own clean
|
|
581
|
+
// fixture: single-sentence paragraphs are commonplace and entirely
|
|
582
|
+
// correct in reference documentation (a one-line lead-in to a code
|
|
583
|
+
// block, an image caption, a short introductory sentence before a list),
|
|
584
|
+
// and a `min: 3` floor flagged more than a dozen ordinary, correct
|
|
585
|
+
// paragraphs in a realistic API-documentation-shaped test file — the
|
|
586
|
+
// same class of over-firing Google's own `list-length` rule avoided by
|
|
587
|
+
// shipping only `min` with no `max` (spec: Google states no upper bound).
|
|
588
|
+
// Shipping only `max: 7` here still enforces the guide's one genuinely
|
|
589
|
+
// actionable direction (a paragraph that has grown too long to scan)
|
|
590
|
+
// without penalizing normal short paragraphs. Both deviations are
|
|
591
|
+
// recorded again in PROVENANCE.md.
|
|
592
|
+
rules['microsoft/paragraph-length'] = {
|
|
593
|
+
severity: 'warn',
|
|
594
|
+
scope: 'paragraph',
|
|
595
|
+
link: 'https://learn.microsoft.com/en-us/style-guide/scannable-content/',
|
|
596
|
+
message: 'Paragraph is %s %s long; Microsoft suggests at most 7 (sentences, as a proxy for lines).',
|
|
597
|
+
assertions: { length: { unit: 'sentences', max: 7 } },
|
|
598
|
+
};
|
|
599
|
+
// "A list should have at least two items but (if possible) no more than
|
|
600
|
+
// seven items." `warn`, matching the guide's own softened upper bound
|
|
601
|
+
// ("if possible").
|
|
602
|
+
rules['microsoft/list-length'] = tokenRule({
|
|
603
|
+
name: 'list-length',
|
|
604
|
+
message: 'List has %s item(s); Microsoft recommends 2-7 (Microsoft).',
|
|
605
|
+
link: LISTS,
|
|
606
|
+
options: { min: 2, max: 7 },
|
|
607
|
+
severity: 'warn',
|
|
608
|
+
});
|
|
609
|
+
// "If a sentence contains more than a comma or two and ending
|
|
610
|
+
// punctuation, consider rewriting it to make it crisp and clear."
|
|
611
|
+
rules['microsoft/comma-density'] = {
|
|
612
|
+
severity: 'warn',
|
|
613
|
+
scope: 'sentence',
|
|
614
|
+
link: 'https://learn.microsoft.com/en-us/style-guide/punctuation/',
|
|
615
|
+
message: 'Sentence has %s commas; Microsoft suggests at most %s.',
|
|
616
|
+
assertions: { occurrence: { pattern: ',', max: 2 } },
|
|
617
|
+
};
|
|
618
|
+
// ======================================================================
|
|
619
|
+
// VOICE / CONTRACTIONS — `warn`.
|
|
620
|
+
// ======================================================================
|
|
621
|
+
// "Use contractions like it's, you'll, you're, we're, and let's." — the
|
|
622
|
+
// signature Microsoft rule and the sharpest difference from other style
|
|
623
|
+
// guides (spelled-out negative/pronoun forms -> contractions). Every pair
|
|
624
|
+
// is a clean word-for-word verb contraction; `applyMatchCase` handles a
|
|
625
|
+
// sentence-initial capitalized match correctly ("Do not" -> "Don't").
|
|
626
|
+
rules['microsoft/use-contractions'] = swapRule({
|
|
627
|
+
pairs: {
|
|
628
|
+
cannot: "can't",
|
|
629
|
+
'can not': "can't",
|
|
630
|
+
'do not': "don't",
|
|
631
|
+
'does not': "doesn't",
|
|
632
|
+
'did not': "didn't",
|
|
633
|
+
'is not': "isn't",
|
|
634
|
+
'are not': "aren't",
|
|
635
|
+
'was not': "wasn't",
|
|
636
|
+
'were not': "weren't",
|
|
637
|
+
'will not': "won't",
|
|
638
|
+
'would not': "wouldn't",
|
|
639
|
+
'should not': "shouldn't",
|
|
640
|
+
'could not': "couldn't",
|
|
641
|
+
'have not': "haven't",
|
|
642
|
+
'has not': "hasn't",
|
|
643
|
+
'had not': "hadn't",
|
|
644
|
+
'it is': "it's",
|
|
645
|
+
'you are': "you're",
|
|
646
|
+
'we are': "we're",
|
|
647
|
+
'they are': "they're",
|
|
648
|
+
'you will': "you'll",
|
|
649
|
+
'let us': "let's",
|
|
650
|
+
},
|
|
651
|
+
message: 'Microsoft style prefers the contraction "%s" over "%s".',
|
|
652
|
+
link: USE_CONTRACTIONS,
|
|
653
|
+
ignoreCase: true,
|
|
654
|
+
wordBoundary: true,
|
|
655
|
+
});
|
|
656
|
+
// "Avoid ambiguous or awkward contractions, such as there'd, it'll, and
|
|
657
|
+
// they'd."
|
|
658
|
+
rules['microsoft/no-awkward-contractions'] = patternRule({
|
|
659
|
+
tokens: [
|
|
660
|
+
"\\b(?:there['\u2019]d|it['\u2019]ll|they['\u2019]d|that['\u2019]ll|there['\u2019]ll)\\b",
|
|
661
|
+
],
|
|
662
|
+
message: 'Avoid the ambiguous contraction "%s" (Microsoft).',
|
|
663
|
+
link: USE_CONTRACTIONS,
|
|
664
|
+
ignoreCase: true,
|
|
665
|
+
});
|
|
666
|
+
// "Don't mix contractions and their spelled-out equivalents in UI text...
|
|
667
|
+
// don't use can't and cannot in the same UI." Textbook fit for
|
|
668
|
+
// first-seen-wins `consistency`.
|
|
669
|
+
rules['microsoft/contraction-consistency'] = {
|
|
670
|
+
severity: 'warn',
|
|
671
|
+
scope: 'summary',
|
|
672
|
+
link: USE_CONTRACTIONS,
|
|
673
|
+
message: '"%s" conflicts with the first-used form "%s" in this file (Microsoft).',
|
|
674
|
+
assertions: {
|
|
675
|
+
consistency: {
|
|
676
|
+
ignoreCase: true,
|
|
677
|
+
either: {
|
|
678
|
+
"can't": 'cannot',
|
|
679
|
+
"don't": 'do not',
|
|
680
|
+
"won't": 'will not',
|
|
681
|
+
"isn't": 'is not',
|
|
682
|
+
"it's": 'it is',
|
|
683
|
+
},
|
|
684
|
+
},
|
|
685
|
+
},
|
|
686
|
+
};
|
|
687
|
+
// "Avoid weak phrasing like there is, there are, and there were."
|
|
688
|
+
rules['microsoft/no-weak-phrasing'] = patternRule({
|
|
689
|
+
tokens: ['\\bthere (?:is|are|was|were)\\b'],
|
|
690
|
+
message: 'Avoid weak phrasing such as "%s"; start the sentence with a verb (Microsoft).',
|
|
691
|
+
link: TOP_10_TIPS,
|
|
692
|
+
scope: ['paragraph', 'list-item'],
|
|
693
|
+
ignoreCase: true,
|
|
694
|
+
});
|
|
695
|
+
// "Avoid please except in situations where the customer is asked to do
|
|
696
|
+
// something inconvenient or the application or site is to blame."
|
|
697
|
+
rules['microsoft/avoid-please'] = patternRule({
|
|
698
|
+
tokens: ['\\bplease\\b'],
|
|
699
|
+
message: 'Avoid "%s" except when asking the customer to do something inconvenient (Microsoft).',
|
|
700
|
+
link: 'https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/p/please',
|
|
701
|
+
ignoreCase: true,
|
|
702
|
+
});
|
|
703
|
+
// ======================================================================
|
|
704
|
+
// US SPELLING / LATIN ABBREVIATIONS / SIMPLE WORDS — `error` (mechanical,
|
|
705
|
+
// unconditional, per the research's own "ship at error" framing).
|
|
706
|
+
// ======================================================================
|
|
707
|
+
// "use the US spelling. For example, use license, not licence." A subset
|
|
708
|
+
// not already covered by recheck/prose's `consistency` rule
|
|
709
|
+
// (behavior/color/license/organize).
|
|
710
|
+
//
|
|
711
|
+
// Each key below is one literal pair per inflection, never an alternation
|
|
712
|
+
// group (`(s)?`, `(ed|ing)`, `(e|es|ed|ing|ation)`) mapped to a single
|
|
713
|
+
// literal `swap` replacement. `swap` replacements are literal text, never
|
|
714
|
+
// `$1` (see the file header's FIX SAFETY note), so an alternation group
|
|
715
|
+
// would collapse every inflection it matches onto the one replacement the
|
|
716
|
+
// config happens to name -- the exact hazard `az-verb-able`/
|
|
717
|
+
// `az-case-only` exist to prevent, without ever writing `$1`:
|
|
718
|
+
// "The team is modelling the traffic pattern." -> "is modeled the"
|
|
719
|
+
// (modelling, an -ing form, would collapse onto the -ed replacement)
|
|
720
|
+
// "The job was cancelling when the timeout..." -> "was canceled when"
|
|
721
|
+
// "Both centres report the same latency." -> "Both center report"
|
|
722
|
+
// (centres, plural, would collapse onto the singular replacement)
|
|
723
|
+
// "The request was authorised by the admin." -> "was authorize by"
|
|
724
|
+
// "Authorisation happens before the redirect." -> "Authorize happens"
|
|
725
|
+
// "Customisation of the theme is optional." -> "Customize of the"
|
|
726
|
+
// Enumerating one literal pair PER inflection gives one match shape, one
|
|
727
|
+
// correct output. `favou?rite` needs the same discipline even though it
|
|
728
|
+
// isn't an alternation group: the optional `u` would let the pattern
|
|
729
|
+
// match the ALREADY-CORRECT "favorite" spelling too (a no-op "fix" that
|
|
730
|
+
// still reports a false violation on correct text) -- narrowed to the UK
|
|
731
|
+
// spelling only.
|
|
732
|
+
//
|
|
733
|
+
// "labeled"/"labeling" and "canceled"/"canceling" each need their own
|
|
734
|
+
// pair: the guide names the -ing form explicitly ("Use one l, not two"
|
|
735
|
+
// for labeled/labeling; "Spell canceled and canceling with one l" for
|
|
736
|
+
// canceled/canceling), so a single-target replacement covering only the
|
|
737
|
+
// -ed form would never produce "labeling"/"canceling" at all.
|
|
738
|
+
//
|
|
739
|
+
// `dialogue box` -> `dialog` is deliberately NOT repeated here:
|
|
740
|
+
// `microsoft/dialog-terminology` already ships this exact pair as part
|
|
741
|
+
// of its pop-up window/dialog box/dialogue box bundle, and a duplicate
|
|
742
|
+
// here would double-report the same span from two different rule names
|
|
743
|
+
// -- the same reasoning `az-grammar-usage`'s own comment documents for
|
|
744
|
+
// `multi-factor`.
|
|
745
|
+
//
|
|
746
|
+
// `centred`/`centring` and `catalogued`/`cataloguing` need their own
|
|
747
|
+
// pairs too: the noun/plural forms (`centre`/`centres`,
|
|
748
|
+
// `catalogue`/`catalogues`) don't cover the verb inflections, and the
|
|
749
|
+
// same one-inflection-per-pair principle above applies to verbs as much
|
|
750
|
+
// as nouns.
|
|
751
|
+
//
|
|
752
|
+
// `centre`/`centres` and `catalogue`/`catalogues` themselves live in
|
|
753
|
+
// `microsoft/us-spelling-detect` below, not here. Unlike the rest of this
|
|
754
|
+
// rule, these four are real ORGANIZATION/PLACE names in their OWN
|
|
755
|
+
// official spelling, not just a British/American variant of an ordinary
|
|
756
|
+
// word: "Centre County, Pennsylvania" is a real US county whose official
|
|
757
|
+
// name keeps the British "re" spelling, and "Bell Centre" (Montreal
|
|
758
|
+
// Canadiens' arena) is officially spelled with "Centre", not "Center" --
|
|
759
|
+
// fixing either would silently rewrite a proper noun's own spelling
|
|
760
|
+
// ("...held at the Bell Centre" -> "...held at the Bell Center"). Same
|
|
761
|
+
// shape for "Catalogue of Life" (a real, commonly-cited global species
|
|
762
|
+
// database whose own name is spelled with the British "ue"). The VERB
|
|
763
|
+
// inflections (`centred`/`centring`, `catalogued`/`cataloguing`) don't
|
|
764
|
+
// carry this risk -- a participle doesn't head a proper noun the way the
|
|
765
|
+
// bare noun does -- and stay fixable here.
|
|
766
|
+
rules['microsoft/us-spelling'] = swapRule({
|
|
767
|
+
pairs: {
|
|
768
|
+
'\\bcentred\\b': 'centered',
|
|
769
|
+
'\\bcentring\\b': 'centering',
|
|
770
|
+
'\\bcatalogued\\b': 'cataloged',
|
|
771
|
+
'\\bcataloguing\\b': 'cataloging',
|
|
772
|
+
'\\bcancelled\\b': 'canceled',
|
|
773
|
+
'\\bcancelling\\b': 'canceling',
|
|
774
|
+
'\\bfavourite\\b': 'favorite',
|
|
775
|
+
'\\bauthorise\\b': 'authorize',
|
|
776
|
+
'\\bauthorises\\b': 'authorizes',
|
|
777
|
+
'\\bauthorised\\b': 'authorized',
|
|
778
|
+
'\\bauthorising\\b': 'authorizing',
|
|
779
|
+
'\\bauthorisation\\b': 'authorization',
|
|
780
|
+
'\\bcustomise\\b': 'customize',
|
|
781
|
+
'\\bcustomises\\b': 'customizes',
|
|
782
|
+
'\\bcustomised\\b': 'customized',
|
|
783
|
+
'\\bcustomising\\b': 'customizing',
|
|
784
|
+
'\\bcustomisation\\b': 'customization',
|
|
785
|
+
'\\blabelled\\b': 'labeled',
|
|
786
|
+
'\\blabelling\\b': 'labeling',
|
|
787
|
+
'\\bmodelled\\b': 'modeled',
|
|
788
|
+
'\\bmodelling\\b': 'modeling',
|
|
789
|
+
},
|
|
790
|
+
message: 'Use the US spelling "%s" instead of "%s" (Microsoft).',
|
|
791
|
+
link: USE_US_SPELLING,
|
|
792
|
+
severity: 'error',
|
|
793
|
+
ignoreCase: true,
|
|
794
|
+
keysAreRegex: true,
|
|
795
|
+
wordBoundary: false,
|
|
796
|
+
});
|
|
797
|
+
// Detection-only sibling: same US-spelling guidance, but the avoid-term
|
|
798
|
+
// is ALSO a real proper noun's own official spelling (see the comment on
|
|
799
|
+
// `microsoft/us-spelling` above) -- "Bell Centre", "Centre County,
|
|
800
|
+
// Pennsylvania", and "Catalogue of Life" all keep the British spelling
|
|
801
|
+
// this rule would otherwise "fix". Severity stays `error`, matching Tier
|
|
802
|
+
// 3's unconditional framing: the GUIDANCE is still always correct, only
|
|
803
|
+
// the auto-fix is unsafe.
|
|
804
|
+
rules['microsoft/us-spelling-detect'] = swapRule({
|
|
805
|
+
pairs: {
|
|
806
|
+
'\\bcentre\\b': 'center',
|
|
807
|
+
'\\bcentres\\b': 'centers',
|
|
808
|
+
'\\bcatalogue\\b': 'catalog',
|
|
809
|
+
'\\bcatalogues\\b': 'catalogs',
|
|
810
|
+
},
|
|
811
|
+
message: 'Use the US spelling "%s" instead of "%s" (Microsoft).',
|
|
812
|
+
link: USE_US_SPELLING,
|
|
813
|
+
severity: 'error',
|
|
814
|
+
fix: false,
|
|
815
|
+
ignoreCase: true,
|
|
816
|
+
keysAreRegex: true,
|
|
817
|
+
wordBoundary: false,
|
|
818
|
+
});
|
|
819
|
+
// "Avoid Latin abbreviations for common English phrases." `e.g.`/`i.e.`
|
|
820
|
+
// keep the leading-\b-only anchoring recheck/google's `no-latinisms`
|
|
821
|
+
// established (a trailing \b right after a period-then-space never
|
|
822
|
+
// matches); `ergo` ends in a word character, so it keeps a normal
|
|
823
|
+
// trailing \b too (this is what stops `ergo` from matching inside
|
|
824
|
+
// "ergonomic"). `via` is deliberately dropped: it is not named as an
|
|
825
|
+
// example to avoid on any fetched page, and Microsoft's own prose uses
|
|
826
|
+
// it (see PROVENANCE.md's contradictions note).
|
|
827
|
+
//
|
|
828
|
+
// `de facto`, `ad hoc`, and `vis-a-vis` live in
|
|
829
|
+
// `microsoft/no-latin-abbreviations-detect` below, not here. The live
|
|
830
|
+
// use-us-spelling page's ONLY sentence covering them is "Avoid
|
|
831
|
+
// non-English words or phrases, such as de facto or ad hoc" — an example
|
|
832
|
+
// list of terms to avoid, with NO replacement word stated for either
|
|
833
|
+
// one, and "vis-a-vis" isn't named on this page (or any fetched page) at
|
|
834
|
+
// all. `e.g./i.e./viz./ergo`, by contrast, come from this same page's own
|
|
835
|
+
// "Use this / Instead of this" TABLE, which gives each an exact
|
|
836
|
+
// single-word target; a stated replacement like "in practice"/"as
|
|
837
|
+
// needed"/"compared with" for the other three would just be a guess, not
|
|
838
|
+
// Microsoft's prescribed text.
|
|
839
|
+
//
|
|
840
|
+
// e.g./i.e./viz./ergo are Latin ABBREVIATIONS translated into a
|
|
841
|
+
// different English phrase -- not a respelling of the same word
|
|
842
|
+
// (contrast "vs." -> "versus" below, which shares the same letters as
|
|
843
|
+
// the word it abbreviates), so these ship detection-only.
|
|
844
|
+
rules['microsoft/no-latin-abbreviations'] = swapRule({
|
|
845
|
+
pairs: {
|
|
846
|
+
'\\be\\.g\\.,?': 'for example',
|
|
847
|
+
'\\bi\\.e\\.,?': 'that is',
|
|
848
|
+
'\\bviz\\.': 'namely',
|
|
849
|
+
'\\bergo\\b': 'therefore',
|
|
850
|
+
},
|
|
851
|
+
message: 'Use "%s" instead of "%s" (Microsoft).',
|
|
852
|
+
link: USE_US_SPELLING,
|
|
853
|
+
severity: 'warn',
|
|
854
|
+
fix: false,
|
|
855
|
+
ignoreCase: true,
|
|
856
|
+
wordBoundary: false,
|
|
857
|
+
keysAreRegex: true,
|
|
858
|
+
});
|
|
859
|
+
rules['microsoft/no-latin-abbreviations-detect'] = patternRule({
|
|
860
|
+
tokens: ['\\bde facto\\b', '\\bad hoc\\b', '\\bvis-[\u00e0a]-vis\\b'],
|
|
861
|
+
message: 'Microsoft style: avoid the non-English phrase "%s" — no single replacement is prescribed; rewrite for the context (Microsoft).',
|
|
862
|
+
link: USE_US_SPELLING,
|
|
863
|
+
severity: 'error',
|
|
864
|
+
ignoreCase: true,
|
|
865
|
+
});
|
|
866
|
+
// "Choose simple verbs without modifiers." / "Don't use two or three
|
|
867
|
+
// words when one will do."
|
|
868
|
+
//
|
|
869
|
+
// `in addition` is anchored against a following "to" that turns it into
|
|
870
|
+
// the standard, grammatically necessary preposition phrase "in addition
|
|
871
|
+
// to X" -- the guide's objection targets the STAND-ALONE transitional
|
|
872
|
+
// adverb ("In addition, configure the timeout"), which "also" replaces
|
|
873
|
+
// cleanly; "also to X" is not grammatical ("In addition to the API key,
|
|
874
|
+
// you need a secret" -> "Also to the API key, you need a secret").
|
|
875
|
+
// Every pair here substitutes a DIFFERENT word/phrase for the avoid-term
|
|
876
|
+
// (not a respelling), so this ships detection-only.
|
|
877
|
+
rules['microsoft/simple-words'] = swapRule({
|
|
878
|
+
pairs: {
|
|
879
|
+
'\\butilize\\b': 'use',
|
|
880
|
+
'\\butilise\\b': 'use',
|
|
881
|
+
'\\bmake use of\\b': 'use',
|
|
882
|
+
'\\bin order to\\b': 'to',
|
|
883
|
+
'\\bas a means to\\b': 'to',
|
|
884
|
+
'\\bin addition\\b(?!\\s+to\\b)': 'also',
|
|
885
|
+
'\\bestablish connectivity\\b': 'connect',
|
|
886
|
+
'\\binform\\b': 'tell',
|
|
887
|
+
},
|
|
888
|
+
message: 'Use "%s" instead of "%s" (Microsoft).',
|
|
889
|
+
link: USE_SIMPLE_WORDS,
|
|
890
|
+
severity: 'warn',
|
|
891
|
+
fix: false,
|
|
892
|
+
ignoreCase: true,
|
|
893
|
+
keysAreRegex: true,
|
|
894
|
+
wordBoundary: false,
|
|
895
|
+
});
|
|
896
|
+
// "using leverage to mean take advantage of" (avoid-jargon); confirmed
|
|
897
|
+
// again on the A-Z leverage entry. Kept as its own rule so its `link:`
|
|
898
|
+
// points at the page that actually states it, rather than reusing
|
|
899
|
+
// use-simple-words' citation for a rule it doesn't cover.
|
|
900
|
+
//
|
|
901
|
+
// `fix: false`: the live a-z/leverage page's full text is "Don't use as a
|
|
902
|
+
// VERB to mean take advantage of. Use take advantage of, use, or another
|
|
903
|
+
// more appropriate word or phrase" — sense-scoped (verb only) AND
|
|
904
|
+
// multiple-alternatives, neither of which a bare-word swap can encode.
|
|
905
|
+
// "leverage" and "leveraged" are also common, correct NOUN/ADJECTIVE
|
|
906
|
+
// forms with an unrelated meaning this page never addresses ("financial
|
|
907
|
+
// leverage", "a highly leveraged company", "a leveraged buyout") — a
|
|
908
|
+
// blind fix corrupts every one of those into "financial use"/"a highly
|
|
909
|
+
// used company"/"a used buyout". Unlike `impact-verb`, there's no small
|
|
910
|
+
// enumerable set of following objects to anchor on (the verb takes
|
|
911
|
+
// almost any direct object: "leverage the API/your data/existing
|
|
912
|
+
// infrastructure/..."), so detection-only is the right fallback here,
|
|
913
|
+
// not a partial anchor.
|
|
914
|
+
rules['microsoft/leverage'] = swapRule({
|
|
915
|
+
pairs: { leverage: 'use', leveraging: 'using', leveraged: 'used' },
|
|
916
|
+
message: 'Rewrite "%s" using "%s" (Microsoft): only the VERB sense ("leverage the API") is targeted — "leverage"/"leveraged" are also common, correct nouns/adjectives ("financial leverage", "a leveraged buyout") a blind substitution would corrupt.',
|
|
917
|
+
link: AVOID_JARGON,
|
|
918
|
+
severity: 'error',
|
|
919
|
+
fix: false,
|
|
920
|
+
ignoreCase: true,
|
|
921
|
+
wordBoundary: true,
|
|
922
|
+
});
|
|
923
|
+
// "such as symbol instead of glyph" (avoid-jargon).
|
|
924
|
+
//
|
|
925
|
+
// `fix: false`: the live a-z/glyph page carries a carve-out a bare swap
|
|
926
|
+
// can't encode: "Don't use to refer generically to a graphic or
|
|
927
|
+
// pictorial image on a button, on an icon, or in a message box. Use
|
|
928
|
+
// symbol instead. It's OK to use glyph in a technical discussion of
|
|
929
|
+
// fonts and characters." Font/Unicode documentation — plausible in
|
|
930
|
+
// Redocly's own developer-audience docs — uses "glyph" as a precise
|
|
931
|
+
// technical term distinct from "symbol" (the visual rendering of a
|
|
932
|
+
// character within a specific font); no position anchor distinguishes
|
|
933
|
+
// "technical discussion of fonts" from the generic-icon sense the guide
|
|
934
|
+
// actually objects to, matching the same developer-audience-carve-out
|
|
935
|
+
// class already excluded for `header`/`disk`/`directory`/`context menu`.
|
|
936
|
+
rules['microsoft/glyph'] = swapRule({
|
|
937
|
+
pairs: { glyph: 'symbol' },
|
|
938
|
+
message: 'Use "%s" instead of "%s" (Microsoft) when referring generically to a UI icon/image — but it\'s OK to use "glyph" in a technical discussion of fonts and characters.',
|
|
939
|
+
link: AVOID_JARGON,
|
|
940
|
+
severity: 'error',
|
|
941
|
+
fix: false,
|
|
942
|
+
ignoreCase: true,
|
|
943
|
+
wordBoundary: true,
|
|
944
|
+
});
|
|
945
|
+
// "Don't create a new word from an existing word" -- bucketize -> group.
|
|
946
|
+
// Different word, not a respelling -- detection-only.
|
|
947
|
+
rules['microsoft/bucketize'] = swapRule({
|
|
948
|
+
pairs: { bucketize: 'group' },
|
|
949
|
+
message: 'Use "%s" instead of "%s" (Microsoft).',
|
|
950
|
+
link: DONT_USE_COMMON_WORDS,
|
|
951
|
+
severity: 'warn',
|
|
952
|
+
fix: false,
|
|
953
|
+
ignoreCase: true,
|
|
954
|
+
wordBoundary: true,
|
|
955
|
+
});
|
|
956
|
+
// "Don't use verbs as nouns or nouns as verbs" -- narrowly anchored to
|
|
957
|
+
// the verb sense with a direct object, since "impact" is also a common,
|
|
958
|
+
// correct noun ("the impact of this change") that must not be rewritten.
|
|
959
|
+
rules['microsoft/impact-verb'] = patternRule({
|
|
960
|
+
tokens: [
|
|
961
|
+
'\\bimpact(?:s|ed|ing)?\\s+(?:performance|productivity|quality|reliability|availability|latency|throughput)\\b',
|
|
962
|
+
],
|
|
963
|
+
message: 'Use "affect" instead of "impact" as a verb (Microsoft): "%s"',
|
|
964
|
+
link: DONT_USE_COMMON_WORDS,
|
|
965
|
+
});
|
|
966
|
+
// "the ask" is a bid/ask-market homograph: unscoped, this pair corrupts
|
|
967
|
+
// "Traders watched the ask tick higher" into "...watched the request
|
|
968
|
+
// tick higher". Detection-only. The pattern below is also scoped against
|
|
969
|
+
// that same bid/ask market sense so the clean fixture doesn't visibly
|
|
970
|
+
// misfire on it -- a false positive a user would call silly is still
|
|
971
|
+
// worth avoiding, even on a detection-only rule.
|
|
972
|
+
rules['microsoft/the-ask'] = swapRule({
|
|
973
|
+
pairs: {
|
|
974
|
+
'\\bthe ask\\b(?!\\s+(?:tick|ticks|price|prices|spread|spreads|size|quote|quotes)\\b)': 'the request',
|
|
975
|
+
},
|
|
976
|
+
message: 'Use "%s" instead of "%s" (Microsoft).',
|
|
977
|
+
link: DONT_USE_COMMON_WORDS,
|
|
978
|
+
fix: false,
|
|
979
|
+
ignoreCase: true,
|
|
980
|
+
keysAreRegex: true,
|
|
981
|
+
wordBoundary: false,
|
|
982
|
+
});
|
|
983
|
+
// ======================================================================
|
|
984
|
+
// BIAS-FREE / MILITARISTIC / DEROGATORY LANGUAGE — `error` (sensitive
|
|
985
|
+
// category; matches the accessibility-terms severity below).
|
|
986
|
+
// ======================================================================
|
|
987
|
+
// Every pair here substitutes a DIFFERENT word/phrase, not a respelling
|
|
988
|
+
// -- `DMZ` is the paradigm case: unscoped, this pair corrupts "Tensions
|
|
989
|
+
// remain high near the DMZ dividing North and South Korea" into
|
|
990
|
+
// "...near the perimeter network dividing...". Severity stays `error`
|
|
991
|
+
// (sensitive-category carve-out, matching `master-slave`/
|
|
992
|
+
// `no-derogatory-slang`/`accessibility-terms`/
|
|
993
|
+
// `racial-ethnic-capitalization`, all of which are also detection-only at
|
|
994
|
+
// `error`) -- only `fix` changes. `DMZ` is also scoped against that same
|
|
995
|
+
// Korean-border sense so the clean fixture doesn't visibly misfire on
|
|
996
|
+
// it -- worth avoiding as noise even on a detection-only rule.
|
|
997
|
+
rules['microsoft/bias-free-terms'] = swapRule({
|
|
998
|
+
pairs: {
|
|
999
|
+
chairman: 'chair',
|
|
1000
|
+
chairwoman: 'chair',
|
|
1001
|
+
mankind: 'humanity',
|
|
1002
|
+
manmade: 'synthetic',
|
|
1003
|
+
'man-made': 'synthetic',
|
|
1004
|
+
manpower: 'workforce',
|
|
1005
|
+
salesman: 'sales representative',
|
|
1006
|
+
salesmen: 'sales representatives',
|
|
1007
|
+
'demilitarized zone': 'perimeter network',
|
|
1008
|
+
'\\bDMZ\\b(?!\\s+(?:dividing|between|separating)\\b)': 'perimeter network',
|
|
1009
|
+
'screened subnet': 'perimeter network',
|
|
1010
|
+
},
|
|
1011
|
+
message: 'Use "%s" instead of "%s" (Microsoft: bias-free communication).',
|
|
1012
|
+
link: BIAS_FREE,
|
|
1013
|
+
severity: 'error',
|
|
1014
|
+
fix: false,
|
|
1015
|
+
ignoreCase: true,
|
|
1016
|
+
keysAreRegex: true,
|
|
1017
|
+
wordBoundary: true,
|
|
1018
|
+
});
|
|
1019
|
+
// `master/slave` ships detection-only rather than in neither direction:
|
|
1020
|
+
// both live pages agree the TERM itself must be avoided; they disagree
|
|
1021
|
+
// only on the REPLACEMENT: bias-free-communication's table gives
|
|
1022
|
+
// "primary/subordinate", while the
|
|
1023
|
+
// dedicated a-z/master-slave page leads with "primary/replica" (also
|
|
1024
|
+
// sanctioning primary/secondary, principal/agent, controller/worker) and
|
|
1025
|
+
// separately rejects "primary/subordinate" as a synonym for parent/child
|
|
1026
|
+
// specifically (not wholesale — see PROVENANCE.md). Shipping NOTHING would
|
|
1027
|
+
// silently permit `master/slave` in a preset with an inclusive-language
|
|
1028
|
+
// mandate; shipping ONE side would guess which of the two pages the guide
|
|
1029
|
+
// actually intends. `pattern`, not `swap`, sidesteps the choice entirely:
|
|
1030
|
+
// no replacement is prescribed, so there is no wrong-target pairing to
|
|
1031
|
+
// ship, and the message names both candidates so a human picks the one
|
|
1032
|
+
// that fits.
|
|
1033
|
+
rules['microsoft/master-slave'] = patternRule({
|
|
1034
|
+
tokens: ['\\bmaster\\s*/\\s*slave\\b', '\\bmaster-slave\\b'],
|
|
1035
|
+
message: 'Avoid "%s" (Microsoft): the guide\'s two pages disagree on the replacement — use "primary/subordinate" (bias-free-communication) or "primary/replica" (also acceptable: primary/secondary, principal/agent, controller/worker; a-z/master-slave) depending on context.',
|
|
1036
|
+
link: MASTER_SLAVE,
|
|
1037
|
+
severity: 'error',
|
|
1038
|
+
ignoreCase: true,
|
|
1039
|
+
});
|
|
1040
|
+
// "add cyber- in front of threat so it reads cyberthreat, all one word
|
|
1041
|
+
// no space no hyphen" -- the spelling normalization only; the guide's
|
|
1042
|
+
// separate "needs a qualifier in front of it" test is not mechanically
|
|
1043
|
+
// decidable and is not enforced here.
|
|
1044
|
+
rules['microsoft/cyberattack-spelling'] = swapRule({
|
|
1045
|
+
pairs: {
|
|
1046
|
+
'cyber attack': 'cyberattack',
|
|
1047
|
+
'cyber-attack': 'cyberattack',
|
|
1048
|
+
'cyber threat': 'cyberthreat',
|
|
1049
|
+
'cyber-threat': 'cyberthreat',
|
|
1050
|
+
},
|
|
1051
|
+
message: 'Use "%s" instead of "%s" (Microsoft).',
|
|
1052
|
+
link: MILITARISTIC_LANGUAGE,
|
|
1053
|
+
severity: 'error',
|
|
1054
|
+
ignoreCase: true,
|
|
1055
|
+
wordBoundary: true,
|
|
1056
|
+
});
|
|
1057
|
+
// "Don't use profane or derogatory terms, such as pimp or bitch." /
|
|
1058
|
+
// "Don't use slang... such as spirit animal." Detection-only: no safe
|
|
1059
|
+
// fixed replacement exists for any of these.
|
|
1060
|
+
rules['microsoft/no-derogatory-slang'] = patternRule({
|
|
1061
|
+
tokens: ['\\bpimp\\b', '\\bbitch\\b', '\\bspirit animal\\b'],
|
|
1062
|
+
message: 'Avoid the derogatory or culturally appropriative term "%s" (Microsoft).',
|
|
1063
|
+
link: BIAS_FREE,
|
|
1064
|
+
severity: 'error',
|
|
1065
|
+
ignoreCase: true,
|
|
1066
|
+
});
|
|
1067
|
+
// "Use title-style capitalization for Asian, Black and African American,
|
|
1068
|
+
// Hispanic and Latinx, ..." Case-sensitive (`ignoreCase: false`): only
|
|
1069
|
+
// the genuinely-lowercase form is flagged, so already-correct title-style
|
|
1070
|
+
// text is never touched. `white`/`multiracial` (which the guide says to
|
|
1071
|
+
// LOWERCASE) are deliberately excluded: a bare capitalized "White" collides
|
|
1072
|
+
// constantly with unrelated proper nouns (White House, White Paper, brand
|
|
1073
|
+
// names) and would be far noisier than valuable.
|
|
1074
|
+
rules['microsoft/racial-ethnic-capitalization'] = swapRule({
|
|
1075
|
+
pairs: {
|
|
1076
|
+
asian: 'Asian',
|
|
1077
|
+
'black and african american': 'Black and African American',
|
|
1078
|
+
'hispanic and latinx': 'Hispanic and Latinx',
|
|
1079
|
+
'native american': 'Native American',
|
|
1080
|
+
'alaska native': 'Alaska Native',
|
|
1081
|
+
'native hawaiian': 'Native Hawaiian',
|
|
1082
|
+
'pacific islander': 'Pacific Islander',
|
|
1083
|
+
'indigenous peoples': 'Indigenous Peoples',
|
|
1084
|
+
},
|
|
1085
|
+
message: 'Use title-style capitalization: "%s" instead of "%s" (Microsoft).',
|
|
1086
|
+
link: BIAS_FREE,
|
|
1087
|
+
severity: 'error',
|
|
1088
|
+
ignoreCase: false,
|
|
1089
|
+
wordBoundary: true,
|
|
1090
|
+
});
|
|
1091
|
+
// ======================================================================
|
|
1092
|
+
// ACCESSIBILITY TERM COLLECTION (Tier 2) — `error`. Detection-only by
|
|
1093
|
+
// design, not merely by caution: the research draft crossed two of the
|
|
1094
|
+
// live table's rows (mapping "handicapped" and "differently abled" to
|
|
1095
|
+
// the WRONG row's replacement) and fabricated a third avoid-phrase
|
|
1096
|
+
// ("afflicted with", which appears nowhere on the live page). Shipping
|
|
1097
|
+
// `pattern`, not `swap`, for the whole category sidesteps that risk
|
|
1098
|
+
// entirely: every avoid-term below is independently confirmed as a term
|
|
1099
|
+
// to avoid, but no specific replacement is prescribed, so there is no
|
|
1100
|
+
// wrong-target pairing to ship.
|
|
1101
|
+
//
|
|
1102
|
+
// This rule covers the COMPLETE, independently re-verified 11-row
|
|
1103
|
+
// accessibility-terms table, extracted per-`<tr>` so a term can never end
|
|
1104
|
+
// up paired with another row's replacement text (the row-crossing defect
|
|
1105
|
+
// the section header above describes). The table has 11 rows, not 10 —
|
|
1106
|
+
// Rows 8 and 10 both list "special needs" mapped to two DIFFERENT
|
|
1107
|
+
// preferred replacements (a genuine self-contradiction on Microsoft's own
|
|
1108
|
+
// page). Both terms below still resolve to the SAME already-shipped
|
|
1109
|
+
// `special needs` token either way, so the duplicate row doesn't affect
|
|
1110
|
+
// what ships — see PROVENANCE.md's "Tier 2 design" section.
|
|
1111
|
+
//
|
|
1112
|
+
// Every new term below stays in this SAME pattern rule (no swap target
|
|
1113
|
+
// prescribed for any of them), split into two groups purely for
|
|
1114
|
+
// documentation clarity — both groups are equally detection-only:
|
|
1115
|
+
// - "Normal" (specific, low collision risk): sight-impaired,
|
|
1116
|
+
// vision-impaired, hearing-impaired, non-verbal, maimed, missing a
|
|
1117
|
+
// limb, birth defect, Special Ed person, normal person/healthy
|
|
1118
|
+
// person (phrase-level, NOT bare "normal" — see below), Asperger's
|
|
1119
|
+
// (both the verified straight U+0027 apostrophe and the curly
|
|
1120
|
+
// U+2019 form, since a curly one would not match a straight-only
|
|
1121
|
+
// pattern).
|
|
1122
|
+
// - "Needs a human, not a substitution" (shipped anyway, scope-guarded
|
|
1123
|
+
// where a real collision exists): `dumb`/`mute` (Row 3's own only
|
|
1124
|
+
// avoid-terms, no Acceptable-column alternative exists for this row
|
|
1125
|
+
// at all); `lame`/`stupid` (Rows 2/8 — general-purpose pejoratives
|
|
1126
|
+
// with heavy ordinary usage; flagging is legitimate guidance,
|
|
1127
|
+
// auto-rewriting would not be, and this IS pattern-only so there is
|
|
1128
|
+
// no auto-rewrite -- a flag is a much smaller cost than a swap would
|
|
1129
|
+
// be); `an epileptic` (Row 4 — the guide's own replacement is
|
|
1130
|
+
// a condition-specific sentence rewrite, "has multiple sclerosis,
|
|
1131
|
+
// cerebral palsy, a seizure disorder, or muscular dystrophy", not a
|
|
1132
|
+
// term a `pattern` or `swap` rule can respell — flagging it at least
|
|
1133
|
+
// tells a human to rewrite the sentence).
|
|
1134
|
+
//
|
|
1135
|
+
// TECHNICAL-MEANING COLLISIONS, scope-limited rather than shipped bare:
|
|
1136
|
+
// - `mute` has an extremely common, entirely correct, unrelated
|
|
1137
|
+
// technical sense as an audio/UI control ("mute the microphone",
|
|
1138
|
+
// "mute notifications", a mute button/icon) — a preset that rewrites
|
|
1139
|
+
// "mute the audio track" would be worse than one that stays quiet,
|
|
1140
|
+
// and even flagging it is only worth doing where the disability
|
|
1141
|
+
// sense is actually likely. Scoped to predicate-adjective and
|
|
1142
|
+
// compound forms ("is/was/are/were/being/been mute", "deaf and
|
|
1143
|
+
// mute", "deaf-mute") — the shapes the guide's own usage and real
|
|
1144
|
+
// ableist writing take — rather than the bare word, which would
|
|
1145
|
+
// also match every "mute the audio"/"put the call on mute"/"mute
|
|
1146
|
+
// button" UI phrasing.
|
|
1147
|
+
// - `normal` (as part of "normal person"/"healthy person") is matched
|
|
1148
|
+
// as the guide's own two/three-word PHRASE, never the bare word —
|
|
1149
|
+
// this is deliberate, not an oversight: bare `\bnormal\b` would also
|
|
1150
|
+
// match a statistical "normal distribution" or "normalize a value",
|
|
1151
|
+
// senses the guide never addresses. The phrase-level match is
|
|
1152
|
+
// unlikely to ever collide with those senses.
|
|
1153
|
+
// Both collisions are also documented in PROVENANCE.md's Tier 2 section.
|
|
1154
|
+
rules['microsoft/accessibility-terms'] = patternRule({
|
|
1155
|
+
tokens: [
|
|
1156
|
+
// -- already shipped (Rows 2, 7, 8 in the re-verified table) --------
|
|
1157
|
+
'\\bcrippled\\b',
|
|
1158
|
+
'\\bhandicapped\\b',
|
|
1159
|
+
'\\bthe handicapped\\b',
|
|
1160
|
+
'\\bpeople with handicaps\\b',
|
|
1161
|
+
'\\bslow learner\\b',
|
|
1162
|
+
'\\bmentally handicapped\\b',
|
|
1163
|
+
'\\bdifferently abled\\b',
|
|
1164
|
+
'\\bspecial needs\\b',
|
|
1165
|
+
'\\baffected by\\b',
|
|
1166
|
+
'\\bstricken with\\b',
|
|
1167
|
+
'\\bsuffers from\\b',
|
|
1168
|
+
'\\ba victim of\\b',
|
|
1169
|
+
// -- Specific, low collision risk ----------------------------------
|
|
1170
|
+
'\\bsight-impaired\\b', // Row 0
|
|
1171
|
+
'\\bvision-impaired\\b', // Row 0 (same row/replacement as sight-impaired)
|
|
1172
|
+
'\\bhearing-impaired\\b', // Row 1
|
|
1173
|
+
'\\bnon-verbal\\b', // Row 3
|
|
1174
|
+
'\\bmaimed\\b', // Row 6
|
|
1175
|
+
'\\bmissing a limb\\b', // Row 6
|
|
1176
|
+
'\\bbirth defect\\b', // Row 6
|
|
1177
|
+
'\\bSpecial Ed person\\b', // Row 8 (ignoreCase below; not "special needs" itself)
|
|
1178
|
+
'\\bnormal person\\b', // Row 5 — phrase-level, see collision note above
|
|
1179
|
+
'\\bhealthy person\\b', // Row 5 — phrase-level, see collision note above
|
|
1180
|
+
"\\bAsperger['\u2019]s\\b", // Row 9 — straight (U+0027) AND curly (U+2019)
|
|
1181
|
+
// -- Needs a human, shipped detection-only anyway ------------------
|
|
1182
|
+
'\\bdumb\\b', // Row 3
|
|
1183
|
+
'\\b(?:is|was|are|were|being|been)\\s+mute\\b', // Row 3, scope-guarded (see above)
|
|
1184
|
+
'\\bdeaf and mute\\b', // Row 3, scope-guarded (see above)
|
|
1185
|
+
'\\bdeaf-mute\\b', // Row 3, scope-guarded (see above)
|
|
1186
|
+
'\\blame\\b', // Row 2
|
|
1187
|
+
'\\bstupid\\b', // Row 8
|
|
1188
|
+
// Row 4. Guarded against "an epileptic seizure/episode/fit/attack" --
|
|
1189
|
+
// legitimate medical usage where "epileptic" is an adjective
|
|
1190
|
+
// describing the EVENT, not the guide's objection (calling a PERSON
|
|
1191
|
+
// "an epileptic" instead of "a person with... a seizure disorder").
|
|
1192
|
+
'\\ban epileptic\\b(?!\\s+(?:seizure|episode|fit|attack|event))',
|
|
1193
|
+
],
|
|
1194
|
+
message: 'Use people-first language instead of "%s" — see the accessibility term collection (Microsoft).',
|
|
1195
|
+
link: ACCESSIBILITY_TERMS,
|
|
1196
|
+
severity: 'error',
|
|
1197
|
+
ignoreCase: true,
|
|
1198
|
+
});
|
|
1199
|
+
// ======================================================================
|
|
1200
|
+
// SPELLING AND HYPHENATION NORMALIZATION (Tier 3) — `error` (pure
|
|
1201
|
+
// mechanics, per the research's own framing).
|
|
1202
|
+
// ======================================================================
|
|
1203
|
+
rules['microsoft/spelling-hyphenation'] = swapRule({
|
|
1204
|
+
pairs: {
|
|
1205
|
+
'\\be-?mail\\b(?<!email)': 'email',
|
|
1206
|
+
'\\bdata ?base\\b(?<!database)': 'database',
|
|
1207
|
+
'\\bend ?point\\b(?<!endpoint)': 'endpoint',
|
|
1208
|
+
'\\bweb ?site\\b(?<!website)': 'website',
|
|
1209
|
+
'\\bweb ?page\\b(?<!webpage)': 'webpage',
|
|
1210
|
+
'\\bwork ?station\\b(?<!workstation)': 'workstation',
|
|
1211
|
+
'\\bscreen ?shot\\b(?<!screenshot)': 'screenshot',
|
|
1212
|
+
'\\btask ?bar\\b(?<!taskbar)': 'taskbar',
|
|
1213
|
+
'\\bname ?space\\b(?<!namespace)': 'namespace',
|
|
1214
|
+
'\\bplug-in\\b': 'plugin',
|
|
1215
|
+
'\\becommerce\\b': 'e-commerce',
|
|
1216
|
+
'\\belearning\\b': 'e-learning',
|
|
1217
|
+
'\\bebook\\b': 'e-book',
|
|
1218
|
+
'\\bcyber-security\\b': 'cybersecurity',
|
|
1219
|
+
'\\bco-author\\b': 'coauthor',
|
|
1220
|
+
// "dial up"/"single sign on" don't need a `(?<!...)` guard the way
|
|
1221
|
+
// the closed-compound patterns above do: the CORRECT spelling for
|
|
1222
|
+
// both ("dial-up", "single sign-on") is HYPHENATED, and the ` ?`
|
|
1223
|
+
// (optional space, not optional hyphen) in these two patterns cannot
|
|
1224
|
+
// match a hyphen at all — the correct form structurally never
|
|
1225
|
+
// matches the pattern, so there is nothing to exclude.
|
|
1226
|
+
'\\bdial ?up\\b': 'dial-up',
|
|
1227
|
+
'\\bread only\\b': 'read-only',
|
|
1228
|
+
'\\bcontext sensitive\\b': 'context-sensitive',
|
|
1229
|
+
'\\bsingle sign ?on\\b': 'single sign-on',
|
|
1230
|
+
'\\bmulti-factor\\b': 'multifactor',
|
|
1231
|
+
'\\bmulti-cloud\\b': 'multicloud',
|
|
1232
|
+
'\\bmulti-tenant\\b': 'multitenant',
|
|
1233
|
+
'\\bwell-being\\b': 'wellbeing',
|
|
1234
|
+
'\\btool ?tip\\b(?<!tooltip)': 'tooltip',
|
|
1235
|
+
// Lives here rather than in `az-lifecycle-verbs`: `imbed` is a
|
|
1236
|
+
// recognized non-standard/alternate spelling of `embed` (same word,
|
|
1237
|
+
// no demonstrated unrelated sense), matching this rule's theme.
|
|
1238
|
+
'\\bimbed\\b': 'embed',
|
|
1239
|
+
},
|
|
1240
|
+
message: 'Microsoft style spells this "%s", not "%s".',
|
|
1241
|
+
link: AZ_BASE + 'e/email',
|
|
1242
|
+
severity: 'error',
|
|
1243
|
+
ignoreCase: true,
|
|
1244
|
+
keysAreRegex: true,
|
|
1245
|
+
wordBoundary: false,
|
|
1246
|
+
});
|
|
1247
|
+
// "The term tooltip is one word and lowercase. Don't spell it as
|
|
1248
|
+
// ToolTip." Shipped as its OWN case-SENSITIVE rule (not folded into
|
|
1249
|
+
// `spelling-hyphenation`'s `ignoreCase: true` pattern, which is exactly
|
|
1250
|
+
// the config-mechanics bug task-10-verify-H.md found: a shared
|
|
1251
|
+
// `ignoreCase: true` flag makes a same-rule "ToolTip"-only pattern also
|
|
1252
|
+
// match the already-correct lowercase "tooltip"). "ToolTip"'s internal
|
|
1253
|
+
// mixed casing doesn't match `applyMatchCase`'s simple
|
|
1254
|
+
// ALL-CAPS/Capitalized heuristics, so the configured lowercase
|
|
1255
|
+
// replacement is inserted as authored, not case-shouted.
|
|
1256
|
+
rules['microsoft/tooltip-capitalization'] = swapRule({
|
|
1257
|
+
pairs: { ToolTip: 'tooltip' },
|
|
1258
|
+
message: 'Use "%s", not "%s" (Microsoft: tooltip is one word, lowercase).',
|
|
1259
|
+
link: AZ_BASE + 't/tooltip',
|
|
1260
|
+
severity: 'error',
|
|
1261
|
+
ignoreCase: false,
|
|
1262
|
+
wordBoundary: true,
|
|
1263
|
+
});
|
|
1264
|
+
// ======================================================================
|
|
1265
|
+
// CASE-ONLY (`fix: false`) — replacement differs from the avoid-term
|
|
1266
|
+
// only by case; `applyMatchCase` would silently no-op the fix.
|
|
1267
|
+
//
|
|
1268
|
+
// Not every case-differing pair is a silent `applyMatchCase` no-op: a
|
|
1269
|
+
// replacement whose internal casing has a second capital letter past the
|
|
1270
|
+
// first (e.g. "DevOps"/"JavaScript"), or is a completely different word
|
|
1271
|
+
// ("web"), doesn't match `applyMatchCase`'s Capitalized/ALL-CAPS
|
|
1272
|
+
// heuristics, so the configured replacement is inserted as authored
|
|
1273
|
+
// rather than reproducing the match -- a REAL, correct fix. Those pairs
|
|
1274
|
+
// (`Big Data`, `Dark Mode`, `darkmode`, `Devops`, `devops`, `bluetooth`,
|
|
1275
|
+
// `boolean`, `Javascript`, `javascript`, `World Wide Web`) ship fixable in
|
|
1276
|
+
// `microsoft/az-case-fixable` below instead of reporting forever for no
|
|
1277
|
+
// reason. Only the genuine no-ops — where match and replacement are the
|
|
1278
|
+
// identical string in two different single-word casings
|
|
1279
|
+
// (`Internet`/`internet`, `WWW`/`www`, ...) — stay here.
|
|
1280
|
+
// ======================================================================
|
|
1281
|
+
rules['microsoft/az-case-only'] = swapRule({
|
|
1282
|
+
pairs: {
|
|
1283
|
+
Internet: 'internet',
|
|
1284
|
+
Intranet: 'intranet',
|
|
1285
|
+
Extranet: 'extranet',
|
|
1286
|
+
Euro: 'euro',
|
|
1287
|
+
WWW: 'www',
|
|
1288
|
+
Registry: 'registry',
|
|
1289
|
+
Spam: 'spam',
|
|
1290
|
+
},
|
|
1291
|
+
message: 'Use "%s" instead of "%s" (Microsoft): rewrite, this fix would silently no-op.',
|
|
1292
|
+
link: AZ_BASE + 'i/internet-intranet-extranet',
|
|
1293
|
+
severity: 'error',
|
|
1294
|
+
fix: false,
|
|
1295
|
+
ignoreCase: false,
|
|
1296
|
+
wordBoundary: true,
|
|
1297
|
+
});
|
|
1298
|
+
// `World Wide Web` -> `web` lives in `microsoft/world-wide-web` below,
|
|
1299
|
+
// not here -- it's a different, shorter name, not a casing/spacing
|
|
1300
|
+
// variant of the same three words the way every other pair here is (all
|
|
1301
|
+
// empirically verified against the live `applyMatchCase` to produce a
|
|
1302
|
+
// real, correct fix, not a no-op: with `ignoreCase: false`, an ALL-CAPS
|
|
1303
|
+
// input like "DEVOPS"/"JAVASCRIPT" can never match these case-sensitive
|
|
1304
|
+
// keys in the first place, so the ALL-CAPS-shout no-op class
|
|
1305
|
+
// `az-case-only` exists to avoid doesn't recur here).
|
|
1306
|
+
//
|
|
1307
|
+
// `boolean` -> `Boolean` lives in `microsoft/az-case-fixable-detect`
|
|
1308
|
+
// below instead: unlike the rest of this rule, it carries an unrelated
|
|
1309
|
+
// legitimate sense, not just a same-word casing difference. Lowercase
|
|
1310
|
+
// `boolean` is the REQUIRED, spec-mandated spelling of
|
|
1311
|
+
// the OpenAPI/JSON Schema type name (`"type": "boolean"`) -- exactly
|
|
1312
|
+
// Redocly's own domain -- and prose describing a schema ("the field is a
|
|
1313
|
+
// boolean") uses the correct lowercase form constantly. Auto-capitalizing
|
|
1314
|
+
// every lowercase "boolean" would corrupt this extremely common,
|
|
1315
|
+
// completely correct technical usage. Detection is still useful for
|
|
1316
|
+
// ordinary prose review; auto-fix is not safe.
|
|
1317
|
+
rules['microsoft/az-case-fixable'] = swapRule({
|
|
1318
|
+
pairs: {
|
|
1319
|
+
'Big Data': 'big data',
|
|
1320
|
+
'Dark Mode': 'dark mode',
|
|
1321
|
+
darkmode: 'dark mode',
|
|
1322
|
+
Devops: 'DevOps',
|
|
1323
|
+
devops: 'DevOps',
|
|
1324
|
+
bluetooth: 'Bluetooth',
|
|
1325
|
+
Javascript: 'JavaScript',
|
|
1326
|
+
javascript: 'JavaScript',
|
|
1327
|
+
},
|
|
1328
|
+
message: 'Use "%s" instead of "%s" (Microsoft).',
|
|
1329
|
+
link: AZ_BASE + 'i/internet-intranet-extranet',
|
|
1330
|
+
severity: 'error',
|
|
1331
|
+
ignoreCase: false,
|
|
1332
|
+
wordBoundary: true,
|
|
1333
|
+
});
|
|
1334
|
+
rules['microsoft/az-case-fixable-detect'] = swapRule({
|
|
1335
|
+
pairs: {
|
|
1336
|
+
boolean: 'Boolean',
|
|
1337
|
+
},
|
|
1338
|
+
message: 'Use "%s" instead of "%s" (Microsoft) in ordinary prose -- but lowercase "boolean" is correct and expected when naming the OpenAPI/JSON Schema type.',
|
|
1339
|
+
link: AZ_BASE + 'i/internet-intranet-extranet',
|
|
1340
|
+
severity: 'error',
|
|
1341
|
+
fix: false,
|
|
1342
|
+
ignoreCase: false,
|
|
1343
|
+
wordBoundary: true,
|
|
1344
|
+
});
|
|
1345
|
+
rules['microsoft/world-wide-web'] = swapRule({
|
|
1346
|
+
pairs: { 'World Wide Web': 'web' },
|
|
1347
|
+
message: 'Use "%s" instead of "%s" (Microsoft).',
|
|
1348
|
+
link: AZ_BASE + 'i/internet-intranet-extranet',
|
|
1349
|
+
severity: 'warn',
|
|
1350
|
+
fix: false,
|
|
1351
|
+
ignoreCase: false,
|
|
1352
|
+
wordBoundary: true,
|
|
1353
|
+
});
|
|
1354
|
+
// ======================================================================
|
|
1355
|
+
// VERB-ABLE (`fix: false`, message says "rewrite") — the replacement is
|
|
1356
|
+
// a noun phrase, but the avoid-term is documented as also usable as a
|
|
1357
|
+
// verb ("whitelist an email address"); a blind fix produces ungrammatical
|
|
1358
|
+
// output ("allow list an email address").
|
|
1359
|
+
// ======================================================================
|
|
1360
|
+
rules['microsoft/az-verb-able'] = swapRule({
|
|
1361
|
+
pairs: {
|
|
1362
|
+
blacklist: 'block list',
|
|
1363
|
+
whitelist: 'allow list',
|
|
1364
|
+
allowlist: 'allow list',
|
|
1365
|
+
blocklist: 'block list',
|
|
1366
|
+
},
|
|
1367
|
+
message: 'Rewrite "%s" using "%s" (Microsoft): a direct substitution may be ungrammatical.',
|
|
1368
|
+
link: AZ_BASE + 'b/blacklist',
|
|
1369
|
+
severity: 'error',
|
|
1370
|
+
fix: false,
|
|
1371
|
+
ignoreCase: true,
|
|
1372
|
+
wordBoundary: true,
|
|
1373
|
+
});
|
|
1374
|
+
// ======================================================================
|
|
1375
|
+
// A-Z WORD LIST (Tier 1) — one-to-one substitutions, grouped thematically.
|
|
1376
|
+
// `error`, matching the research's "ship at error" framing. Every
|
|
1377
|
+
// Tier-4 (audience/UI-conditional) entry, every developer-audience
|
|
1378
|
+
// carve-out relevant to Redocly, and every entry with an unresolved
|
|
1379
|
+
// substring/homograph collision risk is excluded — see the file header
|
|
1380
|
+
// and PROVENANCE.md's "Excluded candidates" table.
|
|
1381
|
+
//
|
|
1382
|
+
// Not every candidate pair is CONFIRMED unconditional: pairs carrying a
|
|
1383
|
+
// conditional, multi-target, or detect-only guidance shape live in a
|
|
1384
|
+
// `*-detect` pattern sibling instead (conditional/multi-target: `quit`,
|
|
1385
|
+
// `deinstall`, `reinitialize`, `crash`, `lock up`, `bottom left`/
|
|
1386
|
+
// `bottom right`, `thank you`, `hierarchical menu`/`secondary menu`,
|
|
1387
|
+
// `running head`/`running foot`, `pound sign`), ship `fix: false`
|
|
1388
|
+
// (detect-only but still a single-target swap: `left-hand`/`right-hand`),
|
|
1389
|
+
// or are dropped outright where the guide's own carve-out is a genuine
|
|
1390
|
+
// meaning change in Redocly's own domain (`terminate`), or excluded as a
|
|
1391
|
+
// low-priority general-audience-only nuance that doesn't change what
|
|
1392
|
+
// ships (`indices` is excluded per its own TOO-RISKY reasoning above;
|
|
1393
|
+
// `backbone`/`natural user interface` already ship as `pattern`, i.e.
|
|
1394
|
+
// already detection-only, in `microsoft/az-no-replacement`). Every OTHER
|
|
1395
|
+
// pair below is CONFIRMED unconditional.
|
|
1396
|
+
// ======================================================================
|
|
1397
|
+
// `crash` and `lock up` live in `microsoft/az-state-failure-detect`
|
|
1398
|
+
// below, not here, for two independent reasons: (1) noun-compound
|
|
1399
|
+
// collision — an unscoped "crash" swap rewrites "Attach the crash dump"
|
|
1400
|
+
// (a diagnostic file, not a verb) into "Attach the fail dump"; (2) the
|
|
1401
|
+
// guide gives a HARDWARE/SOFTWARE split target ("Use fail for disks or
|
|
1402
|
+
// other hardware, or stop responding for programs") that a single
|
|
1403
|
+
// literal `swap` replacement can't express — the same one-match-
|
|
1404
|
+
// shape/one-output principle `us-spelling` above follows. `hang`/`hangs`
|
|
1405
|
+
// keep a single target ("stop(s) responding") but are anchored against
|
|
1406
|
+
// the unrelated phrasal-verb idioms "hang on/up/around/out/together"
|
|
1407
|
+
// (retain, end a call, loiter), none of which describe a system that
|
|
1408
|
+
// stopped responding.
|
|
1409
|
+
//
|
|
1410
|
+
// The live h/hang page's quote is itself scoped to "a situation in which
|
|
1411
|
+
// a program encounters a problem and can't close itself" — it says
|
|
1412
|
+
// nothing about "hang" the ordinary English word, which has a long tail
|
|
1413
|
+
// of idioms/senses beyond the phrasal verbs above: "get the hang of it"
|
|
1414
|
+
// (a knack, not a system), "hang in there"/"hang tight"/"hang loose"/
|
|
1415
|
+
// "hang fire" (encouragement/waiting idioms), and the literal sense ("the
|
|
1416
|
+
// picture hangs from the ceiling"). The exclusion list also covers "of"
|
|
1417
|
+
// (the idiom's own preposition), "in", "tight", "loose", "fire", plus
|
|
1418
|
+
// "from"/"over" for the literal-suspension sense — not exhaustive
|
|
1419
|
+
// ("hang" is an ordinary word with many senses, the same breadth
|
|
1420
|
+
// `crash`/`lock up` are detection-only for), but it closes the specific
|
|
1421
|
+
// gap a plausible Redocly onboarding sentence ("Once you get the hang of
|
|
1422
|
+
// the API...") would otherwise hit.
|
|
1423
|
+
//
|
|
1424
|
+
// "hang" itself ships detection-only (not just anchored): "hang by a
|
|
1425
|
+
// thread", "hang back", "hang in there"/"hang tight"/"hang loose"/"hang
|
|
1426
|
+
// fire", "get the hang of it", the literal suspension sense ("hangs from
|
|
1427
|
+
// the ceiling") are an open-ended tail of idioms no anchor list
|
|
1428
|
+
// converges on. Severity follows fixability (`error` -> `warn`,
|
|
1429
|
+
// word-choice not structural).
|
|
1430
|
+
rules['microsoft/az-state-failure'] = swapRule({
|
|
1431
|
+
pairs: {
|
|
1432
|
+
'\\bhangs\\b(?!\\s+(?:on|up|around|out|together|of|in|tight|loose|fire|from|over)\\b)': 'stops responding',
|
|
1433
|
+
'\\bhang\\b(?!\\s+(?:on|up|around|out|together|of|in|tight|loose|fire|from|over)\\b)': 'stop responding',
|
|
1434
|
+
},
|
|
1435
|
+
message: 'Microsoft style: use "%s" instead of "%s".',
|
|
1436
|
+
link: AZ_BASE + 'h/hang',
|
|
1437
|
+
severity: 'warn',
|
|
1438
|
+
fix: false,
|
|
1439
|
+
ignoreCase: true,
|
|
1440
|
+
keysAreRegex: true,
|
|
1441
|
+
wordBoundary: false,
|
|
1442
|
+
});
|
|
1443
|
+
// Detection-only sibling of the rule above: `crash` and `lock up` both
|
|
1444
|
+
// carry a hardware-vs-software multi-target split a single `swap`
|
|
1445
|
+
// replacement can't express, so this ships as `pattern` (no fixed
|
|
1446
|
+
// target at all — the same discipline `microsoft/accessibility-terms`
|
|
1447
|
+
// uses for its own multi-target/crossed-row risk) rather than guessing
|
|
1448
|
+
// which of the two applies. `crash` is anchored against the noun
|
|
1449
|
+
// compounds ("crash dump/report/log/course/test/site") a bare match
|
|
1450
|
+
// would otherwise flag on entirely correct technical prose.
|
|
1451
|
+
rules['microsoft/az-state-failure-detect'] = patternRule({
|
|
1452
|
+
tokens: ['\\bcrash\\b(?!\\s+(?:dump|report|log|course|test|site)\\b)', '\\block up\\b'],
|
|
1453
|
+
message: 'Microsoft style: "%s" needs a context-specific replacement (fail for hardware, stop responding for programs).',
|
|
1454
|
+
link: AZ_BASE + 'c/crash',
|
|
1455
|
+
severity: 'error',
|
|
1456
|
+
ignoreCase: true,
|
|
1457
|
+
});
|
|
1458
|
+
// `terminate` is dropped entirely (not just anchored) — "Terminate the
|
|
1459
|
+
// instance/process/session/connection" is standard, correct
|
|
1460
|
+
// cloud-infrastructure vocabulary throughout Redocly's own
|
|
1461
|
+
// API-documentation domain (a genuine meaning change, not a false match:
|
|
1462
|
+
// the guide's sense is "close an app or window"), and no reliable
|
|
1463
|
+
// positional anchor separates that sense from the guide's UI sense the
|
|
1464
|
+
// way `exit`'s determiner-based anchor below does — see PROVENANCE.md's
|
|
1465
|
+
// "Excluded candidates" table. `quit`, `deinstall`, and `reinitialize`
|
|
1466
|
+
// live in `microsoft/az-lifecycle-verbs-detect` below instead: the guide
|
|
1467
|
+
// gives `quit` FOUR distinct replacements depending on meaning (not a
|
|
1468
|
+
// single swap), and marks `deinstall` and `reinitialize` conditional.
|
|
1469
|
+
// `exit`, `launch`, and `boot` stay fixable, anchored to exclude the noun
|
|
1470
|
+
// compounds/senses that would otherwise corrupt them: "exit code", "the
|
|
1471
|
+
// product launch", "boot disk" (verb position requires a following
|
|
1472
|
+
// object article, or excludes trailing/leading noun-compound words) —
|
|
1473
|
+
// the same anchoring technique `microsoft/no-click` uses for `click`,
|
|
1474
|
+
// extended from character lookaround to word lookaround.
|
|
1475
|
+
//
|
|
1476
|
+
// `imbed` -> `embed` is a same-word spelling variant (it lives in
|
|
1477
|
+
// `microsoft/spelling-hyphenation`, Tier 3, where it belongs
|
|
1478
|
+
// thematically, not here). Every OTHER pair here substitutes a DIFFERENT
|
|
1479
|
+
// word for the avoid-term (not a respelling) -- `exit`/`launch`/`boot`
|
|
1480
|
+
// are exactly the shape already anchored against noun-compound
|
|
1481
|
+
// collisions above, which is itself evidence they carry real homograph
|
|
1482
|
+
// risk, not proof a blind swap is safe. Detection-only; severity follows
|
|
1483
|
+
// fixability.
|
|
1484
|
+
rules['microsoft/az-lifecycle-verbs'] = swapRule({
|
|
1485
|
+
pairs: {
|
|
1486
|
+
'\\bcarry out\\b': 'run',
|
|
1487
|
+
'(?<!\\b(?:the|an|no|emergency)\\s)\\bexit\\b(?!\\s+(?:code|status|button|sign|strategy|interview|poll|ramp|velocity|row)\\b)': 'close',
|
|
1488
|
+
'(?<!\\b(?:product|software|game|website|app|feature|rocket|mission)\\s)\\blaunch\\b(?!\\s+(?:date|event|party|window|site|pad|day|plan|schedule|announcement)\\b)': 'open',
|
|
1489
|
+
'\\bboot\\b(?!\\s+(?:disk|sector|loader|sequence|process|time|options?|record|partition|menu|order|camera)\\b)': 'turn on',
|
|
1490
|
+
'\\bundelete\\b': 'restore',
|
|
1491
|
+
'\\binstantiate\\b': 'create an instance of',
|
|
1492
|
+
'\\biconize\\b': 'minimize',
|
|
1493
|
+
},
|
|
1494
|
+
message: 'Microsoft style: use "%s" instead of "%s".',
|
|
1495
|
+
link: AZ_BASE + 'b/boot',
|
|
1496
|
+
severity: 'warn',
|
|
1497
|
+
fix: false,
|
|
1498
|
+
ignoreCase: true,
|
|
1499
|
+
keysAreRegex: true,
|
|
1500
|
+
wordBoundary: false,
|
|
1501
|
+
});
|
|
1502
|
+
// Detection-only sibling: `quit` (multi-target), `deinstall`, and
|
|
1503
|
+
// `reinitialize` (both conditional, sharing the identical "if the UI or
|
|
1504
|
+
// API uses [it] in a label" carve-out already excluded for their sibling
|
|
1505
|
+
// `reboot` — see the file header's Tier-4 section). None of these have a
|
|
1506
|
+
// homograph/noun-compound collision risk the way `exit`/`launch`/`boot`
|
|
1507
|
+
// do; they're detection-only for a confirmation-strength reason, not a
|
|
1508
|
+
// corruption risk.
|
|
1509
|
+
rules['microsoft/az-lifecycle-verbs-detect'] = patternRule({
|
|
1510
|
+
tokens: ['\\bquit\\b', '\\bdeinstall\\b', '\\breinitialize\\b'],
|
|
1511
|
+
message: 'Microsoft style: "%s" needs a context-specific replacement or carries a conditional exception — see the a-z word list before rewriting.',
|
|
1512
|
+
link: AZ_BASE + 'q/quit',
|
|
1513
|
+
severity: 'error',
|
|
1514
|
+
ignoreCase: true,
|
|
1515
|
+
});
|
|
1516
|
+
// NOTE: `deprecated` -> `obsolete` is deliberately excluded here even
|
|
1517
|
+
// though the underlying A-Z entry is CONFIRMED: verifier G's own quote
|
|
1518
|
+
// for it carries a "(cond.)" marker ("Avoid in content for a technical
|
|
1519
|
+
// audience. Don't use in content for a general audience.") — the exact
|
|
1520
|
+
// same audience-conditional shape as the five confirmed Tier-1/Tier-4
|
|
1521
|
+
// conflicts, just not one of the five the corrections doc named
|
|
1522
|
+
// explicitly. Redocly's docs ARE technical-audience content (and
|
|
1523
|
+
// "deprecated" is itself load-bearing OpenAPI vocabulary), so this is
|
|
1524
|
+
// excluded rather than shipped unconditionally — see PROVENANCE.md.
|
|
1525
|
+
//
|
|
1526
|
+
// `SKU` and `SMB` are dropped entirely, not shipped here. Both are bare,
|
|
1527
|
+
// case-shouted acronyms with a common, correct, unrelated technical sense
|
|
1528
|
+
// in exactly Redocly's own domain — `SKU` as a standard e-commerce/
|
|
1529
|
+
// inventory field ("the SKU field") and `SMB` as the Server Message Block
|
|
1530
|
+
// network protocol ("mount the SMB share") — with no syntactic anchor
|
|
1531
|
+
// distinguishing either sense from Microsoft's intended one (both are
|
|
1532
|
+
// just the bare acronym in similar noun position). `SKU` carries a
|
|
1533
|
+
// second, independent hazard even where the guide's sense IS intended:
|
|
1534
|
+
// its replacement ("edition") is a single word, so an ALL-CAPS match
|
|
1535
|
+
// (`applyMatchCase`'s shouting branch) would insert "EDITION", and the
|
|
1536
|
+
// guide itself names four acceptable alternatives ("subscription,
|
|
1537
|
+
// edition, version, or tier"), not one. Excluded per the same
|
|
1538
|
+
// developer-audience-carve-out reasoning as `header`/`disk`/`client`/
|
|
1539
|
+
// `utility` — see PROVENANCE.md's "Excluded candidates" table.
|
|
1540
|
+
//
|
|
1541
|
+
// Every pair below is a different-word/phrase substitution
|
|
1542
|
+
// (`EULA`/`End-User License Agreement` -> `license terms` is an acronym
|
|
1543
|
+
// expanding to a DIFFERENT descriptive phrase, the same shape as `DMZ` ->
|
|
1544
|
+
// `perimeter network`, not the acronym's own literal expansion).
|
|
1545
|
+
// Detection-only; severity follows fixability.
|
|
1546
|
+
rules['microsoft/az-judgment-words'] = swapRule({
|
|
1547
|
+
pairs: {
|
|
1548
|
+
'\\bfinalize\\b': 'finish',
|
|
1549
|
+
'\\bbug fix\\b': 'software update',
|
|
1550
|
+
'\\bbeta\\b(?!\\s+(?:distribution|function|coefficient|particle|blocker|decay)\\b)': 'preview',
|
|
1551
|
+
'\\bEULA\\b': 'license terms',
|
|
1552
|
+
'\\bEnd-User License Agreement\\b': 'license terms',
|
|
1553
|
+
},
|
|
1554
|
+
message: 'Microsoft style: use "%s" instead of "%s".',
|
|
1555
|
+
link: AZ_BASE + 'f/finalize',
|
|
1556
|
+
severity: 'warn',
|
|
1557
|
+
fix: false,
|
|
1558
|
+
ignoreCase: true,
|
|
1559
|
+
keysAreRegex: true,
|
|
1560
|
+
wordBoundary: false,
|
|
1561
|
+
});
|
|
1562
|
+
// "Don't use unless you have no other choice." No fixed replacement is
|
|
1563
|
+
// given -- and "actionable" is an adjective, so a direct-substitution
|
|
1564
|
+
// swap to the relative clause "that you can act on" would be
|
|
1565
|
+
// ungrammatical in most positions ("actionable insights" ->
|
|
1566
|
+
// "that you can act on insights"). Detection-only.
|
|
1567
|
+
rules['microsoft/actionable'] = patternRule({
|
|
1568
|
+
tokens: ['\\bactionable\\b'],
|
|
1569
|
+
message: 'Avoid "%s"; rewrite using "that you can act on" (Microsoft).',
|
|
1570
|
+
link: AZ_BASE + 'a/actionable',
|
|
1571
|
+
ignoreCase: true,
|
|
1572
|
+
});
|
|
1573
|
+
// "U.S." and "U.S.A." end in a period: a TRAILING `\b` right after a
|
|
1574
|
+
// period-then-space never matches (both are non-word characters) — the
|
|
1575
|
+
// same class of bug recheck/google's `no-latinisms` fixed for `vs.`.
|
|
1576
|
+
// Leading-only `\b`, baked into the regex source via `keysAreRegex`, is
|
|
1577
|
+
// used for those two keys instead; "U.S.A." (11 chars) and "U.S." (4
|
|
1578
|
+
// chars) are both pairs in this SAME rule so `dropOverlappedShorterMatches`
|
|
1579
|
+
// resolves the overlap by keeping the longer match on "U.S.A." text.
|
|
1580
|
+
// `thank you` -> `thanks` lives in `microsoft/az-geography-detect` below,
|
|
1581
|
+
// not here — the guide marks it conditional ("formal/serious content
|
|
1582
|
+
// OK"). `USA`/`U.S.A.`/`U.S.` -> `US` is an abbreviation-punctuation
|
|
1583
|
+
// normalization of the SAME term (it lives in `microsoft/usa-abbreviation`
|
|
1584
|
+
// below, kept fixable). `Far East` -> `East Asia` is a genuine
|
|
1585
|
+
// terminology substitution -- the two terms don't even have identical
|
|
1586
|
+
// scope (Far East traditionally includes Southeast Asia; East Asia
|
|
1587
|
+
// doesn't) -- so it stays here, detection-only.
|
|
1588
|
+
rules['microsoft/az-geography'] = swapRule({
|
|
1589
|
+
pairs: {
|
|
1590
|
+
'\\bFar East\\b': 'East Asia',
|
|
1591
|
+
},
|
|
1592
|
+
message: 'Microsoft style: use "%s" instead of "%s".',
|
|
1593
|
+
link: AZ_BASE + 'f/far-east',
|
|
1594
|
+
severity: 'warn',
|
|
1595
|
+
fix: false,
|
|
1596
|
+
ignoreCase: true,
|
|
1597
|
+
keysAreRegex: true,
|
|
1598
|
+
wordBoundary: false,
|
|
1599
|
+
});
|
|
1600
|
+
// `fix: false`. "USA"/"U.S.A."/"U.S." are abbreviation-punctuation
|
|
1601
|
+
// normalizations of the SAME term in general prose, but plenty of real
|
|
1602
|
+
// organizations keep the "wrong" form as part of their own official
|
|
1603
|
+
// name: "USA Gymnastics" (the US national governing body for the sport),
|
|
1604
|
+
// "U.S. Bank" (a top-10 US bank), "U.S.A. Track and Field" (a national
|
|
1605
|
+
// governing body), "U.S. Steel", "U.S. Robotics". Normalizing any of
|
|
1606
|
+
// these silently corrupts the org's own name ("USA Gymnastics" ->
|
|
1607
|
+
// "US Gymnastics"). Severity drops error -> warn, matching this file's
|
|
1608
|
+
// policy for every other rule that flips to detection-only for a
|
|
1609
|
+
// word-choice/phrasing reason rather than a structural one.
|
|
1610
|
+
rules['microsoft/usa-abbreviation'] = swapRule({
|
|
1611
|
+
pairs: {
|
|
1612
|
+
'\\bUSA\\b': 'US',
|
|
1613
|
+
'\\bU\\.S\\.A\\.': 'US',
|
|
1614
|
+
'\\bU\\.S\\.': 'US',
|
|
1615
|
+
},
|
|
1616
|
+
message: 'Microsoft style: use "%s" instead of "%s".',
|
|
1617
|
+
link: AZ_BASE + 'f/far-east',
|
|
1618
|
+
severity: 'warn',
|
|
1619
|
+
fix: false,
|
|
1620
|
+
ignoreCase: true,
|
|
1621
|
+
keysAreRegex: true,
|
|
1622
|
+
wordBoundary: false,
|
|
1623
|
+
});
|
|
1624
|
+
rules['microsoft/az-geography-detect'] = patternRule({
|
|
1625
|
+
tokens: ['\\bthank you\\b'],
|
|
1626
|
+
message: 'Microsoft style: prefer "thanks" over "%s" in most content — see the a-z word list for the formal/serious-content exception.',
|
|
1627
|
+
link: AZ_BASE + 't/thanks-thank-you',
|
|
1628
|
+
severity: 'error',
|
|
1629
|
+
ignoreCase: true,
|
|
1630
|
+
});
|
|
1631
|
+
// `bottom left`/`bottom right` live in
|
|
1632
|
+
// `microsoft/az-direction-layout-detect` below, not here — the guide's
|
|
1633
|
+
// carve-out ("except in discussions of the BottomLeft/BottomRight
|
|
1634
|
+
// properties") is a real API-property-name collision in exactly
|
|
1635
|
+
// Redocly's domain. `left-hand`/`right-hand` -> `fix: false`: these are
|
|
1636
|
+
// DETECT-ONLY (no replacement is actually stated on the live page for
|
|
1637
|
+
// the MODIFIER sense; "left"/"right" are this preset's own inference).
|
|
1638
|
+
// Every pair below substitutes a different word/phrase, not a
|
|
1639
|
+
// respelling -- `left-justified`/`right-justified` -> `left-aligned`/
|
|
1640
|
+
// `right-aligned` is a real typography homograph risk too (justification
|
|
1641
|
+
// and alignment are DIFFERENT properties: justified text stretches to
|
|
1642
|
+
// fill the line width, aligned text doesn't). Detection-only; severity
|
|
1643
|
+
// follows fixability.
|
|
1644
|
+
rules['microsoft/az-direction-layout'] = swapRule({
|
|
1645
|
+
pairs: {
|
|
1646
|
+
'top left': 'upper left',
|
|
1647
|
+
'top right': 'upper right',
|
|
1648
|
+
'far-left': 'leftmost',
|
|
1649
|
+
'far-right': 'rightmost',
|
|
1650
|
+
'left-justified': 'left-aligned',
|
|
1651
|
+
'right-justified': 'right-aligned',
|
|
1652
|
+
'ragged right': 'left-aligned',
|
|
1653
|
+
},
|
|
1654
|
+
message: 'Microsoft style: use "%s" instead of "%s".',
|
|
1655
|
+
link: AZ_BASE + 'f/far-left-far-right',
|
|
1656
|
+
severity: 'warn',
|
|
1657
|
+
fix: false,
|
|
1658
|
+
ignoreCase: true,
|
|
1659
|
+
wordBoundary: true,
|
|
1660
|
+
});
|
|
1661
|
+
rules['microsoft/az-direction-layout-detect'] = patternRule({
|
|
1662
|
+
tokens: ['\\bbottom left\\b', '\\bbottom right\\b'],
|
|
1663
|
+
message: 'Microsoft style: use "lower left"/"lower right" instead of "%s" — except when discussing the BottomLeft/BottomRight API properties (Microsoft).',
|
|
1664
|
+
link: AZ_BASE + 'b/bottom-left-bottom-right',
|
|
1665
|
+
severity: 'error',
|
|
1666
|
+
ignoreCase: true,
|
|
1667
|
+
});
|
|
1668
|
+
rules['microsoft/left-hand-right-hand'] = swapRule({
|
|
1669
|
+
pairs: { 'left-hand': 'left', 'right-hand': 'right' },
|
|
1670
|
+
message: 'Rewrite "%s" using "%s" (Microsoft): no replacement is stated for the modifier sense.',
|
|
1671
|
+
link: AZ_BASE + 'l/left-leftmost-left-hand',
|
|
1672
|
+
severity: 'error',
|
|
1673
|
+
fix: false,
|
|
1674
|
+
ignoreCase: true,
|
|
1675
|
+
wordBoundary: true,
|
|
1676
|
+
});
|
|
1677
|
+
// UI nouns. `radio button`, `disjoint selection` (and its siblings
|
|
1678
|
+
// `contiguous selection`/`nonadjacent selection`/`noncontiguous
|
|
1679
|
+
// selection`, which share the identical "except for a technical
|
|
1680
|
+
// audience" carve-out in the same guide sentence), and `header`/`context
|
|
1681
|
+
// menu` (developer-audience carve-outs) are deliberately excluded — see
|
|
1682
|
+
// the file header and PROVENANCE.md.
|
|
1683
|
+
//
|
|
1684
|
+
// `blade` is anchored against the noun compounds ("blade
|
|
1685
|
+
// server/servers/enclosure/chassis/center(s)/centre(s)") that would
|
|
1686
|
+
// otherwise corrupt it ("A blade server occupies one slot" -> "A pane
|
|
1687
|
+
// server occupies one slot") — the term means an Azure UI panel, not a
|
|
1688
|
+
// physical server module, and the two senses share no verb-position cue
|
|
1689
|
+
// the way `exit`/`launch`/`boot` do, so this is anchored on the
|
|
1690
|
+
// following noun instead. `hierarchical menu`/`secondary menu` and
|
|
1691
|
+
// `running head`/`running foot` live in `microsoft/az-ui-nouns-detect`
|
|
1692
|
+
// below, not here — both are conditional in the guide.
|
|
1693
|
+
// `blade` -> `pane` ships detection-only despite the anchor above:
|
|
1694
|
+
// needing that anchor against "blade server" is itself evidence of
|
|
1695
|
+
// homograph risk, not proof the anchor is complete. Severity follows
|
|
1696
|
+
// fixability.
|
|
1697
|
+
rules['microsoft/az-ui-nouns'] = swapRule({
|
|
1698
|
+
pairs: {
|
|
1699
|
+
'\\bblade\\b(?!\\s+(?:server|servers|enclosure|chassis|centers?|centres?)\\b)': 'pane',
|
|
1700
|
+
'\\binsertion point\\b': 'pointer',
|
|
1701
|
+
},
|
|
1702
|
+
message: 'Microsoft style: use "%s" instead of "%s".',
|
|
1703
|
+
link: AZ_BASE + 'b/blade',
|
|
1704
|
+
severity: 'warn',
|
|
1705
|
+
fix: false,
|
|
1706
|
+
ignoreCase: true,
|
|
1707
|
+
keysAreRegex: true,
|
|
1708
|
+
wordBoundary: false,
|
|
1709
|
+
});
|
|
1710
|
+
rules['microsoft/az-ui-nouns-detect'] = patternRule({
|
|
1711
|
+
tokens: [
|
|
1712
|
+
'\\bhierarchical menu\\b',
|
|
1713
|
+
'\\bsecondary menu\\b',
|
|
1714
|
+
'\\brunning head\\b',
|
|
1715
|
+
'\\brunning foot\\b',
|
|
1716
|
+
],
|
|
1717
|
+
message: 'Microsoft style: "%s" carries a conditional exception — see the a-z word list before rewriting.',
|
|
1718
|
+
link: AZ_BASE + 'h/hierarchical-menu',
|
|
1719
|
+
severity: 'error',
|
|
1720
|
+
ignoreCase: true,
|
|
1721
|
+
});
|
|
1722
|
+
// `italics`/`italicized` live in `microsoft/italic-as-noun` below, not
|
|
1723
|
+
// here. The guide's own rule ("Use [italic] only as an adjective, not as
|
|
1724
|
+
// a noun") means a direct-substitution swap is ungrammatical in exactly
|
|
1725
|
+
// the position the avoid-term occupies: "Use italics for emphasis" ->
|
|
1726
|
+
// "Use italic for emphasis" (a bare adjective with nothing to modify) —
|
|
1727
|
+
// the identical VERB-ABLE-shaped hazard as `actionable`, adjective-for-
|
|
1728
|
+
// noun instead of clause-for-verb.
|
|
1729
|
+
//
|
|
1730
|
+
// `roman` is anchored against the civilization/proper-noun sense ("Roman
|
|
1731
|
+
// numerals/Empire/alphabet/...") that would otherwise corrupt it ("Roman
|
|
1732
|
+
// numerals are not supported" -> "regular type numerals are not
|
|
1733
|
+
// supported") — a homograph collision of the `aka`-inside-`Akamai`
|
|
1734
|
+
// shape, not a position-based one, so it's anchored on the following
|
|
1735
|
+
// noun instead. Every pair substitutes a different word -- `roman` ->
|
|
1736
|
+
// `regular type` ships detection-only despite that anchor: needing it
|
|
1737
|
+
// against a long list of proper-noun collisions (Roman numerals/Empire/
|
|
1738
|
+
// alphabet/...) is itself evidence of homograph risk. Severity follows
|
|
1739
|
+
// fixability.
|
|
1740
|
+
rules['microsoft/az-typography'] = swapRule({
|
|
1741
|
+
pairs: {
|
|
1742
|
+
'\\btypeface\\b': 'font',
|
|
1743
|
+
'\\btype style\\b': 'font style',
|
|
1744
|
+
'\\bbolded\\b': 'bold',
|
|
1745
|
+
'\\bboldface\\b': 'bold',
|
|
1746
|
+
'\\broman\\b(?!\\s+(?:numeral|numerals|empire|alphabet|calendar|law|catholic|republic|mythology|god|gods|ruins?|coins?|holiday|road|roads|bath|baths|army|legion|forum|senate|aqueduct)\\b)': 'regular type',
|
|
1747
|
+
},
|
|
1748
|
+
message: 'Microsoft style: use "%s" instead of "%s".',
|
|
1749
|
+
link: AZ_BASE + 'r/roman',
|
|
1750
|
+
severity: 'warn',
|
|
1751
|
+
fix: false,
|
|
1752
|
+
ignoreCase: true,
|
|
1753
|
+
keysAreRegex: true,
|
|
1754
|
+
wordBoundary: false,
|
|
1755
|
+
});
|
|
1756
|
+
rules['microsoft/italic-as-noun'] = patternRule({
|
|
1757
|
+
tokens: ['\\bitalics\\b', '\\bitalicized\\b'],
|
|
1758
|
+
message: 'Avoid "%s"; rewrite using "italic" as an adjective, e.g. "italic text" (Microsoft).',
|
|
1759
|
+
link: AZ_BASE + 'i/italic',
|
|
1760
|
+
severity: 'error',
|
|
1761
|
+
ignoreCase: true,
|
|
1762
|
+
});
|
|
1763
|
+
// `directory`, `disk`, and `context menu` (already excluded above) are
|
|
1764
|
+
// Redocly's own developer-audience carve-outs; the remaining filesystem
|
|
1765
|
+
// terms below have no such conflict.
|
|
1766
|
+
// `home directory` -> `root directory` is the paradigm case: unscoped,
|
|
1767
|
+
// this pair corrupts "...so the CLI can find the user's home directory
|
|
1768
|
+
// for its config files" into "...find the user's root directory..." --
|
|
1769
|
+
// semantically wrong, since the Unix `$HOME` sense has nothing to do
|
|
1770
|
+
// with `/`. Every other pair here is also a different-word substitution.
|
|
1771
|
+
// Detection-only; severity follows fixability.
|
|
1772
|
+
rules['microsoft/az-filesystem'] = swapRule({
|
|
1773
|
+
pairs: {
|
|
1774
|
+
'child folder': 'subfolder',
|
|
1775
|
+
// Scoped against the Unix/CLI $HOME sense ("so the CLI can find the
|
|
1776
|
+
// user's home directory for its config files") so the clean fixture
|
|
1777
|
+
// doesn't visibly misfire on it -- worth avoiding as noise even on a
|
|
1778
|
+
// detection-only rule.
|
|
1779
|
+
'\\bhome directory\\b(?!\\s+for\\s+(?:its|the|your|his|her|their)?\\s*config)': 'root directory',
|
|
1780
|
+
'graphics adapter': 'video card',
|
|
1781
|
+
'display adapter': 'video card',
|
|
1782
|
+
'video adapter': 'video card',
|
|
1783
|
+
'graphics card': 'video card',
|
|
1784
|
+
'display driver': 'video driver',
|
|
1785
|
+
'graphics driver': 'video driver',
|
|
1786
|
+
'remote drive': 'network drive',
|
|
1787
|
+
},
|
|
1788
|
+
message: 'Microsoft style: use "%s" instead of "%s".',
|
|
1789
|
+
link: AZ_BASE + 'c/child-folder',
|
|
1790
|
+
severity: 'warn',
|
|
1791
|
+
fix: false,
|
|
1792
|
+
ignoreCase: true,
|
|
1793
|
+
keysAreRegex: true,
|
|
1794
|
+
wordBoundary: true,
|
|
1795
|
+
});
|
|
1796
|
+
// `labelled`/`labelling` are deliberately NOT here: both already ship in
|
|
1797
|
+
// `microsoft/us-spelling`, targeting the same "labeled"/"labeling"
|
|
1798
|
+
// replacement — a duplicate pair here would double-report the same span
|
|
1799
|
+
// from two different rule names, exactly the class of collision the
|
|
1800
|
+
// `multi-factor` note above already avoids. `indices` is deliberately NOT
|
|
1801
|
+
// here either: the guide's own carve-out is "use indices only in the
|
|
1802
|
+
// context of mathematical expressions" — "array indices"/"loop indices"
|
|
1803
|
+
// are extremely common, correct usage in Redocly's own developer-audience
|
|
1804
|
+
// domain (matching the `header`/`disk`/`directory` carve-out class), and
|
|
1805
|
+
// no positional anchor reliably tells a math use from a non-math one.
|
|
1806
|
+
// Excluded rather than shipped guessing which sense applies — see
|
|
1807
|
+
// PROVENANCE.md's "Excluded candidates" table.
|
|
1808
|
+
//
|
|
1809
|
+
// `as well as` and `or greater`/`or higher`/`or lower` live in
|
|
1810
|
+
// `microsoft/az-grammar-usage-detect` below, not here. The guide
|
|
1811
|
+
// discussing a term is not the same as a blind textual substitution
|
|
1812
|
+
// being safe for it:
|
|
1813
|
+
// - `as well as` -> `and`: the live page (a/as-well-as) says "Don't use
|
|
1814
|
+
// as a synonym for and" — a caution against CONFLATING the two, not
|
|
1815
|
+
// an instruction to replace the text. "As well as being fast, the
|
|
1816
|
+
// API is reliable." -> "And being fast, the API is reliable." is not
|
|
1817
|
+
// grammatical English; "and" cannot head a sentence the way "as well
|
|
1818
|
+
// as" (a subordinating phrase) can.
|
|
1819
|
+
// - `or greater`/`or higher`/`or lower` -> `or later`/`or earlier`: the
|
|
1820
|
+
// live pages (g/greater-better, h/higher, l/lower) scope this to
|
|
1821
|
+
// "identifying multiple versions of programs or apps" — a
|
|
1822
|
+
// VERSION-NUMBER rule, not a general-magnitude rule. "A score of 80
|
|
1823
|
+
// or higher to pass" -> "A score of 80 or later to pass" is
|
|
1824
|
+
// nonsensical. `or higher` is ALSO multi-target on its own live page
|
|
1825
|
+
// (OK unchanged for display resolution; "or faster" for processor
|
|
1826
|
+
// speed; only "or later" for version numbers) — no single literal
|
|
1827
|
+
// swap target can express that, the same one-shape/one-output
|
|
1828
|
+
// principle behind `az-state-failure`'s crash/lock-up split.
|
|
1829
|
+
// No syntactic anchor reliably tells a version-number context ("Windows
|
|
1830
|
+
// 10 or higher") from an ordinary magnitude comparison ("a score of 80
|
|
1831
|
+
// or higher") — both are literally "number or higher" — so this ships
|
|
1832
|
+
// detection-only rather than guessing. See PROVENANCE.md's fix-posture
|
|
1833
|
+
// section.
|
|
1834
|
+
//
|
|
1835
|
+
// Split: the nine pairs below are all SAME-WORD normalizations -- UK/US
|
|
1836
|
+
// spelling variants (`towards`/
|
|
1837
|
+
// `upwards`/`afterwards` just add/drop a trailing "s", the same relation
|
|
1838
|
+
// as `centre`/`center`) or non-standard/alternate forms of the identical
|
|
1839
|
+
// word (`useable`/`usable`, `moveable`/`movable` are alternate spellings;
|
|
1840
|
+
// `broadcasted`/`broadcast` and `matrixes`/`appendixes` -> `matrices`/
|
|
1841
|
+
// `appendices` are non-standard vs. standard inflections of the same
|
|
1842
|
+
// word, the same class as "alot" -> "a lot"). These stay fixable.
|
|
1843
|
+
rules['microsoft/az-grammar-usage'] = swapRule({
|
|
1844
|
+
pairs: {
|
|
1845
|
+
towards: 'toward',
|
|
1846
|
+
upwards: 'upward',
|
|
1847
|
+
afterwards: 'afterward',
|
|
1848
|
+
useable: 'usable',
|
|
1849
|
+
moveable: 'movable',
|
|
1850
|
+
broadcasted: 'broadcast',
|
|
1851
|
+
matrixes: 'matrices',
|
|
1852
|
+
appendixes: 'appendices',
|
|
1853
|
+
zeroes: 'zeros',
|
|
1854
|
+
},
|
|
1855
|
+
message: 'Microsoft style: use "%s" instead of "%s".',
|
|
1856
|
+
link: AZ_BASE + 'a/as-well-as',
|
|
1857
|
+
severity: 'error',
|
|
1858
|
+
ignoreCase: true,
|
|
1859
|
+
wordBoundary: true,
|
|
1860
|
+
});
|
|
1861
|
+
// The remaining pairs from the same original rule are DIFFERENT-word/
|
|
1862
|
+
// preposition substitutions, not respellings -- `different to` ->
|
|
1863
|
+
// `different from` swaps a different preposition entirely (not an added/
|
|
1864
|
+
// dropped letter); `alphabetic`/`numerical` are established, correct
|
|
1865
|
+
// technical terms in their own right ("alphabetic character", "numeric
|
|
1866
|
+
// keypad") a blind swap would corrupt. Detection-only.
|
|
1867
|
+
rules['microsoft/az-grammar-usage-substitutions'] = swapRule({
|
|
1868
|
+
pairs: {
|
|
1869
|
+
'whether or not': 'whether',
|
|
1870
|
+
'center around': 'center on',
|
|
1871
|
+
'different to': 'different from',
|
|
1872
|
+
'inside of': 'inside',
|
|
1873
|
+
'outside of': 'outside',
|
|
1874
|
+
'off of': 'off',
|
|
1875
|
+
administrate: 'administer',
|
|
1876
|
+
alphabetic: 'alphabetical',
|
|
1877
|
+
mathematic: 'mathematical',
|
|
1878
|
+
numerical: 'numeric',
|
|
1879
|
+
},
|
|
1880
|
+
message: 'Microsoft style: use "%s" instead of "%s".',
|
|
1881
|
+
link: AZ_BASE + 'a/as-well-as',
|
|
1882
|
+
severity: 'warn',
|
|
1883
|
+
fix: false,
|
|
1884
|
+
ignoreCase: true,
|
|
1885
|
+
wordBoundary: true,
|
|
1886
|
+
});
|
|
1887
|
+
rules['microsoft/az-grammar-usage-detect'] = patternRule({
|
|
1888
|
+
tokens: ['\\bas well as\\b', '\\bor greater\\b', '\\bor higher\\b', '\\bor lower\\b'],
|
|
1889
|
+
message: 'Microsoft style: "%s" needs a context-specific rewrite, not a blind substitution — "as well as" is a caution against treating it as a synonym for "and", not an instruction to replace it; "or greater/higher/lower" only becomes "or later/earlier" when identifying program or app version numbers, not general magnitude (Microsoft).',
|
|
1890
|
+
link: AZ_BASE + 'a/as-well-as',
|
|
1891
|
+
severity: 'error',
|
|
1892
|
+
ignoreCase: true,
|
|
1893
|
+
});
|
|
1894
|
+
// NOTE: "multi-factor authentication" -> "multifactor authentication" is
|
|
1895
|
+
// deliberately NOT repeated here — `microsoft/spelling-hyphenation`'s
|
|
1896
|
+
// `\bmulti-factor\b` -> `multifactor` pair already covers this phrase
|
|
1897
|
+
// (and every other "multi-factor X" occurrence); a duplicate pair here
|
|
1898
|
+
// would double-report the same span from two different rule names.
|
|
1899
|
+
// The guide's own quote names three terms ("Use OK instead of okay or
|
|
1900
|
+
// all right. Never use alright."); `all right`/`alright` live in
|
|
1901
|
+
// `microsoft/az-abbreviations-substitutions` below instead of here (see
|
|
1902
|
+
// that rule's comment). `pound sign` lives in
|
|
1903
|
+
// `microsoft/az-abbreviations-names-detect` below, not here — the guide
|
|
1904
|
+
// carries a narrow carve-out ("OK to use pound key (#) ... to refer to
|
|
1905
|
+
// the keypad on a telephone").
|
|
1906
|
+
// Split: `defrag`/`okay` are same-word abbreviations/spelling variants
|
|
1907
|
+
// with no demonstrated unrelated sense -- stay fixable.
|
|
1908
|
+
rules['microsoft/az-abbreviations-names'] = swapRule({
|
|
1909
|
+
pairs: {
|
|
1910
|
+
defrag: 'defragment',
|
|
1911
|
+
okay: 'OK',
|
|
1912
|
+
},
|
|
1913
|
+
message: 'Microsoft style: use "%s" instead of "%s".',
|
|
1914
|
+
link: AZ_BASE + 'h/hexadecimal',
|
|
1915
|
+
severity: 'error',
|
|
1916
|
+
ignoreCase: true,
|
|
1917
|
+
wordBoundary: true,
|
|
1918
|
+
});
|
|
1919
|
+
// The remaining pairs from the same original rule flip to detection-only,
|
|
1920
|
+
// each for its own reason:
|
|
1921
|
+
// - `spec` -> `specification`: the exact named corruption case ("The
|
|
1922
|
+
// contractor built the connector on spec" -> "...on specification" --
|
|
1923
|
+
// "on spec" is a bid/contract idiom unrelated to "specification").
|
|
1924
|
+
// Same-word-abbreviation shape (like `hex`/`defrag`) but the SECOND
|
|
1925
|
+
// mechanical exception applies: the avoid-term is also a different
|
|
1926
|
+
// word in another sense.
|
|
1927
|
+
// - `hex` -> `hexadecimal`: same exception -- "hex" is also a curse/
|
|
1928
|
+
// spell ("put a hex on") and a mechanical-fastener term ("hex nut",
|
|
1929
|
+
// "hex bolt"), both common and unrelated to hexadecimal notation.
|
|
1930
|
+
// - `alright`/`all right` -> `OK`: a genuine different-word substitution
|
|
1931
|
+
// ("alright" is a non-standard spelling, but "OK" is not the same
|
|
1932
|
+
// word normalized -- it's a different word entirely); "all right" is
|
|
1933
|
+
// also two ordinary words that can appear compositionally ("not all
|
|
1934
|
+
// right answers are equally weighted"), which this pair would corrupt.
|
|
1935
|
+
// - `MSFT` -> `Microsoft`: not a word-choice issue but a genuine fix
|
|
1936
|
+
// defect -- `MSFT` is virtually always written all-caps (it's a stock
|
|
1937
|
+
// ticker), and `applyMatchCase`'s all-caps branch shouts a single-word
|
|
1938
|
+
// replacement, so the "fix" would produce "MICROSOFT" (wrong casing
|
|
1939
|
+
// for a proper noun/trademark), not the configured "Microsoft".
|
|
1940
|
+
rules['microsoft/az-abbreviations-substitutions'] = swapRule({
|
|
1941
|
+
pairs: {
|
|
1942
|
+
hex: 'hexadecimal',
|
|
1943
|
+
// Scoped against the "on spec" bid/contract idiom ("The contractor
|
|
1944
|
+
// built the connector on spec") so the clean fixture doesn't visibly
|
|
1945
|
+
// misfire on it -- worth avoiding as noise even on a detection-only
|
|
1946
|
+
// rule.
|
|
1947
|
+
'(?<!\\bon\\s)\\bspec\\b': 'specification',
|
|
1948
|
+
MSFT: 'Microsoft',
|
|
1949
|
+
alright: 'OK',
|
|
1950
|
+
'all right': 'OK',
|
|
1951
|
+
},
|
|
1952
|
+
message: 'Microsoft style: use "%s" instead of "%s".',
|
|
1953
|
+
link: AZ_BASE + 'h/hexadecimal',
|
|
1954
|
+
severity: 'warn',
|
|
1955
|
+
fix: false,
|
|
1956
|
+
ignoreCase: true,
|
|
1957
|
+
keysAreRegex: true,
|
|
1958
|
+
wordBoundary: true,
|
|
1959
|
+
});
|
|
1960
|
+
rules['microsoft/az-abbreviations-names-detect'] = patternRule({
|
|
1961
|
+
tokens: ['\\bpound sign\\b'],
|
|
1962
|
+
message: 'Microsoft style: use "number sign" instead of "%s" — except for the literal phone-keypad key (Microsoft).',
|
|
1963
|
+
link: AZ_BASE + 'n/number-sign',
|
|
1964
|
+
severity: 'error',
|
|
1965
|
+
ignoreCase: true,
|
|
1966
|
+
});
|
|
1967
|
+
// `navigate` and `scroll` are Tier-4 audience/UI conditionals (excluded —
|
|
1968
|
+
// see the file header).
|
|
1969
|
+
//
|
|
1970
|
+
// `visit` would need anchoring against the noun-compound sense ("visit
|
|
1971
|
+
// counts/duration/frequency/history/log/data" — an analytics metric, not
|
|
1972
|
+
// a verb) to avoid corrupting it ("Visit counts are aggregated per day"
|
|
1973
|
+
// -> "Go to counts are aggregated per day"), but it lives in
|
|
1974
|
+
// `microsoft/az-navigation-detect` below instead, fully detection-only.
|
|
1975
|
+
// The live v/visit page's full text is more permissive than "always use
|
|
1976
|
+
// go to": "use go to in most cases" (not "always"), and "It's OK to use
|
|
1977
|
+
// visit ... if you're using a tone that's meant to imply [a suggestion,
|
|
1978
|
+
// or the intention of browsing around]" — with the guide's OWN worked
|
|
1979
|
+
// example using "Visit" approvingly ("Visit the product website to learn
|
|
1980
|
+
// about offerings..."). A blind fix would rewrite Microsoft's own
|
|
1981
|
+
// approved example. Worse, a noun-compound anchor only excludes SPECIFIC
|
|
1982
|
+
// following words — "visit" preceded by an article ("Schedule a visit",
|
|
1983
|
+
// "during my visit") is a common, correct noun sense such an anchor would
|
|
1984
|
+
// never cover, and "Go to" isn't a grammatical noun ("Schedule a go to
|
|
1985
|
+
// with the doctor"). No anchor can tell a tone ("suggestion" vs.
|
|
1986
|
+
// "action") apart, so this ships detection-only. `hot link` has no such
|
|
1987
|
+
// nuance (unconditional, single named replacement) and stays fixable
|
|
1988
|
+
// here.
|
|
1989
|
+
//
|
|
1990
|
+
// `bookmark` -> `favorite` needs its own rule with `fix: false`, not a
|
|
1991
|
+
// pair in this one: the guide names `bookmark` VERB-ABLE ("Bookmark this
|
|
1992
|
+
// page" is a verb use whose replacement, "favorite," is not reliably
|
|
1993
|
+
// accepted as a verb outside informal/social-product UI copy) —
|
|
1994
|
+
// matching `az-verb-able`'s own treatment for the identical hazard
|
|
1995
|
+
// shape, and `fix` is a whole-RULE flag, not per-pair.
|
|
1996
|
+
//
|
|
1997
|
+
// `hot link` -> `link` drops a word rather than respelling one --
|
|
1998
|
+
// detection-only; severity follows fixability.
|
|
1999
|
+
rules['microsoft/az-navigation'] = swapRule({
|
|
2000
|
+
pairs: {
|
|
2001
|
+
'\\bhot link\\b': 'link',
|
|
2002
|
+
},
|
|
2003
|
+
message: 'Microsoft style: use "%s" instead of "%s".',
|
|
2004
|
+
link: AZ_BASE + 'v/visit',
|
|
2005
|
+
severity: 'warn',
|
|
2006
|
+
fix: false,
|
|
2007
|
+
ignoreCase: true,
|
|
2008
|
+
keysAreRegex: true,
|
|
2009
|
+
wordBoundary: false,
|
|
2010
|
+
});
|
|
2011
|
+
rules['microsoft/az-navigation-detect'] = patternRule({
|
|
2012
|
+
tokens: ['\\bvisit\\b(?!\\s+(?:count|counts|duration|frequency|history|log|data)\\b)'],
|
|
2013
|
+
message: 'Microsoft style: use "go to" instead of "%s" in most cases — but "visit" is OK for a suggestion/browsing tone (Microsoft); see the a-z word list before rewriting.',
|
|
2014
|
+
link: AZ_BASE + 'v/visit',
|
|
2015
|
+
severity: 'error',
|
|
2016
|
+
ignoreCase: true,
|
|
2017
|
+
});
|
|
2018
|
+
rules['microsoft/bookmark-favorite'] = swapRule({
|
|
2019
|
+
pairs: { bookmark: 'favorite' },
|
|
2020
|
+
message: 'Rewrite "%s" using "%s" (Microsoft): a direct substitution may be ungrammatical.',
|
|
2021
|
+
link: AZ_BASE + 'b/bookmark',
|
|
2022
|
+
severity: 'error',
|
|
2023
|
+
fix: false,
|
|
2024
|
+
ignoreCase: true,
|
|
2025
|
+
wordBoundary: true,
|
|
2026
|
+
});
|
|
2027
|
+
// From the guide's "(all don't use)" bundle: only the terms with NO
|
|
2028
|
+
// fixed replacement given anywhere on their own page are detection-only
|
|
2029
|
+
// here.
|
|
2030
|
+
rules['microsoft/az-no-replacement'] = patternRule({
|
|
2031
|
+
tokens: [
|
|
2032
|
+
'\\bblack box\\b',
|
|
2033
|
+
'\\bdot-com\\b',
|
|
2034
|
+
'\\bedutainment\\b',
|
|
2035
|
+
'\\bhoneypot\\b',
|
|
2036
|
+
'\\bbackbone\\b',
|
|
2037
|
+
'\\bwordwrap\\b',
|
|
2038
|
+
'\\bnatural user interface\\b',
|
|
2039
|
+
'\\bNUI\\b',
|
|
2040
|
+
'\\bsubaddress\\b',
|
|
2041
|
+
],
|
|
2042
|
+
message: 'Don\'t use "%s" (Microsoft); be specific instead.',
|
|
2043
|
+
link: AZ_BASE + 'b/black-box',
|
|
2044
|
+
severity: 'error',
|
|
2045
|
+
ignoreCase: true,
|
|
2046
|
+
});
|
|
2047
|
+
// From the same bundle, but each of these has a real, single, stated
|
|
2048
|
+
// replacement, unlike the no-replacement terms above.
|
|
2049
|
+
//
|
|
2050
|
+
// `print out` needs anchoring against the noun-compound sense ("a print
|
|
2051
|
+
// out OF the receipt" — a printed copy) to avoid corrupting it. The
|
|
2052
|
+
// guide's own quote is verb-scoped ("As a verb, use print instead of
|
|
2053
|
+
// print out"); the noun sense is a separate, grammatically distinct
|
|
2054
|
+
// usage ("print out" + "of" + the described item) the guide's entry
|
|
2055
|
+
// never addresses. The noun sense doesn't require a following "of"
|
|
2056
|
+
// though — "Keep the print out safe"/"Attach the print out to the
|
|
2057
|
+
// ticket" are equally common noun uses a following-"of" anchor alone
|
|
2058
|
+
// would miss. A negative lookbehind excluding a preceding
|
|
2059
|
+
// determiner/possessive closes that gap, the same noun-signaling
|
|
2060
|
+
// position `leverage`'s own comment considers — the verb sense ("print
|
|
2061
|
+
// out the report") is never preceded by a determiner directly, so this
|
|
2062
|
+
// doesn't touch genuine violations.
|
|
2063
|
+
//
|
|
2064
|
+
// Every pair is a different-word/phrase substitution, not a respelling
|
|
2065
|
+
// -- including `print out` -> `print`, despite the extensive
|
|
2066
|
+
// noun-compound anchoring above (anchoring reduces false fixes, it
|
|
2067
|
+
// doesn't turn a word-choice substitution into a same-word
|
|
2068
|
+
// normalization). Detection-only; severity follows fixability.
|
|
2069
|
+
rules['microsoft/az-real-replacements'] = swapRule({
|
|
2070
|
+
pairs: {
|
|
2071
|
+
'\\bfriendly name\\b': 'display name',
|
|
2072
|
+
'\\bprint queue\\b': 'list of documents',
|
|
2073
|
+
'\\bprinter queue\\b': 'list of documents',
|
|
2074
|
+
'\\bdata record\\b': 'record',
|
|
2075
|
+
'\\be-form\\b': 'form',
|
|
2076
|
+
'\\bupsize\\b': 'scale up',
|
|
2077
|
+
'\\bworking memory\\b': 'available memory',
|
|
2078
|
+
'\\bsoft copy\\b': 'file',
|
|
2079
|
+
'(?<!\\b(?:a|an|the|this|that|your|my|its|his|her|their|our)\\s)\\bprint out\\b(?!\\s+of\\b)': 'print',
|
|
2080
|
+
'\\bsearch and replace\\b': 'find and replace',
|
|
2081
|
+
'\\btarget drive\\b': 'destination drive',
|
|
2082
|
+
'\\btarget file\\b': 'destination file',
|
|
2083
|
+
},
|
|
2084
|
+
message: 'Microsoft style: use "%s" instead of "%s".',
|
|
2085
|
+
link: AZ_BASE + 'f/friendly-name',
|
|
2086
|
+
severity: 'warn',
|
|
2087
|
+
fix: false,
|
|
2088
|
+
ignoreCase: true,
|
|
2089
|
+
keysAreRegex: true,
|
|
2090
|
+
wordBoundary: false,
|
|
2091
|
+
});
|
|
2092
|
+
// ======================================================================
|
|
2093
|
+
// UI VERBS AND CHECKBOX/DIALOG TERMINOLOGY — `warn`. The single sharpest
|
|
2094
|
+
// divergence from recheck/google (which allows "click"): Microsoft bans
|
|
2095
|
+
// all input-specific verbs.
|
|
2096
|
+
// ======================================================================
|
|
2097
|
+
// "Don't use input-specific verbs, such as click or swipe." Anchored to
|
|
2098
|
+
// exclude the hyphen-joined compounds `double-click`/`right-click` (a
|
|
2099
|
+
// real substring risk: \bclick\b DOES match inside "double-click" once a
|
|
2100
|
+
// hyphen precedes it, since a hyphen is a non-word character) and the
|
|
2101
|
+
// unrelated compounds `clickstream`/`clickthrough`.
|
|
2102
|
+
//
|
|
2103
|
+
// The live c/click page says "Avoid this VERB" — the ban is verb-scoped,
|
|
2104
|
+
// so the hyphen/letter-adjacent compound exclusion above isn't enough on
|
|
2105
|
+
// its own: it doesn't cover the ordinary NOUN sense ("click count",
|
|
2106
|
+
// "clicks per session", "track clicks") that's genuinely common in
|
|
2107
|
+
// analytics/UI-event documentation — plausible in Redocly's own domain.
|
|
2108
|
+
// A noun-compound follow-word exclusion for `click`/`clicks` closes that
|
|
2109
|
+
// gap, matching the same technique `impact-verb`/`blade`/`exit` already
|
|
2110
|
+
// use. Residual, accepted risk (not anchored): the live page's own
|
|
2111
|
+
// carve-out "It's OK to use click when you need to describe mouse
|
|
2112
|
+
// actions specifically" isn't mechanically detectable (same class as
|
|
2113
|
+
// `hex`'s mechanical-fastener sense) — low practical likelihood in
|
|
2114
|
+
// Redocly's API-documentation domain, left as a documented residual per
|
|
2115
|
+
// PROVENANCE.md rather than expanded further.
|
|
2116
|
+
//
|
|
2117
|
+
// `click`/`clicks`/`clicking`/`clicked` -> `select` ships detection-only:
|
|
2118
|
+
// even with the extensive noun-compound anchoring above, this is a
|
|
2119
|
+
// different-word substitution for a highly polysemous UI verb, not a
|
|
2120
|
+
// respelling. Severity was already `warn` by default.
|
|
2121
|
+
rules['microsoft/no-click'] = swapRule({
|
|
2122
|
+
pairs: {
|
|
2123
|
+
'click on': 'select',
|
|
2124
|
+
'(?<![\\w-])click(?![a-zA-Z])(?!\\s+(?:count|counts|rate|rates|event|events|tracking|data|metrics?|history|id|ids|per)\\b)': 'select',
|
|
2125
|
+
'(?<![\\w-])clicks(?![a-zA-Z])(?!\\s+(?:count|counts|rate|rates|event|events|tracking|data|metrics?|history|per)\\b)': 'selects',
|
|
2126
|
+
'(?<![\\w-])clicking(?![a-zA-Z])': 'selecting',
|
|
2127
|
+
'(?<![\\w-])clicked(?![a-zA-Z])': 'selected',
|
|
2128
|
+
},
|
|
2129
|
+
message: 'Use "%s" instead of "%s" (Microsoft: avoid input-specific verbs).',
|
|
2130
|
+
fix: false,
|
|
2131
|
+
link: DESCRIBING_UI,
|
|
2132
|
+
ignoreCase: true,
|
|
2133
|
+
wordBoundary: false,
|
|
2134
|
+
keysAreRegex: true,
|
|
2135
|
+
});
|
|
2136
|
+
// "Don't use press, depress, hit, or strike [to describe pressing a
|
|
2137
|
+
// key]. Use select instead." Narrowly anchored to recognizable key-press
|
|
2138
|
+
// phrasing — bare "press"/"hit"/"strike" are far too polysemous
|
|
2139
|
+
// (press releases, press charges, hit a milestone, strike a balance) to
|
|
2140
|
+
// match unconditionally. Detection-only: the correct rewrite depends on
|
|
2141
|
+
// the surrounding sentence.
|
|
2142
|
+
rules['microsoft/press-key-verb'] = patternRule({
|
|
2143
|
+
tokens: [
|
|
2144
|
+
'\\b(?:press|hit|strike)\\s+(?:the\\s+)?(?:Enter|Tab|Esc|Escape|Delete|Backspace|spacebar|Ctrl|Shift|Alt)\\b',
|
|
2145
|
+
'\\b(?:press|hit|strike)\\s+the\\s+\\S+\\s+key\\b',
|
|
2146
|
+
],
|
|
2147
|
+
message: 'Use "select" to describe pressing a key, not "%s" (Microsoft).',
|
|
2148
|
+
link: 'https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/h/hit',
|
|
2149
|
+
ignoreCase: true,
|
|
2150
|
+
});
|
|
2151
|
+
// "Don't use [uncheck/unmark/unselect]. Use clear [for checkboxes]."
|
|
2152
|
+
// Bare "check"/"deselect" are deliberately excluded: "check" is
|
|
2153
|
+
// extremely polysemous (check the logs, check that X is true), and the
|
|
2154
|
+
// guide's replacement for "deselect" differs by UI-element type
|
|
2155
|
+
// ("clear" for checkboxes, "cancel the selection" elsewhere) in a way a
|
|
2156
|
+
// blind swap can't resolve.
|
|
2157
|
+
// Unscoped, this pair corrupts "Use the API to unmark a conversation as
|
|
2158
|
+
// read..." into "...to clear a conversation as read...". Different-word
|
|
2159
|
+
// substitution, not a respelling. Detection-only; severity was already
|
|
2160
|
+
// `warn` by default.
|
|
2161
|
+
rules['microsoft/checkbox-verbs'] = swapRule({
|
|
2162
|
+
pairs: { uncheck: 'clear', unmark: 'clear', unselect: 'clear' },
|
|
2163
|
+
message: 'Use "%s" instead of "%s" for checkboxes (Microsoft).',
|
|
2164
|
+
link: DESCRIBING_UI,
|
|
2165
|
+
fix: false,
|
|
2166
|
+
ignoreCase: true,
|
|
2167
|
+
wordBoundary: true,
|
|
2168
|
+
});
|
|
2169
|
+
// "Don't use pop-up window, dialog box, or dialogue box."
|
|
2170
|
+
// `pop-up window` -> `dialog` conflates two different UI concepts (not
|
|
2171
|
+
// every pop-up is a dialog) and `dialog box`/`dialogue box` -> `dialog`
|
|
2172
|
+
// drops a word rather than respelling one. Detection-only; severity was
|
|
2173
|
+
// already `warn` by default.
|
|
2174
|
+
rules['microsoft/dialog-terminology'] = swapRule({
|
|
2175
|
+
pairs: {
|
|
2176
|
+
'pop-up window': 'dialog',
|
|
2177
|
+
'dialog box': 'dialog',
|
|
2178
|
+
'dialogue box': 'dialog',
|
|
2179
|
+
},
|
|
2180
|
+
message: 'Use "%s" instead of "%s" (Microsoft).',
|
|
2181
|
+
link: FORMATTING_TEXT_IN_INSTRUCTIONS,
|
|
2182
|
+
fix: false,
|
|
2183
|
+
ignoreCase: true,
|
|
2184
|
+
wordBoundary: true,
|
|
2185
|
+
});
|
|
2186
|
+
// "Don't use mouse over or move the mouse pointer to." (Conditionally OK
|
|
2187
|
+
// for beginner-skill content, per the same page — low risk for reference
|
|
2188
|
+
// documentation.) Note the link's slug: it's
|
|
2189
|
+
// "mouse-mouse-interaction-terms", not "mouse-and-mouse-interaction-terms"
|
|
2190
|
+
// -- the latter 404s.
|
|
2191
|
+
// `mouse over` -> `hover over` is a different-word substitution, not a
|
|
2192
|
+
// respelling. Detection-only; severity was already `warn` by default.
|
|
2193
|
+
rules['microsoft/mouse-over'] = swapRule({
|
|
2194
|
+
pairs: { 'mouse over': 'hover over' },
|
|
2195
|
+
message: 'Use "%s" instead of "%s" (Microsoft).',
|
|
2196
|
+
link: 'https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/term-collections/mouse-mouse-interaction-terms',
|
|
2197
|
+
fix: false,
|
|
2198
|
+
ignoreCase: true,
|
|
2199
|
+
wordBoundary: true,
|
|
2200
|
+
});
|
|
2201
|
+
// "Don't put a space around the plus sign (+) in keyboard shortcuts."
|
|
2202
|
+
// Detection-only (not `swap`): `swap` replacements are literal, and the
|
|
2203
|
+
// fix would need to reproduce whichever modifier key matched -- not
|
|
2204
|
+
// possible without capture-group interpolation, which the engine does
|
|
2205
|
+
// not support (see the file header).
|
|
2206
|
+
rules['microsoft/keyboard-shortcut-plus-spacing'] = patternRule({
|
|
2207
|
+
tokens: ['\\b(?:Ctrl|Alt|Shift|Cmd)\\s+\\+\\s+'],
|
|
2208
|
+
message: 'Don\'t put a space around "+" in a keyboard shortcut (Microsoft): "%s"',
|
|
2209
|
+
link: FORMATTING_TEXT_IN_INSTRUCTIONS,
|
|
2210
|
+
});
|
|
2211
|
+
// "Don't use log in, login, log into, log on, ... Use sign in or sign
|
|
2212
|
+
// out instead." `fix: false`: "login"/"logon" are frequently used as
|
|
2213
|
+
// NOUNS or adjectives ("the login page", "your login credentials"),
|
|
2214
|
+
// where "sign in" (a verb phrase) does not slot in grammatically —the
|
|
2215
|
+
// same class of mismatch as `az-verb-able` above, just noun-for-noun
|
|
2216
|
+
// reversed.
|
|
2217
|
+
rules['microsoft/sign-in-sign-out'] = swapRule({
|
|
2218
|
+
pairs: {
|
|
2219
|
+
'log into': 'sign in to',
|
|
2220
|
+
'log onto': 'sign in to',
|
|
2221
|
+
'log in': 'sign in',
|
|
2222
|
+
login: 'sign in',
|
|
2223
|
+
'log on': 'sign in',
|
|
2224
|
+
logon: 'sign in',
|
|
2225
|
+
'log off': 'sign out',
|
|
2226
|
+
'log out': 'sign out',
|
|
2227
|
+
logout: 'sign out',
|
|
2228
|
+
'sign into': 'sign in to',
|
|
2229
|
+
signin: 'sign in',
|
|
2230
|
+
'sign off': 'sign out',
|
|
2231
|
+
},
|
|
2232
|
+
message: 'Rewrite "%s" as "%s" (Microsoft): "login"/"logon" as a noun needs a sentence rewrite.',
|
|
2233
|
+
link: 'https://learn.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/l/log-on-log-off',
|
|
2234
|
+
fix: false,
|
|
2235
|
+
ignoreCase: true,
|
|
2236
|
+
wordBoundary: true,
|
|
2237
|
+
});
|
|
2238
|
+
// ==========================================================================
|
|
2239
|
+
// DETECTION-ONLY (2026-07-30): structural override, not a per-rule policy.
|
|
2240
|
+
//
|
|
2241
|
+
// Every individual `fix: false` set above (and every rule that never had a
|
|
2242
|
+
// `fix` option to begin with) is now REDUNDANT, not load-bearing -- this
|
|
2243
|
+
// loop forces every rule in this preset to `fix: false` regardless of what
|
|
2244
|
+
// its own builder call sets, so a future contributor cannot silently
|
|
2245
|
+
// reintroduce fixing here by adding a new pair or omitting `fix: false` on
|
|
2246
|
+
// a new `swapRule()` call. See this file's header doc ("DETECTION-ONLY BY
|
|
2247
|
+
// DESIGN" section) and `presets/microsoft/PROVENANCE.md`'s "Detection-only"
|
|
2248
|
+
// section for why: five independent adversarial probes of this preset's
|
|
2249
|
+
// (and `recheck/google`'s) previously-fixable pairs, across three rounds of
|
|
2250
|
+
// narrowing the fix-safety criterion, found a RISING corruption rate (the
|
|
2251
|
+
// last round: 18 of 29 probed pairs, 62%) spanning every category once
|
|
2252
|
+
// believed safe, including spelling and hyphenation. The conclusion was
|
|
2253
|
+
// that a rule's category does not predict fix-safety -- so the fix is
|
|
2254
|
+
// structural, not another round of narrowing.
|
|
2255
|
+
//
|
|
2256
|
+
// The permanent guarantee this creates is `preset-microsoft.test.ts`'s
|
|
2257
|
+
// "no rule in recheck/microsoft is fixable" test, which reads this LIVE
|
|
2258
|
+
// returned object (not a hand-maintained list of rule names) -- the same
|
|
2259
|
+
// derive-from-the-preset shape the per-pair coverage gate already uses.
|
|
2260
|
+
// Detection is unaffected: `execute()` still runs and reports for every
|
|
2261
|
+
// rule; only `fix()` is gated off, via `core/runner.ts`'s
|
|
2262
|
+
// `rule.fix !== false` check.
|
|
2263
|
+
for (const rule of Object.values(rules)) {
|
|
2264
|
+
rule.fix = false;
|
|
2265
|
+
}
|
|
2266
|
+
return rules;
|
|
2267
|
+
}
|
|
2268
|
+
//# sourceMappingURL=microsoft.js.map
|