added a way to bundle the whole app
This commit is contained in:
parent
872d29f3e1
commit
249c88da83
5 changed files with 119 additions and 116 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,2 +1,3 @@
|
||||||
|
dist/
|
||||||
node_modules/
|
node_modules/
|
||||||
public/client.js
|
public/client.js
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ if [ ${#ARGS[@]} -lt 3 ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
script_dir="$(cd "$(dirname "$0")" && pwd)"
|
script_dir="$(cd "$(dirname "$0")" && pwd)"
|
||||||
node_args=("$script_dir/server.js")
|
node_args=("$script_dir/dist/server.cjs")
|
||||||
[ -n "$STATIC" ] && node_args+=("$STATIC")
|
[ -n "$STATIC" ] && node_args+=("$STATIC")
|
||||||
[ -n "$OUTFILE" ] && node_args+=("--out" "$OUTFILE")
|
[ -n "$OUTFILE" ] && node_args+=("--out" "$OUTFILE")
|
||||||
node_args+=("${ARGS[@]}")
|
node_args+=("${ARGS[@]}")
|
||||||
|
|
|
||||||
12
package-lock.json
generated
12
package-lock.json
generated
|
|
@ -7,13 +7,14 @@
|
||||||
"": {
|
"": {
|
||||||
"name": "difftool",
|
"name": "difftool",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
|
"hasInstallScript": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pierre/diffs": "^1.2.7",
|
"@pierre/diffs": "1.2.7",
|
||||||
"@pierre/trees": "^1.0.0-beta.4",
|
"express": "5.2.1"
|
||||||
"express": "^5.1.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"esbuild": "^0.28.0"
|
"@pierre/trees": "1.0.0-beta.4",
|
||||||
|
"esbuild": "0.28.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
|
|
@ -489,6 +490,7 @@
|
||||||
"version": "1.0.0-beta.4",
|
"version": "1.0.0-beta.4",
|
||||||
"resolved": "https://registry.npmjs.org/@pierre/trees/-/trees-1.0.0-beta.4.tgz",
|
"resolved": "https://registry.npmjs.org/@pierre/trees/-/trees-1.0.0-beta.4.tgz",
|
||||||
"integrity": "sha512-OfT1yk9ne8Te5+GB5zUY8yqE6B8BqjBHQJleH4lu8ltwNpoocZl4vXt1AzlEExpxI/pp+AFX5QG+lR3JjtTEag==",
|
"integrity": "sha512-OfT1yk9ne8Te5+GB5zUY8yqE6B8BqjBHQJleH4lu8ltwNpoocZl4vXt1AzlEExpxI/pp+AFX5QG+lR3JjtTEag==",
|
||||||
|
"dev": true,
|
||||||
"license": "apache-2.0",
|
"license": "apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"preact": "11.0.0-beta.0",
|
"preact": "11.0.0-beta.0",
|
||||||
|
|
@ -1460,6 +1462,7 @@
|
||||||
"version": "11.0.0-beta.0",
|
"version": "11.0.0-beta.0",
|
||||||
"resolved": "https://registry.npmjs.org/preact/-/preact-11.0.0-beta.0.tgz",
|
"resolved": "https://registry.npmjs.org/preact/-/preact-11.0.0-beta.0.tgz",
|
||||||
"integrity": "sha512-IcODoASASYwJ9kxz7+MJeiJhvLriwSb4y4mHIyxdgaRZp6kPUud7xytrk/6GZw8U3y6EFJaRb5wi9SrEK+8+lg==",
|
"integrity": "sha512-IcODoASASYwJ9kxz7+MJeiJhvLriwSb4y4mHIyxdgaRZp6kPUud7xytrk/6GZw8U3y6EFJaRb5wi9SrEK+8+lg==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
|
|
@ -1470,6 +1473,7 @@
|
||||||
"version": "6.6.5",
|
"version": "6.6.5",
|
||||||
"resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.6.5.tgz",
|
"resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.6.5.tgz",
|
||||||
"integrity": "sha512-O6MHzYNIKYaiSX3bOw0gGZfEbOmlIDtDfWwN1JJdc/T3ihzRT6tGGSEWE088dWrEDGa1u7101q+6fzQnO9XCPA==",
|
"integrity": "sha512-O6MHzYNIKYaiSX3bOw0gGZfEbOmlIDtDfWwN1JJdc/T3ihzRT6tGGSEWE088dWrEDGa1u7101q+6fzQnO9XCPA==",
|
||||||
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"preact": ">=10 || >= 11.0.0-0"
|
"preact": ">=10 || >= 11.0.0-0"
|
||||||
|
|
|
||||||
12
package.json
12
package.json
|
|
@ -4,15 +4,17 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@pierre/diffs": "^1.2.7",
|
"@pierre/diffs": "1.2.7",
|
||||||
"@pierre/trees": "^1.0.0-beta.4",
|
"express": "5.2.1"
|
||||||
"express": "^5.1.0"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "esbuild src/client.js --bundle --outfile=public/client.js --format=esm",
|
"build:client": "esbuild src/client.js --bundle --outfile=public/client.js --format=esm",
|
||||||
|
"build:server": "esbuild server.js --bundle --platform=node --target=node20 --outfile=dist/server.cjs --format=cjs && mkdir -p dist/public && cp -r public/. dist/public",
|
||||||
|
"build": "npm run build:server && npm run build:client",
|
||||||
"postinstall": "npm run build"
|
"postinstall": "npm run build"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"esbuild": "^0.28.0"
|
"@pierre/trees": "1.0.0-beta.4",
|
||||||
|
"esbuild": "0.28.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
60
server.js
60
server.js
|
|
@ -7,7 +7,9 @@ import { fileURLToPath } from 'url';
|
||||||
import { parsePatchFiles, DEFAULT_THEMES } from '@pierre/diffs';
|
import { parsePatchFiles, DEFAULT_THEMES } from '@pierre/diffs';
|
||||||
import { preloadFileDiff } from '@pierre/diffs/ssr';
|
import { preloadFileDiff } from '@pierre/diffs/ssr';
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = typeof __filename !== 'undefined'
|
||||||
|
? dirname(__filename)
|
||||||
|
: dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
// --- arg parsing ---
|
// --- arg parsing ---
|
||||||
|
|
||||||
|
|
@ -86,17 +88,18 @@ if (files.length === 0) {
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- SSR diffs ---
|
// --- SSR diffs + helpers + mode dispatch ---
|
||||||
|
|
||||||
const ssrOptions = {
|
(async () => {
|
||||||
|
const ssrOptions = {
|
||||||
theme: DEFAULT_THEMES,
|
theme: DEFAULT_THEMES,
|
||||||
themeType: 'system',
|
themeType: 'system',
|
||||||
diffStyle: 'unified',
|
diffStyle: 'unified',
|
||||||
useTokenTransformer: true,
|
useTokenTransformer: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderedFiles = [];
|
const renderedFiles = [];
|
||||||
for (const fileDiff of files) {
|
for (const fileDiff of files) {
|
||||||
try {
|
try {
|
||||||
const result = await preloadFileDiff({ fileDiff, options: ssrOptions });
|
const result = await preloadFileDiff({ fileDiff, options: ssrOptions });
|
||||||
renderedFiles.push({
|
renderedFiles.push({
|
||||||
|
|
@ -110,21 +113,19 @@ for (const fileDiff of files) {
|
||||||
prerenderedHTML: null,
|
prerenderedHTML: null,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- helpers ---
|
const finalPatch = patch;
|
||||||
|
|
||||||
function escapeHtml(str) {
|
function escapeHtml(str) {
|
||||||
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
function fileId(name) {
|
function fileId(name) {
|
||||||
return 'DF-' + name.replace(/[^a-zA-Z0-9_-]/g, '_');
|
return 'DF-' + name.replace(/[^a-zA-Z0-9_-]/g, '_');
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- HTML template ---
|
function pageHtml(opts) {
|
||||||
|
|
||||||
function pageHtml(opts) {
|
|
||||||
const { mode, sidebarHtml, extraScript } = opts;
|
const { mode, sidebarHtml, extraScript } = opts;
|
||||||
|
|
||||||
const fileSections = renderedFiles.map((f) => {
|
const fileSections = renderedFiles.map((f) => {
|
||||||
|
|
@ -262,11 +263,9 @@ function pageHtml(opts) {
|
||||||
${extraScript}
|
${extraScript}
|
||||||
</body>
|
</body>
|
||||||
</html>`;
|
</html>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- static HTML generation (shared by --static and --out) ---
|
function buildStaticHtml() {
|
||||||
|
|
||||||
function buildStaticHtml() {
|
|
||||||
const sidebarHtml = `
|
const sidebarHtml = `
|
||||||
<div class="sidebar">
|
<div class="sidebar">
|
||||||
<div class="file-list">
|
<div class="file-list">
|
||||||
|
|
@ -274,7 +273,7 @@ ${renderedFiles.map(f => ` <a class="file-item" href="#${fileId(f.name)
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
const escapedPatch = escapeHtml(patch);
|
const escapedPatch = escapeHtml(finalPatch);
|
||||||
|
|
||||||
const extraScript = ` <script>
|
const extraScript = ` <script>
|
||||||
document.querySelectorAll('.file-item').forEach(el => {
|
document.querySelectorAll('.file-item').forEach(el => {
|
||||||
|
|
@ -298,20 +297,18 @@ ${renderedFiles.map(f => ` <a class="file-item" href="#${fileId(f.name)
|
||||||
<summary id="patch-toggle" style="cursor:pointer;font-size:0.8125rem;color:#2563eb">View raw patch</summary>
|
<summary id="patch-toggle" style="cursor:pointer;font-size:0.8125rem;color:#2563eb">View raw patch</summary>
|
||||||
<pre id="patch-content" class="raw-patch">${escapedPatch}</pre>
|
<pre id="patch-content" class="raw-patch">${escapedPatch}</pre>
|
||||||
</details>\n</body>`);
|
</details>\n</body>`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- mode dispatch ---
|
// --- mode dispatch ---
|
||||||
|
|
||||||
if (outFile) {
|
if (outFile) {
|
||||||
// -- out mode: write HTML to specified path, exit --
|
|
||||||
const html = buildStaticHtml();
|
const html = buildStaticHtml();
|
||||||
writeFileSync(outFile, html, 'utf-8');
|
writeFileSync(outFile, html, 'utf-8');
|
||||||
console.log(`Written to ${outFile}`);
|
console.log(`Written to ${outFile}`);
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isStatic) {
|
if (isStatic) {
|
||||||
// -- static mode: generate self-contained HTML file, open in browser, exit --
|
|
||||||
const html = buildStaticHtml();
|
const html = buildStaticHtml();
|
||||||
const tmpFile = join(tmpdir(), `difftool-${Date.now()}.html`);
|
const tmpFile = join(tmpdir(), `difftool-${Date.now()}.html`);
|
||||||
writeFileSync(tmpFile, html, 'utf-8');
|
writeFileSync(tmpFile, html, 'utf-8');
|
||||||
|
|
@ -325,14 +322,12 @@ if (isStatic) {
|
||||||
} catch {
|
} catch {
|
||||||
console.log(`HTML file saved to ${tmpFile}`);
|
console.log(`HTML file saved to ${tmpFile}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// -- server mode: start Express --
|
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
app.use(express.static(join(__dirname, 'public')));
|
app.use(express.static(join(__dirname, 'public')));
|
||||||
|
|
||||||
app.get('/patch', (req, res) => {
|
app.get('/patch', (req, res) => {
|
||||||
res.type('text/plain').send(patch);
|
res.type('text/plain').send(finalPatch);
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/', (req, res) => {
|
app.get('/', (req, res) => {
|
||||||
|
|
@ -353,4 +348,5 @@ if (isStatic) {
|
||||||
console.log(`Difftool running at http://localhost:${port}`);
|
console.log(`Difftool running at http://localhost:${port}`);
|
||||||
console.log(`Patch endpoint: http://localhost:${port}/patch`);
|
console.log(`Patch endpoint: http://localhost:${port}/patch`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
})();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue