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,202 @@
1
+ // Helpers shared by the built-in validators (validators/) and the runner
2
+ // (commands/validate.js).
3
+ //
4
+ // Validators read the book's conventions from book.config.yml. Paths (here,
5
+ // in lint.exclude, and in validators.allow) are relative to the book root,
6
+ // which is the working directory when run through `book-kit validate`.
7
+
8
+ import fs from 'node:fs';
9
+ import path from 'node:path';
10
+ import * as yaml from 'js-yaml';
11
+ import { deepMerge, loadConfig } from './config.js';
12
+ import { smallcapsAcronyms, universalAcronyms } from './acronyms.js';
13
+
14
+ export const config = loadConfig();
15
+ const SRC_DIR = config.paths.src;
16
+ const INDEX_PATH = config.paths.index;
17
+ // How a chapter entry under paths.src starts in index.yml ("- src/").
18
+ const SRC_ENTRY_PREFIX = `- ${path.normalize(SRC_DIR).replace(/\/+$/, '')}/`;
19
+ export const TAKEAWAYS_SUFFIX = config.structure.takeaways_suffix;
20
+ export const REQUIRED_CALLOUT = config.structure.required_callout;
21
+
22
+ // Files to exclude from validation (index.yml presence, TODO checks, and the
23
+ // required callout): lint.exclude in book.config.yml.
24
+ export const EXCLUDE_PATTERNS = config.lint.exclude;
25
+
26
+ // Machine-generated pages (lint.generated) whose text is derived from
27
+ // already-validated sources. Skip prose-level lexical validators that would
28
+ // misfire on them, e.g. acronyms locked inside link text that can't be
29
+ // smallcapped. List them in lint.exclude too for the per-chapter checks.
30
+ export const GENERATED_FILES = new Set(config.lint.generated);
31
+
32
+ // Acronyms that should always use [ACRONYM]{.smallcaps} formatting: the kit's
33
+ // list (lib/acronyms.js, shared with the no-bare-acronyms textlint rule) plus
34
+ // lint.acronyms. The smallcaps validator only flags acronyms it knows about.
35
+ export const KNOWN_ACRONYMS = smallcapsAcronyms(config);
36
+
37
+ // Acronyms readers know without expansion on first use: the kit's list plus
38
+ // lint.universal_acronyms.
39
+ export const UNIVERSALLY_KNOWN_ACRONYMS = universalAcronyms(config);
40
+
41
+ /**
42
+ * A validator's intentional exceptions (validators.allow.<name>).
43
+ * @param {string} name - Validator name
44
+ * @returns {Set<string>}
45
+ */
46
+ export function allowFor(name) {
47
+ return new Set(config.validators.allow?.[name] ?? []);
48
+ }
49
+
50
+ /**
51
+ * A validator's options: its defaults deep-merged with
52
+ * validators.options.<name> (maps merge; lists and scalars replace).
53
+ * @param {string} name - Validator name
54
+ * @param {object} [defaults] - The validator's own defaults
55
+ * @param {object} [cfg] - Resolved config (the book's, by default)
56
+ * @returns {object}
57
+ */
58
+ export function optionsFor(name, defaults = {}, cfg = config) {
59
+ return deepMerge(structuredClone(defaults), cfg.validators?.options?.[name] ?? {});
60
+ }
61
+
62
+ /**
63
+ * Get all markdown chapters from src directory
64
+ * @returns {string[]} Array of chapter paths
65
+ */
66
+ export function getChapters() {
67
+ const srcDir = SRC_DIR;
68
+ if (!fs.existsSync(srcDir)) {
69
+ return [];
70
+ }
71
+ return fs.readdirSync(srcDir)
72
+ .filter(file => file.endsWith('.md'))
73
+ .sort()
74
+ .map(file => path.join(srcDir, file));
75
+ }
76
+
77
+ /**
78
+ * Load the index.yml file and get input-files
79
+ * @returns {string[]} Array of chapter paths from index
80
+ */
81
+ export function getIndexFiles() {
82
+ const indexPath = INDEX_PATH;
83
+ if (!fs.existsSync(indexPath)) {
84
+ console.error('Error: index.yml not found');
85
+ process.exit(1);
86
+ }
87
+
88
+ const indexContent = fs.readFileSync(indexPath, 'utf8');
89
+ const index = yaml.load(indexContent);
90
+ return index['input-files'] || [];
91
+ }
92
+
93
+ /**
94
+ * Check if a chapter should be excluded from validation
95
+ * @param {string} chapter - Path to chapter file
96
+ * @returns {boolean} True if chapter should be excluded
97
+ */
98
+ export function isExcluded(chapter) {
99
+ return EXCLUDE_PATTERNS.includes(chapter);
100
+ }
101
+
102
+ /**
103
+ * Parse index.yml to find section intro files (first heading file in each section).
104
+ * Sections are identified by YAML comment lines (# Section name).
105
+ * For each section, finds the first file whose content starts with a markdown heading.
106
+ * @returns {{section: string, file: string}[]} Array of section intro entries
107
+ */
108
+ export function getSectionIntros() {
109
+ const indexPath = INDEX_PATH;
110
+ if (!fs.existsSync(indexPath)) {
111
+ return [];
112
+ }
113
+
114
+ const lines = fs.readFileSync(indexPath, 'utf8').split('\n');
115
+ const intros = [];
116
+ let currentSection = null;
117
+ let foundIntro = false;
118
+
119
+ for (const line of lines) {
120
+ const trimmed = line.trim();
121
+
122
+ // Detect YAML comment section headers (e.g., "# Part one")
123
+ if (trimmed.startsWith('#') && !trimmed.startsWith('#-')) {
124
+ currentSection = trimmed.replace(/^#+\s*/, '');
125
+ foundIntro = false;
126
+ continue;
127
+ }
128
+
129
+ // Find the first file in the section whose content starts with a heading
130
+ if (currentSection && !foundIntro && trimmed.startsWith(SRC_ENTRY_PREFIX)) {
131
+ const file = trimmed.replace(/^-\s*/, '');
132
+ if (fs.existsSync(file)) {
133
+ const content = fs.readFileSync(file, 'utf8');
134
+ const linesInFile = content.split('\n');
135
+ const firstHeadingLine = linesInFile.find((l) => /^#+\s/.test(l.trim()));
136
+ if (firstHeadingLine) {
137
+ intros.push({ section: currentSection, file });
138
+ foundIntro = true;
139
+ }
140
+ }
141
+ }
142
+ }
143
+
144
+ return intros;
145
+ }
146
+
147
+ /**
148
+ * Generate a pandoc-style anchor ID from heading text.
149
+ * Strips smallcaps spans, explicit IDs, inline code, and converts to kebab-case.
150
+ * @param {string} headingText - Raw heading line (including ## prefix)
151
+ * @returns {string} The anchor ID
152
+ */
153
+ export function headingToAnchor(headingText) {
154
+ let text = headingText.replace(/^#+\s*/, '');
155
+
156
+ // Use explicit anchor ID if present: {#my-id}
157
+ const explicitId = text.match(/\{#([a-z0-9-]+)\}/);
158
+ if (explicitId) return explicitId[1];
159
+
160
+ // Strip smallcaps spans: [ACRONYM]{.smallcaps} -> ACRONYM
161
+ text = text.replace(/\\?\[([^\]]*?)\\?\]\{\.smallcaps[^}]*\}/g, '$1');
162
+
163
+ // Strip other pandoc attributes: {epub:type=...} etc.
164
+ text = text.replace(/\{[^}]*\}/g, '');
165
+
166
+ // Strip inline code backticks
167
+ text = text.replace(/`([^`]*)`/g, '$1');
168
+
169
+ // Strip links: [text](url) -> text
170
+ text = text.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1');
171
+
172
+ // Strip bold/italic markers
173
+ text = text.replace(/[*_]+/g, '');
174
+
175
+ // Convert to pandoc-style ID
176
+ return text
177
+ .toLowerCase()
178
+ .trim()
179
+ .replace(/[^\w\s-]/g, '') // Remove non-word chars except spaces and hyphens
180
+ .replace(/\s+/g, '-') // Spaces to hyphens
181
+ .replace(/-+/g, '-') // Collapse multiple hyphens
182
+ .replace(/^-|-$/g, ''); // Trim leading/trailing hyphens
183
+ }
184
+
185
+ /**
186
+ * Collect all heading anchors from production chapters.
187
+ * @param {string[]} indexFiles - Files listed in index.yml
188
+ * @returns {Set<string>} Set of all anchor IDs
189
+ */
190
+ export function collectAllAnchors(indexFiles) {
191
+ const anchors = new Set();
192
+ for (const file of indexFiles) {
193
+ if (!fs.existsSync(file)) continue;
194
+ const content = fs.readFileSync(file, 'utf8');
195
+ for (const line of content.split('\n')) {
196
+ if (/^#{1,6}\s/.test(line)) {
197
+ anchors.add(headingToAnchor(line));
198
+ }
199
+ }
200
+ }
201
+ return anchors;
202
+ }
package/package.json CHANGED
@@ -1,6 +1,100 @@
1
1
  {
2
2
  "name": "book-kit",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.6.0",
4
+ "description": "Build and quality tooling for Markdown books: EPUB 3, Kindle, print-ready PDF and PDF/X-1a, HTML, and DOCX from one source, via pandoc, WeasyPrint, and Tailwind CSS.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/benbalter/book-kit.git"
10
+ },
11
+ "publishConfig": {
12
+ "access": "public",
13
+ "provenance": true
14
+ },
15
+ "bin": {
16
+ "book-kit": "bin/book-kit.js"
17
+ },
18
+ "exports": {
19
+ "./css/theme.css": "./css/theme.css",
20
+ "./css/theme/*": "./css/theme/*",
21
+ "./lib/*": "./lib/*",
22
+ "./presets/*": "./presets/*",
23
+ "./package.json": "./package.json"
24
+ },
25
+ "files": [
26
+ "LICENSE",
27
+ "NOTICE.md",
28
+ "bin/",
29
+ "commands/",
30
+ "configs/",
31
+ "css/",
32
+ "defaults/",
33
+ "emoji/",
34
+ "filters/",
35
+ "fonts/*.md",
36
+ "fonts/recipe.json",
37
+ "just/",
38
+ "lib/",
39
+ "presets/",
40
+ "profiles/",
41
+ "requirements.txt",
42
+ "rules/",
43
+ "templates/",
44
+ "toolchain.json",
45
+ "validators/"
46
+ ],
47
+ "engines": {
48
+ "node": ">=22"
49
+ },
50
+ "scripts": {
51
+ "test": "vitest run",
52
+ "lint": "eslint .",
53
+ "setup:python": "python3 -m pip install -r requirements.txt"
54
+ },
55
+ "dependencies": {
56
+ "@tailwindcss/cli": "^4.3.3",
57
+ "@tailwindcss/typography": "^0.5.20",
58
+ "@textlint-rule/textlint-rule-no-duplicate-abbr": "1.0.2",
59
+ "@textlint-rule/textlint-rule-no-unmatched-pair": "2.0.4",
60
+ "cssnano": "^9.1.0",
61
+ "js-yaml": "^5.4.1",
62
+ "markdownlint-cli2": "0.23.3",
63
+ "postcss": "^8.5.26",
64
+ "postcss-preset-env": "11.6.1",
65
+ "sharp": "^0.35.5",
66
+ "tailwindcss": "^4.3.3",
67
+ "textlint": "15.8.0",
68
+ "textlint-filter-rule-comments": "1.3.0",
69
+ "textlint-rule-abbr-within-parentheses": "1.0.2",
70
+ "textlint-rule-alex": "5.0.0",
71
+ "textlint-rule-common-misspellings": "1.0.1",
72
+ "textlint-rule-diacritics": "2.1.4",
73
+ "textlint-rule-doubled-spaces": "1.0.2",
74
+ "textlint-rule-en-capitalization": "2.0.3",
75
+ "textlint-rule-en-max-word-count": "2.0.1",
76
+ "textlint-rule-max-comma": "4.0.0",
77
+ "textlint-rule-no-start-duplicated-conjunction": "2.0.2",
78
+ "textlint-rule-sentence-length": "5.2.1",
79
+ "textlint-rule-stop-words": "5.2.0",
80
+ "textlint-rule-terminology": "5.2.16",
81
+ "textlint-rule-write-good": "2.0.0"
82
+ },
83
+ "devDependencies": {
84
+ "@eslint/js": "10.0.1",
85
+ "eslint": "10.13.0",
86
+ "globals": "17.13.0",
87
+ "vitest": "^5.0.2"
88
+ },
89
+ "overrides": {
90
+ "@tailwindcss/typography": {
91
+ "postcss-selector-parser": "^7.1.6"
92
+ },
93
+ "markdownlint-cli2": {
94
+ "smol-toml": "^1.9.0"
95
+ },
96
+ "micromark-extension-math": {
97
+ "katex": "^0.18.2"
98
+ }
99
+ }
100
+ }
@@ -0,0 +1,35 @@
1
+ ## business-nonfiction house style
2
+
3
+ These are the preset's conventions. `book-kit validate` and `just prose` enforce most of them; the rest are judgment calls for the writer and editor.
4
+
5
+ ### TL;DRs and takeaways
6
+
7
+ - Every chapter opens with `::: {.tldr custom-style="tldr"}` right after its H2. It has one or two sentences that state the chapter's argument, not its topic.
8
+ - Section `*-key-takeaways.md` chapters collect the TL;DRs. After adding or editing a TL;DR, or changing `index.yml`, run `just key-takeaways`.
9
+
10
+ ### Section openers
11
+
12
+ - A section's first file is an H1 with `{.section-opener}`, followed by a short `.lead` paragraph. It has no TL;DR.
13
+
14
+ ### Role callouts
15
+
16
+ - Use `.for-managers` and `.for-ics` only where the advice genuinely differs by role, and give a chapter that has one the other as well.
17
+ - `.pro-tip` is for a concrete, actionable tip; `.common-objection` states a reader's pushback and answers it.
18
+
19
+ ### Mechanics
20
+
21
+ - Acronyms: write them as `[API]{.smallcaps}`, and plurals as `[API]{.smallcaps}s`. Expand each one on first use, in book order.
22
+ - Dashes: em dashes (`—`) with no spaces for asides; en dashes (`–`) for number ranges.
23
+ - Quotes: periods and commas go inside quotation marks; colons and semicolons go outside. Use the serial comma.
24
+ - Footnotes get chapter-specific names (`[^style-guide]`, not `[^1]`), since chapters build into one document.
25
+
26
+ ### Openings
27
+
28
+ - The first paragraph after the TL;DR shows the problem in action, states a provocative truth, asks the reader's question, or grounds the chapter in a specific experience.
29
+ - No throat-clearing ("In this chapter, we will explore…"), and no manufactured hypotheticals ("Picture this," "Imagine…," "Let's say…"). The hypothetical-hooks validator flags those.
30
+ - If the TL;DR already makes the point, skip the hook and start with the substance.
31
+
32
+ ### Prose
33
+
34
+ - The kit's prose rules flag clichés, buzzwords, vague intensifiers, cultural idioms, assumptive phrasing ("simply," "of course"), and AI-sounding patterns. Read each finding in context: quoted bad examples and deliberate repetition aren't errors.
35
+ - Vary paragraph length and section shape. Bold-label bullet lists shouldn't stand in for an argument.
@@ -0,0 +1,128 @@
1
+ # business-nonfiction: the house style book-kit grew up with, for practical
2
+ # nonfiction aimed at managers and individual contributors. A book opts in with
3
+ #
4
+ # extends: business-nonfiction
5
+ #
6
+ # in book.config.yml. Keys here override the kit defaults; the book's own keys
7
+ # override these. Every key is documented in defaults/book.config.yml.
8
+ #
9
+ # Through 0.3 this was the kit default. Extending it reproduces that default
10
+ # exactly (tests/presets.test.js checks), so a book upgrading from 0.3 keeps
11
+ # its output by adding the line above.
12
+
13
+ # Six callouts: a TL;DR that opens every chapter, role-specific guidance for
14
+ # managers and individual contributors, and section takeaways. The two role
15
+ # callouts pair both ways: a chapter with one should have the other
16
+ # (the callout-balance validator).
17
+ callouts:
18
+ - class: tldr
19
+ label: "TL;DR:"
20
+ aria: Summary
21
+ - class: pro-tip
22
+ label: "Pro-Tip:"
23
+ aria: Pro tip
24
+ emoji: "💡"
25
+ emoji_cp: 1f4a1
26
+ emoji_alt: light bulb
27
+ - class: for-managers
28
+ label: "For managers:"
29
+ aria: Guidance for managers
30
+ pair_with: for-ics
31
+ emoji: "👔"
32
+ emoji_cp: 1f454
33
+ emoji_alt: necktie
34
+ - class: for-ics
35
+ label: "For individual contributors:"
36
+ aria: Guidance for individual contributors
37
+ pair_with: for-managers
38
+ emoji: "💻"
39
+ emoji_cp: 1f4bb
40
+ emoji_alt: laptop
41
+ - class: common-objection
42
+ label: "Common objection:"
43
+ aria: Common objection
44
+ emoji: "🤔"
45
+ emoji_cp: 1f914
46
+ emoji_alt: thinking
47
+ - class: takeaways
48
+ aria: Key takeaways
49
+
50
+ structure:
51
+ # Every chapter opens with a TL;DR.
52
+ required_callout: tldr
53
+ # Each section ends with a generated chapter collecting its TL;DRs
54
+ # (`just key-takeaways`).
55
+ takeaways_suffix: -key-takeaways
56
+ takeaways_skip_sections: [Front matter, Appendix, Conclusion, Back cover]
57
+
58
+ validators:
59
+ options:
60
+ # The short name callout-balance warnings use for .for-ics.
61
+ callout-balance:
62
+ names: { for-ics: For ICs }
63
+ # The house-style checks the kit defaults turn off, back on at each
64
+ # validator's own severity.
65
+ severity:
66
+ section-intros: "on"
67
+ section-intro-no-tldr: "on"
68
+ section-intro-has-lead: "on"
69
+ section-intro-final-lead: "on"
70
+ no-transition-todos: "on"
71
+ min-opener-length: "on"
72
+ chapter-lengths: "on"
73
+ smallcaps: "on"
74
+ escaped-smallcaps: "on"
75
+ callout-balance: "on"
76
+ tldr-length: "on"
77
+ acronym-expansion: "on"
78
+ em-dashes: "on"
79
+ number-range-dashes: "on"
80
+ quote-punctuation: "on"
81
+ sentence-starters: "on"
82
+ list-punctuation: "on"
83
+ bold-keywords: "on"
84
+ hypothetical-hooks: "on"
85
+ paragraph-length: "on"
86
+
87
+ lint:
88
+ # Prose-style textlint settings on top of the kit's mechanical base.
89
+ textlint:
90
+ rules:
91
+ write-good:
92
+ passive: false
93
+ so: false
94
+ weasel: false
95
+ thereIs: true
96
+ tooWordy: false
97
+ adverb: false
98
+ cliches: true
99
+ eprime: false
100
+ terminology:
101
+ defaultTerms: false
102
+ exclude: [HTML, ID, URL, URLs]
103
+ terms: [[Github, GitHub], [Javascript, JavaScript], [Typescript, TypeScript], [e-mail, email], [on-line, online], [web site, website]]
104
+ sentence-length:
105
+ max: 350
106
+ en-max-word-count:
107
+ max: 80
108
+ stop-words:
109
+ defaultWords: false
110
+ # Inclusive-language checks, with words that are fine in business prose
111
+ # (and in this style's own advice) allowed.
112
+ alex:
113
+ allow: [actors-actresses, adult, adults, amateur, angry, attack, baby, ball, bigger, blind, bomb, burn, burns, buried, clearly, crack, cracks, dad-mom, damn, daughter-son, daughters-sons, dead, deposit, desire, destroys, devil, die, dirty, dive, doom, easy, enemy, everyone-knows, failed, failure, failures, faith, fear, fellowship, fight, fire, firing, gal-guy, god, hang, harder, he-she, her-him, hero-heroine, hijacking, hole, host-hostess, just, kids, kill, kills, lie, lies, master, master-key, newsman-newswoman, nuts, obvious, of-course, period, premature, radical, reject, remains, shoot, sick, simple, special, straightforward, suck, sucks, trap, traps, tribe]
114
+ profanitySureness: 2
115
+ max-comma:
116
+ max: 6
117
+ # All six of the kit's house-style textlint rules.
118
+ rules:
119
+ no-ai-like-patterns: true
120
+ no-assumptive: true
121
+ no-bare-acronyms: true
122
+ no-cliches: true
123
+ no-cultural-idioms: true
124
+ no-vague-intensifiers: true
125
+ markdownlint:
126
+ proper-names:
127
+ names: [GitHub, JavaScript, TypeScript, Markdown, README]
128
+ code_blocks: false
@@ -0,0 +1,42 @@
1
+ %!PS-Adobe-3.0
2
+ % PDF/X-1a:2001 output intent definition for Ghostscript
3
+ %
4
+ % This file defines the output intent metadata required by the PDF/X-1a:2001
5
+ % standard. It references the USWebCoatedSWOP.icc CMYK ICC profile, which is
6
+ % the standard profile for U.S. web offset printing.
7
+ %
8
+ % Used by: commands/convert-pdfx.sh via Ghostscript
9
+ % See also: commands/download-icc-profile.sh for obtaining the ICC profile
10
+ %
11
+ % The pdfmark sequence follows Ghostscript's PDFX_def.ps example (see
12
+ % NOTICE.md).
13
+ %
14
+ % The __ICC_PROFILE_PATH__ and __TITLE__ placeholders are replaced at runtime by
15
+ % convert-pdfx.sh (the title is PostScript-escaped there).
16
+
17
+ % Document info dictionary
18
+ [ /GTS_PDFXVersion (PDF/X-1a:2001)
19
+ /Title (__TITLE__)
20
+ /Trapped /False
21
+ /DOCINFO pdfmark
22
+
23
+ % ICC profile path (placeholder substituted at build time)
24
+ /ICCProfile (__ICC_PROFILE_PATH__) def
25
+
26
+ % Create a stream object for the ICC profile
27
+ [/_objdef {icc_PDFX} /type /stream /OBJ pdfmark
28
+ [{icc_PDFX} << /N 4 >> /PUT pdfmark
29
+ [{icc_PDFX} ICCProfile (r) file /PUT pdfmark
30
+
31
+ % Output intent dictionary
32
+ [/_objdef {OutputIntent_PDFX} /type /dict /OBJ pdfmark
33
+ [{OutputIntent_PDFX} <<
34
+ /Type /OutputIntent
35
+ /S /GTS_PDFX
36
+ /OutputCondition (U.S. Web Coated \(SWOP\) v2)
37
+ /OutputConditionIdentifier (CGATS TR 001)
38
+ /RegistryName (http://www.color.org)
39
+ /Info (U.S. Web Coated \(SWOP\) v2)
40
+ /DestOutputProfile {icc_PDFX}
41
+ >> /PUT pdfmark
42
+ [{Catalog} << /OutputIntents [ {OutputIntent_PDFX} ] >> /PUT pdfmark
@@ -0,0 +1,14 @@
1
+ # Python build dependencies. Versions must match toolchain.json (the single
2
+ # source of truth); tests/toolchain.test.js enforces it.
3
+ # weasyprint - renders the print PDF (drives pagination: bump deliberately)
4
+ # pikepdf - PDF/X-1a conversion (convert-pdfx)
5
+ # Pillow - emoji compositing (composite-*-emoji)
6
+ # python-docx - DOCX reference template (generate-reference-docx)
7
+ # fonttools - subsets the upstream fonts at build time (book-kit fonts)
8
+ # brotli - WOFF2 compression for fonttools
9
+ weasyprint==70.0
10
+ pikepdf==10.10.0
11
+ Pillow==12.3.0
12
+ python-docx==1.2.0
13
+ fonttools==4.67.0
14
+ brotli==1.2.0