• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright Joyent, Inc. and other Node contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the
5// "Software"), to deal in the Software without restriction, including
6// without limitation the rights to use, copy, modify, merge, publish,
7// distribute, sublicense, and/or sell copies of the Software, and to permit
8// persons to whom the Software is furnished to do so, subject to the
9// following conditions:
10//
11// The above copyright notice and this permission notice shall be included
12// in all copies or substantial portions of the Software.
13//
14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20// USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22'use strict';
23
24const { promises: fs } = require('fs');
25const path = require('path');
26const unified = require('unified');
27const markdown = require('remark-parse');
28const remark2rehype = require('remark-rehype');
29const raw = require('rehype-raw');
30const htmlStringify = require('rehype-stringify');
31
32const { replaceLinks } = require('./markdown');
33const linksMapper = require('./links-mapper');
34const html = require('./html');
35const json = require('./json');
36
37// Parse the args.
38// Don't use nopt or whatever for this. It's simple enough.
39
40const args = process.argv.slice(2);
41let filename = null;
42let nodeVersion = null;
43let outputDir = null;
44let apilinks = {};
45let versions = {};
46
47async function main() {
48  for (const arg of args) {
49    if (!arg.startsWith('--')) {
50      filename = arg;
51    } else if (arg.startsWith('--node-version=')) {
52      nodeVersion = arg.replace(/^--node-version=/, '');
53    } else if (arg.startsWith('--output-directory=')) {
54      outputDir = arg.replace(/^--output-directory=/, '');
55    } else if (arg.startsWith('--apilinks=')) {
56      const linkFile = arg.replace(/^--apilinks=/, '');
57      const data = await fs.readFile(linkFile, 'utf8');
58      if (!data.trim()) {
59        throw new Error(`${linkFile} is empty`);
60      }
61      apilinks = JSON.parse(data);
62    } else if (arg.startsWith('--versions-file=')) {
63      const versionsFile = arg.replace(/^--versions-file=/, '');
64      const data = await fs.readFile(versionsFile, 'utf8');
65      if (!data.trim()) {
66        throw new Error(`${versionsFile} is empty`);
67      }
68      versions = JSON.parse(data);
69    }
70  }
71
72  nodeVersion = nodeVersion || process.version;
73
74  if (!filename) {
75    throw new Error('No input file specified');
76  } else if (!outputDir) {
77    throw new Error('No output directory specified');
78  }
79
80  const input = await fs.readFile(filename, 'utf8');
81
82  const content = await unified()
83    .use(replaceLinks, { filename, linksMapper })
84    .use(markdown)
85    .use(html.preprocessText, { nodeVersion })
86    .use(json.jsonAPI, { filename })
87    .use(html.firstHeader)
88    .use(html.preprocessElements, { filename })
89    .use(html.buildToc, { filename, apilinks })
90    .use(remark2rehype, { allowDangerousHtml: true })
91    .use(raw)
92    .use(htmlStringify)
93    .process(input);
94
95  const myHtml = await html.toHTML({ input, content, filename, nodeVersion,
96                                     versions });
97  const basename = path.basename(filename, '.md');
98  const htmlTarget = path.join(outputDir, `${basename}.html`);
99  const jsonTarget = path.join(outputDir, `${basename}.json`);
100
101  return Promise.allSettled([
102    fs.writeFile(htmlTarget, myHtml),
103    fs.writeFile(jsonTarget, JSON.stringify(content.json, null, 2)),
104  ]);
105}
106
107main()
108  .then((tasks) => {
109    // Filter rejected tasks
110    const errors = tasks.filter(({ status }) => status === 'rejected')
111      .map(({ reason }) => reason);
112
113    // Log errors
114    for (const error of errors) {
115      console.error(error);
116    }
117
118    // Exit process with code 1 if some errors
119    if (errors.length > 0) {
120      return process.exit(1);
121    }
122
123    // Else with code 0
124    process.exit(0);
125  })
126  .catch((error) => {
127    console.error(error);
128
129    process.exit(1);
130  });
131