UNPKG

ecmarkup

Version:

Custom element definitions and core utilities for markup that specifies ECMAScript and related technologies.

268 lines (267 loc) 9.48 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const commandLineUsage = require("command-line-usage"); const path = require("path"); const fs = require("fs"); const ecmarkup = require("./ecmarkup"); const utils = require("./utils"); const args_1 = require("./args"); const arg_parser_1 = require("./arg-parser"); const debounce = require('promise-debounce'); function usage() { console.log(commandLineUsage([ { header: 'ecmarkup', content: 'Compile ecmarkup documents to html.', }, { header: 'Usage', content: ['ecmarkup in.emu out.html', 'ecmarkup --multipage in.emu out/'], }, { header: 'Options', hide: ['files', 'js-out', 'css-out', 'old-toc'], optionList: args_1.options, }, ])); } function fail(msg) { console.error(msg); process.exit(1); } const args = (0, arg_parser_1.parse)(args_1.options, usage); if (args.version) { const p = require(path.resolve(__dirname, '..', 'package.json')); console.log(`ecmarkup v${p.version}`); process.exit(0); } for (const [key, value] of Object.entries(args)) { if (value === null) { fail(`--${key} requires an argument`); } } if (args.files.length < 1) { fail('You must specify an input file'); } if (args.files.length > 2) { fail('Found extra argument ' + args.files[2]); } const infile = args.files[0]; const outfile = args.files[1]; if (args.strict && args.watch) { fail('Cannot use --strict with --watch'); } if (args['old-toc']) { fail('--old-toc has been removed; specify --printable to get a printable document'); } if (args['js-out'] || args['css-out']) { fail('--js-out and --css-out have been removed; specify --assets-dir instead'); } if (args.assets != null && !['none', 'inline', 'external'].includes(args.assets)) { fail('--assets must be "none", "inline", or "external"'); } if (args.assets != null && args.assets !== 'external' && args['assets-dir'] != null) { fail(`--assets=${args.assets} cannot be used with --assets-dir"`); } if (args.multipage) { if (args.assets != null && !['none', 'inline'].includes(args.assets)) { fail('--multipage implies --assets=external'); } if (!outfile) { fail('When using --multipage you must specify an output directory'); } if (fs.existsSync(outfile) && !fs.lstatSync(outfile).isDirectory()) { fail('When using --multipage, outfile (' + outfile + ') must be a directory'); } fs.mkdirSync(path.resolve(outfile, 'multipage'), { recursive: true }); } const LOOKS_LIKE_FILE_REGEX = /^\.{0,2}\//; const watching = new Map(); const build = debounce(async function build() { var _a; try { const opts = { multipage: args.multipage, outfile, extraBiblios: [], lintSpec: !!args['lint-spec'], }; if (args.verbose) { opts.log = utils.logVerbose; } if (args['mark-effects']) { opts.markEffects = true; } if (args['no-toc'] != null) { opts.toc = !args['no-toc']; } if (args.printable != null) { opts.printable = args.printable; } if (args.assets != null) { opts.assets = args.assets; } if (args['assets-dir'] != null) { opts.assetsDir = args['assets-dir']; } let warned = false; for (let toResolve of (_a = args['load-biblio']) !== null && _a !== void 0 ? _a : []) { if (LOOKS_LIKE_FILE_REGEX.test(toResolve)) { toResolve = path.resolve(process.cwd(), toResolve); } try { const bib = require(toResolve); if (Array.isArray(bib)) { opts.extraBiblios.push(...bib); } else { opts.extraBiblios.push(bib); } } catch { fail(`could not find biblio ${toResolve}`); } } const errorFormatter = args['error-formatter']; let toResolve = errorFormatter; if (LOOKS_LIKE_FILE_REGEX.test(errorFormatter)) { toResolve = path.resolve(process.cwd(), errorFormatter); } let formatter; try { formatter = require(toResolve); } catch { fail(`could not find formatter ${errorFormatter}`); } const warnings = []; opts.warn = err => { var _a; warned = true; const file = normalizePath((_a = err.file) !== null && _a !== void 0 ? _a : args.files[0]); // prettier-ignore const message = `${args.strict ? 'Error' : 'Warning'}: ${file}:${err.line == null ? '' : `${err.line}:${err.column}:`} ${err.message}`; utils.logWarning(message); warnings.push(err); }; // Respect a reproducible build timestamp. // https://reproducible-builds.org/specs/source-date-epoch/ if (process.env.SOURCE_DATE_EPOCH) { const sde = process.env.SOURCE_DATE_EPOCH.trim(); if (!/^[0-9]+$/.test(sde)) { fail(`SOURCE_DATE_EPOCH value ${sde} is not valid`); } const ts = +sde; opts.date = new Date(ts * 1000); } const spec = await ecmarkup.build(args.files[0], utils.readFile, opts); if (args.verbose) { utils.logVerbose(warned ? 'Completed with errors.' : 'Done.'); } const pending = []; if (args['write-biblio']) { if (args.verbose) { utils.logVerbose('Writing biblio file to ' + args['write-biblio']); } const exported = spec.exportBiblio(); if (exported != null) { pending.push(utils.writeFile(args['write-biblio'], JSON.stringify(exported))); } } if (args.verbose && warned) { warnings.sort((a, b) => { var _a, _b; const aPath = normalizePath((_a = a.file) !== null && _a !== void 0 ? _a : infile); const bPath = normalizePath((_b = a.file) !== null && _b !== void 0 ? _b : infile); if (aPath !== bPath) { return aPath.localeCompare(bPath); } if (a.line === b.line) { if (a.column === b.column) { return 0; } if (a.column == null) { return -1; } if (b.column == null) { return 1; } return a.column - b.column; } if (a.line == null) { return -1; } if (b.line == null) { return 1; } return a.line - b.line; }); const results = warnings.map(err => { var _a; return ({ filePath: normalizePath((_a = err.file) !== null && _a !== void 0 ? _a : infile), messages: [{ severity: args.strict ? 2 : 1, ...err }], errorCount: args.strict ? 1 : 0, warningCount: args.strict ? 0 : 1, // for now, nothing is fixable fixableErrorCount: 0, fixableWarningCount: 0, source: err.source, }); }); console.error(formatter(results)); } if (!args.strict || !warned) { if (outfile) { if (args.verbose) { utils.logVerbose('Writing output...'); } for (const [file, contents] of spec.generatedFiles) { pending.push(utils.writeFile(file, contents)); } } else { process.stdout.write(spec.generatedFiles.get(null)); } } await Promise.all(pending); if (args.strict && warned) { utils.logVerbose('Exiting with an error due to errors (omit --strict to write output anyway)'); if (!args.verbose) { utils.logVerbose('Rerun with --verbose to see detailed error information'); } process.exit(1); } if (args.watch) { const toWatch = new Set(spec.imports.map(i => i.importLocation).concat(infile)); // remove any files that we're no longer watching for (const [file, watcher] of watching) { if (!toWatch.has(file)) { watcher.close(); watching.delete(file); } } // watch any new files for (const file of toWatch) { if (!watching.has(file)) { watching.set(file, fs.watch(file, build)); } } } } catch (e) { if (args.watch) { process.stderr.write(e.stack); } else { throw e; } } }); build().catch(e => { console.error(e); process.exit(1); }); function normalizePath(absolute) { return path.relative(process.cwd(), absolute); }