Skip to content

Commit 71dccad

Browse files
Merge pull request #6 from hipstersmoothie/non-ts
only load plugin when typescript is available
2 parents 762e412 + e613c4c commit 71dccad

3 files changed

Lines changed: 238 additions & 215 deletions

File tree

.eslintrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"@typescript-eslint/explicit-function-return-type": 0,
1717
"@typescript-eslint/camelcase": 0,
1818
"complexity": 0,
19-
"import/no-unresolved": 0
19+
"import/no-unresolved": 0,
20+
"import/extensions": 0
2021
}
2122
}

src/index.ts

Lines changed: 13 additions & 214 deletions
Original file line numberDiff line numberDiff line change
@@ -1,221 +1,20 @@
1-
import path from "path";
2-
import createDebug from "debug";
3-
import * as webpack from "webpack";
4-
import ts from "typescript";
5-
import * as docGen from "react-docgen-typescript";
6-
import generateDocgenCodeBlock from "react-docgen-typescript-loader/dist/generateDocgenCodeBlock";
7-
import match from "micromatch";
1+
/* eslint-disable */
82

9-
const debugExclude = createDebug("docgen:exclude");
10-
const debugInclude = createDebug("docgen:include");
3+
import type { DocgenPluginType, PluginOptions } from './plugin'
114

