@faircopy/rules-nlp 1.8.0 → 1.9.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 +2 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +49 -2
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ rulesets: ['@faircopy/rules-nlp'],
|
|
|
13
13
|
rules: {
|
|
14
14
|
'no-expletive-openers': 'warn',
|
|
15
15
|
'no-filter-words': 'warn',
|
|
16
|
+
'no-hedge-words': 'warn',
|
|
16
17
|
'no-empty-transformation-claims': 'warn',
|
|
17
18
|
'no-passive-voice': 'warn',
|
|
18
19
|
'no-redundant-pairs': 'warn',
|
|
@@ -33,6 +34,7 @@ Package-qualified IDs like `@faircopy/rules-nlp/no-passive-voice` still work and
|
|
|
33
34
|
| `no-empty-transformation-claims` | Flag broad transformation cliches like `transform the way teams work` |
|
|
34
35
|
| `no-expletive-openers` | Flag sentence openings like `There are` |
|
|
35
36
|
| `no-filter-words` | Ban filter phrases like `I think` and `it seems` |
|
|
37
|
+
| `no-hedge-words` | Flag hedge words like `kind of` and `somewhat` |
|
|
36
38
|
| `no-passive-voice` | Flag likely passive-voice constructions |
|
|
37
39
|
| `no-redundant-pairs` | Flag redundant fixed phrases like `first and foremost` |
|
|
38
40
|
| `no-weak-modals` | Flag hedged modal claims like `can help` and `might improve` |
|
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,11 @@ interface NoEmptyTransformationClaimsOptions {
|
|
|
21
21
|
}
|
|
22
22
|
declare const noEmptyTransformationClaims: Rule<NoEmptyTransformationClaimsOptions>;
|
|
23
23
|
|
|
24
|
+
interface NoHedgeWordsOptions {
|
|
25
|
+
hedges?: string[];
|
|
26
|
+
}
|
|
27
|
+
declare const noHedgeWords: Rule<NoHedgeWordsOptions>;
|
|
28
|
+
|
|
24
29
|
interface NoNominalizedPhrasesOptions {
|
|
25
30
|
suffixes?: string[];
|
|
26
31
|
allowedWords?: string[];
|
|
@@ -57,4 +62,4 @@ declare const noWeakModals: Rule<NoWeakModalsOptions>;
|
|
|
57
62
|
/** All NLP rules keyed by their rule ID. */
|
|
58
63
|
declare const ruleRegistry: Map<string, Rule>;
|
|
59
64
|
|
|
60
|
-
export { type NoBuzzwordStacksOptions, type NoEmptyTransformationClaimsOptions, type NoExpletiveOpenersOptions, type NoFilterWordsOptions, type NoNominalizedPhrasesOptions, type NoPassiveVoiceOptions, type NoPronounLedClaimsOptions, type NoRedundantPairsOptions, type NoStackedAdjectivesOptions, type NoWeakModalsOptions, noBuzzwordStacks, noEmptyTransformationClaims, noExpletiveOpeners, noFilterWords, noNominalizedPhrases, noPassiveVoice, noPronounLedClaims, noRedundantPairs, noStackedAdjectives, noWeakModals, ruleRegistry };
|
|
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 };
|
package/dist/index.js
CHANGED
|
@@ -238,6 +238,51 @@ function normalize(value) {
|
|
|
238
238
|
return value.toLowerCase().replace(/\s+/g, " ").trim();
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
+
// src/no-hedge-words.ts
|
|
242
|
+
var DEFAULT_HEDGES = [
|
|
243
|
+
"kind of",
|
|
244
|
+
"sort of",
|
|
245
|
+
"somewhat",
|
|
246
|
+
"fairly",
|
|
247
|
+
"pretty",
|
|
248
|
+
"rather",
|
|
249
|
+
"quite",
|
|
250
|
+
"arguably",
|
|
251
|
+
"relatively",
|
|
252
|
+
"more or less"
|
|
253
|
+
];
|
|
254
|
+
var noHedgeWords = {
|
|
255
|
+
id: "no-hedge-words",
|
|
256
|
+
description: "Flag hedge words that soften claims",
|
|
257
|
+
defaults: { hedges: DEFAULT_HEDGES },
|
|
258
|
+
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.',
|
|
259
|
+
check({ text, sourceMap, options }) {
|
|
260
|
+
const diagnostics = [];
|
|
261
|
+
const hedges = options.hedges?.length ? options.hedges : DEFAULT_HEDGES;
|
|
262
|
+
for (const hedge of hedges) {
|
|
263
|
+
const re = new RegExp(`\\b${escapeRegExp(hedge).replace(/\\s+/g, "\\s+")}\\b`, "gi");
|
|
264
|
+
let match;
|
|
265
|
+
while ((match = re.exec(text)) !== null) {
|
|
266
|
+
const phrase = match[0];
|
|
267
|
+
const start = sourceMap[match.index];
|
|
268
|
+
const end = sourceMap[match.index + phrase.length - 1];
|
|
269
|
+
if (start === void 0 || end === void 0) continue;
|
|
270
|
+
diagnostics.push({
|
|
271
|
+
ruleId: "no-hedge-words",
|
|
272
|
+
severity: "warn",
|
|
273
|
+
message: `remove hedge "${phrase.toLowerCase()}"`,
|
|
274
|
+
range: { start, end: end + 1 },
|
|
275
|
+
help: noHedgeWords.help
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return diagnostics;
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
function escapeRegExp(value) {
|
|
283
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
284
|
+
}
|
|
285
|
+
|
|
241
286
|
// src/no-nominalized-phrases.ts
|
|
242
287
|
var DEFAULT_SUFFIXES = ["tion", "sion", "ment", "ance", "ence", "ity"];
|
|
243
288
|
var DEFAULT_ALLOWED_WORDS = [
|
|
@@ -396,7 +441,7 @@ var noRedundantPairs = {
|
|
|
396
441
|
const diagnostics = [];
|
|
397
442
|
const phrases = options.phrases?.length ? options.phrases : DEFAULT_PHRASES3;
|
|
398
443
|
for (const phrase of phrases) {
|
|
399
|
-
const re = new RegExp(`\\b${
|
|
444
|
+
const re = new RegExp(`\\b${escapeRegExp2(phrase).replace(/\\s+/g, "\\s+")}\\b`, "gi");
|
|
400
445
|
let match;
|
|
401
446
|
while ((match = re.exec(text)) !== null) {
|
|
402
447
|
const matchedPhrase = match[0];
|
|
@@ -415,7 +460,7 @@ var noRedundantPairs = {
|
|
|
415
460
|
return diagnostics;
|
|
416
461
|
}
|
|
417
462
|
};
|
|
418
|
-
function
|
|
463
|
+
function escapeRegExp2(value) {
|
|
419
464
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
420
465
|
}
|
|
421
466
|
|
|
@@ -497,6 +542,7 @@ var ruleRegistry = /* @__PURE__ */ new Map([
|
|
|
497
542
|
["no-empty-transformation-claims", noEmptyTransformationClaims],
|
|
498
543
|
["no-expletive-openers", noExpletiveOpeners],
|
|
499
544
|
["no-filter-words", noFilterWords],
|
|
545
|
+
["no-hedge-words", noHedgeWords],
|
|
500
546
|
["no-nominalized-phrases", noNominalizedPhrases],
|
|
501
547
|
["no-passive-voice", noPassiveVoice],
|
|
502
548
|
["no-pronoun-led-claims", noPronounLedClaims],
|
|
@@ -509,6 +555,7 @@ export {
|
|
|
509
555
|
noEmptyTransformationClaims,
|
|
510
556
|
noExpletiveOpeners,
|
|
511
557
|
noFilterWords,
|
|
558
|
+
noHedgeWords,
|
|
512
559
|
noNominalizedPhrases,
|
|
513
560
|
noPassiveVoice,
|
|
514
561
|
noPronounLedClaims,
|
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-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'\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 { 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 { 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 { 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-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","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-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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@faircopy/rules-nlp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.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.
|
|
22
|
+
"@faircopy/core": "1.9.0",
|
|
23
23
|
"compromise": "^14.15.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|