extract2md
Version:
Client-side PDF to Markdown conversion with OCR and optional LLM rewrite. Core dependencies bundled for offline use.
253 lines (210 loc) • 8.82 kB
JavaScript
/**
* Deployment validation script for Extract2MD
* This script validates the package is ready for deployment
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
class DeploymentValidator {
constructor() {
this.errors = [];
this.warnings = [];
this.success = [];
}
log(message, type = 'info') {
const timestamp = new Date().toISOString();
const prefix = type === 'error' ? '❌' : type === 'warning' ? '⚠️' : '✅';
console.log(`${prefix} [${timestamp}] ${message}`);
if (type === 'error') this.errors.push(message);
else if (type === 'warning') this.warnings.push(message);
else this.success.push(message);
}
async validate() {
console.log('🚀 Starting Extract2MD Deployment Validation...\n');
// Check package structure
this.validatePackageStructure();
// Check build outputs
this.validateBuildOutputs();
// Check documentation
this.validateDocumentation();
// Check configuration files
this.validateConfiguration();
// Check TypeScript definitions
this.validateTypeDefinitions();
// Summary
this.printSummary();
return this.errors.length === 0;
}
validatePackageStructure() {
this.log('Validating package structure...');
const requiredFiles = [
'package.json',
'src/index.js',
'src/types/index.d.ts',
'dist/assets/extract2md.umd.js',
'README.md',
'MIGRATION.md',
'DEPLOYMENT.md',
'config.example.json'
];
const requiredDirs = [
'src/converters',
'src/engines',
'src/utils',
'examples',
'test'
];
for (const file of requiredFiles) {
const filePath = path.resolve(__dirname, '..', file);
if (fs.existsSync(filePath)) {
this.log(`Required file found: ${file}`);
} else {
this.log(`Missing required file: ${file}`, 'error');
}
}
for (const dir of requiredDirs) {
const dirPath = path.resolve(__dirname, '..', dir);
if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
this.log(`Required directory found: ${dir}`);
} else {
this.log(`Missing required directory: ${dir}`, 'error');
}
}
}
validateBuildOutputs() {
this.log('Validating build outputs...');
const buildFiles = [
'dist/assets/extract2md.umd.js',
'dist/assets/tesseract-worker.min.js',
'dist/assets/tesseract-core.wasm.js',
'dist/pdf.worker.min.mjs'
];
for (const file of buildFiles) {
const filePath = path.resolve(__dirname, '..', file);
if (fs.existsSync(filePath)) {
const stats = fs.statSync(filePath);
const sizeInMB = (stats.size / (1024 * 1024)).toFixed(2);
this.log(`Build file found: ${file} (${sizeInMB} MB)`);
if (stats.size === 0) {
this.log(`Build file is empty: ${file}`, 'error');
}
} else {
this.log(`Missing build file: ${file}`, 'error');
}
}
}
validateDocumentation() {
this.log('Validating documentation...');
const docFiles = {
'README.md': ['Installation', 'Scenarios', 'Configuration'],
'MIGRATION.md': ['Legacy API', 'Migration', 'Backwards Compatibility'],
'DEPLOYMENT.md': ['Distribution', 'Performance', 'Security']
};
for (const [file, keywords] of Object.entries(docFiles)) {
const filePath = path.resolve(__dirname, '..', file);
if (fs.existsSync(filePath)) {
const content = fs.readFileSync(filePath, 'utf8');
for (const keyword of keywords) {
if (content.toLowerCase().includes(keyword.toLowerCase())) {
this.log(`Documentation section found in ${file}: ${keyword}`);
} else {
this.log(`Missing documentation section in ${file}: ${keyword}`, 'warning');
}
}
} else {
this.log(`Missing documentation file: ${file}`, 'error');
}
}
}
validateConfiguration() {
this.log('Validating configuration files...');
// Check package.json
const packagePath = path.resolve(__dirname, '..', 'package.json');
if (fs.existsSync(packagePath)) {
try {
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
const requiredFields = ['name', 'version', 'main', 'module', 'types'];
for (const field of requiredFields) {
if (pkg[field]) {
this.log(`package.json has required field: ${field}`);
} else {
this.log(`package.json missing field: ${field}`, 'error');
}
}
// Check if main file exists
if (pkg.main && fs.existsSync(path.resolve(__dirname, '..', pkg.main))) {
this.log(`Main entry point exists: ${pkg.main}`);
} else {
this.log(`Main entry point missing: ${pkg.main}`, 'error');
}
} catch (error) {
this.log(`Invalid JSON in package.json: ${error.message}`, 'error');
}
}
// Check example config
const configPath = path.resolve(__dirname, '..', 'config.example.json');
if (fs.existsSync(configPath)) {
try {
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
if (config.ocr && config.webllm) {
this.log('Example configuration is valid');
} else {
this.log('Example configuration missing required sections', 'warning');
}
} catch (error) {
this.log(`Invalid example configuration: ${error.message}`, 'error');
}
}
}
validateTypeDefinitions() {
this.log('Validating TypeScript definitions...');
const typesPath = path.resolve(__dirname, '..', 'src/types/index.d.ts');
if (fs.existsSync(typesPath)) {
const content = fs.readFileSync(typesPath, 'utf8');
const requiredInterfaces = [
'OCRConfig',
'WebLLMConfig',
'Extract2MDConfig',
'Extract2MDConverter'
];
for (const interfaceName of requiredInterfaces) {
if (content.includes(`interface ${interfaceName}`) ||
content.includes(`declare class ${interfaceName}`) ||
content.includes(`export class ${interfaceName}`)) {
this.log(`TypeScript interface found: ${interfaceName}`);
} else {
this.log(`Missing TypeScript interface: ${interfaceName}`, 'error');
}
}
} else {
this.log('TypeScript definitions file not found', 'error');
}
}
printSummary() {
console.log('\n📊 Deployment Validation Summary');
console.log('=====================================');
console.log(`✅ Successful checks: ${this.success.length}`);
console.log(`⚠️ Warnings: ${this.warnings.length}`);
console.log(`❌ Errors: ${this.errors.length}`);
if (this.errors.length > 0) {
console.log('\n🚨 Critical Issues Found:');
this.errors.forEach(error => console.log(` - ${error}`));
}
if (this.warnings.length > 0) {
console.log('\n⚠️ Warnings:');
this.warnings.forEach(warning => console.log(` - ${warning}`));
}
console.log('\n' + (this.errors.length === 0 ? '🎉 Package is ready for deployment!' : '🔧 Please fix the errors before deployment.'));
}
}
// Run validation
const validator = new DeploymentValidator();
validator.validate().then(isValid => {
process.exit(isValid ? 0 : 1);
}).catch(error => {
console.error('Validation failed:', error);
process.exit(1);
});