feat: add dev script for previewing changes - #226
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
|
||
| // --- LOCAL SERVER --- | ||
| console.log('\nStarting local server...'); | ||
| spawn('npx', ['serve', './out'], { stdio: 'inherit', shell: true }); |
There was a problem hiding this comment.
Can we hold on to these PIDs so if something happens we can force kill them, e.g.
const spawn = (cmd, args) => {
const child = spawn(cmd, args, {
stdio: 'inherit',
windowsHide: true,
});
children.add(child);
child.once('close', () => children.delete(child));
child.once('error', () => children.delete(child));
return child;
}975528c to
db809ec
Compare
db809ec to
acef01e
Compare
| args.push('-i', filePath); | ||
|
|
||
| // Calculate the matching output directory | ||
| const normalizedPath = filePath.replace(/\\/g, '/'); | ||
| const dir = dirname(normalizedPath); | ||
|
|
||
| const relativeDir = dir.replace(/^pages\/?/, ''); | ||
|
|
||
| const outPath = relativeDir ? join('./out', relativeDir) : './out'; | ||
|
|
||
| args.push('-o', outPath); |
There was a problem hiding this comment.
I think the sidebar vanishes here ? maybe we can include it,wdyt?
There was a problem hiding this comment.
Can u elaborate, its just putting the modified html file into the respective ./out directly
There was a problem hiding this comment.
Did u reach anything for this ?!
|
btw @TusharThakur04 can you please recheck the package-lock file as i think it is downgrading many versions like shikhi and more |
acef01e to
40f6906
Compare
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
@webpack/gsoc-2026 PTAL |
WalkthroughThe PR adds a 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 2c8d59c5-07f0-4e52-9f67-2fe75c6fe469
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (2)
package.jsonscripts/dev/index.mjs
| args.push('-o', outPath); | ||
| } | ||
|
|
||
| await execFileAsync(docKitBin, args, { shell: true }); |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
sed -n '1,140p' scripts/dev/index.mjs
printf '\n--- package metadata and relevant references ---\n'
rg -n --glob 'package.json' --glob '*.mjs' --glob '*.js' 'doc-kit|runDocKit|execFileAsync|fs\.watch|scripts' . | head -200Repository: webpack/webpack-doc-kit
Length of output: 7443
🏁 Script executed:
sed -n '1,140p' scripts/dev/index.mjs
printf '\n--- references ---\n'
rg -n --glob 'package.json' --glob '*.mjs' --glob '*.js' 'doc-kit|runDocKit|execFileAsync|fs\.watch' .Repository: webpack/webpack-doc-kit
Length of output: 6292
🏁 Script executed:
nl -ba scripts/dev/index.mjs | sed -n '1,140p'Repository: webpack/webpack-doc-kit
Length of output: 197
🌐 Web query:
Node.js child_process.execFile shell true argument handling shell metacharacters Windows POSIX documentation
💡 Result:
In Node.js, the child_process.execFile function spawns a command directly by default, without a shell [1][2]. When the shell option is set to true, the command is instead executed within a shell, which interprets shell metacharacters [1][3]. Key details regarding shell handling: Execution Mechanism: When shell is true, Node.js uses /bin/sh on POSIX (Unix, Linux, macOS) systems and the value of process.env.ComSpec on Windows (typically cmd.exe) [1][4]. You can also specify a custom shell by providing a string instead of a boolean [2][5]. Security Warning: The official documentation explicitly warns that if the shell option is enabled, you must not pass unsanitized user input to the function [1][2]. Because the shell interprets metacharacters (such as &, |,;, $, >, etc.), any unsanitized input containing these characters can be exploited to trigger arbitrary command execution [1][4][6]. Argument Handling: When shell is true, Node.js joins the provided arguments with spaces and passes the resulting string to the shell [7]. This behavior means that the shell will parse the entire string, including the joined arguments, which is why it is susceptible to metacharacter injection [2][7]. Platform Nuance: On Windows, the shell option is sometimes necessary to execute.bat or.cmd files, as they are not directly executable as standalone processes [3][4]. However, developers should remain aware that relying on shell execution introduces the aforementioned security risks and behavioral differences regarding how arguments are parsed [2][7].
Citations:
- 1: https://nodejs.org/api/child_process.html
- 2: https://github.com/nodejs/node/blob/master/doc/api/child_process.md
- 3: https://beta.docs.nodejs.org/child_process.html
- 4: https://nodejs.org/docs/latest-v22.x/api/child_process.html
- 5: http://nodejs.org/docs/latest/api/child_process.html
- 6: https://nodejs.org/api/child_process.md
- 7: child_process should individually escape args[] on shell: true nodejs/node#29532
🏁 Script executed:
node - <<'JS'
const { execFile } = require('node:child_process');
const marker = `/tmp/coderabbit-shell-probe-${process.pid}`;
execFile('printf', ['%s', `safe; touch ${marker}`], { shell: true }, (error) => {
const fs = require('node:fs');
console.log(JSON.stringify({
error: error && error.message,
injectedCommandExecuted: fs.existsSync(marker)
}));
try { fs.unlinkSync(marker); } catch {}
});
JSRepository: webpack/webpack-doc-kit
Length of output: 467
Injection (CWE-78): Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')
Reachability: External
Remove shell execution from runDocKit.
fs.watch supplies a filename that reaches execFileAsync with shell: true. Shell metacharacters in a watched filename can execute an additional command during a rebuild. Invoke DocKit without a shell.
There was a problem hiding this comment.
What if we use execa ? then we can remove shell: true.
| clearTimeout(debounceTimer); | ||
| debounceTimer = setTimeout(async () => { | ||
| if (isBuilding) return; | ||
|
|
||
| isBuilding = true; | ||
|
|
||
| if (ext === '.md' || ext === '.mdx') { | ||
| // Wrap the single file in an array to match the new function signature | ||
| console.log( | ||
| `\nFile changed: ${fullPath} \nRunning fast partial build...` | ||
| ); | ||
| await runDocKit(fullPath); | ||
| } else { | ||
| // Any other file change (jsx, css, js, mjs, png in public, etc) triggers a full build | ||
| console.log(`\nFile changed: ${fullPath} \nRunning full build...`); | ||
| await runDocKit(); | ||
| } | ||
|
|
||
| isBuilding = false; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Do not discard rebuild requests while a build runs.
If a second change occurs after the first build starts, Line 71 returns without recording it. When the first build finishes, no timer remains for the second change. The preview can stay stale until another file change occurs.
If runDocKit rejects, Line 87 does not run. isBuilding then remains true and all later changes are skipped. Store a pending rebuild request. Reset isBuilding in finally, and catch build errors before scheduling the pending rebuild.
| const child = nativeSpawn(cmd, args, { | ||
| stdio: 'inherit', | ||
| shell: true, | ||
| }); | ||
|
|
||
| children.add(child); | ||
| child.once('close', () => children.delete(child)); | ||
| child.once('error', () => children.delete(child)); | ||
|
|
||
| return child; | ||
| }; | ||
|
|
||
| const cleanup = () => { | ||
| for (const child of children) { | ||
| if (!child.killed) { | ||
| child.kill('SIGINT'); | ||
| } | ||
| } | ||
| process.exit(); | ||
| }; | ||
|
|
||
| process.on('SIGINT', cleanup); | ||
| process.on('SIGTERM', cleanup); | ||
| process.on('exit', () => { | ||
| for (const child of children) { | ||
| if (!child.killed) child.kill(); | ||
| } | ||
| }); | ||
|
|
||
| console.log('\nStarting local server...'); | ||
| spawn('npx', ['serve', './out']); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- file inventory ---'
git ls-files scripts/dev
printf '%s\n' '--- AST outline ---'
ast-grep outline scripts/dev/index.mjs --view expanded || true
printf '%s\n' '--- relevant source ---'
cat -n scripts/dev/index.mjs | sed -n '1,180p'
printf '%s\n' '--- process API references ---'
rg -n -C 4 'spawn|execFile|execFileAsync|children|cleanup|SIGINT|SIGTERM|process\.exit|shell:' scripts/dev package.jsonRepository: webpack/webpack-doc-kit
Length of output: 7925
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- npx resolution ---'
command -v npx || true
if command -v npx >/dev/null 2>&1; then
file "$(command -v npx)" || true
fi
printf '%s\n' '--- shell child and descendant probe ---'
node --input-type=module <<'JS'
import { spawn } from 'node:child_process';
const child = spawn('sh', ['-c', 'sleep 30'], {
stdio: 'ignore',
shell: true,
});
const sleepPid = await new Promise((resolve, reject) => {
const timer = setInterval(() => {
const result = spawn('ps', ['-eo', 'pid=,ppid=,pgid=,comm=,args='], {
stdio: ['ignore', 'pipe', 'ignore'],
});
let output = '';
result.stdout.on('data', chunk => { output += chunk; });
result.once('close', () => {
const row = output.split('\n').find(line =>
line.trim().endsWith('sleep 30')
);
if (row) {
clearInterval(timer);
resolve(Number(row.trim().split(/\s+/)[0]));
}
});
}, 20);
child.once('error', reject);
});
const ps = () => new Promise((resolve, reject) => {
const result = spawn('ps', ['-o', 'pid=,ppid=,pgid=,comm=,args=', '-p', `${child.pid},${sleepPid}`], {
stdio: ['ignore', 'pipe', 'ignore'],
});
let output = '';
result.stdout.on('data', chunk => { output += chunk; });
result.once('error', reject);
result.once('close', () => resolve(output.trim()));
});
console.log(`tracked_pid=${child.pid}`);
console.log(`descendant_pid=${sleepPid}`);
console.log('before_signal:');
console.log(await ps());
const killResult = child.kill('SIGINT');
console.log(`kill_return=${killResult}`);
console.log(`child_killed_immediately=${child.killed}`);
await new Promise(resolve => setTimeout(resolve, 150));
console.log('after_signal:');
console.log(await ps());
try { process.kill(sleepPid, 'SIGKILL'); } catch {}
try { child.kill('SIGKILL'); } catch {}
JSRepository: webpack/webpack-doc-kit
Length of output: 153
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- npx resolution ---'
command -v npx || true
if command -v npx >/dev/null 2>&1; then
file "$(command -v npx)" || true
fi
printf '%s\n' '--- leftover probe processes ---'
ps -eo pid=,ppid=,pgid=,comm=,args= | awk '$0 ~ /[s]leep 30/ || $0 ~ /[n]ode --input-type=module/ {print}' || true
printf '%s\n' '--- bounded shell-child probe ---'
node --input-type=module <<'JS'
import { spawn } from 'node:child_process';
const runPs = () => new Promise((resolve) => {
const ps = spawn('ps', ['-eo', 'pid=,ppid=,pgid=,comm=,args='], {
stdio: ['ignore', 'pipe', 'ignore'],
});
let output = '';
ps.stdout.on('data', chunk => { output += chunk; });
ps.once('close', () => resolve(output));
});
const tracked = spawn('sh', ['-c', 'sleep 5'], {
stdio: 'ignore',
shell: true,
});
try {
await new Promise(resolve => setTimeout(resolve, 100));
const before = (await runPs()).split('\n')
.filter(line => line.includes(` ${tracked.pid} `) || line.trim().endsWith('sleep 5'))
.join('\n');
console.log(`tracked_pid=${tracked.pid}`);
console.log('before_signal:');
console.log(before || '<no matching rows>');
const killReturn = tracked.kill('SIGINT');
console.log(`kill_return=${killReturn}`);
console.log(`child_killed_immediately=${tracked.killed}`);
await new Promise(resolve => setTimeout(resolve, 100));
const after = (await runPs()).split('\n')
.filter(line => line.trim().endsWith('sleep 5'))
.join('\n');
console.log('after_signal_descendant:');
console.log(after || '<no sleep descendant>');
} finally {
try { tracked.kill('SIGKILL'); } catch {}
await new Promise(resolve => setTimeout(resolve, 100));
}
JSRepository: webpack/webpack-doc-kit
Length of output: 7451
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- npx launcher ---'
ls -l /usr/local/bin/npx || true
head -n 8 /usr/local/bin/npx 2>/dev/null || true
printf '%s\n' '--- corrected process-group probe ---'
node --input-type=module <<'JS'
import { spawn } from 'node:child_process';
const readPs = () => new Promise(resolve => {
const ps = spawn('ps', ['-eo', 'pid=,ppid=,pgid=,comm=,args='], {
stdio: ['ignore', 'pipe', 'ignore'],
});
let text = '';
ps.stdout.on('data', chunk => { text += chunk; });
ps.once('close', () => resolve(text));
});
const rowsFor = (text, pids) => text.split('\n').filter(line => {
const fields = line.trim().split(/\s+/);
return fields.length >= 2 &&
(pids.has(Number(fields[0])) || pids.has(Number(fields[1])));
}).join('\n');
const tracked = spawn("sh -c 'sleep 5'", [], {
stdio: 'ignore',
shell: true,
});
const probePids = new Set([tracked.pid]);
try {
await new Promise(resolve => setTimeout(resolve, 100));
const beforeText = await readPs();
const beforeRows = beforeText.split('\n').filter(line => {
const fields = line.trim().split(/\s+/);
return fields.length >= 2 &&
(Number(fields[0]) === tracked.pid || Number(fields[1]) === tracked.pid);
});
for (const line of beforeRows) {
const pid = Number(line.trim().split(/\s+/)[0]);
if (pid) probePids.add(pid);
}
console.log(`tracked_pid=${tracked.pid}`);
console.log('before_signal:');
console.log(beforeRows.join('\n') || '<no matching rows>');
const result = tracked.kill('SIGINT');
console.log(`kill_return=${result}`);
console.log(`child_killed_immediately=${tracked.killed}`);
await new Promise(resolve => setTimeout(resolve, 100));
console.log('after_signal:');
console.log(rowsFor(await readPs(), probePids) || '<no matching rows>');
} finally {
for (const pid of probePids) {
try { process.kill(pid, 'SIGKILL'); } catch {}
}
try { tracked.kill('SIGKILL'); } catch {}
await new Promise(resolve => setTimeout(resolve, 100));
}
JSRepository: webpack/webpack-doc-kit
Length of output: 667
🏁 Script executed:
#!/bin/bash
set -eu
node --input-type=module <<'JS'
import { spawn } from 'node:child_process';
const snapshot = () => new Promise(resolve => {
const ps = spawn('ps', ['-eo', 'pid=,ppid=,pgid=,comm=,args='], {
stdio: ['ignore', 'pipe', 'ignore'],
});
let text = '';
ps.stdout.on('data', chunk => { text += chunk; });
ps.once('close', () => resolve(text));
});
const descendantsOf = (text, rootPid) => {
const rows = text.split('\n').map(line => {
const fields = line.trim().split(/\s+/);
return fields.length >= 2
? { pid: Number(fields[0]), ppid: Number(fields[1]), line }
: null;
}).filter(Boolean);
const pids = new Set([rootPid]);
let changed = true;
while (changed) {
changed = false;
for (const row of rows) {
if (pids.has(row.ppid) && !pids.has(row.pid)) {
pids.add(row.pid);
changed = true;
}
}
}
return rows.filter(row => pids.has(row.pid)).map(row => row.line).join('\n');
};
const tracked = spawn('npx', ['--no-install', '--', '/bin/sleep', '5'], {
stdio: 'ignore',
shell: true,
});
try {
await new Promise(resolve => setTimeout(resolve, 300));
console.log(`tracked_pid=${tracked.pid}`);
console.log('before_signal:');
console.log(descendantsOf(await snapshot(), tracked.pid) || '<no matching rows>');
console.log(`kill_return=${tracked.kill('SIGINT')}`);
console.log(`child_killed_immediately=${tracked.killed}`);
await new Promise(resolve => setTimeout(resolve, 300));
console.log('after_signal:');
console.log(descendantsOf(await snapshot(), tracked.pid) || '<no matching rows>');
} finally {
try { tracked.kill('SIGKILL'); } catch {}
const remaining = (await snapshot()).split('\n');
for (const line of remaining) {
const fields = line.trim().split(/\s+/);
if (fields.length >= 2 && Number(fields[1]) === tracked.pid) {
try { process.kill(Number(fields[0]), 'SIGKILL'); } catch {}
}
}
await new Promise(resolve => setTimeout(resolve, 100));
}
JSRepository: webpack/webpack-doc-kit
Length of output: 587
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- package metadata and scripts ---'
cat -n package.json | sed -n '1,220p'
printf '%s\n' '--- serve references and platform references ---'
rg -n -i -C 3 'serve|windows|win32|linux|macos|darwin|platform|npm run dev|development environment' \
README.md CONTRIBUTING.md docs package.json package-lock.json yarn.lock pnpm-lock.yaml 2>/dev/null || true
printf '%s\n' '--- lockfile entries for serve ---'
rg -n -C 5 '"serve"|"node_modules/serve"|bin/serve' package-lock.json yarn.lock pnpm-lock.yaml 2>/dev/null || trueRepository: webpack/webpack-doc-kit
Length of output: 50381
🌐 Web query:
Node.js child_process spawn shell true ChildProcess kill process group official documentation
💡 Result:
In Node.js, the child_process.spawn method, when used with the shell option set to true, creates a new process inside a shell [1]. Official Node.js documentation does not provide a built-in method to automatically kill a process group or a process's children when using spawn [1]. Calling child.kill on the child process instance only signals the immediate shell process spawned by Node.js, often leaving its child processes (the actual commands executed within the shell) orphaned or running [1][2]. To terminate a process group or tree, common community-supported approaches include: 1. Detached Process Group (Unix-like systems): By setting the detached option to true in the spawn options, the child process becomes the leader of a new process group [1][3]. You can then attempt to kill the entire group by passing a negative PID to process.kill (e.g., process.kill(-child.pid, signal)) [3][2]. 2. Tree-Killing Utilities: For cross-platform support and easier management, many developers use third-party packages such as "tree-kill," which are designed to recursively identify and terminate all processes in a process tree started by a parent [4]. The official Node.js documentation specifically cautions that when running processes in a shell, sub-processes of those child processes may not be signaled when attempting to kill the parent [1]. Always exercise caution when using the shell option due to security risks associated with passing unsanitized user input [1].
Citations:
- 1: https://nodejs.org/api/child_process.html
- 2: https://medium.com/@almenon214/killing-processes-with-node-772ffdd19aad
- 3: Allow child.kill() and exec() to kill child's children nodejs/node-v0.x-archive#1811
- 4: https://stackoverflow.com/questions/18694684/spawn-and-kill-a-process-in-node-js
🏁 Script executed:
#!/bin/bash
set -eu
node --input-type=module <<'JS'
import { spawn } from 'node:child_process';
const snapshot = () => new Promise(resolve => {
const ps = spawn('ps', ['-eo', 'pid=,ppid=,pgid=,comm=,args='], {
stdio: ['ignore', 'pipe', 'ignore'],
});
let text = '';
ps.stdout.on('data', chunk => { text += chunk; });
ps.once('close', () => resolve(text));
});
const tree = (text, rootPid) => {
const rows = text.split('\n').map(line => {
const fields = line.trim().split(/\s+/);
return fields.length >= 2
? { pid: Number(fields[0]), ppid: Number(fields[1]), line }
: null;
}).filter(Boolean);
const pids = new Set([rootPid]);
for (let changed = true; changed;) {
changed = false;
for (const row of rows) {
if (pids.has(row.ppid) && !pids.has(row.pid)) {
pids.add(row.pid);
changed = true;
}
}
}
return { rows: rows.filter(row => pids.has(row.pid)), pids };
};
const tracked = spawn("sh -c 'sleep 5; printf done >/dev/null'", [], {
stdio: 'ignore',
shell: true,
});
const probePids = new Set([tracked.pid]);
try {
await new Promise(resolve => setTimeout(resolve, 100));
let current = tree(await snapshot(), tracked.pid);
for (const row of current.rows) probePids.add(row.pid);
console.log(`tracked_pid=${tracked.pid}`);
console.log('before_signal:');
console.log(current.rows.map(row => row.line).join('\n') || '<no matching rows>');
console.log(`kill_return=${tracked.kill('SIGINT')}`);
console.log(`child_killed_immediately=${tracked.killed}`);
await new Promise(resolve => setTimeout(resolve, 150));
current = tree(await snapshot(), tracked.pid);
console.log('after_signal:');
console.log(current.rows.map(row => row.line).join('\n') || '<no matching rows>');
for (const row of current.rows) probePids.add(row.pid);
} finally {
for (const pid of probePids) {
try { process.kill(pid, 'SIGKILL'); } catch {}
}
await new Promise(resolve => setTimeout(resolve, 100));
}
JSRepository: webpack/webpack-doc-kit
Length of output: 480
Terminate the complete server process tree before exiting. With shell: true, children tracks only the direct child. The shell can remain alive with its server descendant after child.kill('SIGINT'); child.killed becomes true immediately, so the exit handler skips it. Spawn without a shell where possible, or terminate the process group/tree and await child closure before calling process.exit().
| let debounceTimer = null; | ||
| let isBuilding = false; | ||
|
|
||
| const handleFileChange = (baseDir, filename) => { | ||
| // Ignore hidden files / temp editor files | ||
| if (!filename || filename.startsWith('.')) return; | ||
|
|
||
| const fullPath = join(baseDir, filename); | ||
| const ext = extname(filename); | ||
|
|
||
| // Debounce rapid save events from editors | ||
| clearTimeout(debounceTimer); | ||
| debounceTimer = setTimeout(async () => { | ||
| if (isBuilding) return; | ||
|
|
||
| isBuilding = true; | ||
|
|
||
| if (ext === '.md' || ext === '.mdx') { | ||
| // Wrap the single file in an array to match the new function signature | ||
| console.log( | ||
| `\nFile changed: ${fullPath} \nRunning fast partial build...` | ||
| ); | ||
| await runDocKit(fullPath); | ||
| } else { | ||
| // Any other file change (jsx, css, js, mjs, png in public, etc) triggers a full build | ||
| console.log(`\nFile changed: ${fullPath} \nRunning full build...`); | ||
| await runDocKit(); | ||
| } | ||
|
|
||
| isBuilding = false; | ||
| }, 500); | ||
| }; |
There was a problem hiding this comment.
| let debounceTimer = null; | |
| let isBuilding = false; | |
| const handleFileChange = (baseDir, filename) => { | |
| // Ignore hidden files / temp editor files | |
| if (!filename || filename.startsWith('.')) return; | |
| const fullPath = join(baseDir, filename); | |
| const ext = extname(filename); | |
| // Debounce rapid save events from editors | |
| clearTimeout(debounceTimer); | |
| debounceTimer = setTimeout(async () => { | |
| if (isBuilding) return; | |
| isBuilding = true; | |
| if (ext === '.md' || ext === '.mdx') { | |
| // Wrap the single file in an array to match the new function signature | |
| console.log( | |
| `\nFile changed: ${fullPath} \nRunning fast partial build...` | |
| ); | |
| await runDocKit(fullPath); | |
| } else { | |
| // Any other file change (jsx, css, js, mjs, png in public, etc) triggers a full build | |
| console.log(`\nFile changed: ${fullPath} \nRunning full build...`); | |
| await runDocKit(); | |
| } | |
| isBuilding = false; | |
| }, 500); | |
| }; | |
| let debounceTimer = null; | |
| let isBuilding = false; | |
| let pendingFiles = new Set(); | |
| let needsFullBuild = false; | |
| const handleFileChange = (baseDir, filename) => { | |
| if (!filename || filename.startsWith('.')) return; | |
| const fullPath = join(baseDir, filename); | |
| const ext = extname(filename); | |
| if (ext === '.md' || ext === '.mdx') { | |
| pendingFiles.add(fullPath); | |
| } else { | |
| needsFullBuild = true; | |
| } | |
| clearTimeout(debounceTimer); | |
| debounceTimer = setTimeout(async () => { | |
| if (isBuilding) return; | |
| while (pendingFiles.size > 0 || needsFullBuild) { | |
| if (needsFullBuild) { | |
| needsFullBuild = false; | |
| pendingFiles.clear(); | |
| isBuilding = true; | |
| try { | |
| await runDocKit(); | |
| } catch (err) { | |
| console.error('\nFull build failed:', err.message); | |
| } finally { | |
| isBuilding = false; | |
| } | |
| } else { | |
| const singleFile = pendingFiles.values().next().value; | |
| pendingFiles.delete(singleFile); | |
| isBuilding = true; | |
| try { | |
| await runDocKit(singleFile); | |
| } catch (err) { | |
| console.error(`\nPartial build failed for ${singleFile}:`, err.message); | |
| } finally { | |
| isBuilding = false; | |
| } | |
| } | |
| } | |
| }, 500); | |
| }; |
Solving coderabbitai review, by saving the coming requests in a unique queue.
There was a problem hiding this comment.
should we do this? i mean we can wait for the ongoing build to complete and then hit another one cuz its mainly for markdown changes in dev environment so will mostly be dealing with a single page at a time.
what we can do is lets say a build is going on and we edit the same page during build and hit save, we can cancel the ongoing run and start the new build with prev and current changes instead of queuing.
WDYT?
Summary
This PR adds a dev script for previewing changes in dev environment
What kind of change does this PR introduce?
feat
Did you add tests for your changes?
no
Does this PR introduce a breaking change?
no
If relevant, what needs to be documented once your changes are merged or what have you already documented?
Use of AI
Summary by CodeRabbit