Skip to content
This repository was archived by the owner on Jan 24, 2025. It is now read-only.

Commit 85499a8

Browse files
authored
feat: docgen refactoring (#545)
* feat(babel-plugin-function-filemeta): add first version of plugin * feat(react-docgen-actual-name-handler): add new package * chore(babel-plugin-export-metadata): change package * feat(docz-core): add react docgen on data server * chore: a lot of improvements
1 parent 96937a9 commit 85499a8

37 files changed

+1739
-226
lines changed

core/docz-core/.babelrc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"plugins": ["lodash"]
3+
}

core/docz-core/package.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"description": "All docz core logic of bundle and parsing is included on this package",
55
"license": "MIT",
66
"main": "dist/index.js",
7-
"umd:main": "dist/index.umd.js",
87
"module": "dist/index.m.js",
98
"typings": "dist/index.d.ts",
109
"source": "src/index.ts",
@@ -33,7 +32,7 @@
3332
"art-template": "^4.13.2",
3433
"babel-loader": "^8.0.2",
3534
"babel-preset-docz": "^0.13.5",
36-
"babel-plugin-export-metadata": "^0.13.5",
35+
"babel-plugin-export-metadata": "^0.13.4",
3736
"babylon": "^6.18.0",
3837
"cache-loader": "^1.2.5",
3938
"chalk": "^2.4.1",
@@ -64,7 +63,11 @@
6463
"poi": "^12.2.4",
6564
"progress-estimator": "^0.2.2",
6665
"react-dev-utils": "^6.1.1",
67-
"react-docgen-typescript-loader": "^3.0.0-rc.0",
66+
"react-docgen": "^2.21.0",
67+
"react-docgen-actual-name-handler": "0.13.5",
68+
"react-docgen-external-proptypes-handler": "^1.0.2",
69+
"react-docgen-imported-proptype-handler": "^1.0.4",
70+
"react-docgen-typescript": "^1.12.2",
6871
"react-hot-loader": "^4.6.3",
6972
"rehype-docz": "^0.13.5",
7073
"rehype-slug": "^2.0.2",
@@ -86,5 +89,6 @@
8689
"webpackbar": "^3.1.4",
8790
"ws": "^6.1.2",
8891
"yargs": "^12.0.5"
89-
}
92+
},
93+
"devDependencies": {}
9094
}

core/docz-core/src/DataServer.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { isFunction } from 'lodash/fp'
44
import { touch } from './utils/fs'
55
import * as paths from './config/paths'
66
import { onSignal } from './utils/on-signal'
7+
import { promiseLogger } from './utils/promise-logger'
78

89
export type Send = (type: string, payload: any) => void
910
export type On = (type: string) => Promise<any>
@@ -26,6 +27,7 @@ export interface Params {
2627
}
2728

