const fs = require('fs');
const path = require('path');
console.log('π Validating package before publishing...\n');
const projectRoot = path.join(__dirname, '..');
// Required files for npm package
const requiredFiles = [
'bin/index.js',
'scripts/install.cjs',
'src/server.py',
'src/__init__.py',
'requirements.txt',
'create_templates.py',
'package.json',
'README.md'
];
// Optional but recommended files
const optionalFiles = [
'LICENSE',
'.npmignore',
'templates/invoice.docx',
'templates/report.docx',
'templates/contract.docx',
'templates/letter.docx'
];
let hasError = false;
let warnings = [];
console.log('Checking required files:');
console.log('------------------------');
// Check required files
for (const file of requiredFiles) {
const filePath = path.join(projectRoot, file);
if (!fs.existsSync(filePath)) {
console.error(`β Missing required file: ${file}`);
hasError = true;
} else {
const stats = fs.statSync(filePath);
const size = (stats.size / 1024).toFixed(1);
console.log(`β
Found: ${file} (${size} KB)`);
}
}
console.log('\nChecking optional files:');
console.log('------------------------');
// Check optional files
for (const file of optionalFiles) {
const filePath = path.join(projectRoot, file);
if (!fs.existsSync(filePath)) {
console.log(`β οΈ Missing optional file: ${file}`);
warnings.push(`Missing ${file}`);
} else {
console.log(`β
Found: ${file}`);
}
}
// Check bin file shebang
console.log('\nChecking executable configuration:');
console.log('-----------------------------------');
const binFile = path.join(projectRoot, 'bin/index.js');
if (fs.existsSync(binFile)) {
const binContent = fs.readFileSync(binFile, 'utf8');
if (!binContent.startsWith('#!/usr/bin/env node')) {
console.error('β bin/index.js must start with shebang: #!/usr/bin/env node');
hasError = true;
} else {
console.log('β
Shebang line present in bin/index.js');
}
// Check if file is executable (Unix-like systems)
try {
fs.accessSync(binFile, fs.constants.X_OK);
console.log('β
bin/index.js is executable');
} catch (e) {
console.log('β οΈ bin/index.js may not be executable (this is OK on Windows)');
}
}
// Check package.json configuration
console.log('\nChecking package.json:');
console.log('----------------------');
const packageJsonPath = path.join(projectRoot, 'package.json');
if (fs.existsSync(packageJsonPath)) {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!packageJson.name) {
console.error('β package.json missing "name" field');
hasError = true;
} else {
console.log(`β
Package name: ${packageJson.name}`);
}
if (!packageJson.version) {
console.error('β package.json missing "version" field');
hasError = true;
} else {
console.log(`β
Package version: ${packageJson.version}`);
}
if (!packageJson.bin || !packageJson.bin['docxtpl-mcp']) {
console.error('β package.json missing bin configuration');
hasError = true;
} else {
console.log('β
Bin entry configured');
}
if (!packageJson.type || packageJson.type !== 'module') {
console.log('β οΈ package.json "type" should be "module" for ES6 imports');
warnings.push('Consider setting "type": "module" in package.json');
} else {
console.log('β
ES6 module type configured');
}
}
// Check Python files
console.log('\nChecking Python components:');
console.log('---------------------------');
const serverPath = path.join(projectRoot, 'src/server.py');
if (fs.existsSync(serverPath)) {
const serverContent = fs.readFileSync(serverPath, 'utf8');
if (serverContent.includes('async def main()')) {
console.log('β
MCP server main function found');
} else {
console.log('β οΈ Could not verify MCP server main function');
}
}
// Summary
console.log('\n' + '='.repeat(50));
if (hasError) {
console.error('\nβ VALIDATION FAILED');
console.error('Please fix the errors above before publishing.\n');
process.exit(1);
} else if (warnings.length > 0) {
console.log('\nβ οΈ VALIDATION PASSED WITH WARNINGS:');
warnings.forEach(w => console.log(` - ${w}`));
console.log('\nβ
Package can be published, but consider addressing the warnings.\n');
} else {
console.log('\nβ
VALIDATION SUCCESSFUL');
console.log('Package is ready to publish!\n');
console.log('Next steps:');
console.log(' 1. Test locally: npm link && npx docxtpl-mcp');
console.log(' 2. Login to npm: npm login');
console.log(' 3. Publish: npm publish');
console.log(' 4. Test published: npx docxtpl-mcp@latest\n');
}