package.cjs.configs.scripts.jsdoc-builder.utils.mjs Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of neeto-commons-frontend Show documentation
Show all versions of neeto-commons-frontend Show documentation
A package encapsulating common code across neeto projects including initializers, utility functions, common components and hooks and so on.
import fs from "fs";
import path from "path";
import * as babelParser from "@babel/parser";
import { curry, __, mergeLeft } from "ramda";
import remarkParse from "remark-parse";
import { unified } from "unified";
import {
ASTERISK,
ASTERISK_WITH_SPACES,
CODE_PATTERN,
DOUBLE_LINE_BREAK_WITH_LEADING_COMMENT,
EXPORTED_TYPES_FOLDER_NAME,
HTML_EXCEPT_IMG_PATTERN,
IMG_PATTERN,
JSDOC_EXAMPLE_TAG,
JSDOC_END_EXAMPLE_TAG,
JSDOC_IMG_HEIGHT,
JSDOC_IMG_WIDTH,
LINE_BREAK,
PARAGRAPH_PATTERN,
SUBHEADING_PATTERN,
TYPES_FOLDER_NAME,
WHITESPACE_PARENTHESIS_REGEX,
} from "./constants.mjs";
import * as babelTypes from "@babel/types";
import _traverse from "@babel/traverse";
import _generate from "@babel/generator";
import { matches } from "@bigbinary/neeto-cist";
const traverse = _traverse.default;
const generate = _generate.default;
const removeWhiteSpaceAndParentheses = string =>
string.replace(WHITESPACE_PARENTHESIS_REGEX, "");
const addLeadingCommentForTextWithLineBreaks = ({
text,
addCommentForFirstItem = true,
addDoubleLineBreaks = true,
}) => {
if (!hasLineBreaks(text)) return addLeadingComment(text);
const joinString = addDoubleLineBreaks
? DOUBLE_LINE_BREAK_WITH_LEADING_COMMENT
: LINE_BREAK;
return text
.split(LINE_BREAK)
.map((item, idx) => {
if (!addCommentForFirstItem && idx === 0) return item;
return addLeadingComment(item);
})
.join(joinString);
};
const addLeadingComment = (text, trimWhiteSpace = false) =>
(trimWhiteSpace ? ASTERISK : ASTERISK_WITH_SPACES) + text;
const transformCode = node => {
let text = "";
text += addLeadingComment(JSDOC_EXAMPLE_TAG);
text += DOUBLE_LINE_BREAK_WITH_LEADING_COMMENT;
text += addLeadingCommentForTextWithLineBreaks({
text: removeCommentTags(node.value),
addDoubleLineBreaks: false,
});
text += LINE_BREAK;
text += addLeadingComment(JSDOC_END_EXAMPLE_TAG);
text += LINE_BREAK;
return text;
};
const hasLineBreaks = text => text?.includes(LINE_BREAK);
const getParagraphChildrenText = node => {
const children = node.children;
if (children.length === 1) return children[0].value;
return children.map(child => child.value || child.children[0].value).join("");
};
const transformParagraph = node => {
let text = "";
text += ASTERISK_WITH_SPACES;
const childrenText = getParagraphChildrenText(node);
if (hasLineBreaks(childrenText)) {
text += addLeadingCommentForTextWithLineBreaks({
text: childrenText,
addCommentForFirstItem: false,
});
} else {
text += childrenText;
}
text += DOUBLE_LINE_BREAK_WITH_LEADING_COMMENT;
return text;
};
const transformImg = node => {
const { src, alt } = parseImgTag(node.value);
let text = ASTERISK_WITH_SPACES;
text += `![${alt}](${src}|height=${JSDOC_IMG_HEIGHT}|width=${JSDOC_IMG_WIDTH})`;
text += DOUBLE_LINE_BREAK_WITH_LEADING_COMMENT;
return text;
};
const transformNodesToText = nodes => {
let text = DOUBLE_LINE_BREAK_WITH_LEADING_COMMENT;
nodes.forEach(node => {
if (matches(CODE_PATTERN, node)) {
text += transformCode(node);
} else if (matches(IMG_PATTERN, node)) {
text += transformImg(node);
} else if (matches(PARAGRAPH_PATTERN, node)) {
text += transformParagraph(node);
}
});
return addLeadingComment(text, true);
};
const parseImgTag = imgTagStr =>
imgTagStr.split(" ").reduce((acc, item) => {
let [attrName, attrValue] = item.split("=");
attrValue = attrValue?.replaceAll(">", "").replaceAll('"', "");
return mergeLeft({ [attrName]: attrValue }, acc);
}, {});
const removeCommentTags = str => str.replaceAll("/*", "").replaceAll("*/", "");
export const parseTypeFile = typeFileContent =>
babelParser.parse(typeFileContent, {
sourceType: "module",
plugins: [["typescript", { dts: true }]],
});
export const buildEntityTitleToEntityDescMap = (nodes, map) => {
nodes.forEach((node, idx) => {
if (!matches(SUBHEADING_PATTERN, node)) return;
const entityName = removeWhiteSpaceAndParentheses(node.children[0].value);
const entityRightSiblings = [];
for (let i = idx + 1; i < nodes.length; i++) {
const siblingNode = nodes[i];
if (matches(HTML_EXCEPT_IMG_PATTERN, siblingNode)) continue;
if (matches(SUBHEADING_PATTERN, siblingNode)) break;
entityRightSiblings.push(siblingNode);
}
const entityRightSiblingsText = transformNodesToText(entityRightSiblings);
map[entityName] = entityRightSiblingsText;
});
};
export const defaultTypeFileTraverser = ({
typeFileAST,
typeFileName,
entityTitleToDescMapOfAllFiles,
}) =>
traverse(typeFileAST, {
ExportNamedDeclaration: ({ node }) => {
const entityName = findEntityName(node);
const entityDesc = entityTitleToDescMapOfAllFiles[entityName];
if (!entityName || !entityDesc) {
return;
}
babelTypes.addComment(node, "leading", entityDesc);
const { code } = generate(typeFileAST, {});
fs.writeFileSync(path.resolve(typeFileName), code);
},
});
export const syncTypeFiles = exportedTypesFolderName => {
const sourceDir = path.resolve(TYPES_FOLDER_NAME);
const destDir = path.resolve(exportedTypesFolderName);
fs.cpSync(sourceDir, destDir, { recursive: true });
};
export const getFileNameList = dirPath => {
let files = [];
const items = fs.readdirSync(dirPath, { withFileTypes: true });
for (const item of items) {
if (item.isDirectory()) {
files = [...files, ...getFileNameList(path.join(dirPath, item.name))];
} else {
files.push(path.join(dirPath, item.name));
}
}
return files;
};
export const getFileContent = fileName =>
fs.readFileSync(path.resolve(fileName), "utf8").toString();
export const parseMarkdown = fileContent =>
unified().use(remarkParse).parse(fileContent);
export const findEntityName = node =>
node?.declaration?.declarations?.[0]?.id?.name || node?.declaration?.id?.name;
export const matchesAny = curry((list, obj) => list.some(matches(__, obj)));