book-kit 0.0.0-stage → 0.6.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.
Files changed (139) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +72 -0
  3. package/README.md +251 -2
  4. package/bin/book-kit.js +115 -0
  5. package/commands/_emoji_io.py +32 -0
  6. package/commands/audit-chapter-lengths.js +244 -0
  7. package/commands/check-epub-size.js +68 -0
  8. package/commands/check-pdf-page-count.js +162 -0
  9. package/commands/composite-kindle-emoji.py +85 -0
  10. package/commands/composite-print-emoji.py +98 -0
  11. package/commands/conventions.js +76 -0
  12. package/commands/convert-pdfx.sh +194 -0
  13. package/commands/download-icc-profile.sh +105 -0
  14. package/commands/fonts.py +212 -0
  15. package/commands/generate-reference-docx.py +344 -0
  16. package/commands/kindle-fonts.js +173 -0
  17. package/commands/links-to-footnotes.js +366 -0
  18. package/commands/markdownlint.js +25 -0
  19. package/commands/minify-css.js +43 -0
  20. package/commands/optimize-images.js +73 -0
  21. package/commands/parity.sh +150 -0
  22. package/commands/postprocess-epub.js +541 -0
  23. package/commands/prep-print-src.js +77 -0
  24. package/commands/prose.js +38 -0
  25. package/commands/release-files.js +89 -0
  26. package/commands/reorder-print-frontmatter.js +79 -0
  27. package/commands/strip-page-rules.js +1168 -0
  28. package/commands/strip-property-rules.js +93 -0
  29. package/commands/update-key-takeaways.js +314 -0
  30. package/commands/validate.js +283 -0
  31. package/configs/markdownlint-cli2.jsonc +98 -0
  32. package/configs/textlintrc.json +22 -0
  33. package/css/theme/01-tokens.css +83 -0
  34. package/css/theme/02-fonts.css +79 -0
  35. package/css/theme/03-prose.css +53 -0
  36. package/css/theme/04-base.css +226 -0
  37. package/css/theme/05-components.css +546 -0
  38. package/css/theme/06-utilities.css +40 -0
  39. package/css/theme/07-ereader.css +245 -0
  40. package/css/theme/08-epub.css +865 -0
  41. package/css/theme/09-back-matter.css +47 -0
  42. package/css/theme/10-dark.css +302 -0
  43. package/css/theme/11-print.css +1229 -0
  44. package/css/theme.css +38 -0
  45. package/defaults/book.config.yml +318 -0
  46. package/defaults/pandoc-base.yml +19 -0
  47. package/defaults/pandoc-docx.yml +6 -0
  48. package/defaults/pandoc-epub-fonts.yml +6 -0
  49. package/defaults/pandoc-epub.yml +15 -0
  50. package/defaults/pandoc-kindle.yml +6 -0
  51. package/emoji/1f382.png +0 -0
  52. package/emoji/1f389.png +0 -0
  53. package/emoji/1f440.png +0 -0
  54. package/emoji/1f44d.png +0 -0
  55. package/emoji/1f44f.png +0 -0
  56. package/emoji/1f454.png +0 -0
  57. package/emoji/1f4a1.png +0 -0
  58. package/emoji/1f4bb.png +0 -0
  59. package/emoji/1f602.png +0 -0
  60. package/emoji/1f603.png +0 -0
  61. package/emoji/1f604.png +0 -0
  62. package/emoji/1f60a.png +0 -0
  63. package/emoji/1f60f.png +0 -0
  64. package/emoji/1f64c.png +0 -0
  65. package/emoji/1f64f.png +0 -0
  66. package/emoji/1f914.png +0 -0
  67. package/emoji/1f923.png +0 -0
  68. package/emoji/2714.png +0 -0
  69. package/emoji/274c.png +0 -0
  70. package/filters/add-div-titles.lua +196 -0
  71. package/filters/arc-notice.lua +36 -0
  72. package/filters/body-emoji-images.lua +48 -0
  73. package/filters/callout-classes.lua +40 -0
  74. package/filters/lib/book_kit.lua +110 -0
  75. package/filters/revision.lua +17 -0
  76. package/filters/strip-comments.lua +40 -0
  77. package/filters/strip-emoji-kindle.lua +134 -0
  78. package/fonts/LICENSE-source-code-pro.md +93 -0
  79. package/fonts/LICENSE-source-sans.md +93 -0
  80. package/fonts/LICENSE-source-serif.md +93 -0
  81. package/fonts/recipe.json +41 -0
  82. package/just/book-kit.just +229 -0
  83. package/lib/acronyms.js +47 -0
  84. package/lib/callouts.js +17 -0
  85. package/lib/config.js +211 -0
  86. package/lib/is-main.js +21 -0
  87. package/lib/lint.js +148 -0
  88. package/lib/pandoc.js +148 -0
  89. package/lib/paragraphs.js +173 -0
  90. package/lib/revision.js +35 -0
  91. package/lib/structure.js +39 -0
  92. package/lib/textlint-github-formatter.js +58 -0
  93. package/lib/validate.js +202 -0
  94. package/package.json +98 -4
  95. package/presets/business-nonfiction/CONVENTIONS.md +35 -0
  96. package/presets/business-nonfiction/preset.yml +128 -0
  97. package/profiles/pdfx-def.ps +42 -0
  98. package/requirements.txt +14 -0
  99. package/rules/no-ai-like-patterns.js +209 -0
  100. package/rules/no-assumptive.js +76 -0
  101. package/rules/no-bare-acronyms.js +129 -0
  102. package/rules/no-cliches.js +544 -0
  103. package/rules/no-cultural-idioms.js +422 -0
  104. package/rules/no-vague-intensifiers.js +94 -0
  105. package/templates/template-epub.html +79 -0
  106. package/templates/template.html +73 -0
  107. package/toolchain.json +42 -0
  108. package/validators/acronym-expansion.js +94 -0
  109. package/validators/bold-keywords.js +56 -0
  110. package/validators/callout-balance.js +74 -0
  111. package/validators/callout-divs.js +66 -0
  112. package/validators/callout-labels.js +102 -0
  113. package/validators/chapter-lengths.js +14 -0
  114. package/validators/chapter.js +57 -0
  115. package/validators/cross-references.js +45 -0
  116. package/validators/doubled-words.js +76 -0
  117. package/validators/duplicate-headings.js +52 -0
  118. package/validators/em-dashes.js +73 -0
  119. package/validators/escaped-smallcaps.js +46 -0
  120. package/validators/footnotes.js +78 -0
  121. package/validators/heading-hierarchy.js +58 -0
  122. package/validators/hypothetical-hooks.js +103 -0
  123. package/validators/index-integrity.js +28 -0
  124. package/validators/index.js +73 -0
  125. package/validators/list-punctuation.js +123 -0
  126. package/validators/min-opener-length.js +87 -0
  127. package/validators/no-bare-url-link-text.js +38 -0
  128. package/validators/no-todo-markers.js +49 -0
  129. package/validators/no-transition-todos.js +34 -0
  130. package/validators/number-range-dashes.js +72 -0
  131. package/validators/paragraph-length.js +62 -0
  132. package/validators/quote-punctuation.js +85 -0
  133. package/validators/section-intro-final-lead.js +90 -0
  134. package/validators/section-intro-has-lead.js +41 -0
  135. package/validators/section-intro-no-tldr.js +37 -0
  136. package/validators/section-intros.js +33 -0
  137. package/validators/sentence-starters.js +132 -0
  138. package/validators/smallcaps.js +67 -0
  139. package/validators/tldr-length.js +58 -0