2829
export interface State {
30+
id: string
2931
init: (params: Params) => Promise<any>
3032
update: (params: Params) => any
3133
close: (params: Params) => any
@@ -56,14 +58,16 @@ export class DataServer {
5658

5759
public async init(): Promise<void> {
5860
await Promise.all(
59-
Array.from(this.states).map(
60-
async state =>
61-
isFunction(state.init) &&
62-
state.init({
63-
state: { ...this.state },
64-
setState: this.setState(),
65-
})
66-
)
61+
Array.from(this.states).map(async state => {
62+
if (!isFunction(state.init)) return
63+
64+
const promise = state.init({
65+
state: { ...this.state },
66+
setState: this.setState(),
67+
})
68+
69+
return promiseLogger(promise, `Initial data for ${state.id}`)
70+
})
6771
)
6872

6973
this.updateStateFile()

core/docz-core/src/commands/args.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ const getInitialDescription = (pkg: any): string =>
2424
export type Env = 'production' | 'development'
2525
export type ThemeConfig = Record<string, any>
2626

27+
export interface DocgenConfig {
28+
handlers?: any[]
29+
resolver?: (ast: any, recast: any) => any
30+
propFilter?: (prop: any) => boolean
31+
}
32+
2733
export interface Menu {
2834
name: string
2935
route?: string
@@ -89,6 +95,7 @@ export interface Config extends Argv {
8995
menu: Menu[]
9096
htmlContext: HtmlContext
9197
themeConfig: ThemeConfig
98+
docgenConfig: DocgenConfig
9299
modifyBundlerConfig<C>(config: C, dev: boolean, args: Config): C
93100
modifyBabelRc(babelrc: BabelRC, args: Config): BabelRC
94101
onCreateWebpackChain<C>(c: C, dev: boolean, args: Config): void

core/docz-core/src/commands/build.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ export const build = async (args: Config) => {
2020
const run = Plugin.runPluginsMethod(config.plugins)
2121
const dataServer = new DataServer()
2222

23+
if (args.propsParser) dataServer.register([states.props(config)])
2324
dataServer.register([states.config(config), states.entries(entries, config)])
2425

2526
try {
2627
await promiseLogger(Entries.writeApp(config, true), 'Parsing mdx files')
27-
await promiseLogger(dataServer.init(), 'Initializing data server')
28+
await promiseLogger(dataServer.init(), 'Running data server')
2829

2930
await promiseLogger(run('onPreBuild', config), 'Running onPreBuild()')
3031
await bundler.build(bundlerConfig)

core/docz-core/src/commands/dev.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,14 @@ export const dev = async (args: Config) => {
4242
config.websocketHost
4343
)
4444

45+
if (args.propsParser) dataServer.register([states.props(newConfig)])
4546
dataServer.register([
4647
states.config(newConfig),
4748
states.entries(entries, newConfig),
4849
])
4950

5051
try {
51-
await promiseLogger(dataServer.init(), 'Initializing data server')
52+
await promiseLogger(dataServer.init(), 'Running data server')
5253
await dataServer.listen()
5354
} catch (err) {
5455
logger.fatal('Failed to process your server:', err)

core/docz-core/src/config/babel.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,21 @@ export const getBabelConfig = async (
3232
const config = merge(localBabelRc, {
3333
presets,
3434
babelrc: false,
35-
...(args.debug && {
36-
cacheDirectory: true,
37-
cacheIdentifier: getCacheIdentifier(
38-
isProd ? 'production' : isDev && 'development',
39-
[
35+
cacheCompression: args.debug ? false : isProd,
36+
cacheDirectory: !args.debug,
37+
cacheIdentifier: args.debug
38+
? null
39+
: getCacheIdentifier(isProd ? 'production' : isDev && 'development', [
4040
'docz',
4141
'docz-theme-default',
4242
'docz-utils',
4343
'docz-core',
4444
'babel-preset-docz',
45-
]
46-
),
47-
}),
48-
cacheCompression: isProd,
45+
]),
4946
compact: isProd,
50-
plugins: !isProd ? [require.resolve('react-hot-loader/babel')] : [],
47+
plugins: [require.resolve('babel-plugin-export-metadata')].concat(
48+
!isProd ? [require.resolve('react-hot-loader/babel')] : []
49+
),
5150
})
5251

5352
const reduce = Plugin.reduceFromPlugins<BabelRC>(args.plugins)

core/docz-core/src/states/config.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@ import { load, finds } from 'load-cfg'
33
import chokidar from 'chokidar'
44
import get from 'lodash/get'
55

6+
import * as paths from '../config/paths'
67
import { Params, State } from '../DataServer'
78
import { Config, Menu, ThemeConfig } from '../commands/args'
89
import { getRepoUrl } from '../utils/repo-info'
9-
import * as paths from '../config/paths'
1010

1111
interface Payload {
1212
title: string
@@ -50,6 +50,7 @@ export const state = (config: Config): State => {
5050
watcher.setMaxListeners(Infinity)
5151

5252
return {
53+
id: 'config',
5354
init: updateConfig(config),
5455
close: () => watcher.close(),
5556
update: async params => {

core/docz-core/src/states/entries.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export const state = (entries: Entries, config: Config): State => {
3737
watcher.setMaxListeners(Infinity)
3838

3939
return {
40+
id: 'entries',
4041
init: updateEntries(entries),
4142
close: () => watcher.close(),
4243
update: async params => {

core/docz-core/src/states/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { state as entries } from './entries'
22
export { state as config } from './config'
3+
export { state as props } from './props'

core/docz-core/src/states/props.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import chokidar from 'chokidar'
2+
import equal from 'fast-deep-equal'
3+
import fastglob from 'fast-glob'
4+
import { State, Params } from '../DataServer'
5+
import { get, omit } from 'lodash/fp'
6+
7+
import * as paths from '../config/paths'
8+
import { Config } from '../commands/args'
9+
import { docgen } from '../utils/docgen'
10+
11+
const getPattern = (ts: boolean) => {
12+
const tsPattern = '**/*.{ts,tsx}'
13+
const jsPattern = '**/*.{js,jsx,mjs}'
14+
return [ts ? tsPattern : jsPattern, '!**/node_modules']
15+
}
16+
17+
const initial = (config: Config) => async (p: Params) => {
18+
const pattern = getPattern(config.typescript)
19+
const files = await fastglob<string>(pattern, { cwd: paths.root })
20+
const metadata = await docgen(files, config)
21+
p.setState('props', metadata)
22+
}
23+
24+
const add = (config: Config) => (p: Params) => async (filepath: string) => {
25+
const old = get('state.props', p)
26+
const metadata = await docgen([filepath], config)
27+
28+
if (metadata && !equal(old, metadata)) {
29+
p.setState('props', {
30+
...old,
31+
...metadata,
32+
})
33+
}
34+
}
35+
36+
const remove = (config: Config) => (p: Params) => async (filepath: string) =>
37+
p.setState('props', omit(filepath, get('state.props', p)))
38+
39+
export const state = (config: Config): State => {
40+
const pattern = getPattern(config.typescript)
41+
const watcher = chokidar.watch(pattern, {
42+
cwd: paths.root,
43+
ignored: /(((^|[\/\\])\..+)|(node_modules))/,
44+
persistent: true,
45+
})
46+
47+
watcher.setMaxListeners(Infinity)
48+
49+
return {
50+
id: 'props',
51+
init: initial(config),
52+
close: () => watcher.close(),
53+
update: async params => {
54+
const addFilepath = add(config)
55+
const removeFilepath = remove(config)
56+
57+
watcher.on('add', addFilepath(params))
58+
watcher.on('change', addFilepath(params))
59+
watcher.on('unlink', removeFilepath(params))
60+
61+
return () => watcher.close()
62+
},
63+
}
64+
}

core/docz-core/src/types.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ declare module 'humanize-string'
1717
declare module 'mini-html-webpack-plugin'
1818
declare module 'p-reduce'
1919
declare module 'progress-estimator'
20+
declare module 'react-docgen'
21+
declare module 'react-docgen-external-proptypes-handler'
22+
declare module 'react-docgen-imported-proptype-handler'
2023
declare module 'react-dev-utils/errorOverlayMiddleware'
2124
declare module 'react-dev-utils/evalSourceMapMiddleware'
2225
declare module 'react-dev-utils/FileSizeReporter'

core/docz-core/src/utils/docgen.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import * as path from 'path'
2+
import * as fs from 'fs-extra'
3+
import { isFunction } from 'lodash/fp'
4+
import logger from 'signale'
5+
import findUp from 'find-up'
6+
import externalProptypesHandler from 'react-docgen-external-proptypes-handler'
7+
import importedProptypesHandler from 'react-docgen-imported-proptype-handler'
8+
import actualNameHandler from 'react-docgen-actual-name-handler'
9+
import reactDocgenTs from 'react-docgen-typescript'
10+
import reactDocgen from 'react-docgen'
11+
12+
import * as paths from '../config/paths'
13+
import { Config } from '../commands/args'
14+
15+
const fileFullPath = (filepath: string) => path.join(paths.root, filepath)
16+
17+
const tsParser = async (files: string[], config: Config) => {
18+
const tsConfigPath = await findUp('tsconfig.json', { cwd: paths.root })
19+
if (!tsConfigPath) return {}
20+
21+
try {
22+
const { parse } = reactDocgenTs.withCustomConfig(tsConfigPath, {
23+
propFilter(prop: any, component: any): any {
24+
if (prop.parent == null) return true
25+
const propFilter = config.docgenConfig.propFilter
26+
const val = propFilter && isFunction(propFilter) && propFilter(prop)
27+
return !prop.parent.fileName.includes('node_modules') || Boolean(val)
28+
},
29+
})
30+
31+
return files
32+
.map(filepath => ({ [fileFullPath(filepath)]: parse(filepath) }))
33+
.reduce((obj, val) => ({ ...obj, ...val }), {})
34+
} catch (err) {
35+
logger.fatal('Error parsing static types.', err)
36+
return {}
37+
}
38+
}
39+
40+
const jsParser = (files: string[], config: Config) => {
41+
const resolver =
42+
config.docgenConfig.resolver ||
43+
reactDocgen.resolver.findAllExportedComponentDefinitions
44+
45+
return files.reduce((memo: any, filepath) => {
46+
const handlers = reactDocgen.defaultHandlers.concat([
47+
externalProptypesHandler(filepath),
48+
importedProptypesHandler(filepath),
49+
actualNameHandler,
50+
])
51+
52+
const code = fs.readFileSync(filepath, 'utf-8')
53+
54+
try {
55+
const data = reactDocgen.parse(code, resolver, handlers)
56+
memo[fileFullPath(filepath)] = data
57+
} catch (err) {
58+
if (err.message !== 'No suitable component definition found.') {
59+
logger.fatal('Error:', filepath, err)
60+
}
61+
}
62+
63+
return memo
64+
}, {})
65+
}
66+
67+
export const docgen = async (files: string[], config: Config) =>
68+
config.typescript ? tsParser(files, config) : jsParser(files, config)

core/docz-core/src/utils/load-config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export const loadConfig = async (args: Config): Promise<Config> => {
3030
'license.md',
3131
],
3232
themeConfig: {},
33+
docgenConfig: {},
3334
modifyBundlerConfig: (config: any) => config,
3435
modifyBabelRc: (babelrc: BabelRC) => babelrc,
3536
onCreateWebpackChain: () => null,

core/docz-core/src/webpack/loaders.ts

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ export const mdx = (config: Config, args: Args) => {
5454
.end()
5555
.exclude.add(excludeNodeModules)
5656
.end()
57-
.use('happypack-mdx')
58-
.loader('happypack/loader?id=mdx')
57+
.use('happypack-jsx')
58+
.loader('happypack/loader?id=jsx')
5959
.end()
6060
.use('mdx-loader')
6161
.loader(require.resolve('@mdx-js/loader'))
@@ -81,33 +81,11 @@ export const setupHappypack = (config: Config, args: Args, babelrc: any) => {
8181
},
8282
]
8383

84-
const loaderWithDocgen = [
85-
args.propsParser &&
86-
args.typescript && {
87-
loader: require.resolve('react-docgen-typescript-loader'),
88-
options: {
89-
propFilter: (prop: any) => {
90-
if (prop.parent == null) return true
91-
return !prop.parent.fileName.includes('node_modules')
92-
},
93-
},
94-
},
95-
]
96-
9784
config.plugin('happypack-jsx').use(HappyPack, [
9885
{
9986
id: 'jsx',
10087
verbose: args.debug,
10188
threadPool: happyThreadPool,
102-
loaders: loaders.concat(loaderWithDocgen).filter(Boolean),
103-
},
104-
])
105-
106-
config.plugin('happypack-mdx').use(HappyPack, [
107-
{
108-
id: 'mdx',
109-
verbose: args.debug,
110-
threadPool: happyThreadPool,
11189
loaders: loaders.filter(Boolean),
11290
},
11391
])

0 commit comments

Comments
 (0)