/ scripts / export-business-plan.js
export-business-plan.js
  1  #!/usr/bin/env node
  2  
  3  /**
  4   * Export Business Plan to HTML
  5   *
  6   * Exports all business plan documents into a single professional HTML file
  7   * with table of contents, styling, and print support.
  8   */
  9  
 10  import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
 11  import { join, dirname } from 'path';
 12  import { fileURLToPath } from 'url';
 13  
 14  const __filename = fileURLToPath(import.meta.url);
 15  const __dirname = dirname(__filename);
 16  const rootDir = join(__dirname, '..');
 17  
 18  // Simple markdown to HTML converter (basic implementation)
 19  function markdownToHtml(markdown) {
 20    let html = markdown;
 21  
 22    // Convert headers
 23    html = html.replace(/^### (.*$)/gim, '<h3>$1</h3>');
 24    html = html.replace(/^## (.*$)/gim, '<h2>$1</h2>');
 25    html = html.replace(/^# (.*$)/gim, '<h1>$1</h1>');
 26  
 27    // Convert bold
 28    html = html.replace(/\*\*(.*?)\*\*/gim, '<strong>$1</strong>');
 29  
 30    // Convert italic
 31    html = html.replace(/\*(.*?)\*/gim, '<em>$1</em>');
 32  
 33    // Convert inline code
 34    html = html.replace(/`([^`]+)`/gim, '<code>$1</code>');
 35  
 36    // Convert code blocks
 37    html = html.replace(/```(\w+)?\n([\s\S]*?)```/gim, '<pre><code>$2</code></pre>');
 38  
 39    // Convert links [text](url)
 40    html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/gim, '<a href="$2">$1</a>');
 41  
 42    // Convert horizontal rules
 43    html = html.replace(/^---$/gim, '<hr>');
 44  
 45    // Convert tables
 46    html = html.replace(/\n\|(.+)\|\n\|[-:| ]+\|\n((?:\|.+\|\n?)*)/gim, (match, header, rows) => {
 47      const headers = header
 48        .split('|')
 49        .map(h => h.trim())
 50        .filter(h => h);
 51      const rowData = rows
 52        .trim()
 53        .split('\n')
 54        .map(row =>
 55          row
 56            .split('|')
 57            .map(cell => cell.trim())
 58            .filter(cell => cell)
 59        );
 60  
 61      let table = '<table class="data-table"><thead><tr>';
 62      headers.forEach(h => (table += `<th>${h}</th>`));
 63      table += '</tr></thead><tbody>';
 64      rowData.forEach(row => {
 65        table += '<tr>';
 66        row.forEach(cell => (table += `<td>${cell}</td>`));
 67        table += '</tr>';
 68      });
 69      table += '</tbody></table>';
 70      return table;
 71    });
 72  
 73    // Convert lists
 74    html = html.replace(/^- (.+)$/gim, '<li>$1</li>');
 75    html = html.replace(/^(\d+)\. (.+)$/gim, '<li>$2</li>');
 76    html = html.replace(/(<li>.*<\/li>\n?)+/gim, '<ul>$&</ul>');
 77  
 78    // Convert paragraphs
 79    html = html
 80      .split('\n\n')
 81      .map(para => {
 82        if (!para.match(/^<[hupldt]/)) {
 83          return `<p>${para}</p>`;
 84        }
 85        return para;
 86      })
 87      .join('\n');
 88  
 89    // Clean up nested ul tags
 90    html = html.replace(/<\/ul>\n<ul>/gim, '');
 91  
 92    return html;
 93  }
 94  
 95  // Business plan documents to include
 96  const documents = [
 97    {
 98      title: 'Executive Summary',
 99      file: 'README.md',
100      sections: ['Quick Start', 'Architecture', 'Multi-Country Support'],
101    },
102    {
103      title: 'Financial Projections',
104      file: 'docs/PROFIT-ESTIMATE.md',
105    },
106    {
107      title: 'Total Addressable Market Analysis',
108      file: 'docs/TAM-EXPANSION.md',
109    },
110    {
111      title: 'Pipeline Capacity & Throughput',
112      file: 'docs/PIPELINE-CAPACITY.md',
113    },
114    {
115      title: 'Outreach Strategy Analysis',
116      file: 'docs/OUTREACH-STRATEGY-ANALYSIS.md',
117    },
118    {
119      title: 'Multi-Country Pricing Strategy',
120      file: 'docs/PRICING-STRATEGY.md',
121    },
122    {
123      title: 'Programmatic Scoring Analysis',
124      file: 'docs/PROGRAMMATIC-SCORING.md',
125    },
126    {
127      title: 'HTML-Only Cost Analysis',
128      file: 'docs/HTML-ONLY-ANALYSIS.md',
129    },
130    {
131      title: 'Template-Based Proposals',
132      file: 'docs/TEMPLATE-PROPOSALS.md',
133    },
134    {
135      title: 'Functional Specification',
136      file: 'docs/FUNCTIONAL-SPEC.md',
137    },
138  ];
139  
140  // Generate HTML
141  function generateBusinessPlanHTML() {
142    console.log('Generating business plan HTML...');
143  
144    // Read all documents
145    const documentContents = [];
146  
147    for (const doc of documents) {
148      const filePath = join(rootDir, doc.file);
149      if (!existsSync(filePath)) {
150        console.warn(`⚠️  File not found: ${doc.file}, skipping...`);
151        continue;
152      }
153  
154      const content = readFileSync(filePath, 'utf-8');
155      documentContents.push({
156        ...doc,
157        content: markdownToHtml(content),
158      });
159      console.log(`✓ Loaded: ${doc.file}`);
160    }
161  
162    // Generate table of contents
163    let toc = '<nav class="toc"><h2>Table of Contents</h2><ol>';
164    documentContents.forEach((doc, index) => {
165      const id = doc.title.toLowerCase().replace(/[^a-z0-9]+/g, '-');
166      toc += `<li><a href="#${id}">${doc.title}</a></li>`;
167    });
168    toc += '</ol></nav>';
169  
170    // Generate document sections
171    let sections = '';
172    documentContents.forEach((doc, index) => {
173      const id = doc.title.toLowerCase().replace(/[^a-z0-9]+/g, '-');
174      sections += `
175        <section class="document-section" id="${id}">
176          <h1 class="section-title">${doc.title}</h1>
177          <div class="section-content">
178            ${doc.content}
179          </div>
180          ${index < documentContents.length - 1 ? '<div class="page-break"></div>' : ''}
181        </section>
182      `;
183    });
184  
185    // HTML template
186    const html = `<!DOCTYPE html>
187  <html lang="en">
188  <head>
189    <meta charset="UTF-8">
190    <meta name="viewport" content="width=device-width, initial-scale=1.0">
191    <title>333 Method - Business Plan</title>
192    <style>
193      /* Reset and base styles */
194      * {
195        margin: 0;
196        padding: 0;
197        box-sizing: border-box;
198      }
199  
200      body {
201        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
202        line-height: 1.6;
203        color: #333;
204        background: #fff;
205        font-size: 14px;
206        max-width: 1200px;
207        margin: 0 auto;
208        padding: 2rem;
209      }
210  
211      /* Cover page */
212      .cover {
213        text-align: center;
214        padding: 4rem 2rem;
215        margin-bottom: 3rem;
216        border-bottom: 3px solid #2563eb;
217        page-break-after: always;
218      }
219  
220      .cover h1 {
221        font-size: 3rem;
222        color: #1e293b;
223        margin-bottom: 1rem;
224      }
225  
226      .cover .subtitle {
227        font-size: 1.5rem;
228        color: #64748b;
229        margin-bottom: 2rem;
230      }
231  
232      .cover .meta {
233        color: #94a3b8;
234        font-size: 1rem;
235      }
236  
237      /* Table of contents */
238      .toc {
239        background: #f8fafc;
240        padding: 2rem;
241        border-radius: 8px;
242        margin-bottom: 3rem;
243        page-break-after: always;
244      }
245  
246      .toc h2 {
247        color: #1e293b;
248        margin-bottom: 1.5rem;
249        font-size: 1.8rem;
250      }
251  
252      .toc ol {
253        list-style: none;
254        counter-reset: toc-counter;
255      }
256  
257      .toc li {
258        counter-increment: toc-counter;
259        margin-bottom: 0.75rem;
260      }
261  
262      .toc li::before {
263        content: counter(toc-counter) ". ";
264        font-weight: bold;
265        color: #2563eb;
266        margin-right: 0.5rem;
267      }
268  
269      .toc a {
270        color: #1e293b;
271        text-decoration: none;
272        font-size: 1.1rem;
273        transition: color 0.2s;
274      }
275  
276      .toc a:hover {
277        color: #2563eb;
278      }
279  
280      /* Section styles */
281      .document-section {
282        margin-bottom: 3rem;
283      }
284  
285      .section-title {
286        color: #1e293b;
287        font-size: 2.5rem;
288        margin-bottom: 2rem;
289        padding-bottom: 0.5rem;
290        border-bottom: 2px solid #e2e8f0;
291      }
292  
293      .section-content h1 {
294        color: #1e293b;
295        font-size: 2rem;
296        margin-top: 2rem;
297        margin-bottom: 1rem;
298      }
299  
300      .section-content h2 {
301        color: #334155;
302        font-size: 1.6rem;
303        margin-top: 1.75rem;
304        margin-bottom: 0.75rem;
305      }
306  
307      .section-content h3 {
308        color: #475569;
309        font-size: 1.3rem;
310        margin-top: 1.5rem;
311        margin-bottom: 0.5rem;
312      }
313  
314      .section-content p {
315        margin-bottom: 1rem;
316        text-align: justify;
317      }
318  
319      .section-content ul,
320      .section-content ol {
321        margin-left: 2rem;
322        margin-bottom: 1rem;
323      }
324  
325      .section-content li {
326        margin-bottom: 0.5rem;
327      }
328  
329      .section-content code {
330        background: #f1f5f9;
331        padding: 0.2rem 0.4rem;
332        border-radius: 3px;
333        font-family: 'Monaco', 'Courier New', monospace;
334        font-size: 0.9em;
335        color: #dc2626;
336      }
337  
338      .section-content pre {
339        background: #1e293b;
340        color: #e2e8f0;
341        padding: 1rem;
342        border-radius: 6px;
343        overflow-x: auto;
344        margin-bottom: 1rem;
345        font-size: 0.85rem;
346      }
347  
348      .section-content pre code {
349        background: none;
350        color: inherit;
351        padding: 0;
352      }
353  
354      .section-content table.data-table {
355        width: 100%;
356        border-collapse: collapse;
357        margin: 1.5rem 0;
358        font-size: 0.9rem;
359      }
360  
361      .section-content table.data-table th {
362        background: #2563eb;
363        color: white;
364        padding: 0.75rem;
365        text-align: left;
366        font-weight: 600;
367      }
368  
369      .section-content table.data-table td {
370        padding: 0.75rem;
371        border-bottom: 1px solid #e2e8f0;
372      }
373  
374      .section-content table.data-table tr:hover {
375        background: #f8fafc;
376      }
377  
378      .section-content a {
379        color: #2563eb;
380        text-decoration: none;
381      }
382  
383      .section-content a:hover {
384        text-decoration: underline;
385      }
386  
387      .section-content hr {
388        border: none;
389        border-top: 1px solid #e2e8f0;
390        margin: 2rem 0;
391      }
392  
393      .section-content strong {
394        color: #1e293b;
395        font-weight: 600;
396      }
397  
398      .section-content em {
399        color: #64748b;
400      }
401  
402      /* Page breaks for printing */
403      .page-break {
404        page-break-after: always;
405        height: 0;
406        margin: 0;
407        padding: 0;
408      }
409  
410      /* Print styles */
411      @media print {
412        body {
413          font-size: 11pt;
414          max-width: 100%;
415          padding: 0;
416        }
417  
418        .cover,
419        .toc,
420        .document-section {
421          page-break-inside: avoid;
422        }
423  
424        .section-title {
425          page-break-after: avoid;
426        }
427  
428        a {
429          color: inherit;
430          text-decoration: none;
431        }
432  
433        .section-content table.data-table {
434          page-break-inside: avoid;
435        }
436      }
437  
438      /* Screen-only navigation */
439      @media screen {
440        .back-to-top {
441          position: fixed;
442          bottom: 2rem;
443          right: 2rem;
444          background: #2563eb;
445          color: white;
446          padding: 0.75rem 1.5rem;
447          border-radius: 50px;
448          text-decoration: none;
449          box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
450          transition: all 0.3s;
451        }
452  
453        .back-to-top:hover {
454          background: #1d4ed8;
455          transform: translateY(-2px);
456          box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15);
457        }
458      }
459    </style>
460  </head>
461  <body>
462    <div class="cover">
463      <h1>333 Method</h1>
464      <div class="subtitle">Business Plan & Strategic Analysis</div>
465      <div class="meta">
466        <p><strong>Generated:</strong> ${new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' })}</p>
467        <p><strong>Status:</strong> Confidential</p>
468      </div>
469    </div>
470  
471    ${toc}
472  
473    ${sections}
474  
475    <a href="#" class="back-to-top">↑ Back to Top</a>
476  
477    <script>
478      // Smooth scrolling for anchor links
479      document.querySelectorAll('a[href^="#"]').forEach(anchor => {
480        anchor.addEventListener('click', function (e) {
481          e.preventDefault();
482          const target = document.querySelector(this.getAttribute('href'));
483          if (target) {
484            target.scrollIntoView({ behavior: 'smooth', block: 'start' });
485          }
486        });
487      });
488    </script>
489  </body>
490  </html>`;
491  
492    // Ensure exports directory exists
493    const exportsDir = join(rootDir, 'exports');
494    if (!existsSync(exportsDir)) {
495      mkdirSync(exportsDir, { recursive: true });
496    }
497  
498    // Write HTML file
499    const timestamp = new Date().toISOString().split('T')[0];
500    const outputPath = join(exportsDir, `333Method-Business-Plan-${timestamp}.html`);
501    writeFileSync(outputPath, html, 'utf-8');
502  
503    console.log('\n✅ Business plan exported successfully!');
504    console.log(`📄 File: ${outputPath}`);
505    console.log(`📊 Included ${documentContents.length} documents`);
506    console.log('\n💡 To view:');
507    console.log(`   Open in browser: file://${outputPath}`);
508    console.log(`   Or use: xdg-open "${outputPath}"`);
509  
510    return outputPath;
511  }
512  
513  // Run
514  try {
515    generateBusinessPlanHTML();
516  } catch (error) {
517    console.error('❌ Error generating business plan:', error);
518    process.exit(1);
519  }