diff --git a/build.js b/build.js index 2e06d1e..04ba9c9 100644 --- a/build.js +++ b/build.js @@ -1,5 +1,30 @@ import * as esbuild from 'esbuild'; -import { cp, mkdir } from 'fs/promises'; +import { cpSync, rmSync, existsSync, mkdirSync, watch } from 'node:fs'; +import { createRequire } from 'node:module'; +import { execSync } from 'child_process'; + +const require = createRequire(import.meta.url); +const pkg = require('./package.json'); +const isProd = process.env.NODE_ENV === 'production'; + +let _commitHash = null; +const getCommitHash = () => { + if (_commitHash) return _commitHash; + try { + _commitHash = execSync('git rev-parse --short HEAD').toString().trim(); + } catch { + _commitHash = 'unknown'; + } + return _commitHash; +}; + +const jsBanner = `/*! + * ${pkg.name} v${pkg.version}+${getCommitHash()} + * ${pkg.description} + * (c) ${new Date().getFullYear()} ${pkg.author} + * ${pkg.repository.url?.replace(/\.git$/, '')} + * Released under the ${pkg.license} License. + */`; const userscriptBanner = `// ==UserScript== // @name Gemini NanoBanana Watermark Remover @@ -20,51 +45,82 @@ const userscriptBanner = `// ==UserScript== // ==/UserScript== `; -async function build() { - console.log(`Start Build... ${process.env.NODE_ENV === 'production' ? 'production' : 'development'}\r\n`); +const copyAssetsPlugin = { + name: 'copy-assets', + setup(build) { + build.onEnd(() => { + console.log('šŸ“‚ Syncing static assets...'); + try { + if (!existsSync('dist/i18n')) mkdirSync('dist/i18n', { recursive: true }); + cpSync('src/i18n', 'dist/i18n', { recursive: true }); + cpSync('public', 'dist', { recursive: true }); + } catch (err) { + console.error('āŒ Asset copy failed:', err); + } + }); + }, +}; - // Build website - app.js - console.log('Building: dist/app.js'); - await esbuild.build({ - entryPoints: ['src/app.js'], - bundle: true, - format: 'esm', - outfile: 'dist/app.js', - loader: { '.png': 'dataurl' }, - publicPath: '/', - minify: process.env.NODE_ENV === 'production' - }); +const commonConfig = { + bundle: true, + loader: { '.png': 'dataurl' }, + minify: isProd, + logLevel: 'info', +}; - // Build website - i18n.js - console.log('Building: dist/i18n.js'); - await esbuild.build({ - entryPoints: ['src/i18n.js'], - bundle: true, - format: 'esm', - outfile: 'dist/i18n.js', - minify: process.env.NODE_ENV === 'production' - }); +// Build website - app.js +const websiteCtx = await esbuild.context({ + ...commonConfig, + entryPoints: ['src/app.js'], + outfile: 'dist/app.js', + platform: 'browser', + target: ['es2020'], + banner: { js: jsBanner }, + sourcemap: !isProd, + plugins: [copyAssetsPlugin], +}); - // Build userscript - console.log('Building: dist/userscript/gemini-watermark-remover.user.js'); - await mkdir('dist/userscript', { recursive: true }); - await esbuild.build({ - entryPoints: ['src/userscript/index.js'], - bundle: true, - format: 'iife', - outfile: 'dist/userscript/gemini-watermark-remover.user.js', - banner: { js: userscriptBanner }, - loader: { '.png': 'dataurl' }, - minify: false - }); +// Build userscript +const userscriptCtx = await esbuild.context({ + ...commonConfig, + entryPoints: ['src/userscript/index.js'], + format: 'iife', + outfile: 'dist/userscript/gemini-watermark-remover.user.js', + banner: { js: userscriptBanner }, + minify: false +}); - // Copy static files - console.log('Copying: src/i18n -> dist/i18n'); - await cp('src/i18n', 'dist/i18n', { recursive: true }); - console.log('Copying: public -> dist'); - await cp('public', 'dist', { recursive: true }); +console.log(`šŸš€ Starting build process... [${isProd ? 'PRODUCTION' : 'DEVELOPMENT'}]`); - console.log('\r\nāœ“ Build complete'); +if (existsSync('dist')) rmSync('dist', { recursive: true }); +mkdirSync('dist/userscript', { recursive: true }); + +if (isProd) { + await Promise.all([websiteCtx.rebuild(), userscriptCtx.rebuild()]); + console.log('āœ… Build complete!'); + process.exit(0); +} else { + await Promise.all([websiteCtx.watch(), userscriptCtx.watch()]); + + const watchDir = (dir, dest) => { + let debounceTimer = null; + + watch(dir, { recursive: true }, (eventType, filename) => { + if (!filename) return; + if (debounceTimer) clearTimeout(debounceTimer); + + debounceTimer = setTimeout(() => { + console.log(`šŸ“‚ Asset changed: ${filename}`); + try { + cpSync(dir, dest, { recursive: true }); + } catch (e) { + console.error('Sync failed:', e); + } + }, 100); + }); + }; + watchDir('src/i18n', 'dist/i18n'); + watchDir('public', 'dist'); + + console.log('šŸ‘€ Watching for changes...'); } - -build().catch(console.error); diff --git a/package.json b/package.json index 46c65cd..e551c55 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,13 @@ { "name": "gemini-watermark-remover", + "description": "Automatically removes watermarks from Gemini AI generated images", "version": "1.0.0", + "author": "journey-ad", + "repository": { + "type": "git", + "url": "https://github.com/journey-ad/gemini-watermark-remover.git" + }, + "license": "MIT", "type": "module", "scripts": { "dev": "node build.js",