@irwinproject/storybook-addon-tsdoc
Version:
Generate mdx documentation from your typescript!
245 lines (244 loc) • 8.78 kB
JavaScript
;
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 path_1 = __importStar(require("path"));
const console_log_colors_1 = require("console-log-colors");
const ts_morph_1 = require("ts-morph");
const fs_1 = require("fs");
const minimatch_1 = require("minimatch");
const renderer_1 = require("./renderer");
require("./utils"); //adds the wrap function to strng prototype.
const renderDeclarations_1 = require("./renderDeclarations");
/**
* TS is a central repository for options. This will also handle code compiling based off a tsconfig
*/
class TS {
/**
* Describes the glob used to identify tsdocs documentation.
*/
static get docsGlob() {
return `${TS.docs}/**/*.mdx`;
}
/**
* Documents a project but catches the errors and outputs it with tsdocs prefix.
*/
static document({ tsconfig, entry, docs, shouldClearDocsOnStart } = {}) {
//apply options if any are provided
if (tsconfig)
this.tsconfig = (0, path_1.isAbsolute)(tsconfig) ? tsconfig : (0, path_1.join)(process.cwd(), tsconfig);
if (entry)
this.entry = entry;
if (docs)
this.docs = (0, path_1.isAbsolute)(docs) ? docs : (0, path_1.join)(process.cwd(), docs);
if (shouldClearDocsOnStart !== undefined)
this.shouldClearDocsOnStart = shouldClearDocsOnStart;
//update the options
//clear the docs dir
if (TS.shouldClearDocsOnStart) {
TS.log("Clearing documents", this.docs);
if ((0, fs_1.existsSync)(this.docs))
(0, fs_1.rmSync)(this.docs, { recursive: true });
(0, fs_1.mkdirSync)(this.docs);
}
else {
if (!(0, fs_1.existsSync)(this.docs))
(0, fs_1.mkdirSync)(this.docs);
}
//move the styles to the docs foler
(0, fs_1.cpSync)((0, path_1.join)(__dirname, "style.css"), (0, path_1.join)(this.docs, "style.css"));
try {
this.documentProject(TS.documentStyle === "file" ? TS.documentSourceFile : TS.documentByDeclaration);
}
catch (e) {
TS.err(e);
}
TS.hasUpdates = false;
}
static watch() {
if (TS.watcher)
return;
TS.watcher = (0, fs_1.watch)(process.cwd(), { recursive: true });
TS.watcher.on("change", (e, fileName) => {
if (!fileName)
return;
fileName = typeof fileName === 'string' ? fileName : fileName.toString('utf-8');
if (!(0, minimatch_1.minimatch)(fileName, TS.entry))
return;
TS.document();
});
}
/**
* Resolves the url to its path name that wil be used. for the path name and the path title
* @param url
* @returns
*/
static resolveUrl(url) {
var _a;
if (!url.startsWith(process.cwd()))
return;
url = url.slice(process.cwd().length + 1); //remove the root.
if (!(0, minimatch_1.minimatch)(url, TS.entry))
return;
const u = TS.aliases.reduce((o, v) => o.replace(...v), url);
const nurl = TS.documentStyle === "declaration" ? u.replace(path_1.default.extname(u), '') : u;
return (_a = TS.decs[nurl]) !== null && _a !== void 0 ? _a : nurl;
}
/**
* Resolves the url to a doc url
*
* This should not be used on urls outside the entry path.
* @param url
* @returns
*/
static resolvedDocFilePath(url) {
return (0, path_1.join)(this.docs, url.replace(/\//g, '-') + '.mdx');
}
/**
* Resolves to a storybook url path value.
* @param url
* @returns {string}
*/
static resolveDocPath(url) {
var _a, _b;
const u = '/docs/' + ((_a = TS.decs[url]) !== null && _a !== void 0 ? _a : url).replace(/[\/\.\(]/g, '-').replace(/[\)]/g, '');
return ((_b = TS.decs[u]) !== null && _b !== void 0 ? _b : u) + '--docs';
;
}
/**
* Create a project (program) and crawl the parsed data.
*/
static documentProject(documentor = TS.documentSourceFile) {
const project = new ts_morph_1.Project({
tsConfigFilePath: this.tsconfig,
});
project.addSourceFilesAtPaths((0, path_1.join)(process.cwd(), this.entry));
project.getSourceFiles().forEach(f => {
const match = (0, minimatch_1.minimatch)(f.getFilePath(), (0, path_1.join)(process.cwd(), this.entry));
if (!match)
project.removeSourceFile(f);
});
TS.log((0, console_log_colors_1.cyan)("Documenting"), (0, path_1.join)(process.cwd(), TS.entry), (0, console_log_colors_1.red)(project.getSourceFiles().length), `file${project.getSourceFiles().length === 1 ? '' : 's'}`);
project.getSourceFiles().forEach(documentor);
}
/**
* Document the source file.
*
* at this time this will create an mdx file if any nodes are traversed in said directory
*
*
* @todo wrap style in style tag since it will never be used in any other way.
* @param source
* @returns
*/
static documentSourceFile(source) {
const path = TS.resolveUrl(source.getFilePath());
if (!path)
return;
const data = (0, renderer_1.render)(path, source);
if (!data)
return;
return (0, fs_1.writeFileSync)(TS.resolvedDocFilePath(path), data);
}
static documentByDeclaration(source) {
(0, renderDeclarations_1.documentDeclarations)(source);
}
/**
* A prefixed log method to make identification easier
* @param args
*/
static log(...args) {
console.log((0, console_log_colors_1.blueBright)("TsDoc"), ...args);
}
/**
* A red prefixed log method.
* @param args
*/
static err(...args) {
console.log((0, console_log_colors_1.red)("TsDoc"), ...args);
}
/**
* A yellow prefixed log method.
* @param args
*/
static warn(...args) {
console.log((0, console_log_colors_1.yellow)("TsDoc"), ...args);
}
/**
* A green prefixed log method.
* @param args
*/
static success(...args) {
console.log((0, console_log_colors_1.green)("TsDoc"), ...args);
}
}
TS.hasUpdates = true;
/**
* The document folder path
*/
TS.docs = (0, path_1.join)(process.cwd(), ".tsdoc");
/**
* The tsconfig path
*/
TS.tsconfig = (0, path_1.join)(process.cwd(), "tsconfig.json");
/**
* @todo change to accept multiple entries.
* @todo add automatic entry based on tsconfig
*/
TS.entry = "src/**/!(*.test|*.stories|*.d).ts";
/**
* @todo add configurable option
*/
TS.aliases = [
[/src\//, ''] //drop the src from the docpath
];
/**
* I hate this property
* @todo virtualize docs in dev environment.
*/
TS.shouldClearDocsOnStart = true;
/**
* @todo support not documenting private variables.
*/
TS.documentPrivate = false;
/**
* Declaration based documentation is in development. IT DOES NOT YET WORK!!!
*/
TS.documentStyle = "declaration";
/**
* 1 stange case I have encountered is when declarations are differenciated by case such as m and M in svg overwrite the file because file systems are not case specific. as such
* a record of all links will be tracked and if an overlap is detected a different path will be provided.
*/
TS.decs = {};
exports.default = TS;