diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..25bf17f --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +18 \ No newline at end of file diff --git a/cli/ascaid-adoc-to-gfm.js b/cli/ascaid-adoc-to-gfm.js index f89f212..5caa9b2 100644 --- a/cli/ascaid-adoc-to-gfm.js +++ b/cli/ascaid-adoc-to-gfm.js @@ -28,7 +28,7 @@ export const adocToGfm = async (srcDir, outDir, ignore, adoctorOptions) => { const readDir = path.join(srcDir, dirname); const html = await invokeInDir(readDir, () => { - return adocConvert(adoc, adoctorOptions); + return adocConvert(adoc, asciidoctorOptions); }); const gfm = await pandocConvert(html, "html", "gfm", ["--wrap=none"]); @@ -62,13 +62,13 @@ program "Recursively convert AsciiDoc files in a directory to GitHub flavored markdown" ) .action(async (srcDir, outDir, { ignore, config, attribute }) => { - const { extensions, asciidoctorOptions: adoctorOptions } = await readConfig( + const { extensions, asciidoctorOptions } = await readConfig( config, attribute ); await registerExtensions(extensions ?? [], path.resolve(".")); - await adocToGfm(srcDir, outDir, ignore, adoctorOptions); + await adocToGfm(srcDir, outDir, ignore, asciidoctorOptions); }); await program.parseAsync(process.argv); diff --git a/cli/ascaid-gfm-to-confluence.js b/cli/ascaid-gfm-to-confluence.js index 1cd01b7..637ef93 100644 --- a/cli/ascaid-gfm-to-confluence.js +++ b/cli/ascaid-gfm-to-confluence.js @@ -1,25 +1,22 @@ -import { Argument, Option, program } from "commander"; import path from "node:path"; import fs from "node:fs"; import assert from "node:assert"; +import { Argument, Option, program } from "commander"; -import { pandocConvert } from "../index.js"; -import { readVersion } from "../index.js"; -import { ConfluenceClient } from "../index.js"; - -const MD_TITLE_REGEX = /^#+\s+(.*)/; - -const isNotNullOrEmptyString = (string_) => { - return ( - string_ != undefined && typeof string_ === "string" && string_.trim() !== "" - ); -}; +import { + ConfluenceClient, + getTitleFromMarkdown, + isNotNullOrEmptyString, + mdConvert, + normalizeSupportedExtnames, + readVersion, +} from "../index.js"; const createPageTree = async (title, filePath) => { const dirContents = await fs.promises.readdir(filePath); const files = dirContents.map((file) => ({ name: file, - extension: path.extname(file), + normalizedExtension: normalizeSupportedExtnames(path.extname(file)), path: `${filePath}/${file}`, isDirectory: fs.lstatSync(`${filePath}/${file}`).isDirectory(), })); @@ -30,22 +27,12 @@ const createPageTree = async (title, filePath) => { if (file.isDirectory) { children.push(await createPageTree(file.name, file.path)); } else { - if (file.extension.toLowerCase() !== ".md") continue; + if (file.normalizedExtension !== ".md") continue; const contents = fs.readFileSync(file.path, { encoding: "utf8" }); - let title = file.name.slice( - 0, - Math.max(0, file.name.length - file.extension.length) - ); - const firstLine = contents - .split(/\n\r?/) - .find((line) => MD_TITLE_REGEX.test(line.trim())); - if (firstLine != undefined) { - title = firstLine.match(MD_TITLE_REGEX)[1].trim(); - } - const body = await pandocConvert(contents, "gfm", "html", [ - "--wrap=none", - ]); + const title = + (await getTitleFromMarkdown(contents)) ?? path.parse(file.name).name; + const body = await mdConvert(contents); children.push({ title, body, diff --git a/cli/ascaid-serve.js b/cli/ascaid-serve.js index 3617048..7d19f29 100644 --- a/cli/ascaid-serve.js +++ b/cli/ascaid-serve.js @@ -19,14 +19,16 @@ program .addOption(configOption) .addOption(attributeOption) .description("Start an AsciiDoc server") - .action(async (rootDir, { config, attribute }) => { - const { extensions, asciidoctorOptions: adoctorOptions } = await readConfig( - config, - attribute - ); - await registerExtensions(extensions ?? [], path.resolve(".")); + .action( + async ( + rootDir, + { config: configFilePath, attribute: attributeOverrideKvs } + ) => { + const config = await readConfig(configFilePath, attributeOverrideKvs); - await startAsciidocServer(rootDir, adoctorOptions); - }); + await registerExtensions(config.extensions ?? [], path.resolve(".")); + await startAsciidocServer(rootDir, config); + } + ); await program.parseAsync(process.argv); diff --git a/cli/ascaid.js b/cli/ascaid.js index 3448d4a..146c55c 100755 --- a/cli/ascaid.js +++ b/cli/ascaid.js @@ -1,6 +1,5 @@ import { program } from "commander"; -import { readVersion } from "../index.js"; -import { checkPandoc } from "../index.js"; +import { readVersion, checkPandoc } from "../index.js"; const version = await readVersion(); diff --git a/index.js b/index.js index 3c9efec..5531b83 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,12 @@ export { checkPandoc, pandocConvert } from "./lib/pandoc-convert.js"; +export { mdConvert, getTitleFromMarkdown } from "./lib/md-convert.js"; export { adocConvert } from "./lib/adoc-convert.js"; -export { invokeInDir, readVersion } from "./lib/utils.js"; +export { + invokeInDir, + readVersion, + normalizeSupportedExtnames, + isNotNullOrEmptyString, +} from "./lib/utils.js"; export { readConfig } from "./lib/config.js"; export { registerExtensions } from "./lib/asciidoctor.js"; export { ConfluenceClient } from "./lib/confluence-client.js"; diff --git a/lib/adoc-convert.js b/lib/adoc-convert.js index ae7ddd8..77e1667 100644 --- a/lib/adoc-convert.js +++ b/lib/adoc-convert.js @@ -1,10 +1,10 @@ import { - adoctor, + asciidoctor, ASCIIDOCTOR_MESSAGE_SEVERITY, memoryLogger, } from "./asciidoctor.js"; -const defaultAdoctorOptions = { +const defaultAsciidoctorOptions = { safe: "server", doctype: "book", standalone: true, @@ -17,9 +17,12 @@ const getNumericAsciidoctorMessageSeverity = (message) => { ); }; -export const adocConvert = async (adoc, adoctorOptions = {}) => { - const mergedAdoctorOptions = { ...defaultAdoctorOptions, ...adoctorOptions }; - const html = adoctor.convert(adoc, mergedAdoctorOptions); +export const adocConvert = async (adoc, asciidoctorOptions = {}) => { + const mergedAsciidoctorOptions = { + ...defaultAsciidoctorOptions, + ...asciidoctorOptions, + }; + const html = asciidoctor.convert(adoc, mergedAsciidoctorOptions); const messages = memoryLogger.getMessages(); for (const message of messages) { diff --git a/lib/adoc-server.js b/lib/adoc-server.js index 0c1a730..6df3b9e 100644 --- a/lib/adoc-server.js +++ b/lib/adoc-server.js @@ -4,10 +4,17 @@ import path from "node:path"; import http from "node:http"; import browserSync from "browser-sync"; -import { fileExists, invokeInDir } from "./utils.js"; +import { + asciidocExtensions, + fileExists, + invokeInDir, + markdownExtensions, + normalizeSupportedExtnames, +} from "./utils.js"; import { adocConvert } from "./adoc-convert.js"; +import { getTitleFromMarkdown, mdConvert } from "./md-convert.js"; -export const createAsciidocMiddleware = (rootDir, adoctorOptions = {}) => { +export const createAsciidocMiddleware = (rootDir, config = {}) => { const absoluteRootDir = path.resolve(rootDir); return async (request, res, next) => { @@ -21,20 +28,37 @@ export const createAsciidocMiddleware = (rootDir, adoctorOptions = {}) => { return res.end(http.STATUS_CODES[res.statusCode]); } - if (/\.(adoc|asciidoc|acs)$/i.test(url.pathname)) { - const adocPath = path.join(absoluteRootDir, url.pathname); - const exists = await fileExists(adocPath); - if (!exists || !adocPath.startsWith(absoluteRootDir)) { + const extname = normalizeSupportedExtnames(path.extname(url.pathname)); + + if ([".md", ".adoc"].includes(extname)) { + const filePath = path.join(absoluteRootDir, url.pathname); + const exists = await fileExists(filePath); + if (!exists || !filePath.startsWith(absoluteRootDir)) { res.statusCode = 404; return res.end(http.STATUS_CODES[res.statusCode]); } - const adoc = await fs.readFile(adocPath, { encoding: "utf8" }); - let html = await invokeInDir(path.dirname(adocPath), () => { - return adocConvert(adoc, adoctorOptions); + const contents = await fs.readFile(filePath, { encoding: "utf8" }); + let html = await invokeInDir(path.dirname(filePath), async () => { + switch (extname) { + case ".adoc": { + return adocConvert(contents, config.asciidoctorOptions); + } + case ".md": { + const title = + (await getTitleFromMarkdown(contents)) ?? "Untitled"; + return mdConvert(contents, config.markdownOptions).then( + (body) => + `