JSdoc을 Markdown으로 변환하기
서론
최근 Toss의 오픈 소스 Slash가 공개되었는데 엄청나게 방대한 양의 문서들이 있었다. 이 많은 유틸들을 어떻게 문서화했는지 궁금해서 파일을 뜯어보았다.
기본적인 베이스는 Docusorous로 마크다운 사이트 빌드 프레임워크를 사용 중이었고 내부 글들은 JSDOC으로 관리되고 있었다.
그럼 여기서 유추할 수 있는 것이 여러 파일들 중에 ts, tsx 파일들만 filter해서 JSDOC에 있는 텍스트를 읽어온 후 마크다운으로 변환한다고 볼 수 있다.
Docusorous는 공식 홈페이지에 문서화가 잘되어있으니 주요 포인트인 변환과정을 살펴보자.
본론
본격적으로 파일을 뜯어보자.
필요한 라이브러리는 node의 파일 시스템 fs, 특정 파일을 쉽게 filter해주는 fast-glob 마크다운을 생성해주는
documentation
이 필요하다.glob를 통해 파일들을 불러와보자.
const files = await glob('fileTest/**/*.{js,jsx}', {
ignore: ['**/*.{spec,test,d,setup,stories,config}.{js,jsx}'],
});
fileTest 라는 폴더에 있는 js와 jsx를 모두 불러오는데 test 같이 문서화에 필요없는 파일들은 제외한다.
export function filterFilesByContent(files, condition) {
return files.filter((file) => {
const content = fs.readFileSync(file, 'utf-8');
return condition(content);
})
}
위에서 파일들을 전부 읽어오는데 condition에 filter 값을 걸어주면 된다.
const DOCS_IGNORE = '/** @gdsc-docs-ignore */';
const filteredContent = filterFilesByContent(files, content => !content.includes(DOCS_IGNORE));
예를 들어 이런 식이다.
export function pathsToBuildEntries(paths) {
return paths.map(path => ({
source: path,
destination: `${destinationFilter('fileTest/docs',path)}.docs.md`,
}));
}
다음으로 소스의 위치, 마크다운을 생성할 위치를 string으로 변경해준다. 나는 별도의 docs 폴더에 생성할 예정이라 filter함수로 경로를 변경해줬다.
const entries = pathsToBuildEntries(filteredContent);
const buildMarkdown = async (source) => {
return build(source, {
external: [],
shallow: true,
extension: 'js',
}).then(formats.md)
}
const generateMarkdown = async (source, destination, markdown) => {
const filename = getFilename(source, { withExtension: false });
const title = `---\ntitle: ${filename}\n---\n`;
const markdownWithTitle = `${title}${markdown}`;
fs.writeFileSync(destination, markdownWithTitle);
};
다음으로 파일을 생성하는 작업인데 documentation의 build함수를 통해서 마크다운을 생성하고 fs.writeFileSync로 파일을 저장? 만들어준다.
export async function generateMarkdownFromEntries(entries) {
await Promise.all(
entries.map( async ({ source, destination }) => {
const markdown = await buildMarkdown(source);
await generateMarkdown(source, destination, markdown);
})
);
}
마지막으로 파일을 생성해준다. 파일 생성은 비동기작업이라 Promise.all로 감싸주었다.