slopless 0.2.17 → 0.2.19

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.
@@ -49,6 +49,7 @@ export const everything = {
49
49
  "required-terms": true,
50
50
  "sentence-case": true,
51
51
  "semantic-thinness": true,
52
+ "significance-density": true,
52
53
  simplicity: true,
53
54
  "skunked-terms": true,
54
55
  "softening-language": true,
@@ -4,12 +4,14 @@ import genericSignposting from "../../rules/syntactic-patterns/lead-ins/generic-
4
4
  import lessonFraming from "../../rules/syntactic-patterns/lead-ins/lesson-framing.js";
5
5
  import llmOpeners from "../../rules/syntactic-patterns/lead-ins/llm-openers.js";
6
6
  import observerGuidance from "../../rules/syntactic-patterns/lead-ins/observer-guidance.js";
7
+ import significanceDensity from "../../rules/syntactic-patterns/lead-ins/significance-density.js";
7
8
  export const leadInRules = {
8
9
  "boilerplate-framing": boilerplateFraming,
9
10
  "formal-transition-density": formalTransitionDensity,
10
11
  "generic-signposting": genericSignposting,
11
12
  "lesson-framing": lessonFraming,
12
13
  "llm-openers": llmOpeners,
13
- "observer-guidance": observerGuidance
14
+ "observer-guidance": observerGuidance,
15
+ "significance-density": significanceDensity
14
16
  };
15
17
  //# sourceMappingURL=lead-ins.js.map
@@ -633,6 +633,30 @@
633
633
  "spread like wildfire",
634
634
  "spring to life",
635
635
  "squeaky wheel gets the grease",
636
+ "stands as a testament to",
637
+ "serves as a reminder that",
638
+ "serves as a testament to",
639
+ "paves the way for",
640
+ "ushers in a new era",
641
+ "speaks volumes about",
642
+ "reflects a broader",
643
+ "stands as a beacon of",
644
+ "represents a paradigm shift",
645
+ "embodies the spirit of",
646
+ "cements its place as",
647
+ "leaves an indelible mark",
648
+ "heralds a new era",
649
+ "signals a seismic shift",
650
+ "drives home the point",
651
+ "brings to the fore",
652
+ "takes center stage",
653
+ "reshapes the landscape",
654
+ "charts a new course",
655
+ "shines a spotlight on",
656
+ "amplifies the need for",
657
+ "redefines the boundaries of",
658
+ "paints a vivid picture of",
659
+ "holds the key to",
636
660
  "stands out like a sore thumb",
637
661
  "start from scratch",
638
662
  "stick in the mud",
@@ -3,7 +3,6 @@
3
3
  "activate the right levers",
4
4
  "apples to apples",
5
5
  "bang for your buck",
6
- "best-in-class solution",
7
6
  "circle back around",
8
7
  "customer-centric innovation",
9
8
  "cross-functional stakeholders",
@@ -35,10 +34,33 @@
35
34
  "synergies across the value chain",
36
35
  "synergy",
37
36
  "take this offline",
37
+ "table stakes",
38
+ "next-level",
39
+ "supercharge",
40
+ "world-class",
41
+ "best-in-class",
42
+ "north star",
38
43
  "think outside the box",
39
44
  "thrown under the bus",
40
45
  "unlock our full potential",
41
46
  "unlock their full potential",
42
47
  "unlock your full potential",
43
- "win-win"
48
+ "win-win",
49
+ "secret sauce",
50
+ "force multiplier",
51
+ "boil the ocean",
52
+ "best-of-breed",
53
+ "go-to-market motion",
54
+ "bleeding-edge",
55
+ "drive value",
56
+ "deliver impact",
57
+ "move fast and break things",
58
+ "10x your",
59
+ "data-driven insights",
60
+ "actionable insights",
61
+ "empower your team",
62
+ "ecosystem of partners",
63
+ "innovate at the speed of",
64
+ "reimagine the future of",
65
+ "built for the enterprise"
44
66
  ]
