@faircopy/rules-nlp 1.9.0 → 1.10.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 CHANGED
@@ -22,6 +22,7 @@ rules: {
22
22
  'no-nominalized-phrases': 'warn',
23
23
  'no-pronoun-led-claims': 'warn',
24
24
  'no-buzzword-stacks': 'warn',
25
+ 'no-vague-quantifiers': 'warn',
25
26
  }
26
27
  ```
27
28
 
@@ -42,3 +43,4 @@ Package-qualified IDs like `@faircopy/rules-nlp/no-passive-voice` still work and
42
43
  | `no-nominalized-phrases` | Flag nominalized `X of Y` phrases like `optimization of onboarding` |
43
44
  | `no-pronoun-led-claims` | Flag vague sentence openers like `This helps` and `It enables` |
44
45
  | `no-buzzword-stacks` | Flag sentences overloaded with abstract benefit nouns |
46
+ | `no-vague-quantifiers` | Flag bare quantifiers without numeric anchors |
package/dist/index.d.ts CHANGED
@@ -53,6 +53,11 @@ interface NoStackedAdjectivesOptions {
53
53
  }
54
54
  declare const noStackedAdjectives: Rule<NoStackedAdjectivesOptions>;
55
55
 
56
+ interface NoVagueQuantifiersOptions {
57
+ quantifiers?: string[];
58
+ }
59
+ declare const noVagueQuantifiers: Rule<NoVagueQuantifiersOptions>;
60
+
56
61
  interface NoWeakModalsOptions {
57
62
  modals?: string[];
58
63
  verbs?: string[];
@@ -62,4 +67,4 @@ declare const noWeakModals: Rule<NoWeakModalsOptions>;
62
67
  /** All NLP rules keyed by their rule ID. */
63
68
  declare const ruleRegistry: Map<string, Rule>;
64
69
 
65
- export { type NoBuzzwordStacksOptions, type NoEmptyTransformationClaimsOptions, type NoExpletiveOpenersOptions, type NoFilterWordsOptions, type NoHedgeWordsOptions, type NoNominalizedPhrasesOptions, type NoPassiveVoiceOptions, type NoPronounLedClaimsOptions, type NoRedundantPairsOptions, type NoStackedAdjectivesOptions, type NoWeakModalsOptions, noBuzzwordStacks, noEmptyTransformationClaims, noExpletiveOpeners, noFilterWords, noHedgeWords, noNominalizedPhrases, noPassiveVoice, noPronounLedClaims, noRedundantPairs, noStackedAdjectives, noWeakModals, ruleRegistry };
70
+ export { type NoBuzzwordStacksOptions, type NoEmptyTransformationClaimsOptions, type NoExpletiveOpenersOptions, type NoFilterWordsOptions, type NoHedgeWordsOptions, type NoNominalizedPhrasesOptions, type NoPassiveVoiceOptions, type NoPronounLedClaimsOptions, type NoRedundantPairsOptions, type NoStackedAdjectivesOptions, type NoVagueQuantifiersOptions, type NoWeakModalsOptions, noBuzzwordStacks, noEmptyTransformationClaims, noExpletiveOpeners, noFilterWords, noHedgeWords, noNominalizedPhrases, noPassiveVoice, noPronounLedClaims, noRedundantPairs, noStackedAdjectives, noVagueQuantifiers, noWeakModals, ruleRegistry };
package/dist/index.js CHANGED
@@ -491,6 +491,57 @@ var noStackedAdjectives = {
491
491
  }
492
492
  };
493
493
 
494
+ // src/no-vague-quantifiers.ts
495
+ var DEFAULT_QUANTIFIERS = [
496
+ "many",
497
+ "several",
498
+ "various",
499
+ "numerous",
500
+ "multiple",
501
+ "some",
502
+ "a number of",
503
+ "a variety of",
504
+ "a range of",
505
+ "a host of",
506
+ "countless",
507
+ "tons of",
508
+ "lots of"
509
+ ];
510
+ var noVagueQuantifiers = {
511
+ id: "no-vague-quantifiers",
512
+ description: "Flag bare quantifiers without numeric anchors",
513
+ defaults: { quantifiers: DEFAULT_QUANTIFIERS },
514
+ help: "Vague quantifiers make claims hard to evaluate. Replace them with a number, range, or concrete scope when precision matters.",
515
+ check({ text, sourceMap, options }) {
516
+ const diagnostics = [];
517
+ const quantifiers = options.quantifiers?.length ? options.quantifiers : DEFAULT_QUANTIFIERS;
518
+ for (const quantifier of quantifiers) {
519
+ const re = new RegExp(
520
+ `\\b${escapeRegExp3(quantifier).replace(/\\s+/g, "\\s+")}\\b`,
521
+ "gi"
522
+ );
523
+ let match;
524
+ while ((match = re.exec(text)) !== null) {
525
+ const matchedQuantifier = match[0];
526
+ const start = sourceMap[match.index];
527
+ const end = sourceMap[match.index + matchedQuantifier.length - 1];
528
+ if (start === void 0 || end === void 0) continue;
529
+ diagnostics.push({
530
+ ruleId: "no-vague-quantifiers",
531
+ severity: "warn",
532
+ message: `replace vague quantifier "${matchedQuantifier}" with a number or concrete scope`,
533
+ range: { start, end: end + 1 },
534
+ help: noVagueQuantifiers.help
535
+ });
536
+ }
537
+ }
538
+ return diagnostics;
539
+ }
540
+ };
541
+ function escapeRegExp3(value) {
542
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
543
+ }
544
+
494
545
  // src/no-weak-modals.ts
495
546
  var DEFAULT_MODALS = ["can", "could", "may", "might"];
496
547
  var DEFAULT_VERBS2 = [
@@ -548,6 +599,7 @@ var ruleRegistry = /* @__PURE__ */ new Map([
548
599
  ["no-pronoun-led-claims", noPronounLedClaims],
549
600
  ["no-redundant-pairs", noRedundantPairs],
550
601
  ["no-stacked-adjectives", noStackedAdjectives],
602
+ ["no-vague-quantifiers", noVagueQuantifiers],
551
603
  ["no-weak-modals", noWeakModals]
552
604
  ]);
553
605
  export {
@@ -561,6 +613,7 @@ export {
561
613
  noPronounLedClaims,
562
614
  noRedundantPairs,
563
615
  noStackedAdjectives,
616
+ noVagueQuantifiers,
564
617
  noWeakModals,
565
618
  ruleRegistry
566
619
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/no-buzzword-stacks.ts","../src/utils.ts","../src/no-expletive-openers.ts","../src/no-filter-words.ts","../src/no-empty-transformation-claims.ts","../src/no-hedge-words.ts","../src/no-nominalized-phrases.ts","../src/no-passive-voice.ts","../src/no-pronoun-led-claims.ts","../src/no-redundant-pairs.ts","../src/no-stacked-adjectives.ts","../src/no-weak-modals.ts","../src/index.ts"],"sourcesContent":["import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\n\nexport interface NoBuzzwordStacksOptions {\n terms?: string[]\n maxTermsPerSentence?: number\n}\n\nconst DEFAULT_TERMS = [\n 'alignment',\n 'automation',\n 'collaboration',\n 'efficiency',\n 'engagement',\n 'experience',\n 'growth',\n 'impact',\n 'innovation',\n 'intelligence',\n 'optimization',\n 'platform',\n 'productivity',\n 'solution',\n 'strategy',\n 'transformation',\n 'value',\n 'velocity',\n 'workflow',\n]\n\nexport const noBuzzwordStacks: Rule<NoBuzzwordStacksOptions> = {\n id: 'no-buzzword-stacks',\n description: 'Flag sentences overloaded with abstract benefit nouns',\n defaults: { terms: DEFAULT_TERMS, maxTermsPerSentence: 2 },\n help: 'Buzzword-heavy sentences make copy sound interchangeable. Replace abstract benefit nouns with the specific product behavior, customer outcome, or proof point.',\n\n check({ text, sourceMap, options }: RuleInput<NoBuzzwordStacksOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const terms = options.terms?.length ? options.terms : DEFAULT_TERMS\n const maxTermsPerSentence = options.maxTermsPerSentence ?? 2\n if (maxTermsPerSentence < 1) return diagnostics\n\n for (const sentence of getSentenceRanges(text)) {\n const hits = getTermHits(sentence.text, terms)\n if (hits.length <= maxTermsPerSentence) continue\n\n const startOffset = sentence.start + hits[0]!.start\n const endOffset = sentence.start + hits[hits.length - 1]!.end\n const start = sourceMap[startOffset]\n const end = sourceMap[endOffset - 1]\n if (start === undefined || end === undefined) continue\n\n const words = hits.map(hit => hit.text.toLowerCase()).join(', ')\n diagnostics.push({\n ruleId: 'no-buzzword-stacks',\n severity: 'warn',\n message: `replace buzzword stack: ${words}`,\n range: { start, end: end + 1 },\n help: noBuzzwordStacks.help,\n })\n }\n\n return diagnostics\n },\n}\n\ninterface SentenceRange {\n text: string\n start: number\n}\n\ninterface TermHit {\n text: string\n start: number\n end: number\n}\n\nfunction getSentenceRanges(text: string): SentenceRange[] {\n const ranges: SentenceRange[] = []\n const re = /[^.!?]+[.!?]?/g\n let match: RegExpExecArray | null\n while ((match = re.exec(text)) !== null) {\n const leadingWhitespaceLength = match[0].match(/^\\s*/)?.[0].length ?? 0\n const sentence = match[0].trim()\n if (!sentence) continue\n ranges.push({ text: sentence, start: match.index + leadingWhitespaceLength })\n }\n return ranges\n}\n\nfunction getTermHits(text: string, terms: string[]): TermHit[] {\n const termPattern = terms.map(escapeRegex).join('|')\n if (!termPattern) return []\n\n const hits: TermHit[] = []\n const re = new RegExp(`\\\\b(${termPattern})\\\\b`, 'gi')\n let match: RegExpExecArray | null\n while ((match = re.exec(text)) !== null) {\n hits.push({ text: match[0], start: match.index, end: match.index + match[0].length })\n }\n return hits\n}\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","import nlp from 'compromise'\nimport type { Diagnostic } from '@faircopy/core'\nimport type { DocView, JsonOffsetEntry, JsonOffsetTerm, MatchView } from './types.js'\n\nexport interface MatchOccurrence {\n text: string\n start: number\n end: number\n}\n\nexport function createDoc(text: string): DocView {\n return nlp(text) as unknown as DocView\n}\n\nexport function getMatchOccurrences(text: string, matches: MatchView): MatchOccurrence[] {\n const json = matches.json({ offset: true, text: true, terms: { offset: true } }) as JsonOffsetEntry[]\n\n return json.flatMap((entry) => {\n const start = entry.offset?.start ?? entry.terms?.[0]?.offset?.start\n const length = entry.offset?.length ?? sumTermLengths(entry.terms)\n\n if (typeof start !== 'number' || typeof length !== 'number' || length <= 0) {\n return []\n }\n\n return [{\n text: entry.text ?? text.slice(start, start + length),\n start,\n end: start + length,\n }]\n })\n}\n\nexport function getOccurrenceRange(sourceMap: number[], occurrence: MatchOccurrence): Diagnostic['range'] | null {\n const start = sourceMap[occurrence.start]\n const end = sourceMap[occurrence.end - 1]\n if (start === undefined || end === undefined) return null\n\n return { start, end: end + 1 }\n}\n\nfunction sumTermLengths(terms: JsonOffsetTerm[] | undefined): number | undefined {\n if (!terms?.length) return undefined\n\n let total = 0\n for (const term of terms) {\n const length = term.offset?.length\n if (typeof length !== 'number') return undefined\n total += length\n }\n\n return total\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\nimport { createDoc, getMatchOccurrences, getOccurrenceRange } from './utils.js'\n\nexport interface NoExpletiveOpenersOptions {\n phrases?: string[]\n}\n\nconst DEFAULT_PHRASES = [\n 'there is',\n 'there are',\n 'there was',\n 'there were',\n 'there will be',\n]\n\nexport const noExpletiveOpeners: Rule<NoExpletiveOpenersOptions> = {\n id: 'no-expletive-openers',\n description: 'Flag sentence openings that delay the real subject',\n defaults: { phrases: DEFAULT_PHRASES },\n help: 'Expletive openers like \"there are\" make copy indirect. Start with the actor, product, or benefit so the sentence lands faster.',\n\n check({ text, sourceMap, options }: RuleInput<NoExpletiveOpenersOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const phrases = options.phrases?.length ? options.phrases : DEFAULT_PHRASES\n const doc = createDoc(text)\n\n for (const phrase of phrases) {\n const matches = doc.match(phrase)\n for (const occurrence of getMatchOccurrences(text, matches)) {\n if (!startsSentence(text, occurrence.start)) continue\n\n const range = getOccurrenceRange(sourceMap, occurrence)\n if (!range) continue\n\n diagnostics.push({\n ruleId: 'no-expletive-openers',\n severity: 'warn',\n message: `start with the real subject instead of \"${occurrence.text.toLowerCase()}\"`,\n range,\n help: noExpletiveOpeners.help,\n })\n }\n }\n\n return diagnostics.sort((left, right) => left.range.start - right.range.start)\n },\n}\n\nfunction startsSentence(text: string, start: number): boolean {\n let index = start - 1\n while (index >= 0 && /\\s/.test(text[index]!)) index--\n return index < 0 || /[.!?]/.test(text[index]!)\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\nimport { createDoc, getMatchOccurrences } from './utils.js'\n\nexport interface NoFilterWordsOptions {\n phrases?: string[]\n}\n\nconst DEFAULT_PHRASES = [\n 'I think',\n 'it seems',\n 'basically',\n 'in order to',\n]\n\nexport const noFilterWords: Rule<NoFilterWordsOptions> = {\n id: 'no-filter-words',\n description: 'Ban filter phrases that distance the claim from the reader',\n defaults: { phrases: DEFAULT_PHRASES },\n help: 'Filter phrases announce a perspective or pad the sentence instead of making the point. Delete the phrase or rewrite the sentence so the claim stands on its own.',\n\n check({ text, sourceMap, options }: RuleInput<NoFilterWordsOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const phrases = options.phrases?.length ? options.phrases : DEFAULT_PHRASES\n const doc = createDoc(text)\n\n for (const phrase of phrases) {\n const matches = doc.match(phrase)\n for (const occurrence of getMatchOccurrences(text, matches)) {\n const start = sourceMap[occurrence.start]\n const end = sourceMap[occurrence.end - 1]\n if (start === undefined || end === undefined) continue\n\n diagnostics.push({\n ruleId: 'no-filter-words',\n severity: 'error',\n message: `remove \"${occurrence.text.toLowerCase()}\" — state the claim directly`,\n range: { start, end: end + 1 },\n help: noFilterWords.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\n\nexport interface NoEmptyTransformationClaimsOptions {\n allowedPhrases?: string[]\n}\n\ninterface Pattern {\n re: RegExp\n message: string\n}\n\nconst PATTERNS: Pattern[] = [\n {\n re: /\\b(?:transform(?:s|ed|ing)?|chang(?:e|es|ed|ing)|reimagin(?:e|es|ed|ing)|revolutioniz(?:e|es|ed|ing))\\s+the\\s+way\\s+(?:you|your\\s+team|teams|companies|businesses|people)\\s+(?:work|build|sell|operate|collaborate|communicate|create|grow|ship|scale|learn|manage)\\b/gi,\n message: 'replace empty transformation claim with a concrete outcome',\n },\n {\n re: /\\bunlock\\s+(?:your|their|team|teams'|your\\s+team's|the\\s+team's|the\\s+full)\\s+(?:potential|productivity|growth|creativity|efficiency)\\b/gi,\n message: 'replace empty unlock claim with the specific benefit',\n },\n {\n re: /\\btake\\s+(?:your|their|team|teams'|your\\s+team's|the\\s+team's)?\\s*(?:productivity|workflow|workflows|growth|collaboration|business|operations|process|processes)\\s+to\\s+the\\s+next\\s+level\\b/gi,\n message: 'replace next-level claim with measurable value',\n },\n]\n\nexport const noEmptyTransformationClaims: Rule<NoEmptyTransformationClaimsOptions> = {\n id: 'no-empty-transformation-claims',\n description: 'Flag broad transformation claims that do not name a concrete outcome',\n defaults: { allowedPhrases: [] },\n help: 'Transformation cliches promise a feeling instead of a result. Replace them with the specific workflow, metric, or customer outcome the product changes.',\n\n check({ text, sourceMap, options }: RuleInput<NoEmptyTransformationClaimsOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const allowedPhrases = new Set((options.allowedPhrases ?? []).map(value => normalize(value)))\n\n for (const pattern of PATTERNS) {\n const re = new RegExp(pattern.re.source, pattern.re.flags)\n let match: RegExpExecArray | null\n while ((match = re.exec(text)) !== null) {\n const phrase = match[0]\n if (allowedPhrases.has(normalize(phrase))) continue\n\n const start = sourceMap[match.index]\n const end = sourceMap[match.index + phrase.length - 1]\n if (start === undefined || end === undefined) continue\n\n diagnostics.push({\n ruleId: 'no-empty-transformation-claims',\n severity: 'warn',\n message: `${pattern.message}: \"${phrase}\"`,\n range: { start, end: end + 1 },\n help: noEmptyTransformationClaims.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n\nfunction normalize(value: string): string {\n return value.toLowerCase().replace(/\\s+/g, ' ').trim()\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\n\nexport interface NoHedgeWordsOptions {\n hedges?: string[]\n}\n\nconst DEFAULT_HEDGES = [\n 'kind of',\n 'sort of',\n 'somewhat',\n 'fairly',\n 'pretty',\n 'rather',\n 'quite',\n 'arguably',\n 'relatively',\n 'more or less',\n]\n\nexport const noHedgeWords: Rule<NoHedgeWordsOptions> = {\n id: 'no-hedge-words',\n description: 'Flag hedge words that soften claims',\n defaults: { hedges: DEFAULT_HEDGES },\n help: 'Hedge words make claims sound uncertain. Remove the hedge or replace the sentence with a specific proof point. Words like \"pretty\" and \"quite\" can be intentional adjectives in some contexts; override hedges when that trade-off is too noisy for your copy.',\n\n check({ text, sourceMap, options }: RuleInput<NoHedgeWordsOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const hedges = options.hedges?.length ? options.hedges : DEFAULT_HEDGES\n\n for (const hedge of hedges) {\n const re = new RegExp(`\\\\b${escapeRegExp(hedge).replace(/\\\\s+/g, '\\\\s+')}\\\\b`, 'gi')\n let match: RegExpExecArray | null\n\n while ((match = re.exec(text)) !== null) {\n const phrase = match[0]\n const start = sourceMap[match.index]\n const end = sourceMap[match.index + phrase.length - 1]\n if (start === undefined || end === undefined) continue\n\n diagnostics.push({\n ruleId: 'no-hedge-words',\n severity: 'warn',\n message: `remove hedge \"${phrase.toLowerCase()}\"`,\n range: { start, end: end + 1 },\n help: noHedgeWords.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\nimport { createDoc, getMatchOccurrences, getOccurrenceRange } from './utils.js'\n\nexport interface NoNominalizedPhrasesOptions {\n suffixes?: string[]\n allowedWords?: string[]\n}\n\nconst DEFAULT_SUFFIXES = ['tion', 'sion', 'ment', 'ance', 'ence', 'ity']\nconst DEFAULT_ALLOWED_WORDS = [\n 'accessibility',\n 'availability',\n 'capacity',\n 'community',\n 'identity',\n 'opportunity',\n 'privacy',\n 'quality',\n 'reliability',\n 'security',\n]\n\nexport const noNominalizedPhrases: Rule<NoNominalizedPhrasesOptions> = {\n id: 'no-nominalized-phrases',\n description: 'Flag nominalized \"X of Y\" phrases that hide the action in a noun',\n defaults: { suffixes: DEFAULT_SUFFIXES, allowedWords: DEFAULT_ALLOWED_WORDS },\n help: 'Nominalized phrases bury the action. Rewrite the phrase with a verb so the sentence says who does what.',\n\n check({ text, sourceMap, options }: RuleInput<NoNominalizedPhrasesOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const suffixes = options.suffixes?.length ? options.suffixes : DEFAULT_SUFFIXES\n const allowedWords = new Set((options.allowedWords?.length ? options.allowedWords : DEFAULT_ALLOWED_WORDS).map(value => value.toLowerCase()))\n const suffixPattern = suffixes.map(escapeRegex).join('|')\n if (!suffixPattern) return diagnostics\n\n const doc = createDoc(text)\n const matches = doc.match(`/[a-z]+(${suffixPattern})/ of .`)\n\n for (const occurrence of getMatchOccurrences(text, matches)) {\n const nominalization = occurrence.text.trim().split(/\\s+/)[0]?.toLowerCase()\n if (!nominalization || allowedWords.has(nominalization)) continue\n\n const range = getOccurrenceRange(sourceMap, occurrence)\n if (!range) continue\n\n diagnostics.push({\n ruleId: 'no-nominalized-phrases',\n severity: 'warn',\n message: `rewrite \"${occurrence.text}\" with a verb`,\n range,\n help: noNominalizedPhrases.help,\n })\n }\n\n return diagnostics\n },\n}\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\nimport { createDoc, getMatchOccurrences } from './utils.js'\n\nexport interface NoPassiveVoiceOptions {\n allowedAuxiliaries?: string[]\n}\n\nconst DEFAULT_ALLOWED_AUXILIARIES = ['is', 'are', 'was', 'were', 'be', 'been', 'being']\n\nexport const noPassiveVoice: Rule<NoPassiveVoiceOptions> = {\n id: 'no-passive-voice',\n description: 'Flag likely passive-voice constructions using POS tagging patterns',\n defaults: { allowedAuxiliaries: DEFAULT_ALLOWED_AUXILIARIES },\n help: 'Passive voice often hides the actor and adds drag. Prefer naming who did the action unless the actor genuinely does not matter.',\n\n check({ text, sourceMap, options }: RuleInput<NoPassiveVoiceOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const auxiliaries = new Set((options.allowedAuxiliaries?.length ? options.allowedAuxiliaries : DEFAULT_ALLOWED_AUXILIARIES).map(value => value.toLowerCase()))\n const doc = createDoc(text)\n const matches = doc.match('(#Copula|#Auxiliary) #PastTense')\n\n for (const occurrence of getMatchOccurrences(text, matches)) {\n const words = occurrence.text.trim().split(/\\s+/)\n if (words.length < 2) continue\n if (!auxiliaries.has(words[0]!.toLowerCase())) continue\n\n const start = sourceMap[occurrence.start]\n const end = sourceMap[occurrence.end - 1]\n if (start === undefined || end === undefined) continue\n\n diagnostics.push({\n ruleId: 'no-passive-voice',\n severity: 'warn',\n message: `rewrite passive construction \"${occurrence.text}\" with a named actor`,\n range: { start, end: end + 1 },\n help: noPassiveVoice.help,\n })\n }\n\n return dedupeDiagnostics(diagnostics)\n },\n}\n\nfunction dedupeDiagnostics(diagnostics: Diagnostic[]): Diagnostic[] {\n const seen = new Set<string>()\n return diagnostics.filter((diagnostic) => {\n const key = `${diagnostic.range.start}:${diagnostic.range.end}`\n if (seen.has(key)) return false\n seen.add(key)\n return true\n })\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\n\nexport interface NoPronounLedClaimsOptions {\n pronouns?: string[]\n verbs?: string[]\n}\n\nconst DEFAULT_PRONOUNS = ['it', 'this', 'that', 'these', 'those']\nconst DEFAULT_VERBS = [\n 'brings',\n 'delivers',\n 'enables',\n 'gives',\n 'helps',\n 'keeps',\n 'lets',\n 'makes',\n 'turns',\n 'unlocks',\n]\n\nexport const noPronounLedClaims: Rule<NoPronounLedClaimsOptions> = {\n id: 'no-pronoun-led-claims',\n description: 'Flag vague claims that start with it, this, that, these, or those',\n defaults: { pronouns: DEFAULT_PRONOUNS, verbs: DEFAULT_VERBS },\n help: 'Pronoun-led claims make the reader infer the subject. Name the feature, product, or user action that creates the outcome.',\n\n check({ text, sourceMap, options }: RuleInput<NoPronounLedClaimsOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const pronouns = options.pronouns?.length ? options.pronouns : DEFAULT_PRONOUNS\n const verbs = options.verbs?.length ? options.verbs : DEFAULT_VERBS\n if (!pronouns.length || !verbs.length) return diagnostics\n\n const re = new RegExp(\n `(?:^|[.!?]\\\\s+)(${pronouns.map(escapeRegex).join('|')})\\\\s+(${verbs.map(escapeRegex).join('|')})\\\\b`,\n 'gi'\n )\n let match: RegExpExecArray | null\n while ((match = re.exec(text)) !== null) {\n const phrase = `${match[1]} ${match[2]}`\n const phraseStart = match.index + match[0].indexOf(phrase)\n const start = sourceMap[phraseStart]\n const end = sourceMap[phraseStart + phrase.length - 1]\n if (start === undefined || end === undefined) continue\n\n diagnostics.push({\n ruleId: 'no-pronoun-led-claims',\n severity: 'warn',\n message: `name the subject instead of \"${phrase.toLowerCase()}\"`,\n range: { start, end: end + 1 },\n help: noPronounLedClaims.help,\n })\n }\n\n return diagnostics\n },\n}\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\n\nexport interface NoRedundantPairsOptions {\n phrases?: string[]\n}\n\nconst DEFAULT_PHRASES = [\n 'first and foremost',\n 'each and every',\n 'various different',\n 'end result',\n 'final outcome',\n 'past history',\n 'future plans',\n 'unexpected surprise',\n 'advance planning',\n]\n\nexport const noRedundantPairs: Rule<NoRedundantPairsOptions> = {\n id: 'no-redundant-pairs',\n description: 'Flag redundant word pairs and padded fixed phrases',\n defaults: { phrases: DEFAULT_PHRASES },\n help: 'Redundant pairs repeat the same idea twice. Keep the stronger word or replace the phrase with a more specific claim.',\n\n check({ text, sourceMap, options }: RuleInput<NoRedundantPairsOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const phrases = options.phrases?.length ? options.phrases : DEFAULT_PHRASES\n\n for (const phrase of phrases) {\n const re = new RegExp(`\\\\b${escapeRegExp(phrase).replace(/\\\\s+/g, '\\\\s+')}\\\\b`, 'gi')\n let match: RegExpExecArray | null\n\n while ((match = re.exec(text)) !== null) {\n const matchedPhrase = match[0]\n const start = sourceMap[match.index]\n const end = sourceMap[match.index + matchedPhrase.length - 1]\n if (start === undefined || end === undefined) continue\n\n diagnostics.push({\n ruleId: 'no-redundant-pairs',\n severity: 'warn',\n message: `tighten redundant phrase \"${matchedPhrase}\"`,\n range: { start, end: end + 1 },\n help: noRedundantPairs.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\nimport { createDoc, getMatchOccurrences, getOccurrenceRange } from './utils.js'\n\nexport interface NoStackedAdjectivesOptions {\n allowedPhrases?: string[]\n}\n\nexport const noStackedAdjectives: Rule<NoStackedAdjectivesOptions> = {\n id: 'no-stacked-adjectives',\n description: 'Flag noun phrases with multiple adjectives before the noun',\n defaults: { allowedPhrases: [] },\n help: 'Stacked adjectives make copy feel generic. Keep the one descriptor that earns its place or replace the phrase with concrete evidence.',\n\n check({ text, sourceMap, options }: RuleInput<NoStackedAdjectivesOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const allowedPhrases = new Set((options.allowedPhrases ?? []).map(value => value.toLowerCase()))\n const doc = createDoc(text)\n const matches = doc.match('#Adjective #Adjective+ #Noun')\n\n for (const occurrence of getMatchOccurrences(text, matches)) {\n if (allowedPhrases.has(occurrence.text.toLowerCase())) continue\n\n const range = getOccurrenceRange(sourceMap, occurrence)\n if (!range) continue\n\n diagnostics.push({\n ruleId: 'no-stacked-adjectives',\n severity: 'warn',\n message: `cut stacked adjectives in \"${occurrence.text}\"`,\n range,\n help: noStackedAdjectives.help,\n })\n }\n\n return diagnostics\n },\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\nimport { createDoc, getMatchOccurrences, getOccurrenceRange } from './utils.js'\n\nexport interface NoWeakModalsOptions {\n modals?: string[]\n verbs?: string[]\n}\n\nconst DEFAULT_MODALS = ['can', 'could', 'may', 'might']\nconst DEFAULT_VERBS = [\n 'boost',\n 'drive',\n 'enable',\n 'help',\n 'improve',\n 'increase',\n 'make',\n 'reduce',\n 'support',\n 'transform',\n 'unlock',\n]\n\nexport const noWeakModals: Rule<NoWeakModalsOptions> = {\n id: 'no-weak-modals',\n description: 'Flag hedged modal claims like \"can help\" and \"might improve\"',\n defaults: { modals: DEFAULT_MODALS, verbs: DEFAULT_VERBS },\n help: 'Hedged modal claims sound tentative. Replace them with the outcome, capability, or proof you can stand behind.',\n\n check({ text, sourceMap, options }: RuleInput<NoWeakModalsOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const modals = new Set((options.modals?.length ? options.modals : DEFAULT_MODALS).map(value => value.toLowerCase()))\n const verbs = new Set((options.verbs?.length ? options.verbs : DEFAULT_VERBS).map(value => value.toLowerCase()))\n const doc = createDoc(text)\n const matches = doc.match('#Modal #Adverb? #Verb')\n\n for (const occurrence of getMatchOccurrences(text, matches)) {\n const words = occurrence.text.trim().split(/\\s+/)\n const modal = words[0]?.toLowerCase()\n const verb = words[words.length - 1]?.toLowerCase()\n if (!modal || !verb || !modals.has(modal) || !verbs.has(verb)) continue\n\n const range = getOccurrenceRange(sourceMap, occurrence)\n if (!range) continue\n\n diagnostics.push({\n ruleId: 'no-weak-modals',\n severity: 'warn',\n message: `replace \"${occurrence.text.toLowerCase()}\" with a direct claim`,\n range,\n help: noWeakModals.help,\n })\n }\n\n return diagnostics\n },\n}\n","import type { Rule } from '@faircopy/core'\nimport { noBuzzwordStacks } from './no-buzzword-stacks.js'\nimport { noExpletiveOpeners } from './no-expletive-openers.js'\nimport { noFilterWords } from './no-filter-words.js'\nimport { noEmptyTransformationClaims } from './no-empty-transformation-claims.js'\nimport { noHedgeWords } from './no-hedge-words.js'\nimport { noNominalizedPhrases } from './no-nominalized-phrases.js'\nimport { noPassiveVoice } from './no-passive-voice.js'\nimport { noPronounLedClaims } from './no-pronoun-led-claims.js'\nimport { noRedundantPairs } from './no-redundant-pairs.js'\nimport { noStackedAdjectives } from './no-stacked-adjectives.js'\nimport { noWeakModals } from './no-weak-modals.js'\n\nexport { noBuzzwordStacks } from './no-buzzword-stacks.js'\nexport { noExpletiveOpeners } from './no-expletive-openers.js'\nexport { noFilterWords } from './no-filter-words.js'\nexport { noEmptyTransformationClaims } from './no-empty-transformation-claims.js'\nexport { noHedgeWords } from './no-hedge-words.js'\nexport { noNominalizedPhrases } from './no-nominalized-phrases.js'\nexport { noPassiveVoice } from './no-passive-voice.js'\nexport { noPronounLedClaims } from './no-pronoun-led-claims.js'\nexport { noRedundantPairs } from './no-redundant-pairs.js'\nexport { noStackedAdjectives } from './no-stacked-adjectives.js'\nexport { noWeakModals } from './no-weak-modals.js'\nexport type { NoBuzzwordStacksOptions } from './no-buzzword-stacks.js'\nexport type { NoExpletiveOpenersOptions } from './no-expletive-openers.js'\nexport type { NoFilterWordsOptions } from './no-filter-words.js'\nexport type { NoEmptyTransformationClaimsOptions } from './no-empty-transformation-claims.js'\nexport type { NoHedgeWordsOptions } from './no-hedge-words.js'\nexport type { NoNominalizedPhrasesOptions } from './no-nominalized-phrases.js'\nexport type { NoPassiveVoiceOptions } from './no-passive-voice.js'\nexport type { NoPronounLedClaimsOptions } from './no-pronoun-led-claims.js'\nexport type { NoRedundantPairsOptions } from './no-redundant-pairs.js'\nexport type { NoStackedAdjectivesOptions } from './no-stacked-adjectives.js'\nexport type { NoWeakModalsOptions } from './no-weak-modals.js'\n\n/** All NLP rules keyed by their rule ID. */\nexport const ruleRegistry: Map<string, Rule> = new Map([\n ['no-buzzword-stacks', noBuzzwordStacks as Rule],\n ['no-empty-transformation-claims', noEmptyTransformationClaims as Rule],\n ['no-expletive-openers', noExpletiveOpeners as Rule],\n ['no-filter-words', noFilterWords as Rule],\n ['no-hedge-words', noHedgeWords as Rule],\n ['no-nominalized-phrases', noNominalizedPhrases as Rule],\n ['no-passive-voice', noPassiveVoice as Rule],\n ['no-pronoun-led-claims', noPronounLedClaims as Rule],\n ['no-redundant-pairs', noRedundantPairs as Rule],\n ['no-stacked-adjectives', noStackedAdjectives as Rule],\n ['no-weak-modals', noWeakModals as Rule],\n])\n"],"mappings":"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","names":["DEFAULT_PHRASES","escapeRegex","escapeRegex","DEFAULT_PHRASES","escapeRegExp","DEFAULT_VERBS"]}
1
+ {"version":3,"sources":["../src/no-buzzword-stacks.ts","../src/utils.ts","../src/no-expletive-openers.ts","../src/no-filter-words.ts","../src/no-empty-transformation-claims.ts","../src/no-hedge-words.ts","../src/no-nominalized-phrases.ts","../src/no-passive-voice.ts","../src/no-pronoun-led-claims.ts","../src/no-redundant-pairs.ts","../src/no-stacked-adjectives.ts","../src/no-vague-quantifiers.ts","../src/no-weak-modals.ts","../src/index.ts"],"sourcesContent":["import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\n\nexport interface NoBuzzwordStacksOptions {\n terms?: string[]\n maxTermsPerSentence?: number\n}\n\nconst DEFAULT_TERMS = [\n 'alignment',\n 'automation',\n 'collaboration',\n 'efficiency',\n 'engagement',\n 'experience',\n 'growth',\n 'impact',\n 'innovation',\n 'intelligence',\n 'optimization',\n 'platform',\n 'productivity',\n 'solution',\n 'strategy',\n 'transformation',\n 'value',\n 'velocity',\n 'workflow',\n]\n\nexport const noBuzzwordStacks: Rule<NoBuzzwordStacksOptions> = {\n id: 'no-buzzword-stacks',\n description: 'Flag sentences overloaded with abstract benefit nouns',\n defaults: { terms: DEFAULT_TERMS, maxTermsPerSentence: 2 },\n help: 'Buzzword-heavy sentences make copy sound interchangeable. Replace abstract benefit nouns with the specific product behavior, customer outcome, or proof point.',\n\n check({ text, sourceMap, options }: RuleInput<NoBuzzwordStacksOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const terms = options.terms?.length ? options.terms : DEFAULT_TERMS\n const maxTermsPerSentence = options.maxTermsPerSentence ?? 2\n if (maxTermsPerSentence < 1) return diagnostics\n\n for (const sentence of getSentenceRanges(text)) {\n const hits = getTermHits(sentence.text, terms)\n if (hits.length <= maxTermsPerSentence) continue\n\n const startOffset = sentence.start + hits[0]!.start\n const endOffset = sentence.start + hits[hits.length - 1]!.end\n const start = sourceMap[startOffset]\n const end = sourceMap[endOffset - 1]\n if (start === undefined || end === undefined) continue\n\n const words = hits.map(hit => hit.text.toLowerCase()).join(', ')\n diagnostics.push({\n ruleId: 'no-buzzword-stacks',\n severity: 'warn',\n message: `replace buzzword stack: ${words}`,\n range: { start, end: end + 1 },\n help: noBuzzwordStacks.help,\n })\n }\n\n return diagnostics\n },\n}\n\ninterface SentenceRange {\n text: string\n start: number\n}\n\ninterface TermHit {\n text: string\n start: number\n end: number\n}\n\nfunction getSentenceRanges(text: string): SentenceRange[] {\n const ranges: SentenceRange[] = []\n const re = /[^.!?]+[.!?]?/g\n let match: RegExpExecArray | null\n while ((match = re.exec(text)) !== null) {\n const leadingWhitespaceLength = match[0].match(/^\\s*/)?.[0].length ?? 0\n const sentence = match[0].trim()\n if (!sentence) continue\n ranges.push({ text: sentence, start: match.index + leadingWhitespaceLength })\n }\n return ranges\n}\n\nfunction getTermHits(text: string, terms: string[]): TermHit[] {\n const termPattern = terms.map(escapeRegex).join('|')\n if (!termPattern) return []\n\n const hits: TermHit[] = []\n const re = new RegExp(`\\\\b(${termPattern})\\\\b`, 'gi')\n let match: RegExpExecArray | null\n while ((match = re.exec(text)) !== null) {\n hits.push({ text: match[0], start: match.index, end: match.index + match[0].length })\n }\n return hits\n}\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","import nlp from 'compromise'\nimport type { Diagnostic } from '@faircopy/core'\nimport type { DocView, JsonOffsetEntry, JsonOffsetTerm, MatchView } from './types.js'\n\nexport interface MatchOccurrence {\n text: string\n start: number\n end: number\n}\n\nexport function createDoc(text: string): DocView {\n return nlp(text) as unknown as DocView\n}\n\nexport function getMatchOccurrences(text: string, matches: MatchView): MatchOccurrence[] {\n const json = matches.json({ offset: true, text: true, terms: { offset: true } }) as JsonOffsetEntry[]\n\n return json.flatMap((entry) => {\n const start = entry.offset?.start ?? entry.terms?.[0]?.offset?.start\n const length = entry.offset?.length ?? sumTermLengths(entry.terms)\n\n if (typeof start !== 'number' || typeof length !== 'number' || length <= 0) {\n return []\n }\n\n return [{\n text: entry.text ?? text.slice(start, start + length),\n start,\n end: start + length,\n }]\n })\n}\n\nexport function getOccurrenceRange(sourceMap: number[], occurrence: MatchOccurrence): Diagnostic['range'] | null {\n const start = sourceMap[occurrence.start]\n const end = sourceMap[occurrence.end - 1]\n if (start === undefined || end === undefined) return null\n\n return { start, end: end + 1 }\n}\n\nfunction sumTermLengths(terms: JsonOffsetTerm[] | undefined): number | undefined {\n if (!terms?.length) return undefined\n\n let total = 0\n for (const term of terms) {\n const length = term.offset?.length\n if (typeof length !== 'number') return undefined\n total += length\n }\n\n return total\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\nimport { createDoc, getMatchOccurrences, getOccurrenceRange } from './utils.js'\n\nexport interface NoExpletiveOpenersOptions {\n phrases?: string[]\n}\n\nconst DEFAULT_PHRASES = [\n 'there is',\n 'there are',\n 'there was',\n 'there were',\n 'there will be',\n]\n\nexport const noExpletiveOpeners: Rule<NoExpletiveOpenersOptions> = {\n id: 'no-expletive-openers',\n description: 'Flag sentence openings that delay the real subject',\n defaults: { phrases: DEFAULT_PHRASES },\n help: 'Expletive openers like \"there are\" make copy indirect. Start with the actor, product, or benefit so the sentence lands faster.',\n\n check({ text, sourceMap, options }: RuleInput<NoExpletiveOpenersOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const phrases = options.phrases?.length ? options.phrases : DEFAULT_PHRASES\n const doc = createDoc(text)\n\n for (const phrase of phrases) {\n const matches = doc.match(phrase)\n for (const occurrence of getMatchOccurrences(text, matches)) {\n if (!startsSentence(text, occurrence.start)) continue\n\n const range = getOccurrenceRange(sourceMap, occurrence)\n if (!range) continue\n\n diagnostics.push({\n ruleId: 'no-expletive-openers',\n severity: 'warn',\n message: `start with the real subject instead of \"${occurrence.text.toLowerCase()}\"`,\n range,\n help: noExpletiveOpeners.help,\n })\n }\n }\n\n return diagnostics.sort((left, right) => left.range.start - right.range.start)\n },\n}\n\nfunction startsSentence(text: string, start: number): boolean {\n let index = start - 1\n while (index >= 0 && /\\s/.test(text[index]!)) index--\n return index < 0 || /[.!?]/.test(text[index]!)\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\nimport { createDoc, getMatchOccurrences } from './utils.js'\n\nexport interface NoFilterWordsOptions {\n phrases?: string[]\n}\n\nconst DEFAULT_PHRASES = [\n 'I think',\n 'it seems',\n 'basically',\n 'in order to',\n]\n\nexport const noFilterWords: Rule<NoFilterWordsOptions> = {\n id: 'no-filter-words',\n description: 'Ban filter phrases that distance the claim from the reader',\n defaults: { phrases: DEFAULT_PHRASES },\n help: 'Filter phrases announce a perspective or pad the sentence instead of making the point. Delete the phrase or rewrite the sentence so the claim stands on its own.',\n\n check({ text, sourceMap, options }: RuleInput<NoFilterWordsOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const phrases = options.phrases?.length ? options.phrases : DEFAULT_PHRASES\n const doc = createDoc(text)\n\n for (const phrase of phrases) {\n const matches = doc.match(phrase)\n for (const occurrence of getMatchOccurrences(text, matches)) {\n const start = sourceMap[occurrence.start]\n const end = sourceMap[occurrence.end - 1]\n if (start === undefined || end === undefined) continue\n\n diagnostics.push({\n ruleId: 'no-filter-words',\n severity: 'error',\n message: `remove \"${occurrence.text.toLowerCase()}\" — state the claim directly`,\n range: { start, end: end + 1 },\n help: noFilterWords.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\n\nexport interface NoEmptyTransformationClaimsOptions {\n allowedPhrases?: string[]\n}\n\ninterface Pattern {\n re: RegExp\n message: string\n}\n\nconst PATTERNS: Pattern[] = [\n {\n re: /\\b(?:transform(?:s|ed|ing)?|chang(?:e|es|ed|ing)|reimagin(?:e|es|ed|ing)|revolutioniz(?:e|es|ed|ing))\\s+the\\s+way\\s+(?:you|your\\s+team|teams|companies|businesses|people)\\s+(?:work|build|sell|operate|collaborate|communicate|create|grow|ship|scale|learn|manage)\\b/gi,\n message: 'replace empty transformation claim with a concrete outcome',\n },\n {\n re: /\\bunlock\\s+(?:your|their|team|teams'|your\\s+team's|the\\s+team's|the\\s+full)\\s+(?:potential|productivity|growth|creativity|efficiency)\\b/gi,\n message: 'replace empty unlock claim with the specific benefit',\n },\n {\n re: /\\btake\\s+(?:your|their|team|teams'|your\\s+team's|the\\s+team's)?\\s*(?:productivity|workflow|workflows|growth|collaboration|business|operations|process|processes)\\s+to\\s+the\\s+next\\s+level\\b/gi,\n message: 'replace next-level claim with measurable value',\n },\n]\n\nexport const noEmptyTransformationClaims: Rule<NoEmptyTransformationClaimsOptions> = {\n id: 'no-empty-transformation-claims',\n description: 'Flag broad transformation claims that do not name a concrete outcome',\n defaults: { allowedPhrases: [] },\n help: 'Transformation cliches promise a feeling instead of a result. Replace them with the specific workflow, metric, or customer outcome the product changes.',\n\n check({ text, sourceMap, options }: RuleInput<NoEmptyTransformationClaimsOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const allowedPhrases = new Set((options.allowedPhrases ?? []).map(value => normalize(value)))\n\n for (const pattern of PATTERNS) {\n const re = new RegExp(pattern.re.source, pattern.re.flags)\n let match: RegExpExecArray | null\n while ((match = re.exec(text)) !== null) {\n const phrase = match[0]\n if (allowedPhrases.has(normalize(phrase))) continue\n\n const start = sourceMap[match.index]\n const end = sourceMap[match.index + phrase.length - 1]\n if (start === undefined || end === undefined) continue\n\n diagnostics.push({\n ruleId: 'no-empty-transformation-claims',\n severity: 'warn',\n message: `${pattern.message}: \"${phrase}\"`,\n range: { start, end: end + 1 },\n help: noEmptyTransformationClaims.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n\nfunction normalize(value: string): string {\n return value.toLowerCase().replace(/\\s+/g, ' ').trim()\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\n\nexport interface NoHedgeWordsOptions {\n hedges?: string[]\n}\n\nconst DEFAULT_HEDGES = [\n 'kind of',\n 'sort of',\n 'somewhat',\n 'fairly',\n 'pretty',\n 'rather',\n 'quite',\n 'arguably',\n 'relatively',\n 'more or less',\n]\n\nexport const noHedgeWords: Rule<NoHedgeWordsOptions> = {\n id: 'no-hedge-words',\n description: 'Flag hedge words that soften claims',\n defaults: { hedges: DEFAULT_HEDGES },\n help: 'Hedge words make claims sound uncertain. Remove the hedge or replace the sentence with a specific proof point. Words like \"pretty\" and \"quite\" can be intentional adjectives in some contexts; override hedges when that trade-off is too noisy for your copy.',\n\n check({ text, sourceMap, options }: RuleInput<NoHedgeWordsOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const hedges = options.hedges?.length ? options.hedges : DEFAULT_HEDGES\n\n for (const hedge of hedges) {\n const re = new RegExp(`\\\\b${escapeRegExp(hedge).replace(/\\\\s+/g, '\\\\s+')}\\\\b`, 'gi')\n let match: RegExpExecArray | null\n\n while ((match = re.exec(text)) !== null) {\n const phrase = match[0]\n const start = sourceMap[match.index]\n const end = sourceMap[match.index + phrase.length - 1]\n if (start === undefined || end === undefined) continue\n\n diagnostics.push({\n ruleId: 'no-hedge-words',\n severity: 'warn',\n message: `remove hedge \"${phrase.toLowerCase()}\"`,\n range: { start, end: end + 1 },\n help: noHedgeWords.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\nimport { createDoc, getMatchOccurrences, getOccurrenceRange } from './utils.js'\n\nexport interface NoNominalizedPhrasesOptions {\n suffixes?: string[]\n allowedWords?: string[]\n}\n\nconst DEFAULT_SUFFIXES = ['tion', 'sion', 'ment', 'ance', 'ence', 'ity']\nconst DEFAULT_ALLOWED_WORDS = [\n 'accessibility',\n 'availability',\n 'capacity',\n 'community',\n 'identity',\n 'opportunity',\n 'privacy',\n 'quality',\n 'reliability',\n 'security',\n]\n\nexport const noNominalizedPhrases: Rule<NoNominalizedPhrasesOptions> = {\n id: 'no-nominalized-phrases',\n description: 'Flag nominalized \"X of Y\" phrases that hide the action in a noun',\n defaults: { suffixes: DEFAULT_SUFFIXES, allowedWords: DEFAULT_ALLOWED_WORDS },\n help: 'Nominalized phrases bury the action. Rewrite the phrase with a verb so the sentence says who does what.',\n\n check({ text, sourceMap, options }: RuleInput<NoNominalizedPhrasesOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const suffixes = options.suffixes?.length ? options.suffixes : DEFAULT_SUFFIXES\n const allowedWords = new Set((options.allowedWords?.length ? options.allowedWords : DEFAULT_ALLOWED_WORDS).map(value => value.toLowerCase()))\n const suffixPattern = suffixes.map(escapeRegex).join('|')\n if (!suffixPattern) return diagnostics\n\n const doc = createDoc(text)\n const matches = doc.match(`/[a-z]+(${suffixPattern})/ of .`)\n\n for (const occurrence of getMatchOccurrences(text, matches)) {\n const nominalization = occurrence.text.trim().split(/\\s+/)[0]?.toLowerCase()\n if (!nominalization || allowedWords.has(nominalization)) continue\n\n const range = getOccurrenceRange(sourceMap, occurrence)\n if (!range) continue\n\n diagnostics.push({\n ruleId: 'no-nominalized-phrases',\n severity: 'warn',\n message: `rewrite \"${occurrence.text}\" with a verb`,\n range,\n help: noNominalizedPhrases.help,\n })\n }\n\n return diagnostics\n },\n}\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\nimport { createDoc, getMatchOccurrences } from './utils.js'\n\nexport interface NoPassiveVoiceOptions {\n allowedAuxiliaries?: string[]\n}\n\nconst DEFAULT_ALLOWED_AUXILIARIES = ['is', 'are', 'was', 'were', 'be', 'been', 'being']\n\nexport const noPassiveVoice: Rule<NoPassiveVoiceOptions> = {\n id: 'no-passive-voice',\n description: 'Flag likely passive-voice constructions using POS tagging patterns',\n defaults: { allowedAuxiliaries: DEFAULT_ALLOWED_AUXILIARIES },\n help: 'Passive voice often hides the actor and adds drag. Prefer naming who did the action unless the actor genuinely does not matter.',\n\n check({ text, sourceMap, options }: RuleInput<NoPassiveVoiceOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const auxiliaries = new Set((options.allowedAuxiliaries?.length ? options.allowedAuxiliaries : DEFAULT_ALLOWED_AUXILIARIES).map(value => value.toLowerCase()))\n const doc = createDoc(text)\n const matches = doc.match('(#Copula|#Auxiliary) #PastTense')\n\n for (const occurrence of getMatchOccurrences(text, matches)) {\n const words = occurrence.text.trim().split(/\\s+/)\n if (words.length < 2) continue\n if (!auxiliaries.has(words[0]!.toLowerCase())) continue\n\n const start = sourceMap[occurrence.start]\n const end = sourceMap[occurrence.end - 1]\n if (start === undefined || end === undefined) continue\n\n diagnostics.push({\n ruleId: 'no-passive-voice',\n severity: 'warn',\n message: `rewrite passive construction \"${occurrence.text}\" with a named actor`,\n range: { start, end: end + 1 },\n help: noPassiveVoice.help,\n })\n }\n\n return dedupeDiagnostics(diagnostics)\n },\n}\n\nfunction dedupeDiagnostics(diagnostics: Diagnostic[]): Diagnostic[] {\n const seen = new Set<string>()\n return diagnostics.filter((diagnostic) => {\n const key = `${diagnostic.range.start}:${diagnostic.range.end}`\n if (seen.has(key)) return false\n seen.add(key)\n return true\n })\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\n\nexport interface NoPronounLedClaimsOptions {\n pronouns?: string[]\n verbs?: string[]\n}\n\nconst DEFAULT_PRONOUNS = ['it', 'this', 'that', 'these', 'those']\nconst DEFAULT_VERBS = [\n 'brings',\n 'delivers',\n 'enables',\n 'gives',\n 'helps',\n 'keeps',\n 'lets',\n 'makes',\n 'turns',\n 'unlocks',\n]\n\nexport const noPronounLedClaims: Rule<NoPronounLedClaimsOptions> = {\n id: 'no-pronoun-led-claims',\n description: 'Flag vague claims that start with it, this, that, these, or those',\n defaults: { pronouns: DEFAULT_PRONOUNS, verbs: DEFAULT_VERBS },\n help: 'Pronoun-led claims make the reader infer the subject. Name the feature, product, or user action that creates the outcome.',\n\n check({ text, sourceMap, options }: RuleInput<NoPronounLedClaimsOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const pronouns = options.pronouns?.length ? options.pronouns : DEFAULT_PRONOUNS\n const verbs = options.verbs?.length ? options.verbs : DEFAULT_VERBS\n if (!pronouns.length || !verbs.length) return diagnostics\n\n const re = new RegExp(\n `(?:^|[.!?]\\\\s+)(${pronouns.map(escapeRegex).join('|')})\\\\s+(${verbs.map(escapeRegex).join('|')})\\\\b`,\n 'gi'\n )\n let match: RegExpExecArray | null\n while ((match = re.exec(text)) !== null) {\n const phrase = `${match[1]} ${match[2]}`\n const phraseStart = match.index + match[0].indexOf(phrase)\n const start = sourceMap[phraseStart]\n const end = sourceMap[phraseStart + phrase.length - 1]\n if (start === undefined || end === undefined) continue\n\n diagnostics.push({\n ruleId: 'no-pronoun-led-claims',\n severity: 'warn',\n message: `name the subject instead of \"${phrase.toLowerCase()}\"`,\n range: { start, end: end + 1 },\n help: noPronounLedClaims.help,\n })\n }\n\n return diagnostics\n },\n}\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\n\nexport interface NoRedundantPairsOptions {\n phrases?: string[]\n}\n\nconst DEFAULT_PHRASES = [\n 'first and foremost',\n 'each and every',\n 'various different',\n 'end result',\n 'final outcome',\n 'past history',\n 'future plans',\n 'unexpected surprise',\n 'advance planning',\n]\n\nexport const noRedundantPairs: Rule<NoRedundantPairsOptions> = {\n id: 'no-redundant-pairs',\n description: 'Flag redundant word pairs and padded fixed phrases',\n defaults: { phrases: DEFAULT_PHRASES },\n help: 'Redundant pairs repeat the same idea twice. Keep the stronger word or replace the phrase with a more specific claim.',\n\n check({ text, sourceMap, options }: RuleInput<NoRedundantPairsOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const phrases = options.phrases?.length ? options.phrases : DEFAULT_PHRASES\n\n for (const phrase of phrases) {\n const re = new RegExp(`\\\\b${escapeRegExp(phrase).replace(/\\\\s+/g, '\\\\s+')}\\\\b`, 'gi')\n let match: RegExpExecArray | null\n\n while ((match = re.exec(text)) !== null) {\n const matchedPhrase = match[0]\n const start = sourceMap[match.index]\n const end = sourceMap[match.index + matchedPhrase.length - 1]\n if (start === undefined || end === undefined) continue\n\n diagnostics.push({\n ruleId: 'no-redundant-pairs',\n severity: 'warn',\n message: `tighten redundant phrase \"${matchedPhrase}\"`,\n range: { start, end: end + 1 },\n help: noRedundantPairs.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\nimport { createDoc, getMatchOccurrences, getOccurrenceRange } from './utils.js'\n\nexport interface NoStackedAdjectivesOptions {\n allowedPhrases?: string[]\n}\n\nexport const noStackedAdjectives: Rule<NoStackedAdjectivesOptions> = {\n id: 'no-stacked-adjectives',\n description: 'Flag noun phrases with multiple adjectives before the noun',\n defaults: { allowedPhrases: [] },\n help: 'Stacked adjectives make copy feel generic. Keep the one descriptor that earns its place or replace the phrase with concrete evidence.',\n\n check({ text, sourceMap, options }: RuleInput<NoStackedAdjectivesOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const allowedPhrases = new Set((options.allowedPhrases ?? []).map(value => value.toLowerCase()))\n const doc = createDoc(text)\n const matches = doc.match('#Adjective #Adjective+ #Noun')\n\n for (const occurrence of getMatchOccurrences(text, matches)) {\n if (allowedPhrases.has(occurrence.text.toLowerCase())) continue\n\n const range = getOccurrenceRange(sourceMap, occurrence)\n if (!range) continue\n\n diagnostics.push({\n ruleId: 'no-stacked-adjectives',\n severity: 'warn',\n message: `cut stacked adjectives in \"${occurrence.text}\"`,\n range,\n help: noStackedAdjectives.help,\n })\n }\n\n return diagnostics\n },\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\n\nexport interface NoVagueQuantifiersOptions {\n quantifiers?: string[]\n}\n\nconst DEFAULT_QUANTIFIERS = [\n 'many',\n 'several',\n 'various',\n 'numerous',\n 'multiple',\n 'some',\n 'a number of',\n 'a variety of',\n 'a range of',\n 'a host of',\n 'countless',\n 'tons of',\n 'lots of',\n]\n\nexport const noVagueQuantifiers: Rule<NoVagueQuantifiersOptions> = {\n id: 'no-vague-quantifiers',\n description: 'Flag bare quantifiers without numeric anchors',\n defaults: { quantifiers: DEFAULT_QUANTIFIERS },\n help:\n 'Vague quantifiers make claims hard to evaluate. Replace them with a number, range, or concrete scope when precision matters.',\n\n check({ text, sourceMap, options }: RuleInput<NoVagueQuantifiersOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const quantifiers = options.quantifiers?.length ? options.quantifiers : DEFAULT_QUANTIFIERS\n\n for (const quantifier of quantifiers) {\n const re = new RegExp(\n `\\\\b${escapeRegExp(quantifier).replace(/\\\\s+/g, '\\\\s+')}\\\\b`,\n 'gi'\n )\n let match: RegExpExecArray | null\n\n while ((match = re.exec(text)) !== null) {\n const matchedQuantifier = match[0]\n const start = sourceMap[match.index]\n const end = sourceMap[match.index + matchedQuantifier.length - 1]\n if (start === undefined || end === undefined) continue\n\n diagnostics.push({\n ruleId: 'no-vague-quantifiers',\n severity: 'warn',\n message: `replace vague quantifier \"${matchedQuantifier}\" with a number or concrete scope`,\n range: { start, end: end + 1 },\n help: noVagueQuantifiers.help,\n })\n }\n }\n\n return diagnostics\n },\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n}\n","import type { Diagnostic, Rule, RuleInput } from '@faircopy/core'\nimport { createDoc, getMatchOccurrences, getOccurrenceRange } from './utils.js'\n\nexport interface NoWeakModalsOptions {\n modals?: string[]\n verbs?: string[]\n}\n\nconst DEFAULT_MODALS = ['can', 'could', 'may', 'might']\nconst DEFAULT_VERBS = [\n 'boost',\n 'drive',\n 'enable',\n 'help',\n 'improve',\n 'increase',\n 'make',\n 'reduce',\n 'support',\n 'transform',\n 'unlock',\n]\n\nexport const noWeakModals: Rule<NoWeakModalsOptions> = {\n id: 'no-weak-modals',\n description: 'Flag hedged modal claims like \"can help\" and \"might improve\"',\n defaults: { modals: DEFAULT_MODALS, verbs: DEFAULT_VERBS },\n help: 'Hedged modal claims sound tentative. Replace them with the outcome, capability, or proof you can stand behind.',\n\n check({ text, sourceMap, options }: RuleInput<NoWeakModalsOptions>): Diagnostic[] {\n const diagnostics: Diagnostic[] = []\n const modals = new Set((options.modals?.length ? options.modals : DEFAULT_MODALS).map(value => value.toLowerCase()))\n const verbs = new Set((options.verbs?.length ? options.verbs : DEFAULT_VERBS).map(value => value.toLowerCase()))\n const doc = createDoc(text)\n const matches = doc.match('#Modal #Adverb? #Verb')\n\n for (const occurrence of getMatchOccurrences(text, matches)) {\n const words = occurrence.text.trim().split(/\\s+/)\n const modal = words[0]?.toLowerCase()\n const verb = words[words.length - 1]?.toLowerCase()\n if (!modal || !verb || !modals.has(modal) || !verbs.has(verb)) continue\n\n const range = getOccurrenceRange(sourceMap, occurrence)\n if (!range) continue\n\n diagnostics.push({\n ruleId: 'no-weak-modals',\n severity: 'warn',\n message: `replace \"${occurrence.text.toLowerCase()}\" with a direct claim`,\n range,\n help: noWeakModals.help,\n })\n }\n\n return diagnostics\n },\n}\n","import type { Rule } from '@faircopy/core'\nimport { noBuzzwordStacks } from './no-buzzword-stacks.js'\nimport { noExpletiveOpeners } from './no-expletive-openers.js'\nimport { noFilterWords } from './no-filter-words.js'\nimport { noEmptyTransformationClaims } from './no-empty-transformation-claims.js'\nimport { noHedgeWords } from './no-hedge-words.js'\nimport { noNominalizedPhrases } from './no-nominalized-phrases.js'\nimport { noPassiveVoice } from './no-passive-voice.js'\nimport { noPronounLedClaims } from './no-pronoun-led-claims.js'\nimport { noRedundantPairs } from './no-redundant-pairs.js'\nimport { noStackedAdjectives } from './no-stacked-adjectives.js'\nimport { noVagueQuantifiers } from './no-vague-quantifiers.js'\nimport { noWeakModals } from './no-weak-modals.js'\n\nexport { noBuzzwordStacks } from './no-buzzword-stacks.js'\nexport { noExpletiveOpeners } from './no-expletive-openers.js'\nexport { noFilterWords } from './no-filter-words.js'\nexport { noEmptyTransformationClaims } from './no-empty-transformation-claims.js'\nexport { noHedgeWords } from './no-hedge-words.js'\nexport { noNominalizedPhrases } from './no-nominalized-phrases.js'\nexport { noPassiveVoice } from './no-passive-voice.js'\nexport { noPronounLedClaims } from './no-pronoun-led-claims.js'\nexport { noRedundantPairs } from './no-redundant-pairs.js'\nexport { noStackedAdjectives } from './no-stacked-adjectives.js'\nexport { noVagueQuantifiers } from './no-vague-quantifiers.js'\nexport { noWeakModals } from './no-weak-modals.js'\nexport type { NoBuzzwordStacksOptions } from './no-buzzword-stacks.js'\nexport type { NoExpletiveOpenersOptions } from './no-expletive-openers.js'\nexport type { NoFilterWordsOptions } from './no-filter-words.js'\nexport type { NoEmptyTransformationClaimsOptions } from './no-empty-transformation-claims.js'\nexport type { NoHedgeWordsOptions } from './no-hedge-words.js'\nexport type { NoNominalizedPhrasesOptions } from './no-nominalized-phrases.js'\nexport type { NoPassiveVoiceOptions } from './no-passive-voice.js'\nexport type { NoPronounLedClaimsOptions } from './no-pronoun-led-claims.js'\nexport type { NoRedundantPairsOptions } from './no-redundant-pairs.js'\nexport type { NoStackedAdjectivesOptions } from './no-stacked-adjectives.js'\nexport type { NoVagueQuantifiersOptions } from './no-vague-quantifiers.js'\nexport type { NoWeakModalsOptions } from './no-weak-modals.js'\n\n/** All NLP rules keyed by their rule ID. */\nexport const ruleRegistry: Map<string, Rule> = new Map([\n ['no-buzzword-stacks', noBuzzwordStacks as Rule],\n ['no-empty-transformation-claims', noEmptyTransformationClaims as Rule],\n ['no-expletive-openers', noExpletiveOpeners as Rule],\n ['no-filter-words', noFilterWords as Rule],\n ['no-hedge-words', noHedgeWords as Rule],\n ['no-nominalized-phrases', noNominalizedPhrases as Rule],\n ['no-passive-voice', noPassiveVoice as Rule],\n ['no-pronoun-led-claims', noPronounLedClaims as Rule],\n ['no-redundant-pairs', noRedundantPairs as Rule],\n ['no-stacked-adjectives', noStackedAdjectives as Rule],\n ['no-vague-quantifiers', noVagueQuantifiers as Rule],\n ['no-weak-modals', noWeakModals as Rule],\n])\n"],"mappings":"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","names":["DEFAULT_PHRASES","escapeRegex","escapeRegex","DEFAULT_PHRASES","escapeRegExp","escapeRegExp","DEFAULT_VERBS"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faircopy/rules-nlp",
3
- "version": "1.9.0",
3
+ "version": "1.10.0",
4
4
  "description": "Optional NLP-powered ruleset for faircopy using compromise",
5
5
  "type": "module",
6
6
  "exports": {
@@ -19,7 +19,7 @@
19
19
  "prepublishOnly": "pnpm run build"
20
20
  },
21
21
  "dependencies": {
22
- "@faircopy/core": "1.9.0",
22
+ "@faircopy/core": "1.10.0",
23
23
  "compromise": "^14.15.0"
24
24
  },
25
25
  "devDependencies": {