12-
interface TypescriptOptions {
13-
/**
14-
* Specify the location of the tsconfig.json to use. Can not be used with
15-
* compilerOptions.
16-
**/
17-
tsconfigPath?: string;
18-
/** Specify TypeScript compiler options. Can not be used with tsconfigPath. */
19-
compilerOptions?: ts.CompilerOptions;
5+
class EmptyPlugin {
6+
constructor(_: PluginOptions) {}
7+
apply() {}
208
}
219

22-
interface LoaderOptions {
23-
/**
24-
* Specify the docgen collection name to use. All docgen information will
25-
* be collected into this global object. Set to null to disable.
26-
*
27-
* @default STORYBOOK_REACT_CLASSES
28-
* @see https://github.com/gongreg/react-storybook-addon-docgen
29-
**/
30-
docgenCollectionName?: string | null;
10+
let plugin: DocgenPluginType;
3111

32-
/**
33-
* Automatically set the component's display name. If you want to set display
34-
* names yourself or are using another plugin to do this, you should disable
35-
* this option.
36-
*
37-
* ```
38-
* class MyComponent extends React.Component {
39-
* ...
40-
* }
41-
*
42-
* MyComponent.displayName = "MyComponent";
43-
* ```
44-
*
45-
* @default true
46-
*/
47-
setDisplayName?: boolean;
48-
49-
/**
50-
* Specify the name of the property for docgen info prop type.
51-
*
52-
* @default "type"
53-
*/
54-
typePropName?: string;
55-
}
56-
57-
export type PluginOptions = docGen.ParserOptions &
58-
LoaderOptions &
59-
TypescriptOptions & {
60-
/** Glob patterns to ignore */
61-
exclude?: [];
62-
/** Glob patterns to include. defaults to ts|tsx */
63-
include?: [];
64-
};
65-
66-
interface Module {
67-
userRequest: string;
68-
request: string;
69-
built?: boolean;
70-
rawRequest?: string;
71-
external?: boolean;
72-
_source: {
73-
_value: string;
74-
};
75-
}
76-
77-
/** Run the docgen parser and inject the result into the output */
78-
function processModule(
79-
parser: docGen.FileParser,
80-
webpackModule: Module,
81-
tsProgram: ts.Program,
82-
loaderOptions: Required<LoaderOptions>
83-
) {
84-
if (!webpackModule) {
85-
return;
86-
}
87-
88-
const componentDocs = parser.parseWithProgramProvider(
89-
webpackModule.userRequest,
90-
() => tsProgram
91-
);
92-
93-
if (!componentDocs.length) {
94-
return;
95-
}
96-
97-
const docs = generateDocgenCodeBlock({
98-
filename: webpackModule.userRequest,
99-
source: webpackModule.userRequest,
100-
componentDocs,
101-
...loaderOptions,
102-
}).substring(webpackModule.userRequest.length);
103-
104-
// eslint-disable-next-line no-underscore-dangle
105-
let source = webpackModule._source._value;
106-
source += `\n${docs}\n`;
107-
// eslint-disable-next-line no-underscore-dangle, no-param-reassign
108-
webpackModule._source._value = source;
109-
}
110-
111-
/** Get the contents of the tsconfig in the system */
112-
function getTSConfigFile(tsconfigPath: string): ts.ParsedCommandLine {
113-
const basePath = path.dirname(tsconfigPath);
114-
const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
115-
116-
return ts.parseJsonConfigFileContent(
117-
configFile.config,
118-
ts.sys,
119-
basePath,
120-
{},
121-
tsconfigPath
122-
);
12+
try {
13+
require.resolve("typescript");
14+
plugin = require('./plugin').default;
15+
} catch (error) {
16+
plugin = EmptyPlugin as any;
12317
}
12418

125-
/** Create a glob matching function. */
126-
const matchGlob = (globs: string[]) => (filename: string) =>
127-
Boolean(filename && globs.find((g) => match([filename], g).length));
128-
129-
/** Inject typescript docgen information into modules at the end of a build */
130-
export default class DocgenPlugin {
131-
private name = "React Docgen Typescript Plugin";
132-
private options: PluginOptions;
133-
134-
constructor(options: PluginOptions) {
135-
this.options = options;
136-
}
137-
138-
apply(compiler: webpack.Compiler) {
139-
const {
140-
tsconfigPath,
141-
docgenCollectionName = "STORYBOOK_REACT_CLASSES",
142-
setDisplayName = true,
143-
typePropName = "type",
144-
compilerOptions: userCompilerOptions,
145-
exclude = [],
146-
include = ["**/**.tsx"],
147-
...docgenOptions
148-
} = this.options;
149-
150-
const isExcluded = matchGlob(exclude);
151-
const isIncluded = matchGlob(include);
152-
153-
let compilerOptions = {
154-
jsx: ts.JsxEmit.React,
155-
module: ts.ModuleKind.CommonJS,
156-
target: ts.ScriptTarget.Latest,
157-
};
158-
159-
if (userCompilerOptions) {
160-
compilerOptions = { ...compilerOptions, ...userCompilerOptions };
161-
}
162-
163-
if (tsconfigPath) {
164-
const { options } = getTSConfigFile(tsconfigPath);
165-
compilerOptions = { ...compilerOptions, ...options };
166-
}
167-
168-
const parser =
169-
(tsconfigPath && docGen.withCustomConfig(tsconfigPath, docgenOptions)) ||
170-
docGen.withCompilerOptions(compilerOptions, docgenOptions);
171-
172-
compiler.hooks.make.tap(this.name, (compilation) => {
173-
compilation.hooks.seal.tap(this.name, () => {
174-
const modulesToProcess: Module[] = [];
175-
176-
compilation.modules.forEach((module: Module) => {
177-
if (!module.built) {
178-
debugExclude(`Ignoring un-built module: ${module.userRequest}`);
179-
return;
180-
}
181-
182-
if (module.external) {
183-
debugExclude(`Ignoring external module: ${module.userRequest}`);
184-
return;
185-
}
186-
187-
if (!module.rawRequest) {
188-
debugExclude(`Ignoring module without "rawRequest": ${module.userRequest}`);
189-
return;
190-
}
191-
192-
if (isExcluded(module.request)) {
193-
debugExclude(`Module not matched in "exclude": ${module.userRequest}`);
194-
return;
195-
}
196-
197-
if (!isIncluded(module.request)) {
198-
debugExclude(`Module not matched in "include": ${module.userRequest}`);
199-
return;
200-
}
201-
202-
debugInclude(module.userRequest);
203-
modulesToProcess.push(module);
204-
});
205-
206-
const tsProgram = ts.createProgram(
207-
modulesToProcess.map((v) => v.userRequest),
208-
compilerOptions
209-
);
210-
211-
modulesToProcess.forEach((m) =>
212-
processModule(parser, m, tsProgram, {
213-
docgenCollectionName,
214-
setDisplayName,
215-
typePropName,
216-
})
217-
);
218-
});
219-
});
220-
}
221-
}
19+
export { PluginOptions } from "./plugin";
20+
export default plugin;

0 commit comments

Comments
 (0)