vite-plugin-react-server 1.4.2 → 1.4.3
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 +48 -313
- package/dist/package.json +123 -13
- package/dist/plugin/bundle/deferredStaticGeneration.js +14 -39
- package/dist/plugin/bundle/manifests.js +30 -48
- package/dist/plugin/config/autoDiscover/resolveAutoDiscover.d.ts.map +1 -1
- package/dist/plugin/config/autoDiscover/resolveAutoDiscover.js +4 -1
- package/dist/plugin/config/envPrefixFromConfig.js +12 -7
- package/dist/plugin/config/getCondition.d.ts.map +1 -1
- package/dist/plugin/config/getCondition.js +7 -5
- package/dist/plugin/dev-server/virtualRscHmrPlugin.js +23 -23
- package/dist/plugin/environments/createBuildEventPlugin.js +88 -98
- package/dist/plugin/environments/createEnvironmentPlugin.js +222 -250
- package/dist/plugin/helpers/createRscRenderHelpers.js +33 -34
- package/dist/plugin/helpers/createSharedLoader.d.ts.map +1 -1
- package/dist/plugin/helpers/createSharedLoader.js +4 -2
- package/dist/plugin/helpers/headlessStreamReuseHandler.js +30 -22
- package/dist/plugin/helpers/headlessStreamState.js +15 -28
- package/dist/plugin/helpers/resolveComponent.d.ts.map +1 -1
- package/dist/plugin/helpers/resolveComponent.js +4 -2
- package/dist/plugin/index.client.d.ts +5 -0
- package/dist/plugin/index.client.d.ts.map +1 -0
- package/dist/plugin/index.client.js +4 -0
- package/dist/plugin/index.d.ts +4 -3
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +10 -5
- package/dist/plugin/index.server.d.ts +5 -0
- package/dist/plugin/index.server.d.ts.map +1 -0
- package/dist/plugin/index.server.js +4 -0
- package/dist/plugin/metrics/createWorkerStartupMetrics.js +31 -13
- package/dist/plugin/orchestrator/createPluginOrchestrator.client.js +41 -38
- package/dist/plugin/orchestrator/createPluginOrchestrator.server.js +43 -46
- package/dist/plugin/plugin.client.js +2 -2
- package/dist/plugin/plugin.server.js +2 -2
- package/dist/plugin/react-static/createBuildLoader.client.js +12 -6
- package/dist/plugin/react-static/createBuildLoader.server.js +255 -235
- package/dist/plugin/react-static/plugin.client.js +684 -770
- package/dist/plugin/react-static/plugin.server.js +517 -603
- package/dist/plugin/react-static/processCssFilesForPages.js +103 -88
- package/dist/plugin/react-static/renderPage.client.js +455 -529
- package/dist/plugin/react-static/renderPage.server.js +485 -508
- package/dist/plugin/react-static/renderPagesBatched.js +277 -275
- package/dist/plugin/react-static/rscToHtmlStream.client.js +48 -29
- package/dist/plugin/react-static/rscToHtmlStream.server.js +62 -37
- package/dist/plugin/react-static/temporaryReferences.server.js +11 -2
- package/dist/plugin/stream/createMainThreadHandlers.js +40 -31
- package/dist/plugin/stream/renderRscStream.server.d.ts.map +1 -1
- package/dist/plugin/stream/renderRscStream.server.js +127 -144
- package/dist/plugin/transformer/createTransformerPlugin.js +226 -265
- package/dist/plugin/utils/checkReactVersion.d.ts +7 -0
- package/dist/plugin/utils/checkReactVersion.d.ts.map +1 -0
- package/dist/plugin/utils/checkReactVersion.js +23 -0
- package/dist/plugin/utils/envUrls.node.js +12 -11
- package/dist/plugin/vendor/vendor-alias.js +84 -114
- package/dist/plugin/vendor/vendor.client.d.ts.map +1 -1
- package/dist/plugin/vendor/vendor.client.js +1 -3
- package/dist/plugin/worker/rsc/handleRscRender.d.ts.map +1 -1
- package/dist/plugin/worker/rsc/handleRscRender.js +3 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +123 -13
- package/plugin/config/autoDiscover/resolveAutoDiscover.ts +4 -0
- package/plugin/config/getCondition.ts +6 -4
- package/plugin/helpers/createSharedLoader.ts +6 -1
- package/plugin/helpers/resolveComponent.ts +6 -1
- package/plugin/index.client.ts +4 -0
- package/plugin/index.server.ts +4 -0
- package/plugin/index.ts +12 -5
- package/plugin/plugin.client.ts +1 -1
- package/plugin/plugin.server.ts +1 -1
- package/plugin/stream/renderRscStream.server.ts +3 -0
- package/plugin/utils/checkReactVersion.ts +28 -0
- package/plugin/vendor/vendor.client.ts +0 -2
- package/plugin/worker/rsc/handleRscRender.ts +2 -0
- package/scripts/generate-toc.mjs +27 -294
package/scripts/generate-toc.mjs
CHANGED
|
@@ -1,311 +1,44 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Generate table of contents for documentation.
|
|
5
|
+
*
|
|
6
|
+
* Reads docs/README.md TOC entries and can inject navigation
|
|
7
|
+
* into individual doc files if they have <!-- TOC START --> / <!-- TOC END --> markers.
|
|
8
|
+
*
|
|
9
|
+
* With the current doc structure, the TOC lives only in docs/README.md
|
|
10
|
+
* and individual files don't embed navigation. This script is kept
|
|
11
|
+
* for future use if navigation footers are desired.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { readFileSync, readdirSync, existsSync } from 'fs';
|
|
4
15
|
import { join, dirname } from 'path';
|
|
5
16
|
import { fileURLToPath } from 'url';
|
|
6
17
|
|
|
7
|
-
const
|
|
8
|
-
const __dirname = dirname(__filename);
|
|
9
|
-
|
|
18
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
19
|
const DOCS_DIR = join(__dirname, '../docs');
|
|
11
20
|
const README_PATH = join(DOCS_DIR, 'README.md');
|
|
12
21
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const TOC_END = '<!-- TOC END -->';
|
|
16
|
-
|
|
17
|
-
function extractTOCFromReadme() {
|
|
18
|
-
const readmeContent = readFileSync(README_PATH, 'utf8');
|
|
19
|
-
|
|
20
|
-
// Find the table of contents section
|
|
21
|
-
const tocStartIndex = readmeContent.indexOf('## Table of Contents');
|
|
22
|
-
const tocEndIndex = readmeContent.indexOf('## Quick Links');
|
|
23
|
-
|
|
24
|
-
if (tocStartIndex === -1 || tocEndIndex === -1) {
|
|
25
|
-
throw new Error('Could not find Table of Contents section in README.md');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const tocSection = readmeContent.slice(tocStartIndex, tocEndIndex).trim();
|
|
29
|
-
|
|
30
|
-
// Parse the TOC and build clickable links for sub-items
|
|
31
|
-
const lines = tocSection.split('\n');
|
|
32
|
-
let currentFile = null;
|
|
33
|
-
let currentNumber = 1;
|
|
34
|
-
let tocWithLinks = [];
|
|
35
|
-
const anchor = (text) => text.toLowerCase().replace(/[^a-z0-9\s-]/g, '').replace(/\s+/g, '-');
|
|
36
|
-
|
|
37
|
-
for (let line of lines) {
|
|
38
|
-
// Main section: "1. [Getting Started](./getting-started.md)"
|
|
39
|
-
const mainMatch = line.match(/^(\d+)\. \[([^\]]+)\]\(\.\/([^)]+)\)/);
|
|
40
|
-
if (mainMatch) {
|
|
41
|
-
const number = parseInt(mainMatch[1]);
|
|
42
|
-
const title = mainMatch[2];
|
|
43
|
-
const filename = mainMatch[3];
|
|
44
|
-
currentFile = filename;
|
|
45
|
-
currentNumber = number;
|
|
46
|
-
|
|
47
|
-
// Always use 1 tab between number and bracket to account for 2-digit numbers
|
|
48
|
-
tocWithLinks.push(`${number}.\t[${title}](./${filename})`);
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
// Sub-item: " - Installation and Setup"
|
|
52
|
-
const subMatch = line.match(/^(\s*)- (.+)$/);
|
|
53
|
-
if (subMatch && currentFile) {
|
|
54
|
-
const text = subMatch[2];
|
|
55
|
-
const link = `./${currentFile}#${anchor(text)}`;
|
|
56
|
-
|
|
57
|
-
// Always use 1 tab for sub-items
|
|
58
|
-
const indent = '\t';
|
|
59
|
-
|
|
60
|
-
tocWithLinks.push(`${indent}- [${text}](${link})`);
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
// Only include lines that are part of the TOC (numbered items, sub-items, or empty lines)
|
|
64
|
-
if (line.trim() === '' || line.match(/^\d+\./) || line.match(/^\s*-/)) {
|
|
65
|
-
tocWithLinks.push(line);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Add navigation note (without the Table of Contents header or auto-generated comment)
|
|
70
|
-
const tocWithNav = `${tocWithLinks.join('\n')}\n\n### Quick Links\n- [🏠 Main Documentation](./README.md)\n- [🚀 Getting Started](./getting-started.md)\n- [📖 GitHub Repository](https://github.com/nicobrinkkemper/vite-plugin-react-server)\n- [🎮 Official Demo](https://github.com/nicobrinkkemper/vite-plugin-react-server-demo-official)\n\n---`;
|
|
71
|
-
|
|
72
|
-
return tocWithNav;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function injectTOCIntoFile(filePath, toc) {
|
|
76
|
-
const content = readFileSync(filePath, 'utf8');
|
|
77
|
-
const fileName = filePath.split('/').pop();
|
|
78
|
-
|
|
79
|
-
// Check if TOC markers already exist
|
|
80
|
-
const hasMarkers = content.includes(TOC_START) && content.includes(TOC_END);
|
|
22
|
+
function main() {
|
|
23
|
+
console.log('📚 Documentation structure:\n');
|
|
81
24
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// Highlight the current file in the TOC
|
|
85
|
-
tocLines = tocLines.map(line => {
|
|
86
|
-
// Match main TOC entry: "1. [Getting Started](./getting-started.md)" or "10. [Advanced Topics](./advanced-topics.md)"
|
|
87
|
-
const mainMatch = line.match(/^(\d+)\.(\s*)\[([^\]]+)\]\(\.\/([^)]+)\)/);
|
|
88
|
-
if (mainMatch) {
|
|
89
|
-
const number = parseInt(mainMatch[1]);
|
|
90
|
-
const spacing = mainMatch[2];
|
|
91
|
-
const title = mainMatch[3];
|
|
92
|
-
const entryFile = mainMatch[4];
|
|
93
|
-
if (entryFile === fileName) {
|
|
94
|
-
// Bold and add indicator, maintaining the spacing
|
|
95
|
-
return `${number}.${spacing}**[${title}](./${entryFile}) ← you are here**`;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return line;
|
|
99
|
-
});
|
|
100
|
-
const tocWithHere = tocLines.join('\n');
|
|
25
|
+
const readme = readFileSync(README_PATH, 'utf8');
|
|
26
|
+
const lines = readme.split('\n');
|
|
101
27
|
|
|
102
|
-
if (hasMarkers) {
|
|
103
|
-
// Always replace everything between markers
|
|
104
|
-
const startIndex = content.indexOf(TOC_START);
|
|
105
|
-
const endIndex = content.indexOf(TOC_END);
|
|
106
|
-
|
|
107
|
-
if (startIndex === -1 || endIndex === -1 || endIndex <= startIndex) {
|
|
108
|
-
console.log(`⚠️ Invalid marker positions in ${filePath}`);
|
|
109
|
-
return 'error';
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// Remove everything between the markers (including the markers themselves)
|
|
113
|
-
const beforeTOC = content.substring(0, startIndex);
|
|
114
|
-
const afterTOC = content.substring(endIndex + TOC_END.length);
|
|
115
|
-
|
|
116
|
-
// Add the new content with markers
|
|
117
|
-
const newContent = beforeTOC + TOC_START + '\n\n## 📚 Documentation Navigation\n\n<!-- Auto-generated TOC - Do not edit manually -->\n\n## Table of Contents\n\n<!-- Auto-generated TOC - Do not edit manually -->\n\n' + tocWithHere + '\n\n' + TOC_END + afterTOC;
|
|
118
|
-
writeFileSync(filePath, newContent, 'utf8');
|
|
119
|
-
return 'updated';
|
|
120
|
-
} else {
|
|
121
|
-
// Find a safe place to insert TOC (not inside a code block)
|
|
122
|
-
const lines = content.split('\n');
|
|
123
|
-
let insertIndex = lines.length;
|
|
124
|
-
let inCodeBlock = false;
|
|
125
|
-
let codeBlockDepth = 0;
|
|
126
|
-
// Scan from the end to find a safe insertion point
|
|
127
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
128
|
-
const line = lines[i];
|
|
129
|
-
const trimmedLine = line.trim();
|
|
130
|
-
// Check for code block markers (including multiple backticks)
|
|
131
|
-
if (trimmedLine.match(/^`{3,}/)) {
|
|
132
|
-
codeBlockDepth++;
|
|
133
|
-
inCodeBlock = codeBlockDepth % 2 === 1;
|
|
134
|
-
continue;
|
|
135
|
-
}
|
|
136
|
-
if (!inCodeBlock && trimmedLine !== '') {
|
|
137
|
-
insertIndex = i + 1;
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
// Insert TOC at the safe position with markers
|
|
142
|
-
lines.splice(insertIndex, 0, '', TOC_START + '\n\n## 📚 Documentation Navigation\n\n<!-- Auto-generated TOC - Do not edit manually -->\n\n## Table of Contents\n\n<!-- Auto-generated TOC - Do not edit manually -->\n\n' + tocWithHere + '\n\n' + TOC_END);
|
|
143
|
-
const newContent = lines.join('\n');
|
|
144
|
-
writeFileSync(filePath, newContent, 'utf8');
|
|
145
|
-
return 'inserted';
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function generateMainReadmeTable() {
|
|
150
|
-
const readmeContent = readFileSync(README_PATH, 'utf8');
|
|
151
|
-
|
|
152
|
-
// Find the table of contents section
|
|
153
|
-
const tocStartIndex = readmeContent.indexOf('## Table of Contents');
|
|
154
|
-
const tocEndIndex = readmeContent.indexOf('## Plugin Implementation Documentation');
|
|
155
|
-
|
|
156
|
-
if (tocStartIndex === -1 || tocEndIndex === -1) {
|
|
157
|
-
throw new Error('Could not find Table of Contents section in README.md');
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const tocSection = readmeContent.slice(tocStartIndex, tocEndIndex).trim();
|
|
161
|
-
|
|
162
|
-
// Parse the TOC to extract file names
|
|
163
|
-
const lines = tocSection.split('\n');
|
|
164
|
-
const entries = [];
|
|
165
|
-
|
|
166
28
|
for (const line of lines) {
|
|
167
|
-
|
|
168
|
-
const match = line.match(/^\d+\.\s+\[([^\]]+)\]\(\.\/([^)]+)\)/);
|
|
29
|
+
const match = line.match(/^\d+\.\s+\[.+\]\(.+\)/);
|
|
169
30
|
if (match) {
|
|
170
|
-
|
|
171
|
-
const
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
let table = '| Topic |\n';
|
|
178
|
-
table += '|-------|\n';
|
|
179
|
-
|
|
180
|
-
for (const entry of entries) {
|
|
181
|
-
table += `| [${entry.title}](./docs/${entry.filename}) |\n`;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
return table;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function updateMainReadmeTable() {
|
|
188
|
-
const mainReadmePath = join(__dirname, '../README.md');
|
|
189
|
-
let content = readFileSync(mainReadmePath, 'utf8');
|
|
190
|
-
|
|
191
|
-
// Remove ALL existing documentation tables using regex
|
|
192
|
-
const docTableRegex = /## Documentation\s*\n\s*\|.*?\n\s*\|.*?\n\s*(\|.*?\n\s*)*/gs;
|
|
193
|
-
content = content.replace(docTableRegex, '');
|
|
194
|
-
|
|
195
|
-
// Find the License section to add documentation before it
|
|
196
|
-
const licenseStart = content.indexOf('## License');
|
|
197
|
-
if (licenseStart === -1) {
|
|
198
|
-
console.log('⚠️ Could not find License section in main README.md');
|
|
199
|
-
return false;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const beforeLicense = content.substring(0, licenseStart);
|
|
203
|
-
const licenseSection = content.substring(licenseStart);
|
|
204
|
-
const newTable = generateMainReadmeTable();
|
|
205
|
-
|
|
206
|
-
// Insert the documentation table just before License
|
|
207
|
-
const newContent = beforeLicense + '## Documentation\n\n' + newTable + '\n\n' + licenseSection;
|
|
208
|
-
writeFileSync(mainReadmePath, newContent, 'utf8');
|
|
209
|
-
|
|
210
|
-
return true;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
function updateMainReadmeTOC() {
|
|
214
|
-
const mainReadmePath = join(__dirname, '../docs/README.md');
|
|
215
|
-
let content = readFileSync(mainReadmePath, 'utf8');
|
|
216
|
-
|
|
217
|
-
// Find the table of contents section - look for the first occurrence
|
|
218
|
-
const tocStartIndex = content.indexOf('## Table of Contents');
|
|
219
|
-
const tocEndIndex = content.indexOf('## Plugin Implementation Documentation');
|
|
220
|
-
|
|
221
|
-
if (tocStartIndex === -1 || tocEndIndex === -1) {
|
|
222
|
-
console.log('⚠️ Could not find Table of Contents section in docs/README.md');
|
|
223
|
-
return false;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const beforeTOC = content.substring(0, tocStartIndex);
|
|
227
|
-
const afterTOC = content.substring(tocEndIndex);
|
|
228
|
-
|
|
229
|
-
// Generate the TOC with tab-based formatting
|
|
230
|
-
const tocSection = content.slice(tocStartIndex, tocEndIndex).trim();
|
|
231
|
-
const lines = tocSection.split('\n');
|
|
232
|
-
let tocWithTabs = [];
|
|
233
|
-
|
|
234
|
-
for (let line of lines) {
|
|
235
|
-
// Main section: "1. [Getting Started](./getting-started.md)"
|
|
236
|
-
const mainMatch = line.match(/^(\d+)\. \[([^\]]+)\]\(\.\/([^)]+)\)/);
|
|
237
|
-
if (mainMatch) {
|
|
238
|
-
const number = parseInt(mainMatch[1]);
|
|
239
|
-
const title = mainMatch[2];
|
|
240
|
-
const filename = mainMatch[3];
|
|
241
|
-
|
|
242
|
-
// Always use 1 tab between number and bracket to account for 2-digit numbers
|
|
243
|
-
tocWithTabs.push(`${number}.\t[${title}](./${filename})`);
|
|
244
|
-
continue;
|
|
245
|
-
}
|
|
246
|
-
// Skip duplicate "## Table of Contents" headers and auto-generated comments
|
|
247
|
-
if (line.trim() === '## Table of Contents' || line.trim() === '<!-- Auto-generated TOC - Do not edit manually -->') {
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
// Skip empty lines that might be between duplicate headers
|
|
251
|
-
if (line.trim() === '') {
|
|
252
|
-
continue;
|
|
31
|
+
// Extract path and check existence
|
|
32
|
+
const pathMatch = line.match(/\(\.\/([^)]+)\)/);
|
|
33
|
+
if (pathMatch) {
|
|
34
|
+
const filePath = join(DOCS_DIR, pathMatch[1]);
|
|
35
|
+
const exists = existsSync(filePath);
|
|
36
|
+
console.log(` ${exists ? '✅' : '❌'} ${line.trim()}`);
|
|
37
|
+
}
|
|
253
38
|
}
|
|
254
|
-
tocWithTabs.push(line);
|
|
255
39
|
}
|
|
256
|
-
|
|
257
|
-
const newTOC = `## Table of Contents\n\n<!-- Auto-generated TOC - Do not edit manually -->\n\n${tocWithTabs.join('\n')}\n`;
|
|
258
|
-
const newContent = beforeTOC + newTOC + afterTOC;
|
|
259
|
-
writeFileSync(mainReadmePath, newContent, 'utf8');
|
|
260
|
-
|
|
261
|
-
return true;
|
|
262
|
-
}
|
|
263
40
|
|
|
264
|
-
|
|
265
|
-
console.log('🔧 Generating table of contents for documentation files...\n');
|
|
266
|
-
|
|
267
|
-
try {
|
|
268
|
-
// Extract TOC from README
|
|
269
|
-
const toc = extractTOCFromReadme();
|
|
270
|
-
|
|
271
|
-
// Get all markdown files except README.md
|
|
272
|
-
const files = readdirSync(DOCS_DIR)
|
|
273
|
-
.filter(file => file.endsWith('.md') && file !== 'README.md')
|
|
274
|
-
.sort();
|
|
275
|
-
|
|
276
|
-
console.log(`📁 Found ${files.length} documentation files:\n`);
|
|
277
|
-
|
|
278
|
-
// Process each file
|
|
279
|
-
const results = [];
|
|
280
|
-
for (const file of files) {
|
|
281
|
-
const filePath = join(DOCS_DIR, file);
|
|
282
|
-
const action = injectTOCIntoFile(filePath, toc);
|
|
283
|
-
results.push({ file, action });
|
|
284
|
-
console.log(` ${action === 'updated' ? '🔄' : '✨'} ${file} - ${action}`);
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// Update main README.md documentation table
|
|
288
|
-
console.log('\n📝 Updating main README.md documentation table...');
|
|
289
|
-
const mainReadmeUpdated = updateMainReadmeTable();
|
|
290
|
-
if (mainReadmeUpdated) {
|
|
291
|
-
console.log(' ✅ Main README.md documentation table updated');
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Update docs README.md TOC with tab-based formatting
|
|
295
|
-
console.log('\n📝 Updating docs README.md TOC formatting...');
|
|
296
|
-
const docsTOCUpdated = updateMainReadmeTOC();
|
|
297
|
-
if (docsTOCUpdated) {
|
|
298
|
-
console.log(' ✅ Docs README.md TOC formatting updated');
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
console.log('\n✅ Table of contents generation complete!');
|
|
302
|
-
console.log(` 📝 ${results.filter(r => r.action === 'updated').length} files updated`);
|
|
303
|
-
console.log(` ➕ ${results.filter(r => r.action === 'inserted').length} files had TOC inserted`);
|
|
304
|
-
|
|
305
|
-
} catch (error) {
|
|
306
|
-
console.error('❌ Error generating table of contents:', error.message);
|
|
307
|
-
process.exit(1);
|
|
308
|
-
}
|
|
41
|
+
console.log('\n✅ Done.');
|
|
309
42
|
}
|
|
310
43
|
|
|
311
|
-
main();
|
|
44
|
+
main();
|