legal-markdown-js
Version:
Node.js implementation of LegalMarkdown for processing legal documents with markdown and YAML - Complete feature parity with Ruby version
285 lines (246 loc) ⢠9.77 kB
JavaScript
/**
* Post-installation script to ensure Puppeteer dependencies are properly configured
* This helps resolve Chrome/Chromium dependency issues that users commonly face
*/
const { spawn } = require('child_process');
const fs = require('fs');
const path = require('path');
const os = require('os');
function log(message) {
console.log(`[legal-markdown-js] ${message}`);
}
function executeCommand(command, args, options = {}) {
return new Promise((resolve, reject) => {
const child = spawn(command, args, {
stdio: 'inherit',
...options
});
const timeout = setTimeout(() => {
child.kill('SIGTERM');
reject(new Error(`Command timeout: ${command} ${args.join(' ')}`));
}, options.timeout || 120000);
child.on('close', (code) => {
clearTimeout(timeout);
if (code === 0) {
resolve(code);
} else {
reject(new Error(`Command failed with exit code ${code}: ${command} ${args.join(' ')}`));
}
});
child.on('error', (error) => {
clearTimeout(timeout);
reject(error);
});
});
}
function detectPlatform() {
const platform = os.platform();
const arch = os.arch();
log(`Detected platform: ${platform} (${arch})`);
return { platform, arch };
}
function checkChromiumInstallation() {
try {
// Check multiple possible locations for Chromium
const possiblePaths = [
// Old Puppeteer location
path.join(__dirname, '..', 'node_modules', 'puppeteer', '.local-chromium'),
// New Puppeteer cache location (user's home)
path.join(os.homedir(), '.cache', 'puppeteer'),
// Alternative cache location
path.join(os.homedir(), '.puppeteer-cache'),
// Project-local cache
path.join(__dirname, '..', '.cache', 'puppeteer')
];
for (const chromiumPath of possiblePaths) {
if (fs.existsSync(chromiumPath)) {
const files = fs.readdirSync(chromiumPath);
if (files.length > 0) {
log(`Puppeteer Chromium found at: ${chromiumPath}`);
return true;
}
}
}
log('Puppeteer Chromium not found in any expected location');
return false;
} catch (error) {
log(`Error checking Chromium installation: ${error.message}`);
return false;
}
}
async function installChromiumDependencies() {
const { platform } = detectPlatform();
try {
if (platform === 'linux') {
log('Installing Linux Chrome dependencies...');
// Check if we're in a container or have apt
try {
await executeCommand('which', ['apt-get'], { stdio: 'ignore' });
log('Attempting to install Chrome dependencies via apt-get...');
const dependencies = [
'ca-certificates',
'fonts-liberation',
'libasound2',
'libatk-bridge2.0-0',
'libatk1.0-0',
'libc6',
'libcairo-gobject2',
'libcairo2',
'libcups2',
'libdbus-1-3',
'libexpat1',
'libfontconfig1',
'libgbm1',
'libgcc1',
'libglib2.0-0',
'libgtk-3-0',
'libnspr4',
'libnss3',
'libpango-1.0-0',
'libpangocairo-1.0-0',
'libstdc++6',
'libx11-6',
'libx11-xcb1',
'libxcb1',
'libxcomposite1',
'libxcursor1',
'libxdamage1',
'libxext6',
'libxfixes3',
'libxi6',
'libxrandr2',
'libxrender1',
'libxss1',
'libxtst6',
'lsb-release',
'wget',
'xdg-utils'
];
// Run apt-get update first
await executeCommand('apt-get', ['update'], { timeout: 120000 });
// Then install dependencies
await executeCommand('apt-get', ['install', '-y', ...dependencies], { timeout: 120000 });
log('Linux Chrome dependencies installed successfully');
} catch (aptError) {
log('Could not install via apt-get (might not have permissions or apt not available)');
log('Please ensure Chrome dependencies are installed manually if PDF generation fails');
}
} else if (platform === 'darwin') {
log('macOS detected - Checking Chrome installation...');
// Check for common Chrome installations on macOS
const chromePaths = [
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
'/Applications/Chromium.app/Contents/MacOS/Chromium',
'~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
'~/Applications/Chromium.app/Contents/MacOS/Chromium'
];
let chromeFound = false;
for (const chromePath of chromePaths) {
const expandedPath = chromePath.replace('~', os.homedir());
if (fs.existsSync(expandedPath)) {
log(`Chrome/Chromium found at: ${expandedPath}`);
chromeFound = true;
break;
}
}
if (!chromeFound) {
log('Chrome/Chromium not found in standard locations');
log('Puppeteer will download its own Chromium');
}
} else if (platform === 'win32') {
log('Windows detected - Chrome should work out of the box');
} else {
log(`Unsupported platform: ${platform}`);
log('PDF generation may not work properly');
}
} catch (error) {
log(`Error installing Chrome dependencies: ${error.message}`);
log('PDF generation may not work properly');
}
}
async function ensurePuppeteerInstallation() {
try {
log('Checking Puppeteer installation...');
const { platform } = detectPlatform();
const puppeteerPath = path.join(__dirname, '..', 'node_modules', 'puppeteer');
if (fs.existsSync(puppeteerPath)) {
const puppeteerPkg = require(path.join(puppeteerPath, 'package.json'));
log(`Puppeteer version: ${puppeteerPkg.version}`);
// Always force download on macOS to ensure compatibility
if (platform === 'darwin' || !checkChromiumInstallation()) {
log('Ensuring Chromium is properly installed for Puppeteer...');
// Set environment variables for macOS
if (platform === 'darwin') {
process.env.PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = 'false';
process.env.PUPPETEER_DOWNLOAD_BASE_URL = 'https://storage.googleapis.com/chromium-browser-snapshots';
}
try {
// First, try the modern Puppeteer command which handles cache correctly
const globalCacheDir = path.join(os.homedir(), '.cache', 'puppeteer-global');
log('Installing Chrome for Puppeteer...');
log(`This will download Chrome to the global cache directory: ${globalCacheDir}`);
// Set up environment variables for global cache installation
const cleanEnv = {
...process.env,
PUPPETEER_CACHE_DIR: globalCacheDir,
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: 'false',
PUPPETEER_SKIP_DOWNLOAD: 'false'
};
try {
// This is the recommended way for Puppeteer v21+
await executeCommand('npx', ['puppeteer', 'browsers', 'install', 'chrome'], {
cwd: path.join(__dirname, '..'),
timeout: 600000,
env: cleanEnv
});
log('Chrome installed successfully via npx');
log(`Chrome should now be available at: ${globalCacheDir}`);
} catch (npxError) {
// Fallback to install script
log('Trying alternative installation via install script...');
await executeCommand('node', ['node_modules/puppeteer/install.mjs'], {
cwd: path.join(__dirname, '..'),
timeout: 600000,
env: cleanEnv
});
log('Chrome installed successfully via install.mjs');
}
} catch (installError) {
log(`\nā ļø Could not automatically install Chrome: ${installError.message}`);
log('\nš§ MANUAL FIX REQUIRED:');
log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
log('Please run ONE of these commands manually:');
log('\n 1. npx puppeteer browsers install chrome');
log(' (Recommended - installs Chrome to user cache)\n');
log(' 2. npm install -g puppeteer');
log(' (Alternative - global installation)\n');
log(' 3. Download Google Chrome from:');
log(' https://www.google.com/chrome/');
log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
log('\nAfter running the command, PDF generation should work.');
}
}
} else {
log('Puppeteer not found in node_modules');
}
} catch (error) {
log(`Error ensuring Puppeteer installation: ${error.message}`);
}
}
async function main() {
log('Running post-installation setup...');
detectPlatform();
await installChromiumDependencies();
await ensurePuppeteerInstallation();
log('Post-installation setup complete');
log('If PDF generation still fails, try running: npx puppeteer browsers install chrome');
}
// Only run if called directly
if (require.main === module) {
main().catch(error => {
log(`Error during post-installation: ${error.message}`);
process.exit(0); // Don't fail the installation
});
}
module.exports = { main, detectPlatform, installChromiumDependencies, ensurePuppeteerInstallation };