Skip to content
This repository was archived by the owner on Apr 27, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@ node_modules
prepare

@girs
@types
dist

**/.claude/settings.local.json
**/.claude/settings.local.json
175 changes: 175 additions & 0 deletions esbuild.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import esbuild from 'esbuild';
import { promises as fs } from 'fs';
import path from 'path';

const isDevelopment = process.argv.includes('-dev') || process.argv.includes('--dev') || process.argv.includes('-d');
const isWatch = process.argv.includes('-watch') || process.argv.includes('--watch') || process.argv.includes('-w');
console.log(`πŸš€ Building in ${isDevelopment ? 'development' : 'production'} mode`);
Comment on lines +5 to +7
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Development vs Prod makes no sense for hyprpanel


const outdir = isDevelopment ? './dist' : '/usr';
const hyprpanelDir = path.join(outdir, 'share/hyprpanel');

const entryPoints = ['app.ts'];
const staticDirs = ['scripts', 'themes', 'assets', 'src/style'];

const copyStaticFiles = {
name: 'copy-static',
setup(build) {
build.onEnd(async () => {
try {
console.log('πŸ“ Copying static files...');
// Create output directory if it doesn't exist
await fs.mkdir(outdir, { recursive: true });
await copyDirs(staticDirs);
await copyLauncherScript();
} catch (error) {
console.error('❌ Error copying static files:', error.message);
}
});
}
};

const buildOptions = {
entryPoints,
entryNames: 'hyprpanel',
outdir: hyprpanelDir,
bundle: true,
format: 'esm',
platform: 'neutral',
external: [
'gi://*',
'system',
],
plugins: [copyStaticFiles], // Plugin to copy static files
tsconfig: './tsconfig.json', // Use tsconfig for type checking and other settings
keepNames: true, // Keep function names for better debugging
sourcemap: false, // Generate sourcemaps in development mode
minify: !isDevelopment, // Minify in production mode
treeShaking: true,
metafile: isDevelopment, // Generate metafile only in development mode
splitting: false, // Disable code splitting for simplicity
logLevel: 'info',
color: true, // Enable colored output
write: true, // Write output to disk
define: {
'DATADIR': JSON.stringify(hyprpanelDir)
},
resolveExtensions: ['.ts', '.tsx', '.js', '.jsx'],
loader: {
'.ts': 'ts',
'.tsx': 'tsx'
}
};

async function copyLauncherScript() {
const sourceFile = 'scripts/hyprpanel_launcher.sh.in';
const destDir = path.join(outdir, 'bin');
const destFile = path.join(destDir, 'hyprpanel');

try {
await fs.access(sourceFile);
const templateContent = await fs.readFile(sourceFile, 'utf8');
const processedContent = templateContent.replace(/@DATADIR@/g, hyprpanelDir);
await fs.mkdir(destDir, { recursive: true });
await fs.writeFile(destFile, processedContent, { mode: 0o755 }); // Make it executable
console.log(`βœ… Launcher script copied to ${hyprpanelDir}`);
} catch (error) {
if (error.code === 'ENOENT') {
console.log('⚠️ Launcher script not found, skipping...');
} else {
console.error('❌ Error copying launcher script:', error.message);
}
}
}

// Plugin for copying static files
async function copyDirs(dirs) {
const copyPromises = dirs.map(async (dir) => {
try {
await fs.access(dir);
const destDir = path.join(outdir, '/share/hyprpanel', dir);

const needsCopy = await shouldCopyDirectory(dir, destDir);

if (needsCopy) {
await fs.mkdir(path.dirname(destDir), { recursive: true });
await fs.cp(dir, destDir, {
recursive: true,
force: true,
preserveTimestamps: true // Preserve timestamps for future incremental builds
});
console.log(`βœ… ${dir} copied`);
} else {
console.log(`⏭️ ${dir} skipped (no changes)`);
}

return { dir, success: true, copied: needsCopy };
} catch (err) {
console.log('⚠️ No scripts directory found');
return { dir, success: false, error: err.message };
}
})
const results = await Promise.all(copyPromises);
return results;
};

// Function to check if a directory needs to be copied
async function shouldCopyDirectory(sourceDir, destDir) {
try {
const [sourceStat, destStat] = await Promise.all([
fs.stat(sourceDir),
fs.stat(destDir).catch(() => null)
]);

if (!destStat) return true;

return sourceStat.mtime > destStat.mtime;
} catch {
return true;
}
};

// Main build function
async function build() {
try {
await esbuild.build(buildOptions);
console.log(`βœ… Build completed successfully!`);
console.log(`πŸ“¦ Output directory: ${outdir}`);
} catch (error) {
console.error('❌ Build failed:', error);
process.exit(1);
}
};

// Function for watch mode
async function watch() {
try {
console.log('πŸ‘€ Starting watch mode...');
const ctx = await esbuild.context({
...buildOptions,
plugins: [
copyStaticFiles,
]
});

await ctx.watch();
console.log('πŸ‘€ Watching for changes... (Press Ctrl+C to stop)');

// Keeping the process alive
process.on('SIGINT', async () => {
console.log('\nπŸ›‘ Stopping watch mode...');
await ctx.dispose();
process.exit(0);
});

} catch (error) {
console.error('❌ Watch mode failed:', error);
process.exit(1);
}
}

if (isWatch) {
watch();
} else {
build();
}
Loading