react-docgen-typescript-webpack-plugin
Version:
Webpack plugin to generate docgen information from Typescript React components.
147 lines (141 loc) • 6.35 kB
JavaScript
;
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;