Skip to content

Commit d734d58

Browse files
committed
clean up CLI code, add ObservablePrerenderError and nb.close().
1 parent 4b8de33 commit d734d58

8 files changed

Lines changed: 593 additions & 475 deletions

File tree

bin/observable-prerender

Lines changed: 56 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
#!/usr/bin/env node
22
const { program } = require("commander");
3-
const { load, DEFAULT_WIDTH, DEFAULT_HEIGHT } = require("../src");
3+
const { load } = require("../src");
44
const {
5-
parseArgRedefines,
6-
parseArgRedefineFiles,
7-
valueOfFile,
5+
applyBrowserOptions,
6+
applyRedefineOptions,
7+
getNotebookConfig,
8+
runRedefines,
9+
consolidateFormat,
810
} = require("./utils.js");
9-
const { join, isAbsolute } = require("path");
11+
const { join } = require("path");
1012
const { writeFileSync } = require("rw").dash;
1113

1214
const formatOptions = new Set([
@@ -20,19 +22,10 @@ const formatOptions = new Set([
2022
"json",
2123
]);
2224

23-
program
25+
let p = program
2426
.version(require("../package.json").version)
2527
.arguments("<notebook> [cells...]")
2628
.description("Pre-render an Observable notebook.")
27-
.option("--redefine <cell:value...>", "Redefine a cell (string only)")
28-
.option(
29-
"--redefine-file <cell:<string,json,ndjson,csv>:value...>",
30-
"Redefine a cell with a file"
31-
)
32-
.option(
33-
"--file-attachments <file attachment:path...>",
34-
"Redefine a file attachment with a local file"
35-
)
3629
.option(
3730
"-f, --format [format]",
3831
`Type of output: One of ${Array.from(formatOptions).join(", ")}.`,
@@ -50,135 +43,67 @@ program
5043
"--token <token>",
5144
"An observablehq.com API token to access the notebook."
5245
)
53-
.option(
54-
"--no-headless",
55-
"Turn off headless mode on the Puppeteer browser, meaning open the browser to the user."
56-
)
57-
.option(
58-
"-w, --width <value>",
59-
`Width of the Puppeteer browser. Default ${DEFAULT_WIDTH}`
60-
)
61-
.option(
62-
"-h, --height <value>",
63-
`Height of the Puppeteer browser. Default ${DEFAULT_HEIGHT}`
64-
)
65-
.option("-v, --verbose", "Print logs to follow progress.")
66-
.action(function (argNotebook, argCells) {
67-
const opts = program.opts();
68-
const {
69-
version,
70-
redefine = [],
71-
redefineFile = [],
72-
fileAttachments = [],
73-
out,
74-
token,
75-
outDir,
76-
headless,
77-
verbose,
78-
} = opts;
46+
.option("-v, --verbose", "Print logs to follow progress.");
7947

80-
let { width, height, format } = opts;
48+
p = applyBrowserOptions(p);
49+
p = applyRedefineOptions(p);
8150

82-
if (width) width = +width;
83-
if (height) height = +height;
51+
p.action(function (argNotebook, argCells) {
52+
const opts = program.opts();
53+
const { out, outDir, verbose } = opts;
54+
let { format } = opts;
8455

85-
if (out && argCells.length > 1) {
86-
console.error(
87-
`Only 1 cell could be passed into 'cells' when '--out' is specified. ${argCells.length} were passed in.`
88-
);
89-
process.exit(1);
90-
}
56+
const notebookConfig = getNotebookConfig(opts);
9157

92-
if (out && outDir) {
93-
console.error(`Only 1 of --out and --out-dir can be specified.`);
94-
process.exit(1);
95-
}
58+
if (out && argCells.length > 1) {
59+
console.error(
60+
`Only 1 cell could be passed into 'cells' when '--out' is specified. ${argCells.length} were passed in.`
61+
);
62+
process.exit(1);
63+
}
9664

97-
if (out && out !== "-") {
98-
const outFileFormat = out.substring(out.lastIndexOf(".") + 1);
99-
if (formatOptions.has(outFileFormat)) {
100-
format = outFileFormat;
101-
} else {
102-
if (!format) {
103-
console.error(
104-
`Unknown file type "${outFileFormat}" passed in for --out parameter. Use the --format flag, or ave the path with one of ${Array.from(
105-
formatOptions
106-
)
107-
.map((d) => `.${d}`)
108-
.join(", ")}.`
109-
);
110-
process.exit(1);
111-
}
112-
}
113-
} else {
114-
if (!format) format = "svg";
115-
}
65+
if (out && outDir) {
66+
console.error(`Only 1 of --out and --out-dir can be specified.`);
67+
process.exit(1);
68+
}
11669

117-
const redefines = parseArgRedefines(redefine);
118-
const redefineFiles = parseArgRedefineFiles(redefineFile);
119-
const redefineFileAttachments = parseArgRedefines(fileAttachments);
70+
format = consolidateFormat(out, format, formatOptions, "svg");
12071

121-
const config = {
122-
width,
123-
height,
124-
headless,
125-
};
126-
if (token) config["OBSERVABLEHQ_API_KEY"] = token;
72+
if (verbose)
73+
console.log(`Loading notebook "${argNotebook}" with cells `, argCells);
12774

128-
if (verbose)
129-
console.log(`Loading notebook "${argNotebook}" with cells `, argCells);
75+
load(argNotebook, argCells, notebookConfig).then(async (notebook) => {
76+
await runRedefines(notebook, opts, verbose);
13077

131-
load(argNotebook, argCells, config).then(async (notebook) => {
132-
for (const redefine of redefines) {
133-
const { cell, value, format } = redefine;
134-
if (verbose) console.log(`Redefining ${cell} with format ${format}`);
135-
const val = format === "number" ? +value : value;
136-
notebook.redefine(cell, val);
137-
}
78+
await Promise.all(
79+
argCells.map(async (cell) => {
80+
const path = out
81+
? out
82+
: join(outDir || process.cwd(), `${cell}.${format}`);
13883

139-
for (const redefineFile of redefineFiles) {
140-
const { cell, value, format: redefineFileFormat } = redefineFile;
14184
if (verbose)
142-
console.log(
143-
`Redefining ${cell} with file ${value} with format ${redefineFileFormat}`
85+
console.log(`Saving cell ${cell} as a ${format} file to ${path}`);
86+
87+
if (format === "svg") return notebook.svg(cell, path);
88+
if (format === "html") return notebook.html(cell, path);
89+
if (format === "text" || format === "txt")
90+
return writeFileSync(path, await notebook.value(cell));
91+
if (format === "json")
92+
return writeFileSync(
93+
path,
94+
JSON.stringify(await notebook.value(cell))
14495
);
145-
const data = await valueOfFile(value, redefineFileFormat);
146-
notebook.redefine(cell, data);
147-
}
148-
149-
if (redefineFileAttachments.length > 0) {
150-
const files = {};
151-
for (const { cell, value } of redefineFileAttachments) {
152-
if (verbose)
153-
console.log(`Replacing FileAttachment ${cell} with file ${value}`);
154-
files[cell] = isAbsolute(value) ? value : join(process.cwd(), value);
155-
}
156-
await notebook.fileAttachments(files);
157-
}
158-
159-
await Promise.all(
160-
argCells.map(async (cell) => {
161-
const path = out
162-
? out
163-
: join(outDir || process.cwd(), `${cell}.${format}`);
164-
165-
if (verbose)
166-
console.log(`Saving cell ${cell} as a ${format} file to ${path}`);
167-
168-
if (format === "svg") return notebook.svg(cell, path);
169-
if (format === "html") return notebook.html(cell, path);
170-
if (format === "text" || format === "txt")
171-
return writeFileSync(path, await notebook.value(cell));
172-
if (format === "json")
173-
return writeFileSync(
174-
path,
175-
JSON.stringify(await notebook.value(cell))
176-
);
177-
return notebook.screenshot(cell, path, { type: format });
178-
})
179-
);
180-
notebook.browser.close();
96+
return notebook.screenshot(cell, path, { type: format });
97+
})
98+
).catch(async (error) => {
99+
console.error(`Error encountered when rendering cells`);
100+
console.error(error);
101+
notebook.close();
102+
process.exit(1);
181103
});
104+
notebook.close();
105+
process.exit(0);
182106
});
107+
});
183108

184109
program.parse(process.argv);

0 commit comments

Comments
 (0)