Skip to content
Merged
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
11 changes: 11 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Get package version
id: pkg
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT

- name: Build image for testing (amd64 only)
uses: docker/build-push-action@v5
with:
Expand All @@ -40,6 +44,8 @@ jobs:
tags: facet:test
cache-from: type=gha
cache-to: type=gha,mode=max
build-args: |
VERSION=${{ steps.pkg.outputs.version }}

- name: Generate PDF from example template
run: |
Expand Down Expand Up @@ -118,6 +124,10 @@ jobs:
type=sha
type=raw,value=latest,enable={{is_default_branch}}

- name: Get package version
id: pkg
run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT

- name: Build and push Docker image
id: build
uses: docker/build-push-action@v5
Expand All @@ -131,3 +141,4 @@ jobs:
cache-to: type=gha,mode=max
build-args: |
BUILDKIT_INLINE_CACHE=1
VERSION=${{ steps.pkg.outputs.version }}
13 changes: 12 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ RUN cd cli && npm run build
# Final stage with Chromium browser
FROM node:20-bookworm-slim

ARG VERSION=dev

# Install Chromium browser and dependencies for Puppeteer
# Using Chromium from Debian repos for better multi-arch support
RUN apt-get update && apt-get install -y \
Expand Down Expand Up @@ -114,6 +116,15 @@ EXPOSE 3000
LABEL org.opencontainers.image.title="Facet" \
org.opencontainers.image.description="Generate beautiful PDFs and datasheets from React templates with Chrome" \
org.opencontainers.image.source="https://github.com/flanksource/facet" \
org.opencontainers.image.vendor="Flanksource"
org.opencontainers.image.vendor="Flanksource" \
org.opencontainers.image.version="${VERSION}"

# Warm the node_modules / .facet cache by rendering the playground sample
RUN cd /app/examples && \
facet pdf SimpleReport.tsx --data simple-data.json --output /tmp/warmup.pdf && \
rm -f /tmp/warmup.pdf

HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD curl -f http://localhost:3010/healthz || exit 1

CMD ["facet", "serve", "--templates-dir", "/templates"]
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: build install test
.PHONY: build install test serve

build:
task build
Expand All @@ -9,3 +9,6 @@ install:

test:
task test

serve:
task serve
7 changes: 7 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ tasks:
- echo "✓ Storybook build completed successfully"
- echo "✓ All stories compiled without errors"

serve:
desc: "Build and start the playground server"
deps:
- build
cmds:
- ./dist/facet serve --templates-dir examples

default:
desc: "Show available tasks"
cmds:
Expand Down
764 changes: 0 additions & 764 deletions cli/playground.html

This file was deleted.

5 changes: 3 additions & 2 deletions cli/src/bundler/vite-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,17 @@ export async function buildTemplate(options: BuildOptions): Promise<BuildResult>
const hash = depHash(pkgDeps);

if (!linkCachedNodeModules(facetRoot, hash, logger) && facetDir.needsInstall()) {
logger.debug('Installing dependencies...');
logger.info('npm install...');
const npmStart = Date.now();
try {
const npmResult = await $`cd ${facetRoot} && npm install --ignore-scripts 2>&1`.quiet();
logger.debug(npmResult.stdout.toString());
} catch (error: any) {
const output = error?.stdout?.toString?.() || error?.stderr?.toString?.() || error?.message || String(error);
throw new Error(`npm install failed in ${facetRoot}:\n${output}`);
}
logger.info(`npm install completed in ${Date.now() - npmStart}ms`);
promoteToCacheAfterInstall(facetRoot, hash, logger);
logger.debug('Dependencies installed');
} else {
logger.debug('Dependencies up to date, skipping npm install');
}
Expand Down
22 changes: 20 additions & 2 deletions cli/src/server/playground-html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,15 @@ const HTML = `<!DOCTYPE html>
<select id="pageSize">
<option value="">Default</option>
<option value="A4">A4</option>
<option value="Letter">Letter</option>
<option value="A3">A3</option>
<option value="Letter">Letter</option>
<option value="Legal">Legal</option>
<option value="Tabloid">Tabloid</option>
<option value="16K">16K</option>
<option value="FHD">FHD (1920×1080)</option>
<option value="QHD">QHD (2560×1440)</option>
<option value="WQHD">WQHD (3440×1440)</option>
<option value="4K">4K (3840×2160)</option>
<option value="5K">5K (5120×2880)</option>
</select>
</label>
<label><input type="checkbox" id="landscape"> Landscape</label>
Expand All @@ -120,6 +125,10 @@ const HTML = `<!DOCTYPE html>
<input type="number" id="marginLeft" placeholder="L" min="0" value="10" title="Left margin (mm)">
<input type="number" id="marginRight" placeholder="R" min="0" value="10" title="Right margin (mm)">
</div>
<div class="sep"></div>
<label><input type="checkbox" id="timestampEnabled" onchange="toggleTimestamp()"> Timestamp</label>
<input type="text" id="timestampUrl" value="http://timestamp.digicert.com"
style="display:none;width:220px" placeholder="TSA URL">
<div class="spacer"></div>
<button class="logs-btn" id="logsBtn" onclick="openLogs()" title="View render log">
Logs <span class="dot" id="logsDot"></span>
Expand Down Expand Up @@ -453,6 +462,11 @@ export default function MyFooter() {

document.addEventListener('click', closeRenderMenu);

function toggleTimestamp() {
document.getElementById('timestampUrl').style.display =
document.getElementById('timestampEnabled').checked ? '' : 'none';
}

/* Log dialog */
function openLogs() {
document.getElementById('logOverlay').classList.add('open');
Expand Down Expand Up @@ -659,13 +673,17 @@ export default function MyFooter() {
if (!isNaN(ml)) margins.left = ml;
if (!isNaN(mr)) margins.right = mr;

const tsEnabled = document.getElementById('timestampEnabled').checked;
const timestampUrl = tsEnabled ? document.getElementById('timestampUrl').value.trim() : '';

const body = { code, format, data, pdfOptions: {} };
if (pageSize) body.pdfOptions.defaultPageSize = pageSize;
if (landscape) body.pdfOptions.landscape = true;
if (debug) body.pdfOptions.debug = true;
if (Object.keys(margins).length) body.pdfOptions.margins = margins;
if (!Object.keys(body.pdfOptions).length) delete body.pdfOptions;
if (Object.keys(deps).length) body.dependencies = deps;
if (timestampUrl) body.signature = { timestampUrl };

const hdr = headerEditor.getValue().trim();
const ftr = footerEditor.getValue().trim();
Expand Down
7 changes: 6 additions & 1 deletion cli/src/server/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ export async function discoverTemplates(dir: string): Promise<TemplateInfo[]> {
for (const name of entries) {
if (name.startsWith('.') || name === 'node_modules') continue;
const fullPath = join(dir, name);
const s = await stat(fullPath);
let s;
try {
s = await stat(fullPath);
} catch {
continue;
}

if (s.isFile() && ENTRY_EXTENSIONS.some(ext => name.endsWith(ext))) {
const templateName = name.replace(/\.[^.]+$/, '');
Expand Down
Loading