@@ -0,0 +1,67 @@
1
+ import fs from 'node:fs';
2
+ import { GENERATED_FILES, KNOWN_ACRONYMS } from '../lib/validate.js';
3
+
4
+ /**
5
+ * Validate that known acronyms use [ACRONYM]{.smallcaps} formatting.
6
+ * Reports warnings for bare acronyms found outside code blocks and URLs.
7
+ * @param {string[]} indexFiles - Files listed in index.yml
8
+ * @returns {{errors: string[], warnings: string[]}}
9
+ */
10
+ export function validateSmallcaps(indexFiles) {
11
+ const warnings = [];
12
+
13
+ for (const file of indexFiles) {
14
+ if (!fs.existsSync(file)) continue;
15
+ if (GENERATED_FILES.has(file)) continue;
16
+ const content = fs.readFileSync(file, 'utf8');
17
+ const lines = content.split('\n');
18
+ let inCodeBlock = false;
19
+
20
+ for (let i = 0; i < lines.length; i++) {
21
+ const line = lines[i];
22
+
23
+ // Track code blocks
24
+ if (line.trim().startsWith('```')) {
25
+ inCodeBlock = !inCodeBlock;
26
+ continue;
27
+ }
28
+ if (inCodeBlock) continue;
29
+
30
+ for (const acr of KNOWN_ACRONYMS) {
31
+ // Build regex for bare acronym with word boundaries. The trailing `s?`
32
+ // catches bare *plurals* ("ICs", "Q&As"): `\bIC\b` can't match inside
33
+ // "ICs" — C and s are both word characters, so there's no boundary
34
+ // between them — which left an entire class of misses invisible. The
35
+ // correct form keeps the "s" outside the span ([IC]{.smallcaps}s), so
36
+ // the leading lookbehind still skips properly-marked-up plurals.
37
+ const pattern = new RegExp(`(?<!\\[)(?<!\\\\\\[)\\b${acr}s?\\b(?!\\]\\{)(?![^[]*\\{\\.)`, 'g');
38
+ let m;
39
+ while ((m = pattern.exec(line)) !== null) {
40
+ const before = line.slice(0, m.index);
41
+
42
+ // Skip if inside inline code (odd number of backticks before)
43
+ if ((before.match(/`/g) || []).length % 2 === 1) continue;
44
+
45
+ // Skip if inside a URL
46
+ if (/https?:\/\/\S*$/.test(before)) continue;
47
+
48
+ // Skip if inside a markdown link URL: [text](http...ACRONYM
49
+ if (/\]\([^)]*$/.test(before)) continue;
50
+
51
+ warnings.push(
52
+ `Bare acronym "${acr}" without smallcaps in ${file}:${i + 1}`
53
+ );
54
+ }
55
+ }
56
+ }
57
+ }
58
+
59
+ return { errors: [], warnings };
60
+ }
61
+
62
+ export default {
63
+ name: 'smallcaps',
64
+ tags: ['style'],
65
+ defaults: {},
66
+ run: (ctx) => validateSmallcaps(ctx.indexFiles),
67
+ };
@@ -0,0 +1,58 @@
1
+ import fs from 'node:fs';
2
+ import { optionsFor } from '../lib/validate.js';
3
+
4
+ const defaults = {
5
+ // Bounds on a TL;DR's word count: past max_words it's a paragraph, under
6
+ // min_words it's a fragment.
7
+ min_words: 8,
8
+ max_words: 80,
9
+ };
10
+
11
+ /**
12
+ * Validate TL;DR sections are reasonable length (1-3 sentences).
13
+ * Flags TL;DRs that are too long (likely paragraph-length) or suspiciously short.
14
+ * @param {string[]} indexFiles - Files listed in index.yml
15
+ * @param {{min_words?: number, max_words?: number}} [options]
16
+ * @returns {{errors: string[], warnings: string[]}}
17
+ */
18
+ export function validateTldrLength(indexFiles, options = optionsFor('tldr-length', defaults)) {
19
+ const warnings = [];
20
+ const MAX_WORDS = options.max_words;
21
+ const MIN_WORDS = options.min_words;
22
+
23
+ for (const file of indexFiles) {
24
+ if (!fs.existsSync(file)) continue;
25
+
26
+ const content = fs.readFileSync(file, 'utf8');
27
+
28
+ // Extract TL;DR content between ::: {.tldr ...} and :::
29
+ const tldrMatch = content.match(
30
+ /:::\s*\{\.tldr[^}]*\}\s*\n([\s\S]*?)\n\s*:::/
31
+ );
32
+ if (!tldrMatch) continue;
33
+
34
+ const tldrText = tldrMatch[1].trim();
35
+ if (!tldrText) continue;
36
+
37
+ const wordCount = tldrText.split(/\s+/).length;
38
+
39
+ if (wordCount > MAX_WORDS) {
40
+ warnings.push(
41
+ `TL;DR too long (${wordCount} words, max ${MAX_WORDS}) in ${file}`
42
+ );
43
+ } else if (wordCount < MIN_WORDS) {
44
+ warnings.push(
45
+ `TL;DR too short (${wordCount} words, min ${MIN_WORDS}) in ${file}`
46
+ );
47
+ }
48
+ }
49
+
50
+ return { errors: [], warnings };
51
+ }
52
+
53
+ export default {
54
+ name: 'tldr-length',
55
+ tags: ['style'],
56
+ defaults,
57
+ run: (ctx) => validateTldrLength(ctx.indexFiles, ctx.options),
58
+ };