@@ -36,6 +36,36 @@ const PREVIEW_OBJECTS = [
36
36
  const PREVIEW_VERBS = ["explore", "discuss", "examine", "cover"];
37
37
  const REASON_STARTERS = ["reason", "factor", "point", "thing"];
38
38
  const ORDINAL_STARTERS = ["one", "another"];
39
+ // Sentence-initial filler that advertises honesty or clears the throat before
40
+ // the point. Anchored to the start of the sentence to stay low false-positive
41
+ // ("she spoke frankly" does not match; "Frankly, ..." does).
42
+ const FILLER_OPENERS = [
43
+ "to be clear",
44
+ "to be honest",
45
+ "let me be clear",
46
+ "let me be honest",
47
+ "in all honesty",
48
+ "here's the thing",
49
+ "here's the kicker",
50
+ "honestly",
51
+ "frankly",
52
+ "candidly",
53
+ "truth be told",
54
+ "if i'm being honest",
55
+ "if i'm being perfectly honest",
56
+ "let me level with you",
57
+ "i'll be straight with you",
58
+ "i'll be frank",
59
+ "no sugarcoating",
60
+ "i won't sugarcoat it",
61
+ "not to put too fine a point on it",
62
+ "let's be real",
63
+ "here's the deal",
64
+ "here's what's really going on",
65
+ "when all is said and done",
66
+ "the fact of the matter is",
67
+ "the bottom line is"
68
+ ];
39
69
  function matchEnumerationPreface(words) {
40
70
  if (words.some((word) => VAGUE_INTROS.includes(word)) &&
41
71
  words.some((word) => CATEGORY_WORDS.includes(word)) &&
@@ -81,6 +111,13 @@ function matchBoilerplateFraming(sentence) {
81
111
  if (starter !== undefined) {
82
112
  matches.push(starter);
83
113
  }
114
+ const lowered = stripped
115
+ .toLocaleLowerCase("en")
116
+ .replaceAll(String.fromCharCode(0x2019), "'");
117
+ const filler = FILLER_OPENERS.find((opener) => lowered.startsWith(opener));
118
+ if (filler !== undefined) {
119
+ matches.push(filler);
120
+ }
84
121
  return matches;
85
122
  }
86
123
  const rule = oneToOneRule({
@@ -3,12 +3,6 @@ import { cleanSentence, containsAny, tokens } from "../../../shared/matchers/pro
3
3
  import { oneToOneRule } from "../../private/textlint-rule-builders.js";
4
4
  import { matchDiscourseEvaluationFrame } from "./private/discourse-evaluation.js";
5
5
  const PREFIXES = ["however, ", "but ", "and ", "so "];
6
- const IMPORTANT_TO_PATTERNS = [
7
- "it's important to note",
8
- "it is important to note",
9
- "it's important to remember",
10
- "it is important to remember"
11
- ];
12
6
  const TRANSITION_PATTERNS = ["that being said", "as such"];
13
7
  const CONSULTATION_PATTERNS = [
14
8
  "it's always best to consult",
@@ -284,7 +278,6 @@ function matchSignposting(sentence) {
284
278
  return { kind: "formulaic-content-setup", signal: formulaicSetup };
285
279
  }
286
280
  const checks = [
287
- ["important-to", IMPORTANT_TO_PATTERNS],
288
281
  ["transition", TRANSITION_PATTERNS],
289
282
  ["consultation-signpost", CONSULTATION_PATTERNS],
290
283
  ["note-signpost", NOTE_PATTERNS],
@@ -0,0 +1,95 @@
1
+ import { splitSentences } from "../../../shared/text/sentences.js";
2
+ import { wordTokens } from "../../../shared/text/tokens.js";
3
+ import { isWhitespace } from "../../../shared/text/whitespace.js";
4
+ import { oneToOneRule } from "../../private/textlint-rule-builders.js";
5
+ // Document-level significance signposting. A single "Notably," or "It's worth noting
6
+ // that" is legitimate (technical and academic writing use them), so this is NOT a
7
+ // per-instance flag. The slop tic is sprinkling several across a short piece: AI prose
8
+ // does this constantly, technical prose almost never (0% of 600 PEP/MDN/RFC docs reach
9
+ // the threshold; ~90% of synthetic marketing slop does). Fires once per document when
10
+ // there are enough sentence-initial significance markers AND they are dense enough.
11
+ const MARKERS = [
12
+ ["notably"],
13
+ ["importantly"],
14
+ ["crucially"],
15
+ ["significantly"],
16
+ ["interestingly"],
17
+ ["remarkably"],
18
+ ["curiously"],
19
+ ["tellingly"],
20
+ ["strikingly"],
21
+ ["worth", "noting"],
22
+ ["it's", "worth", "noting"],
23
+ ["it", "is", "worth", "noting"],
24
+ ["it's", "important", "to", "note"],
25
+ ["it", "is", "important", "to", "note"],
26
+ ["it's", "important", "to", "remember"],
27
+ ["it", "is", "important", "to", "remember"],
28
+ ["it", "bears", "mentioning"]
29
+ ];
30
+ const MINIMUM_COUNT = 3;
31
+ const MINIMUM_RATE_PER_1000 = 3;
32
+ function markerLength(tokens) {
33
+ for (const marker of MARKERS) {
34
+ if (marker.every((word, index) => tokens[index]?.normalized === word)) {
35
+ return marker.length;
36
+ }
37
+ }
38
+ return 0;
39
+ }
40
+ function countWords(text) {
41
+ let count = 0;
42
+ let inWord = false;
43
+ for (const character of text) {
44
+ if (isWhitespace(character)) {
45
+ inWord = false;
46
+ }
47
+ else if (!inWord) {
48
+ count += 1;
49
+ inWord = true;
50
+ }
51
+ }
52
+ return count;
53
+ }
54
+ const rule = oneToOneRule({
55
+ detect: (unit) => {
56
+ const hits = [];
57
+ for (const sentence of splitSentences(unit.text)) {
58
+ const tokens = wordTokens(sentence.text);
59
+ const length = markerLength(tokens);
60
+ if (length === 0) {
61
+ continue;
62
+ }
63
+ const first = tokens[0];
64
+ const last = tokens[length - 1];
65
+ if (first === undefined || last === undefined) {
66
+ continue;
67
+ }
68
+ hits.push({
69
+ end: sentence.start + last.end,
70
+ start: sentence.start + first.start
71
+ });
72
+ }
73
+ const words = countWords(unit.text);
74
+ const rate = words === 0 ? 0 : (hits.length * 1000) / words;
75
+ const firstHit = hits[0];
76
+ if (hits.length < MINIMUM_COUNT ||
77
+ rate < MINIMUM_RATE_PER_1000 ||
78
+ firstHit === undefined) {
79
+ return [];
80
+ }
81
+ return [
82
+ {
83
+ evidence: `${hits.length} significance signposts in ${words} words`,
84
+ label: "significance-signposting",
85
+ range: { end: firstHit.end, start: firstHit.start }
86
+ }
87
+ ];
88
+ },
89
+ family: "syntactic-patterns",
90
+ formatMessage: (report) => `Significance signposting density: ${report.evidence}. Cut the signposting and lead with the point.`,
91
+ ruleId: "syntactic-patterns:significance-density",
92
+ unitKind: "document"
93
+ });
94
+ export default rule;
95
+ //# sourceMappingURL=significance-density.js.map
@@ -19,12 +19,7 @@
19
19
  "hope this message finds you well",
20
20
  "this is not an exhaustive list"
21
21
  ],
22
- "paragraphStartsWith": [
23
- "it is important to note",
24
- "it's important to note",
25
- "it is worth noting",
26
- "it's worth noting"
27
- ],
22
+ "paragraphStartsWith": [],
28
23
  "contains": [
29
24
  "want me to delve deeper",
30
25
  "would you like me to delve",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slopless",
3
- "version": "0.2.17",
3
+ "version": "0.2.19",
4
4
  "description": "Deterministic textlint rules and CLI for catching prose slop in English Markdown.",
5
5
  "keywords": [
6
6
  "textlint",