diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml
index 2e8f99e..4c04dfa 100644
--- a/.github/workflows/push.yml
+++ b/.github/workflows/push.yml
@@ -3,10 +3,12 @@ name: Node CI
on:
push:
branches:
- - master
+ - main
+ - next
pull_request:
branches:
- - master
+ - main
+ - next
jobs:
build:
diff --git a/package.json b/package.json
index 72c48bd..72a3a36 100644
--- a/package.json
+++ b/package.json
@@ -39,8 +39,7 @@
"flat-cache": "^3.0.4",
"micromatch": "^4.0.2",
"react-docgen-typescript": "^1.22.0",
- "tslib": "^2.0.0",
- "webpack-sources": "^2.2.0"
+ "tslib": "^2.0.0"
},
"devDependencies": {
"@types/debug": "^4.1.5",
@@ -50,7 +49,6 @@
"@types/micromatch": "^4.0.1",
"@types/node": "^14.0.12",
"@types/react": "^17.0.0",
- "@types/webpack-sources": "^2.1.0",
"@typescript-eslint/eslint-plugin": "^4.9.0",
"@typescript-eslint/parser": "^4.9.0",
"auto": "^10.2.3",
diff --git a/src/__tests__/__fixtures__/SubComponent.tsx b/src/__tests__/__fixtures__/SubComponent.tsx
new file mode 100644
index 0000000..a58358e
--- /dev/null
+++ b/src/__tests__/__fixtures__/SubComponent.tsx
@@ -0,0 +1,11 @@
+import * as React from "react";
+
+export default function Root(props: { name: string }) {
+ return root {props.name};
+}
+
+function Sub(props: { name: string }) {
+ return sub {props.name};
+}
+
+Root.Sub = Sub;
diff --git a/src/__tests__/__snapshots__/generateDocgenCodeBlock.test.ts.snap b/src/__tests__/__snapshots__/generateDocgenCodeBlock.test.ts.snap
index 316ff3d..7dc4fc1 100644
--- a/src/__tests__/__snapshots__/generateDocgenCodeBlock.test.ts.snap
+++ b/src/__tests__/__snapshots__/generateDocgenCodeBlock.test.ts.snap
@@ -209,6 +209,35 @@ try {
catch (__react_docgen_typescript_loader_error) { }"
`;
+exports[`component fixture SubComponent.tsx has code block generated 1`] = `
+"import * as React from \\"react\\";
+
+export default function Root(props: { name: string }) {
+ return root {props.name};
+}
+
+function Sub(props: { name: string }) {
+ return sub {props.name};
+}
+
+Root.Sub = Sub;
+
+try {
+ // @ts-ignore
+ SubComponent.displayName = \\"SubComponent\\";
+ // @ts-ignore
+ SubComponent.__docgenInfo = { \\"description\\": \\"\\", \\"displayName\\": \\"SubComponent\\", \\"props\\": { \\"name\\": { \\"defaultValue\\": null, \\"description\\": \\"\\", \\"name\\": \\"name\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"string\\" } } } };
+}
+catch (__react_docgen_typescript_loader_error) { }
+try {
+ // @ts-ignore
+ default.Sub.displayName = \\"default.Sub\\";
+ // @ts-ignore
+ default.Sub.__docgenInfo = { \\"description\\": \\"\\", \\"displayName\\": \\"default.Sub\\", \\"props\\": { \\"name\\": { \\"defaultValue\\": null, \\"description\\": \\"\\", \\"name\\": \\"name\\", \\"required\\": true, \\"type\\": { \\"name\\": \\"string\\" } } } };
+}
+catch (__react_docgen_typescript_loader_error) { }"
+`;
+
exports[`component fixture TextOnlyComponent.tsx has code block generated 1`] = `
"import * as React from \\"react\\";
diff --git a/src/dependency.ts b/src/dependency.ts
index 6d9d67a..5057baf 100644
--- a/src/dependency.ts
+++ b/src/dependency.ts
@@ -18,6 +18,10 @@ class DocGenDependency extends NullDependency {
this.codeBlock = codeBlock;
}
+ getModuleEvaluationSideEffectsState(): boolean {
+ return false;
+ }
+
updateHash: webpack.dependencies.NullDependency["updateHash"] = (hash) => {
hash.update(this.codeBlock);
};
@@ -46,6 +50,7 @@ class DocGenTemplate extends NullDependency.Template
};
}
+// eslint-disable-next-line
// @ts-ignore TODO: How to type this correctly?
DocGenDependency.Template = DocGenTemplate;
diff --git a/src/plugin.ts b/src/plugin.ts
index d5920e7..d0384cb 100644
--- a/src/plugin.ts
+++ b/src/plugin.ts
@@ -143,6 +143,18 @@ export default class DocgenPlugin implements webpack.WebpackPluginInstance {
}
apply(compiler: webpack.Compiler): void {
+ // Property compiler.version is set only starting from webpack 5
+ const webpackVersion = compiler.webpack?.version || "";
+ const isWebpack5 = parseInt(webpackVersion.split(".")[0], 10) >= 5;
+
+ if (isWebpack5) {
+ this.applyWebpack5(compiler);
+ } else {
+ this.applyWebpack4(compiler);
+ }
+ }
+
+ applyWebpack5(compiler: webpack.Compiler): void {
const pluginName = "DocGenPlugin";
const {
docgenOptions,
@@ -156,29 +168,24 @@ export default class DocgenPlugin implements webpack.WebpackPluginInstance {
const { exclude = [], include = ["**/**.tsx"] } = this.options;
const isExcluded = matchGlob(exclude);
const isIncluded = matchGlob(include);
- // Property compiler.version is set only starting from webpack 5
- const webpackVersion = compiler.webpack?.version || "";
- const isWebpack5 = parseInt(webpackVersion.split(".")[0], 10) >= 5;
compiler.hooks.compilation.tap(
pluginName,
(compilation: webpack.Compilation) => {
- if (isWebpack5) {
- // Since this file is needed only for webpack 5, load it only then
- // to simplify the implementation of the file.
- //
- // eslint-disable-next-line
- const { DocGenDependency } = require("./dependency");
+ // Since this file is needed only for webpack 5, load it only then
+ // to simplify the implementation of the file.
+ //
+ // eslint-disable-next-line
+ const { DocGenDependency } = require("./dependency");
- compilation.dependencyTemplates.set(
- // eslint-disable-next-line
- // @ts-ignore: Webpack 4 type
- DocGenDependency,
- // eslint-disable-next-line
- // @ts-ignore: Webpack 4 type
- new DocGenDependency.Template()
- );
- }
+ compilation.dependencyTemplates.set(
+ // eslint-disable-next-line
+ // @ts-ignore: Webpack 4 type
+ DocGenDependency,
+ // eslint-disable-next-line
+ // @ts-ignore: Webpack 4 type
+ new DocGenDependency.Template()
+ );
compilation.hooks.seal.tap(pluginName, () => {
const modulesToProcess: [string, webpack.Module][] = [];
@@ -191,6 +198,30 @@ export default class DocgenPlugin implements webpack.WebpackPluginInstance {
const nameForCondition = module.nameForCondition() || "";
+ // Ignore already built modules for webpack 5
+ if (!compilation.builtModules.has(module)) {
+ debugExclude(`Ignoring un-built module: ${nameForCondition}`);
+ return;
+ }
+
+ // Ignore external modules
+ // eslint-disable-next-line
+ // @ts-ignore: Webpack 4 type
+ if (module.external) {
+ debugExclude(`Ignoring external module: ${nameForCondition}`);
+ return;
+ }
+
+ // Ignore raw requests
+ // eslint-disable-next-line
+ // @ts-ignore: Webpack 4 type
+ if (!module.rawRequest) {
+ debugExclude(
+ `Ignoring module without "rawRequest": ${nameForCondition}`
+ );
+ return;
+ }
+
if (isExcluded(nameForCondition)) {
debugExclude(
`Module not matched in "exclude": ${nameForCondition}`
@@ -217,38 +248,122 @@ export default class DocgenPlugin implements webpack.WebpackPluginInstance {
// 3. Process and parse each module and add the type information
// as a dependency
modulesToProcess.forEach(([name, module]) => {
- if (isWebpack5) {
- // Since this file is needed only for webpack 5, load it only then
- // to simplify the implementation of the file.
- //
+ // Since this file is needed only for webpack 5, load it only then
+ // to simplify the implementation of the file.
+ //
+ // eslint-disable-next-line
+ const { DocGenDependency } = require("./dependency");
+
+ module.addDependency(
// eslint-disable-next-line
- const { DocGenDependency } = require("./dependency");
-
- module.addDependency(
- // eslint-disable-next-line
- // @ts-ignore: Webpack 4 type
- new DocGenDependency(
- generateDocgenCodeBlock({
- filename: name,
- source: name,
- componentDocs: docGenParser.parseWithProgramProvider(
- name,
- () => tsProgram
- ),
- ...generateOptions,
- }).substring(name.length)
- )
- );
- } else {
- // Assume webpack 4 or earlier
- processModule(docGenParser, module, tsProgram, generateOptions);
- }
+ // @ts-ignore: Webpack 4 type
+ new DocGenDependency(
+ generateDocgenCodeBlock({
+ filename: name,
+ source: name,
+ componentDocs: docGenParser.parseWithProgramProvider(
+ name,
+ () => tsProgram
+ ),
+ ...generateOptions,
+ }).substring(name.length)
+ )
+ );
});
});
}
);
}
+ applyWebpack4(compiler: webpack.Compiler): void {
+ const { docgenOptions, compilerOptions } = this.getOptions();
+ const parser = docGen.withCompilerOptions(compilerOptions, docgenOptions);
+ const { exclude = [], include = ["**/**.tsx"] } = this.options;
+ const isExcluded = matchGlob(exclude);
+ const isIncluded = matchGlob(include);
+
+ compiler.hooks.make.tap(this.name, (compilation) => {
+ compilation.hooks.seal.tap(this.name, () => {
+ const modulesToProcess: webpack.Module[] = [];
+
+ compilation.modules.forEach((module: webpack.Module) => {
+ // eslint-disable-next-line
+ // @ts-ignore: Webpack 4 type
+ if (!module.built) {
+ // eslint-disable-next-line
+ // @ts-ignore: Webpack 4 type
+ debugExclude(`Ignoring un-built module: ${module.userRequest}`);
+ return;
+ }
+
+ // eslint-disable-next-line
+ // @ts-ignore: Webpack 4 type
+ if (module.external) {
+ // eslint-disable-next-line
+ // @ts-ignore: Webpack 4 type
+ debugExclude(`Ignoring external module: ${module.userRequest}`);
+ return;
+ }
+
+ // eslint-disable-next-line
+ // @ts-ignore: Webpack 4 type
+ if (!module.rawRequest) {
+ debugExclude(
+ // eslint-disable-next-line
+ // @ts-ignore: Webpack 4 type
+ `Ignoring module without "rawRequest": ${module.userRequest}`
+ );
+ return;
+ }
+
+ // eslint-disable-next-line
+ // @ts-ignore: Webpack 4 type
+ if (isExcluded(module.userRequest)) {
+ debugExclude(
+ // eslint-disable-next-line
+ // @ts-ignore: Webpack 4 type
+ `Module not matched in "exclude": ${module.userRequest}`
+ );
+ return;
+ }
+
+ // eslint-disable-next-line
+ // @ts-ignore: Webpack 4 type
+ if (!isIncluded(module.userRequest)) {
+ debugExclude(
+ // eslint-disable-next-line
+ // @ts-ignore: Webpack 4 type
+ `Module not matched in "include": ${module.userRequest}`
+ );
+ return;
+ }
+
+ // eslint-disable-next-line
+ // @ts-ignore: Webpack 4 type
+ debugInclude(module.userRequest);
+ modulesToProcess.push(module);
+ });
+
+ const tsProgram = ts.createProgram(
+ // eslint-disable-next-line
+ // @ts-ignore: Webpack 4 type
+ modulesToProcess.map((v) => v.userRequest),
+ compilerOptions
+ );
+
+ modulesToProcess.forEach((m) =>
+ processModule(parser, m, tsProgram, {
+ docgenCollectionName: "STORYBOOK_REACT_CLASSES",
+ setDisplayName: true,
+ typePropName: "type",
+ })
+ );
+
+ cache.save();
+ });
+ });
+ }
+
getOptions(): {
docgenOptions: docGen.ParserOptions;
generateOptions: {
diff --git a/yarn.lock b/yarn.lock
index a269182..b77f79d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -982,25 +982,11 @@
"@types/prop-types" "*"
csstype "^3.0.2"
-"@types/source-list-map@*":
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
- integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==
-
"@types/stack-utils@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.0.tgz#7036640b4e21cc2f259ae826ce843d277dad8cff"
integrity sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==
-"@types/webpack-sources@^2.1.0":
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-2.1.0.tgz#8882b0bd62d1e0ce62f183d0d01b72e6e82e8c10"
- integrity sha512-LXn/oYIpBeucgP1EIJbKQ2/4ZmpvRl+dlrFdX7+94SKRUV3Evy3FsfMZY318vGhkWUS5MPhtOM3w1/hCOAOXcg==
- dependencies:
- "@types/node" "*"
- "@types/source-list-map" "*"
- source-map "^0.7.3"
-
"@types/yargs-parser@*":
version "15.0.0"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
@@ -6377,7 +6363,7 @@ webpack-merge@^5.7.3:
clone-deep "^4.0.1"
wildcard "^2.0.0"
-webpack-sources@^2.1.1, webpack-sources@^2.2.0:
+webpack-sources@^2.1.1:
version "2.2.0"
resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.2.0.tgz#058926f39e3d443193b6c31547229806ffd02bac"
integrity sha512-bQsA24JLwcnWGArOKUxYKhX3Mz/nK1Xf6hxullKERyktjNMC4x8koOeaDNTA2fEJ09BdWLbM/iTW0ithREUP0w==