UNPKG

react-docgen-typescript-webpack-plugin

Version:

Webpack plugin to generate docgen information from Typescript React components.

147 lines (141 loc) 6.35 kB
'use strict'; var Ajv = require('ajv'); var path = require('path'); var parser_js = require('react-docgen-typescript/lib/parser.js'); var ajv = new Ajv(); var validate = ajv.compile({ type: "object", properties: { includes: { type: "array", items: { type: "object", }, }, excludes: { type: "array", items: { type: "object", }, }, docgenCollectionName: { type: "string", }, verbose: { type: "boolean", }, }, additionalProperties: false, }); function validateOptions(options) { if (options === undefined) return; var valid = validate(options); if (!valid) { var errorObj = validate.errors[0]; var message = "react-docgen-typescript-webpack-plugin: "; if (errorObj.dataPath) message += errorObj.dataPath + " "; message += errorObj.message; throw new Error(message); } } function generateDocgenCodeBlock(componentDoc, filename, docgenCollectionName) { var displayName = componentDoc.displayName, description = componentDoc.description, props = componentDoc.props; var docgenCollectionKeyBase = path.relative("./", path.resolve("./", filename)) .replace(/\\/g, "/"); return "\ntry {\n (exports." + displayName + " || " + displayName + ").displayName = \"" + displayName + "\";\n\n (exports." + displayName + " || " + displayName + ").__docgenInfo = {\n description: \"" + escapeString(description) + "\",\n displayName: \"" + displayName + "\",\n props: {\n " + Object.entries(props) .map(function (_a) { var propName = _a[0], prop = _a[1]; return "\"" + escapeString(propName) + "\": {\n defaultValue: " + (prop.defaultValue != null && typeof prop.defaultValue === "object" && "value" in prop.defaultValue && typeof prop.defaultValue.value === "string" ? " {\n value: \"" + escapeString(prop.defaultValue.value) + "\"\n }" : null) + ",\n description: \"" + escapeString(prop.description) + "\",\n name: \"" + prop.name + "\",\n required: " + (prop.required ? "true" : "false") + ",\n type: {\n name: \"" + escapeString(prop.type.name) + "\"\n }\n }"; }) .join(",\n") + "\n }\n }\n\n " + (docgenCollectionName ? "\n if (typeof " + docgenCollectionName + " !== \"undefined\") {\n " + docgenCollectionName + "[\"" + docgenCollectionKeyBase + "#" + displayName + "\"] = {\n name: \"" + displayName + "\",\n docgenInfo: (exports." + displayName + " || " + displayName + ").__docgenInfo,\n path: \"" + escapeString(docgenCollectionKeyBase) + "\"\n }\n }\n " : "") + "\n} catch (e) {}\n "; } // Add escapes for quotes in strings. // Replace newlines with \n. // See: https://stackoverflow.com/questions/770523/escaping-strings-in-javascript function escapeString(str) { return (str + "") .replace(/[\\"']/g, "\\$&") .replace(/\u0000/g, "\\0") .replace(/\n/g, "\\n"); } var Plugin = /** @class */ (function () { function Plugin(options) { if (options === void 0) { options = {}; } var _this = this; this.apply = function (compiler) { compiler.plugin("compilation", function (compilation) { compilation.plugin("seal", function () { compilation.modules.forEach(function (module) { // Skip ignored / external modules if (!module.built || module.external || !module.rawRequest) { _this.log("Ignoring module request: " + module.request); return; } // Check module against whitelist/blacklist. if (!_this.shouldProcess(module)) return; _this.log("Processing request: " + module.request); // Generate doc types. processModule(module, _this.options.docgenCollectionName); }); }); }); }; // Check module filename against the include and exclude options. this.shouldProcess = function (module) { var shouldProcess = _this.options.includes.some(function (i) { return i.test(module.userRequest); }); if (!shouldProcess) { // this.log( // `Ignoring module because it did not match includes: ${module.request}`, // ); return false; } shouldProcess = !_this.options.excludes.some(function (i) { return i.test(module.userRequest); }); if (!shouldProcess) { // this.log( // `Ignoring module because it matched excludes: ${module.request}`, // ); return false; } return true; }; validateOptions(options); this.options = { includes: options.includes || [/\.tsx$/], excludes: options.excludes || [/node_modules/], docgenCollectionName: options.docgenCollectionName || "STORYBOOK_REACT_CLASSES", }; this.log = options.verbose ? verboseLog : function () { }; } return Plugin; }()); function processModule(module, docgenCollectionName) { var componentDocs = parser_js.parse(module.userRequest); if (!componentDocs.length) return; var source = module._source._value; componentDocs.forEach(function (componentDoc) { source += "\n" + generateDocgenCodeBlock(componentDoc, module.userRequest, docgenCollectionName) + "\n"; }); module._source._value = source; } function verboseLog(message) { console.log("react-docgen-typescript-webpack-plugin: " + message); } module.exports = Plugin;