175 lines
4.9 KiB
JavaScript
175 lines
4.9 KiB
JavaScript
import { spawn } from 'child_process';
|
|
import { resolve } from 'path';
|
|
import { normalizePath } from 'vite';
|
|
import picomatch from 'picomatch';
|
|
|
|
// Build queue to prevent overlapping builds
|
|
class BuildQueue {
|
|
constructor() {
|
|
this.queue = [];
|
|
this.pending = false;
|
|
}
|
|
|
|
enqueue(buildFn) {
|
|
return new Promise((resolve, reject) => {
|
|
this.queue.push({ buildFn, resolve, reject });
|
|
this.process();
|
|
});
|
|
}
|
|
|
|
async process() {
|
|
if (this.pending || this.queue.length === 0) return;
|
|
|
|
this.pending = true;
|
|
const { buildFn, resolve, reject } = this.queue.shift();
|
|
|
|
try {
|
|
await buildFn();
|
|
resolve();
|
|
} catch (error) {
|
|
reject(error);
|
|
} finally {
|
|
this.pending = false;
|
|
this.process();
|
|
}
|
|
}
|
|
}
|
|
|
|
const buildQueue = new BuildQueue();
|
|
|
|
export default function hotReloadPlugin(options = {}) {
|
|
const config = {
|
|
// Files to watch for changes
|
|
watchFiles: [
|
|
"./site/**/*.blade.php",
|
|
"./site/**/*.md",
|
|
"./site/**/*.html",
|
|
"./config/**/*.php",
|
|
"./app/**/*.php",
|
|
"./zap.yml"
|
|
],
|
|
|
|
// Files to ignore
|
|
ignoreFiles: [
|
|
'node_modules/**',
|
|
'build_local/**',
|
|
'.git/**',
|
|
'vendor/**'
|
|
],
|
|
|
|
// Build command
|
|
buildCommand: 'php zap build',
|
|
|
|
// Delay before refresh (ms)
|
|
refreshDelay: 100,
|
|
|
|
// Always do full page reload
|
|
always: true,
|
|
|
|
// Override with user options
|
|
...options
|
|
};
|
|
|
|
function runSiteBuild() {
|
|
return new Promise((resolve, reject) => {
|
|
const [command, ...args] = config.buildCommand.split(' ');
|
|
|
|
const build = spawn(command, args, {
|
|
stdio: 'inherit',
|
|
shell: true
|
|
});
|
|
|
|
build.on('exit', (code) => {
|
|
if (code === 0) resolve();
|
|
else reject(new Error(`Build failed with code ${code}`));
|
|
});
|
|
});
|
|
}
|
|
|
|
return {
|
|
name: 'static-site-hot-reload',
|
|
|
|
config() {
|
|
return {
|
|
server: {
|
|
watch: {
|
|
disableGlobbing: false,
|
|
ignored: config.ignoreFiles.map(pattern =>
|
|
resolve(process.cwd(), pattern)
|
|
).map(normalizePath)
|
|
}
|
|
}
|
|
};
|
|
},
|
|
|
|
configureServer(server) {
|
|
// Normalize file paths
|
|
const watchFiles = config.watchFiles.map(pattern =>
|
|
resolve(process.cwd(), pattern)
|
|
).map(normalizePath);
|
|
|
|
const shouldReload = picomatch(watchFiles);
|
|
|
|
// Add files to Vite's watcher
|
|
server.watcher.add(watchFiles);
|
|
|
|
const handleFileChange = async (filePath) => {
|
|
if (!shouldReload(filePath)) return;
|
|
|
|
const start = performance.now();
|
|
|
|
try {
|
|
// Queue the build to prevent overlaps
|
|
await buildQueue.enqueue(() => runSiteBuild());
|
|
|
|
const duration = Math.round(performance.now() - start);
|
|
|
|
// Delay the refresh
|
|
setTimeout(() => {
|
|
server.config.logger.info(
|
|
`full reload for ${filePath} - build: ${duration}ms`,
|
|
{ timestamp: true, clear: true }
|
|
);
|
|
|
|
// Send full reload command
|
|
server.ws.send({
|
|
type: 'full-reload',
|
|
path: config.always ? '*' : filePath
|
|
});
|
|
}, config.refreshDelay);
|
|
|
|
} catch (error) {
|
|
server.config.logger.error(`Build failed: ${error.message}`);
|
|
}
|
|
};
|
|
|
|
// Listen for file changes
|
|
server.watcher.on('add', handleFileChange);
|
|
server.watcher.on('change', handleFileChange);
|
|
|
|
// Initial build
|
|
runSiteBuild().then(() => {
|
|
server.config.logger.info('Initial build completed');
|
|
}).catch((error) => {
|
|
server.config.logger.error(`Initial build failed: ${error.message}`);
|
|
});
|
|
},
|
|
|
|
// Handle hot updates
|
|
handleHotUpdate({ file }) {
|
|
const watchFiles = config.watchFiles.map(pattern =>
|
|
resolve(process.cwd(), pattern)
|
|
).map(normalizePath);
|
|
|
|
const shouldReload = picomatch(watchFiles);
|
|
|
|
if (shouldReload(file)) {
|
|
// Return empty array to prevent HMR, we'll do full reload instead
|
|
return [];
|
|
}
|
|
|
|
// Let Vite handle other files normally
|
|
return undefined;
|
|
}
|
|
};
|
|
} |