mirror of
https://github.com/johannesjo/super-productivity.git
synced 2026-01-23 19:04:43 +00:00
Add copyRecursive() helper function to handle both files and directories when copying plugin builds to bundled-plugins. This ensures i18n folders and other directories are properly copied. Previously, only files were copied (checking isFile()), which caused i18n folders to be skipped. Now all three plugin build commands (procrastination- buster, sync-md, ai-productivity-prompts) properly copy directory trees. Fixes: 404 errors for i18n/de.json and other directory contents
297 lines
8.7 KiB
JavaScript
Executable file
297 lines
8.7 KiB
JavaScript
Executable file
#!/usr/bin/env node
|
|
|
|
const { exec } = require('child_process');
|
|
const { promisify } = require('util');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const execAsync = promisify(exec);
|
|
|
|
// Colors for console output
|
|
const colors = {
|
|
reset: '\x1b[0m',
|
|
bright: '\x1b[1m',
|
|
green: '\x1b[32m',
|
|
yellow: '\x1b[33m',
|
|
red: '\x1b[31m',
|
|
cyan: '\x1b[36m',
|
|
};
|
|
|
|
function log(message, color = '') {
|
|
if (process.argv.includes('--silent')) return;
|
|
console.log(`${color}${message}${colors.reset}`);
|
|
}
|
|
|
|
// Recursive copy function that handles both files and directories
|
|
function copyRecursive(src, dest) {
|
|
const stat = fs.statSync(src);
|
|
if (stat.isDirectory()) {
|
|
if (!fs.existsSync(dest)) {
|
|
fs.mkdirSync(dest, { recursive: true });
|
|
}
|
|
const entries = fs.readdirSync(src);
|
|
for (const entry of entries) {
|
|
copyRecursive(path.join(src, entry), path.join(dest, entry));
|
|
}
|
|
} else {
|
|
fs.copyFileSync(src, dest);
|
|
}
|
|
}
|
|
|
|
// Plugin configurations
|
|
const plugins = [
|
|
{
|
|
name: 'procrastination-buster',
|
|
path: 'procrastination-buster',
|
|
needsInstall: true,
|
|
copyToAssets: true,
|
|
buildCommand: async (pluginPath) => {
|
|
await execAsync(`cd ${pluginPath} && npm run build`);
|
|
// Copy to assets directory
|
|
const targetDir = path.join(
|
|
__dirname,
|
|
'../../../src/assets/bundled-plugins/procrastination-buster',
|
|
);
|
|
if (!fs.existsSync(targetDir)) {
|
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
}
|
|
const distPath = path.join(pluginPath, 'dist');
|
|
if (fs.existsSync(distPath)) {
|
|
const files = fs.readdirSync(distPath);
|
|
for (const file of files) {
|
|
const src = path.join(distPath, file);
|
|
const dest = path.join(targetDir, file);
|
|
copyRecursive(src, dest);
|
|
}
|
|
}
|
|
return 'Built and copied to assets';
|
|
},
|
|
},
|
|
// Migrated built-in plugins
|
|
{
|
|
name: 'api-test-plugin',
|
|
path: 'api-test-plugin',
|
|
needsInstall: true,
|
|
copyToAssets: true,
|
|
buildCommand: async (pluginPath) => {
|
|
// Copy to assets directory
|
|
const targetDir = path.join(
|
|
__dirname,
|
|
'../../../src/assets/bundled-plugins/api-test-plugin',
|
|
);
|
|
if (!fs.existsSync(targetDir)) {
|
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
}
|
|
const files = [
|
|
'manifest.json',
|
|
'plugin.js',
|
|
'index.html',
|
|
'config-schema.json',
|
|
'icon.svg',
|
|
];
|
|
for (const file of files) {
|
|
const src = path.join(pluginPath, file);
|
|
const dest = path.join(targetDir, file);
|
|
if (fs.existsSync(src)) {
|
|
fs.copyFileSync(src, dest);
|
|
}
|
|
}
|
|
return 'Copied to assets';
|
|
},
|
|
},
|
|
{
|
|
name: 'sync-md',
|
|
path: 'sync-md',
|
|
needsInstall: true,
|
|
copyToAssets: true,
|
|
buildCommand: async (pluginPath) => {
|
|
try {
|
|
// Try normal build first
|
|
await execAsync(`cd ${pluginPath} && npm run build`);
|
|
} catch (buildError) {
|
|
// If normal build fails, try emergency build
|
|
console.log(' Normal build failed, trying emergency build...');
|
|
await execAsync(`cd ${pluginPath} && node scripts/emergency-build.js`);
|
|
}
|
|
|
|
// Copy to assets directory
|
|
const targetDir = path.join(
|
|
__dirname,
|
|
'../../../src/assets/bundled-plugins/sync-md',
|
|
);
|
|
if (!fs.existsSync(targetDir)) {
|
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
}
|
|
const distPath = path.join(pluginPath, 'dist');
|
|
if (fs.existsSync(distPath)) {
|
|
const files = fs.readdirSync(distPath);
|
|
for (const file of files) {
|
|
const src = path.join(distPath, file);
|
|
const dest = path.join(targetDir, file);
|
|
copyRecursive(src, dest);
|
|
}
|
|
}
|
|
return 'Built and copied to assets';
|
|
},
|
|
},
|
|
{
|
|
name: 'yesterday-tasks-plugin',
|
|
path: 'yesterday-tasks-plugin',
|
|
needsInstall: true,
|
|
copyToAssets: true,
|
|
buildCommand: async (pluginPath) => {
|
|
// Copy to assets directory
|
|
const targetDir = path.join(
|
|
__dirname,
|
|
'../../../src/assets/bundled-plugins/yesterday-tasks-plugin',
|
|
);
|
|
if (!fs.existsSync(targetDir)) {
|
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
}
|
|
const files = ['manifest.json', 'plugin.js', 'index.html', 'icon.svg'];
|
|
for (const file of files) {
|
|
const src = path.join(pluginPath, file);
|
|
const dest = path.join(targetDir, file);
|
|
if (fs.existsSync(src)) {
|
|
fs.copyFileSync(src, dest);
|
|
}
|
|
}
|
|
return 'Copied to assets';
|
|
},
|
|
},
|
|
{
|
|
name: 'ai-productivity-prompts',
|
|
path: 'ai-productivity-prompts',
|
|
needsInstall: true,
|
|
copyToAssets: true,
|
|
buildCommand: async (pluginPath) => {
|
|
await execAsync(`cd ${pluginPath} && npm run build`);
|
|
// Copy to assets directory
|
|
const targetDir = path.join(
|
|
__dirname,
|
|
'../../../src/assets/bundled-plugins/ai-productivity-prompts',
|
|
);
|
|
if (!fs.existsSync(targetDir)) {
|
|
fs.mkdirSync(targetDir, { recursive: true });
|
|
}
|
|
const distPath = path.join(pluginPath, 'dist');
|
|
if (fs.existsSync(distPath)) {
|
|
const files = fs.readdirSync(distPath);
|
|
for (const file of files) {
|
|
const src = path.join(distPath, file);
|
|
const dest = path.join(targetDir, file);
|
|
copyRecursive(src, dest);
|
|
}
|
|
}
|
|
return 'Built and copied to assets';
|
|
},
|
|
},
|
|
];
|
|
|
|
async function buildPlugin(plugin) {
|
|
const startTime = Date.now();
|
|
log(`\n📦 Building ${plugin.name}...`, colors.cyan);
|
|
|
|
try {
|
|
// Check if plugin directory exists
|
|
if (!fs.existsSync(plugin.path)) {
|
|
throw new Error(`Plugin directory not found: ${plugin.path}`);
|
|
}
|
|
|
|
// Install dependencies if needed
|
|
if (plugin.needsInstall) {
|
|
const packageJsonPath = path.join(plugin.path, 'package.json');
|
|
const nodeModulesPath = path.join(plugin.path, 'node_modules');
|
|
|
|
if (fs.existsSync(packageJsonPath)) {
|
|
log(` Installing dependencies...`, colors.yellow);
|
|
try {
|
|
// Try to install dependencies
|
|
await execAsync(`cd ${plugin.path} && npm install --include=dev`);
|
|
} catch (installError) {
|
|
// If install fails, check if node_modules exists and continue
|
|
if (fs.existsSync(nodeModulesPath)) {
|
|
log(
|
|
` Using existing dependencies (install failed but node_modules exists)`,
|
|
colors.yellow,
|
|
);
|
|
} else {
|
|
throw installError;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Run build command
|
|
log(` Building...`, colors.yellow);
|
|
const result = await plugin.buildCommand(plugin.path);
|
|
|
|
const duration = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
log(`✅ ${plugin.name} - ${result} (${duration}s)`, colors.green);
|
|
|
|
return { plugin: plugin.name, success: true, duration };
|
|
} catch (error) {
|
|
const duration = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
log(`❌ ${plugin.name} - Build failed: ${error.message} (${duration}s)`, colors.red);
|
|
|
|
return { plugin: plugin.name, success: false, error: error.message, duration };
|
|
}
|
|
}
|
|
|
|
async function buildAll() {
|
|
log('\n🚀 Building all plugins...', colors.bright);
|
|
const startTime = Date.now();
|
|
|
|
// Build plugins in parallel
|
|
const results = await Promise.all(plugins.map(buildPlugin));
|
|
|
|
// Summary
|
|
const totalDuration = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
const successful = results.filter((r) => r.success).length;
|
|
const failed = results.filter((r) => !r.success).length;
|
|
|
|
log('\n📊 Build Summary:', colors.bright);
|
|
log(` Total plugins: ${plugins.length}`);
|
|
log(` Successful: ${successful}`, colors.green);
|
|
if (failed > 0) {
|
|
log(` Failed: ${failed}`, colors.red);
|
|
}
|
|
log(` Total time: ${totalDuration}s`);
|
|
|
|
// List outputs
|
|
log('\n📁 Build outputs:', colors.bright);
|
|
|
|
// Check for minimal plugin zip
|
|
if (fs.existsSync('minimal-plugin.zip')) {
|
|
log(` • minimal-plugin.zip`);
|
|
}
|
|
|
|
// Check for other plugin outputs
|
|
for (const plugin of plugins.slice(1)) {
|
|
const distPath = path.join(plugin.path, 'dist');
|
|
if (fs.existsSync(distPath)) {
|
|
const pluginZip = path.join(distPath, 'plugin.zip');
|
|
if (fs.existsSync(pluginZip)) {
|
|
const stats = fs.statSync(pluginZip);
|
|
const size = (stats.size / 1024).toFixed(1);
|
|
log(` • ${plugin.path}/dist/plugin.zip (${size} KB)`);
|
|
} else {
|
|
log(` • ${plugin.path}/dist/`);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (failed > 0) {
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Run if called directly
|
|
if (require.main === module) {
|
|
buildAll().catch((error) => {
|
|
log(`\n❌ Build failed: ${error.message}`, colors.red);
|
|
process.exit(1);
|
|
});
|
|
}
|
|
|
|
module.exports = { buildAll };
|