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
112 changes: 6 additions & 106 deletions bin/flavian.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Flavian CLI — the first-class entry point for Flavian's conversion pipelines.
//
// flavian pipeline indesign <input> [options]
// flavian pipeline ingest <input> --out-dir <dir> [options]
//
// Today it exposes the InDesign pipeline (#62–#65): parse an .idml/.pdf (or a
// pre-parsed IR JSON), map design tokens, and generate a complete FSE theme.
Expand Down Expand Up @@ -37,19 +36,13 @@ async function main() {
process.exit(command ? 0 : 2);
}

if (command === 'indesign') {
await runIndesign(rest);
return;
}

if (command === 'ingest') {
await runIngestCommand(rest);
return;
if (command !== 'indesign') {
fail(`Unknown pipeline: ${command}`);
printPipelineUsage();
process.exit(2);
}

fail(`Unknown pipeline: ${command}`);
printPipelineUsage();
process.exit(2);
await runIndesign(rest);
}

async function runIndesign(args) {
Expand Down Expand Up @@ -132,71 +125,6 @@ async function runIndesign(args) {
process.exit(result.report.data.valid ? 0 : 1);
}

async function runIngestCommand(args) {
const opts = { quiet: false };
let input;

let i = 0;
const value = (flag) => {
const next = args[i + 1];
if (next === undefined || next.startsWith('-')) {
fail(`${flag} requires a value`);
process.exit(2);
}
i += 1;
return next;
};

for (; i < args.length; i += 1) {
const arg = args[i];
switch (arg) {
case '--out-dir': case '-o': opts.outDir = value(arg); break;
case '--dpi': opts.dpi = Number(value(arg)); break;
case '--name': opts.name = value(arg); break;
case '--quiet': case '-q': opts.quiet = true; break;
case '-h': case '--help': printIngestUsage(); process.exit(0); break;
default:
if (!input && !arg.startsWith('-')) input = arg;
else { fail(`Unknown argument: ${arg}`); printIngestUsage(); process.exit(2); }
}
}

if (!input) {
fail('an input is required (an .idml/.pdf file or an IR JSON)');
printIngestUsage();
process.exit(2);
}
if (!opts.outDir) {
fail('--out-dir is required');
printIngestUsage();
process.exit(2);
}

// Lazy import so `--help` never pays for loading the pipeline.
const { runIngest } = await import(PIPELINE('ingest/index.js'));

const summary = await runIngest({
input,
outDir: path.resolve(opts.outDir),
dpi: opts.dpi,
name: opts.name,
});

if (!opts.quiet) {
const c = summary.counts;
process.stderr.write(
[
`ingest: ${summary.format} → ${path.relative(process.cwd(), summary.dir) || '.'}`,
`assets: ${c.assetsResolved}/${c.imageFrames} resolved (staged ${summary.assetsWritten})`,
'bundle: ir.json, assets/, assets.manifest.json',
summary.warnings.length ? `warnings: ${summary.warnings.length}` : null,
].filter(Boolean).join('\n') + '\n',
);
}

process.exit(0);
}

/** Resolve the theme output directory from --output, config.output, or default. */
function resolveOutDir(outputFlag, configOutput, slug) {
if (outputFlag) return path.resolve(outputFlag); // explicit theme dir
Expand Down Expand Up @@ -265,7 +193,6 @@ function printRootUsage() {
'',
'Commands:',
' pipeline indesign <input> Convert an InDesign document to a WordPress FSE theme',
' pipeline ingest <input> Stage an InDesign source as a generator-ready bundle',
'',
'Run `flavian pipeline indesign --help` for pipeline options.',
'',
Expand All @@ -280,9 +207,8 @@ function printPipelineUsage() {
'',
'Pipelines:',
' indesign <input> Convert an .idml/.pdf (or IR JSON) to an FSE theme',
' ingest <input> Stage a source as a generator-ready bundle (ir.json + assets/)',
'',
'Run `flavian pipeline <name> --help` for options.',
'Run `flavian pipeline indesign --help` for options.',
'',
].join('\n'),
);
Expand Down Expand Up @@ -328,32 +254,6 @@ function printIndesignUsage() {
);
}

function printIngestUsage() {
process.stderr.write(
[
'Usage: flavian pipeline ingest <input> --out-dir <dir> [options]',
'',
'Extract an InDesign source into a generator-ready bundle: ir.json (the',
'intermediate IR the generator consumes), assets/ (image bytes pulled from the',
'source, named to match the generator), and assets.manifest.json.',
'',
'The bundle feeds straight into generation:',
' flavian pipeline indesign <dir>/ir.json --asset-dir <dir>/assets',
'',
'Arguments:',
' <input> An .idml or .pdf file (or a pre-parsed IR JSON)',
'',
'Options:',
' -o, --out-dir <dir> Bundle output directory (required)',
' --dpi <n> DPI when parsing .idml/.pdf (default 96)',
' --name <str> Override the document name',
' -q, --quiet Suppress the stderr summary',
' -h, --help Show this help',
'',
].join('\n'),
);
}

main().catch((err) => {
fail(err.message);
process.exit(1);
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@
},
"scripts": {
"flavian": "node bin/flavian.mjs",
"pipeline:ingest": "node bin/flavian.mjs pipeline ingest",
"init": "node scripts/init.mjs",
"test:init": "node --test \"tests/init/**/*.test.mjs\"",
"test:pipeline": "pnpm --filter @flavian/pipeline test",
"pipeline:ingest": "node packages/pipeline/bin/ingest.mjs",
"pipeline:stage-assets": "node packages/pipeline/bin/stage-assets.mjs",
"test:canva-e2e": "node --test tests/canva-e2e/*.test.mjs",
"test:headless-e2e": "node --test tests/headless-e2e/*.test.mjs",
"visual:capture": "node tests/visual/capture.mjs",
Expand Down
98 changes: 98 additions & 0 deletions packages/pipeline/bin/stage-assets.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/usr/bin/env node
// CLI: stage an InDesign source's image assets into a generator-ready bundle.
//
// flavian-stage-assets <source.idml | doc.pdf | ir.json> --out-dir <dir> [options]
//
// Parses the source to the IR, extracts its image bytes (embedded in the IDML
// zip, or decoded from the PDF), and writes a bundle the theme generator can
// consume directly: ir.json, assets/<staged files>, assets.manifest.json. Then:
//
// flavian-generate-theme <dir>/ir.json --asset-dir <dir>/assets --out-dir theme
//
// Complements `flavian-ingest` (which derives the IR + content model): this
// stage is the one that resolves and stages the document's image bytes.
//
// Options:
// -o, --out-dir <dir> Bundle output directory (required). Created if missing.
// --dpi <n> DPI for unit normalization when parsing (default 96).
// --name <str> Override the document name.
// -q, --quiet Suppress the stderr summary.
// -h, --help Show this help.

import { runAssetStage } from '../src/indesign/assets/stage.js';

const args = process.argv.slice(2);
const opts = { quiet: false };
let input;

let i = 0;
function wantsValue(flag) {
const next = args[i + 1];
if (next === undefined || next.startsWith('-')) {
process.stderr.write(`${flag} requires a value\n`);
process.exit(2);
}
return next;
}

for (; i < args.length; i += 1) {
const arg = args[i];
switch (arg) {
case '--out-dir': case '-o': opts.outDir = wantsValue(arg); i += 1; break;
case '--dpi': opts.dpi = Number(wantsValue(arg)); i += 1; break;
case '--name': opts.name = wantsValue(arg); i += 1; break;
case '--quiet': case '-q': opts.quiet = true; break;
case '-h': case '--help': printUsage(); process.exit(0); break;
default:
if (!input && !arg.startsWith('-')) input = arg;
else { process.stderr.write(`Unknown argument: ${arg}\n`); printUsage(); process.exit(2); }
}
}

if (!input) {
process.stderr.write('an input is required (an .idml/.pdf file or an IR JSON)\n');
printUsage();
process.exit(2);
}
if (!opts.outDir) {
process.stderr.write('--out-dir is required\n');
printUsage();
process.exit(2);
}

try {
const summary = await runAssetStage({ input, outDir: opts.outDir, dpi: opts.dpi, name: opts.name });
if (!opts.quiet) {
const c = summary.counts;
process.stderr.write(
[
`stage-assets: ${summary.format} → ${opts.outDir}`,
`assets: ${c.assetsResolved}/${c.imageFrames} resolved (staged ${summary.assetsWritten})`,
`bundle: ir.json, assets/, assets.manifest.json`,
summary.warnings.length ? `warnings: ${summary.warnings.length}` : null,
]
.filter(Boolean)
.join('\n') + '\n',
);
}
process.exit(0);
} catch (err) {
process.stderr.write(`error: ${err.message}\n`);
process.exit(1);
}

function printUsage() {
process.stderr.write(
[
'Usage: flavian-stage-assets <source.idml | doc.pdf | ir.json> --out-dir <dir> [options]',
'',
'Options:',
' -o, --out-dir <dir> Bundle output directory (required)',
' --dpi <n> DPI for unit normalization when parsing (default 96)',
' --name <str> Override the document name',
' -q, --quiet Suppress the stderr summary',
' -h, --help Show this help',
'',
].join('\n'),
);
}
4 changes: 2 additions & 2 deletions packages/pipeline/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
"flavian-ingest": "./bin/ingest.mjs",
"flavian-parse-idml": "./bin/parse-idml.mjs",
"flavian-parse-pdf": "./bin/parse-pdf.mjs",
"flavian-ingest": "./bin/ingest.mjs",
"flavian-stage-assets": "./bin/stage-assets.mjs",
"flavian-map-tokens": "./bin/map-tokens.mjs",
"flavian-generate-theme": "./bin/generate-theme.mjs"
},
"scripts": {
"test": "node --test",
"ingest": "node bin/ingest.mjs"
"stage-assets": "node bin/stage-assets.mjs"
},
"dependencies": {
"fast-xml-parser": "^5.7.0",
Expand Down
Loading
Loading