UNPKG

storybook-addon-jsdoc-to-mdx

Version:

Storybook addon that automatically generates MDX documentation from JSDoc comments in your TypeScript and JavaScript files. Supports HTML tags in comments, complex TypeScript types, and integrates seamlessly with Storybook 7.x and 8.x.

205 lines (204 loc) 9.49 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); const analyzeFolders_1 = require("./analyzeFolders"); const ts_morph_1 = require("ts-morph"); const fs = __importStar(require("fs")); jest.mock("fs"); jest.mock('typescript', () => (Object.assign(Object.assign({}, jest.requireActual('typescript')), { createPrinter: jest.fn().mockReturnValue({ printFile: jest.fn().mockReturnValue('') }), createSourceFile: jest.fn().mockReturnValue({ getEnd: jest.fn().mockReturnValue(0), getFullText: jest.fn().mockReturnValue(''), }), ScriptTarget: { Latest: jest.fn() }, ScriptKind: { TS: jest.fn() } }))); // Complementary functions for creating mocks function createMockSourceFile(filePath = "path/to/file.ts") { return { getFilePath: jest.fn().mockReturnValue(filePath), forEachChild: jest.fn(), }; } function createMockNode() { return { getKind: jest.fn(), getKindName: jest.fn(), getJsDocs: jest.fn(() => [{ getFullText: jest.fn().mockReturnValue("Some JSDoc comment") }]), getParentOrThrow: jest.fn(), getName: jest.fn().mockReturnValue("exampleMethod"), getText: jest.fn(), forEachChild: jest.fn(), getParent: jest.fn(), }; } // Global test variables const folderPaths = ["path/to/folder1", "path/to/folder2"]; const extensions = ["ts", "tsx"]; // Assuming tsx is another extension you might be interested in const mockSourceFile = createMockSourceFile(); // Mock configuration before each test beforeEach(() => { jest.clearAllMocks(); ts_morph_1.Project.prototype.addSourceFilesAtPaths = jest.fn(); ts_morph_1.Project.prototype.getSourceFiles = jest.fn(() => [mockSourceFile]); }); describe("analyzeFolders", () => { it("should create a new Project and add source files for all extensions", () => { (0, analyzeFolders_1.analyzeFolders)(folderPaths, extensions); expect(ts_morph_1.Project.prototype.addSourceFilesAtPaths).toHaveBeenCalledTimes(folderPaths.length * extensions.length); folderPaths.forEach((folderPath) => { extensions.forEach((extension) => { const expectedPathRegex = new RegExp(folderPath.replace(/\//g, "[\\\\/]") + "[\\\\/].*\\." + extension); expect(ts_morph_1.Project.prototype.addSourceFilesAtPaths).toHaveBeenCalledWith(expect.stringMatching(expectedPathRegex)); }); }); expect(ts_morph_1.Project.prototype.getSourceFiles).toHaveBeenCalled(); }); }); describe("analyzeSourceFile", () => { it("should process source file correctly", () => { (0, analyzeFolders_1.analyzeSourceFile)(mockSourceFile, folderPaths); expect(mockSourceFile.getFilePath).toHaveBeenCalled(); }); }); describe("generateMdxContent", () => { it("should generate MDX content from JSDoc comments", () => { const jsDocComments = [ { name: "exampleFunction", type: "Function", comment: "This is an example function.", code: "function exampleFunction() {}", }, { name: "anotherFunction", type: "Function", comment: "This is another example function.", code: "function anotherFunction() {}", }, ]; const content = (0, analyzeFolders_1.generateMdxContent)(jsDocComments, "example/pathName"); expect(content).toBeDefined(); expect(content).toContain("exampleFunction"); expect(content).toContain("This is an example function."); expect(content).toContain("anotherFunction"); expect(content).toContain("This is another example function."); }); }); describe("writeMdxFile", () => { it("should write content to a file", () => { const content = "Some MDX content"; const filePath = "path/to/file.doc.mdx"; (0, analyzeFolders_1.writeMdxFile)(filePath, content); expect(fs.writeFileSync).toHaveBeenCalledWith(filePath, content); }); }); describe("processNode", () => { it("should collect JSDoc comments from nodes", () => { const mockNode = createMockNode(); mockNode.getKind = jest.fn().mockReturnValue(ts_morph_1.SyntaxKind.FunctionDeclaration); mockNode.getJsDocs.mockReturnValue([ { getFullText: jest.fn().mockReturnValue("This is a test JSDoc comment for a function"), }, ]); const jsDocComments = []; (0, analyzeFolders_1.processNode)(mockNode, jsDocComments); expect(jsDocComments.length).toBeGreaterThan(0); expect(jsDocComments[0].comment).toContain("This is a test JSDoc comment for a function"); }); it("should call processNode for each child node", () => { mockSourceFile.forEachChild = jest.fn().mockImplementation((callback) => { const mockNode = createMockNode(); callback(mockNode); }); (0, analyzeFolders_1.analyzeSourceFile)(mockSourceFile, folderPaths); expect(mockSourceFile.forEachChild).toHaveBeenCalled(); }); }); describe("analyzeSourceFile with JSDoc comments", () => { it("should generate and write MDX content if JSDoc comments are found", () => { // Przykładowe rozszerzenia plików do przetestowania const extensions = ["ts", "tsx"]; extensions.forEach((extension) => { const mockFilePath = `path/to/mockFile.${extension}`; const mockMdxFilePath = `path/to/mockFile.doc.mdx`; const mockBaseDir = "path/to"; const mockSourceFile = createMockSourceFile(mockFilePath); // Konfiguracja mocka, aby symulować obecność komentarzy JSDoc mockSourceFile.forEachChild = jest.fn().mockImplementation((callback) => { const mockNode = createMockNode(); mockNode.getKind.mockReturnValue(ts_morph_1.SyntaxKind.FunctionDeclaration); mockNode.getJsDocs.mockReturnValue([ { getFullText: jest.fn().mockReturnValue("This is a test JSDoc comment for a function") } ]); callback(mockNode); }); (0, analyzeFolders_1.analyzeSourceFile)(mockSourceFile, [mockBaseDir]); // Sprawdzenie, czy fs.writeFileSync zostało wywołane z oczekiwanymi argumentami expect(fs.writeFileSync).toHaveBeenCalledWith(mockMdxFilePath, expect.any(String)); }); }); }); describe("processNode with MethodDeclaration inside ClassDeclaration", () => { it("should get text from parent class for method declaration node", () => { const mockParentClassNode = createMockNode(); mockParentClassNode.getKind.mockReturnValue(ts_morph_1.SyntaxKind.ClassDeclaration); mockParentClassNode.getText.mockReturnValue("class ExampleClass { exampleMethod() {} }"); const mockMethodNode = createMockNode(); mockMethodNode.getParentOrThrow.mockReturnValue(mockParentClassNode); mockMethodNode.getKind.mockReturnValue(ts_morph_1.SyntaxKind.MethodDeclaration); mockMethodNode.getParent.mockReturnValue(mockParentClassNode); // Explicit type for jsDocComments with required 'code' const jsDocComments = []; (0, analyzeFolders_1.processNode)(mockMethodNode, jsDocComments); expect(mockParentClassNode.getText).toHaveBeenCalled(); }); }); describe("processNode with ClassDeclaration", () => { it("should call processNode for each child of a class declaration node", () => { const mockClassNode = createMockNode(); mockClassNode.getKind.mockReturnValue(ts_morph_1.SyntaxKind.ClassDeclaration); const mockChildNode = createMockNode(); mockClassNode.forEachChild.mockImplementation((callback) => { callback(mockChildNode); }); const jsDocComments = []; (0, analyzeFolders_1.processNode)(mockClassNode, jsDocComments); expect(mockClassNode.forEachChild).toHaveBeenCalled(); }); });