@patternfly/documentation-framework 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3964 -0
- package/README.md +92 -0
- package/app.js +118 -0
- package/assets/android-chrome-36x36.png +0 -0
- package/assets/android-chrome-48x48.png +0 -0
- package/assets/android-chrome-96x96.png +0 -0
- package/assets/favicon-16x16.png +0 -0
- package/assets/favicon-32x32.png +0 -0
- package/assets/favicon-48x48.png +0 -0
- package/assets/favicon.ico +0 -0
- package/assets/manifest.json +29 -0
- package/components/autoLinkHeader/autoLinkHeader.css +22 -0
- package/components/autoLinkHeader/autoLinkHeader.js +41 -0
- package/components/cssVariables/cssSearch.js +32 -0
- package/components/cssVariables/cssVariables.css +15 -0
- package/components/cssVariables/cssVariables.js +233 -0
- package/components/example/example.css +106 -0
- package/components/example/example.js +196 -0
- package/components/example/exampleToolbar.js +208 -0
- package/components/example/missing-thumbnail.jpg +0 -0
- package/components/footer/RHLogo.png +0 -0
- package/components/footer/footer.css +114 -0
- package/components/footer/footer.js +262 -0
- package/components/gdprBanner/gdprBanner.css +14 -0
- package/components/gdprBanner/gdprBanner.js +31 -0
- package/components/index.js +12 -0
- package/components/inlineAlert/inlineAlert.js +19 -0
- package/components/link/link.js +68 -0
- package/components/propsTable/propTypeWithLinks.js +33 -0
- package/components/propsTable/propsTable.js +82 -0
- package/components/sideNav/sideNav.css +17 -0
- package/components/sideNav/sideNav.js +95 -0
- package/components/tableOfContents/tableOfContents.css +77 -0
- package/components/tableOfContents/tableOfContents.js +89 -0
- package/components/topNav/topNav.css +30 -0
- package/components/topNav/topNav.js +24 -0
- package/global.css +7 -0
- package/helpers/asyncComponentFactory.js +41 -0
- package/helpers/capitalize.js +7 -0
- package/helpers/codeTitleTransformer.js +40 -0
- package/helpers/codesandbox.js +204 -0
- package/helpers/copy.js +19 -0
- package/helpers/createHandlebars.js +26 -0
- package/helpers/d3-timer.js +7 -0
- package/helpers/extractExampleHelpers.js +24 -0
- package/helpers/fonts.js +164 -0
- package/helpers/getId.js +6 -0
- package/helpers/getTitle.js +5 -0
- package/helpers/index.js +9 -0
- package/helpers/liveCodeTypes.js +4 -0
- package/helpers/slugger.js +35 -0
- package/helpers/trackEvent.js +17 -0
- package/helpers/xterm-addon-fit.js +3 -0
- package/helpers/xterm.js +8 -0
- package/images/patternfly-logo.svg +14 -0
- package/images/patternfly-orb.svg +28 -0
- package/layouts/index.js +1 -0
- package/layouts/logo.svg +33 -0
- package/layouts/sideNavLayout/sideNavLayout.css +104 -0
- package/layouts/sideNavLayout/sideNavLayout.js +237 -0
- package/package.json +87 -0
- package/pages/404/404.css +16 -0
- package/pages/404/404.svg +26 -0
- package/pages/404/chart.svg +24 -0
- package/pages/404/components.svg +1 -0
- package/pages/404/guidelines.svg +1 -0
- package/pages/404/index.js +136 -0
- package/pages/404/patternfly-flat.svg +1 -0
- package/pages/global-css-variables.md +108 -0
- package/pages/img/component-variable-mapping.png +0 -0
- package/pages/red-hat-font.md +40 -0
- package/routes-client.js +1 -0
- package/routes-generated.js +1 -0
- package/routes.js +155 -0
- package/scripts/cli/build.js +95 -0
- package/scripts/cli/buildClient.js +23 -0
- package/scripts/cli/buildServer.js +24 -0
- package/scripts/cli/cli.js +63 -0
- package/scripts/cli/generate.js +23 -0
- package/scripts/cli/helpers.js +9 -0
- package/scripts/cli/init.js +68 -0
- package/scripts/cli/serve.js +13 -0
- package/scripts/cli/start.js +32 -0
- package/scripts/md/anchor-header.js +69 -0
- package/scripts/md/auto-link-url.js +33 -0
- package/scripts/md/example-captions.js +67 -0
- package/scripts/md/jsxAttributes.js +20 -0
- package/scripts/md/mdx-ast-to-mdx-hast.js +210 -0
- package/scripts/md/mdx-hast-to-jsx.js +233 -0
- package/scripts/md/parseMD.js +308 -0
- package/scripts/md/remove-comments.js +21 -0
- package/scripts/md/styled-tags.js +87 -0
- package/scripts/md/typecheck.js +133 -0
- package/scripts/tsDocgen.js +155 -0
- package/scripts/webpack/getHtmlWebpackPlugins.js +78 -0
- package/scripts/webpack/prerender.js +59 -0
- package/scripts/webpack/webpack.base.config.js +159 -0
- package/scripts/webpack/webpack.client.config.js +146 -0
- package/scripts/webpack/webpack.server.config.js +55 -0
- package/scripts/writeScreenshots.js +66 -0
- package/templates/design-guidelines.js +13 -0
- package/templates/html.ejs +52 -0
- package/templates/mdx.css +308 -0
- package/templates/mdx.js +185 -0
- package/templates/patternfly-docs/content/extensions/extension/design-guidelines/design-guidelines.md +25 -0
- package/templates/patternfly-docs/content/extensions/extension/design-guidelines/img/card-elements.png +0 -0
- package/templates/patternfly-docs/content/extensions/extension/examples/Basic.jsx +4 -0
- package/templates/patternfly-docs/content/extensions/extension/examples/basic.md +25 -0
- package/templates/patternfly-docs/pages/index.js +27 -0
- package/templates/patternfly-docs/patternfly-docs.config.js +6 -0
- package/templates/patternfly-docs/patternfly-docs.css.js +8 -0
- package/templates/patternfly-docs/patternfly-docs.routes.js +12 -0
- package/templates/patternfly-docs/patternfly-docs.source.js +24 -0
- package/templates/sitemap.ejs +10 -0
- package/versions.json +870 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const unified = require('unified');
|
|
4
|
+
const remove = require('unist-util-remove');
|
|
5
|
+
const visit = require('unist-util-visit');
|
|
6
|
+
const toVfile = require('to-vfile'); // https://github.com/vfile/vfile
|
|
7
|
+
const vfileReport = require('vfile-reporter');
|
|
8
|
+
const yaml = require('js-yaml'); // https://github.com/nodeca/js-yaml
|
|
9
|
+
const chokidar = require('chokidar');
|
|
10
|
+
const { sync } = require('glob');
|
|
11
|
+
const { typecheck } = require('./typecheck');
|
|
12
|
+
const { makeSlug } = require('../../helpers/slugger');
|
|
13
|
+
const { liveCodeTypes } = require('../../helpers/liveCodeTypes');
|
|
14
|
+
const { tsDocgen } = require('../tsDocgen');
|
|
15
|
+
|
|
16
|
+
let exitCode = 0;
|
|
17
|
+
const outputBase = path.join(process.cwd(), `patternfly-docs/generated`);
|
|
18
|
+
const tsDocs = {};
|
|
19
|
+
const routes = {};
|
|
20
|
+
const globs = {
|
|
21
|
+
props: [],
|
|
22
|
+
md: [],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function toReactComponent(mdFilePath, source, buildMode) {
|
|
26
|
+
// vfiles allow for nicer error messages and have native `unified` support
|
|
27
|
+
const vfile = toVfile.readSync(mdFilePath);
|
|
28
|
+
|
|
29
|
+
const relPath = path.relative(path.join(process.cwd(), '../..'), vfile.path).split(path.sep).join(path.posix.sep);
|
|
30
|
+
|
|
31
|
+
let jsx;
|
|
32
|
+
let outPath;
|
|
33
|
+
let pageData = {};
|
|
34
|
+
let frontmatter = {};
|
|
35
|
+
|
|
36
|
+
unified()
|
|
37
|
+
.use(require('remark-parse'))
|
|
38
|
+
.use(require('remark-frontmatter'), ['yaml'])
|
|
39
|
+
// Extract frontmatter
|
|
40
|
+
.use(() => (tree, file) => {
|
|
41
|
+
const yamlNode = tree.children.shift();
|
|
42
|
+
if (!yamlNode) {
|
|
43
|
+
return file.info('no frontmatter, skipping');
|
|
44
|
+
}
|
|
45
|
+
frontmatter = yaml.safeLoad(yamlNode.value);
|
|
46
|
+
|
|
47
|
+
// Fail early
|
|
48
|
+
if (!frontmatter.id) {
|
|
49
|
+
file.fail('id attribute is required in frontmatter for PatternFly docs');
|
|
50
|
+
}
|
|
51
|
+
source = frontmatter.source || source;
|
|
52
|
+
const slug = makeSlug(source, frontmatter.section, frontmatter.id);
|
|
53
|
+
outPath = path.join(outputBase, `${slug}.js`);
|
|
54
|
+
|
|
55
|
+
let sourceRepo = 'patternfly-org';
|
|
56
|
+
if (source.includes('html')) {
|
|
57
|
+
sourceRepo = 'patternfly';
|
|
58
|
+
}
|
|
59
|
+
else if (source.includes('react')) {
|
|
60
|
+
sourceRepo = 'patternfly-react';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const propComponents = [...new Set(frontmatter.propComponents || [])]
|
|
64
|
+
.filter(propComponent => {
|
|
65
|
+
if (tsDocs[propComponent]) {
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
file.message(`Prop component ${propComponent} missing from tsDocgen`);
|
|
69
|
+
return false;
|
|
70
|
+
})
|
|
71
|
+
.map(propComponent => tsDocs[propComponent])
|
|
72
|
+
|
|
73
|
+
const normalizedPath = relPath
|
|
74
|
+
.replace('node_modules/@patternfly/patternfly/docs', 'src/patternfly')
|
|
75
|
+
.replace(/node_modules\/@patternfly\/react-([\w-])/, (_, match) => `packages/react-${match}`)
|
|
76
|
+
.replace(/\.\.\//g, '');
|
|
77
|
+
|
|
78
|
+
pageData = {
|
|
79
|
+
id: frontmatter.id,
|
|
80
|
+
section: frontmatter.section || '',
|
|
81
|
+
source,
|
|
82
|
+
slug,
|
|
83
|
+
sourceLink: `https://github.com/patternfly/${
|
|
84
|
+
sourceRepo}/blob/main/${
|
|
85
|
+
normalizedPath}`,
|
|
86
|
+
hideTOC: frontmatter.hideTOC || false
|
|
87
|
+
};
|
|
88
|
+
// Temporarily override section for Demo tabs until we port this upstream
|
|
89
|
+
if (frontmatter.section === 'demos' && routes[slug.replace('demos', 'components')]) {
|
|
90
|
+
// Temporarily override section until https://github.com/patternfly/patternfly-react/pull/4862 is in react-docs
|
|
91
|
+
pageData.section = 'components';
|
|
92
|
+
pageData.source = `${source}-demos`;
|
|
93
|
+
pageData.slug = makeSlug(pageData.source, pageData.section, pageData.id);
|
|
94
|
+
outPath = path.join(outputBase, `${pageData.slug}.js`);
|
|
95
|
+
}
|
|
96
|
+
if (frontmatter.title) {
|
|
97
|
+
pageData.title = frontmatter.title;
|
|
98
|
+
}
|
|
99
|
+
if (propComponents.length > 0) {
|
|
100
|
+
pageData.propComponents = propComponents;
|
|
101
|
+
}
|
|
102
|
+
if (frontmatter.optIn) {
|
|
103
|
+
pageData.optIn = frontmatter.optIn;
|
|
104
|
+
}
|
|
105
|
+
if (frontmatter.beta) {
|
|
106
|
+
pageData.beta = frontmatter.beta;
|
|
107
|
+
}
|
|
108
|
+
if (frontmatter.cssPrefix) {
|
|
109
|
+
pageData.cssPrefix = Array.isArray(frontmatter.cssPrefix)
|
|
110
|
+
? frontmatter.cssPrefix
|
|
111
|
+
: [frontmatter.cssPrefix];
|
|
112
|
+
}
|
|
113
|
+
if (frontmatter.katacodaBroken) {
|
|
114
|
+
pageData.katacodaBroken = frontmatter.katacodaBroken;
|
|
115
|
+
}
|
|
116
|
+
if (frontmatter.katacodaLayout) {
|
|
117
|
+
pageData.katacodaLayout = frontmatter.katacodaLayout;
|
|
118
|
+
}
|
|
119
|
+
if (frontmatter.hideNavItem) {
|
|
120
|
+
pageData.hideNavItem = frontmatter.hideNavItem;
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
// Delete HTML comments
|
|
124
|
+
.use(require('./remove-comments'))
|
|
125
|
+
// remark-mdx removes auto-link support
|
|
126
|
+
// this adds it back ONLY for links which are easily differentiable from JSX
|
|
127
|
+
.use(require('./auto-link-url'))
|
|
128
|
+
// Support for JSX in MD
|
|
129
|
+
.use(require('remark-mdx'))
|
|
130
|
+
// remark-mdx leaves paragraphs as normal MD, but inside MDX we expect it not to.
|
|
131
|
+
.use(() => tree => {
|
|
132
|
+
visit(tree, 'mdxBlockElement', node => {
|
|
133
|
+
if (node.children[0] && node.children[0].type === 'paragraph') {
|
|
134
|
+
const newChildren = node.children[0].children;
|
|
135
|
+
node.children.shift();
|
|
136
|
+
node.children = newChildren.concat(node.children);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
})
|
|
140
|
+
// Support for import/exports in MD
|
|
141
|
+
.use(require('remark-mdxjs'))
|
|
142
|
+
// Insert footnotes
|
|
143
|
+
.use(require('remark-footnotes'))
|
|
144
|
+
// Remove whitespace
|
|
145
|
+
.use(require('remark-squeeze-paragraphs'))
|
|
146
|
+
// Support example captions
|
|
147
|
+
.use(require('./example-captions'))
|
|
148
|
+
// .use(require('remark-rehype'))
|
|
149
|
+
// .use(require('rehype-react'), { createElement: require('react').createElement })
|
|
150
|
+
// Transform AST to JSX elements. Includes special code block parsing
|
|
151
|
+
.use(require('./mdx-ast-to-mdx-hast'), {
|
|
152
|
+
watchExternal(file) {
|
|
153
|
+
if (buildMode === 'start') {
|
|
154
|
+
const watcher = chokidar.watch(file, { ignoreInitial: true });
|
|
155
|
+
watcher.on('change', () => {
|
|
156
|
+
sourceMDFile(mdFilePath, source, buildMode);
|
|
157
|
+
writeIndex();
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
// Don't allow exports
|
|
163
|
+
.use(() => tree => remove(tree, 'export'))
|
|
164
|
+
// Comments aren't very useful in generated files no one wants to look at
|
|
165
|
+
.use(() => tree => remove(tree, 'comment'))
|
|
166
|
+
// Extract examples to create fullscreen page routes
|
|
167
|
+
// Needs to be run after mdx-ast-to-mdx-hast which parses meta properties
|
|
168
|
+
.use(() => (tree, file) => {
|
|
169
|
+
const isExample = node =>
|
|
170
|
+
node.type === 'element'
|
|
171
|
+
&& node.tagName === 'Example'
|
|
172
|
+
&& liveCodeTypes.includes(node.properties.lang)
|
|
173
|
+
&& !node.properties.noLive;
|
|
174
|
+
visit(tree, isExample, node => {
|
|
175
|
+
if (node.properties.isFullscreen) {
|
|
176
|
+
pageData.fullscreenExamples = pageData.fullscreenExamples || [];
|
|
177
|
+
pageData.fullscreenExamples.push(node.title);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
pageData.examples = pageData.examples || [];
|
|
181
|
+
pageData.examples.push(node.title);
|
|
182
|
+
}
|
|
183
|
+
// Typecheck TS examples
|
|
184
|
+
if (node.properties.lang === 'ts') {
|
|
185
|
+
const typerrors = typecheck(
|
|
186
|
+
path.join(pageData.id, node.title + '.tsx'), // Needs to be unique per-example
|
|
187
|
+
node.properties.code
|
|
188
|
+
);
|
|
189
|
+
typerrors.forEach(({ line, character, message }) => {
|
|
190
|
+
line = node.position.start.line + line + 1;
|
|
191
|
+
const column = character;
|
|
192
|
+
if (buildMode === 'start') {
|
|
193
|
+
// Don't fail to start over types
|
|
194
|
+
file.message(`\u001b[31m THIS WILL FAIL THE BUILD\u001b[0m\n ${message}`, { line, column });
|
|
195
|
+
} else {
|
|
196
|
+
console.log('\u001b[31m');
|
|
197
|
+
file.fail(`\n ${message}\n`, { line, column });
|
|
198
|
+
console.log('\u001b[0m');
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
})
|
|
204
|
+
// Add custom PatternFly doc design things
|
|
205
|
+
.use(require('./anchor-header'), toc => {
|
|
206
|
+
if (!pageData.hideTOC && toc.length > 0) {
|
|
207
|
+
pageData.toc = toc;
|
|
208
|
+
}
|
|
209
|
+
delete pageData.hideTOC;
|
|
210
|
+
})
|
|
211
|
+
.use(require('./styled-tags'))
|
|
212
|
+
// Transform HAST object to JSX string
|
|
213
|
+
.use(require('./mdx-hast-to-jsx'), {
|
|
214
|
+
getOutPath: () => outPath,
|
|
215
|
+
getRelPath: () => path.relative(path.dirname(outPath), vfile.dirname).split(path.sep).join(path.posix.sep), // for imports
|
|
216
|
+
getPageData: () => pageData // For @reach/router routing
|
|
217
|
+
})
|
|
218
|
+
.process(vfile, (err, file) => {
|
|
219
|
+
if (err) {
|
|
220
|
+
console.error(vfileReport(err));
|
|
221
|
+
exitCode = 2;
|
|
222
|
+
} else {
|
|
223
|
+
// console.log(relPath, '->', path.relative(process.cwd(), outPath));
|
|
224
|
+
if (file.messages.length > 0) {
|
|
225
|
+
console.log(vfileReport(file));
|
|
226
|
+
}
|
|
227
|
+
jsx = file.contents;
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
jsx,
|
|
233
|
+
pageData,
|
|
234
|
+
outPath
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function sourcePropsFile(file) {
|
|
239
|
+
tsDocgen(file)
|
|
240
|
+
.filter(({ hide }) => !hide)
|
|
241
|
+
.forEach(({ name, description, props }) => {
|
|
242
|
+
tsDocs[name] = { name, description, props };
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function sourceMDFile(file, source, buildMode) {
|
|
247
|
+
if (path.basename(file).startsWith('_')) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const { jsx, pageData, outPath } = toReactComponent(file, source, buildMode);
|
|
251
|
+
|
|
252
|
+
if (jsx) {
|
|
253
|
+
fs.outputFileSync(outPath, jsx);
|
|
254
|
+
routes[pageData.slug] = {
|
|
255
|
+
id: pageData.id,
|
|
256
|
+
title: pageData.title || pageData.id,
|
|
257
|
+
toc: pageData.toc || [],
|
|
258
|
+
...(pageData.examples && { examples: pageData.examples }),
|
|
259
|
+
...(pageData.fullscreenExamples && { fullscreenExamples: pageData.fullscreenExamples }),
|
|
260
|
+
section: pageData.section,
|
|
261
|
+
source: pageData.source,
|
|
262
|
+
...(pageData.katacodaLayout && { katacodaLayout: pageData.katacodaLayout }),
|
|
263
|
+
...(pageData.hideNavItem && { hideNavItem: pageData.hideNavItem })
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function writeIndex() {
|
|
269
|
+
const stringifyRoute = ([route, pageData]) => `'${route}': {\n ${Object.entries(pageData)
|
|
270
|
+
.map(([key, val]) => `${key}: ${JSON.stringify(val)}`)
|
|
271
|
+
.concat(`Component: () => import(/* webpackChunkName: "${route.substr(1)}/index" */ '.${route}')`)
|
|
272
|
+
.join(',\n ')}\n }`;
|
|
273
|
+
|
|
274
|
+
const indexContent = `module.exports = {\n ${Object.entries(routes)
|
|
275
|
+
.map(stringifyRoute)
|
|
276
|
+
.join(',\n ')}\n};`;
|
|
277
|
+
fs.outputFileSync(path.join(outputBase, 'index.js'), indexContent);
|
|
278
|
+
|
|
279
|
+
return exitCode;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
module.exports = {
|
|
283
|
+
sourceProps(glob, ignore) {
|
|
284
|
+
globs.props.push({ glob, ignore });
|
|
285
|
+
sync(glob, { ignore }).forEach(sourcePropsFile);
|
|
286
|
+
},
|
|
287
|
+
sourceMD(glob, source, ignore, buildMode) {
|
|
288
|
+
globs.md.push({ glob, source, ignore });
|
|
289
|
+
sync(glob, { ignore }).forEach(file => sourceMDFile(file, source, buildMode));
|
|
290
|
+
},
|
|
291
|
+
writeIndex,
|
|
292
|
+
watchMD() {
|
|
293
|
+
globs.props.forEach(({ glob, ignore }) => {
|
|
294
|
+
const propWatcher = chokidar.watch(glob, { ignored: ignore, ignoreInitial: true });
|
|
295
|
+
propWatcher.on('add', sourcePropsFile);
|
|
296
|
+
propWatcher.on('change', sourcePropsFile);
|
|
297
|
+
});
|
|
298
|
+
globs.md.forEach(({ glob, source, ignore }) => {
|
|
299
|
+
const mdWatcher = chokidar.watch(glob, { ignored: ignore, ignoreInitial: true });
|
|
300
|
+
function onMDFileChange(file) {
|
|
301
|
+
sourceMDFile(file, source, 'start');
|
|
302
|
+
writeIndex();
|
|
303
|
+
}
|
|
304
|
+
mdWatcher.on('add', onMDFileChange);
|
|
305
|
+
mdWatcher.on('change', onMDFileChange);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// https://github.com/remarkjs/remark/tree/main/packages/remark-parse#processoruseparse-options
|
|
2
|
+
|
|
3
|
+
function plugin({ beginMarker = '<!--', endMarker = '-->' } = {}) {
|
|
4
|
+
const Parser = this.Parser
|
|
5
|
+
const tokenizers = Parser.prototype.blockTokenizers
|
|
6
|
+
const methods = Parser.prototype.blockMethods
|
|
7
|
+
|
|
8
|
+
tokenizers.comments = function tokenizeComment(eat, value) {
|
|
9
|
+
const trimmed = value.trimRight();
|
|
10
|
+
const endIndex = trimmed.indexOf(endMarker);
|
|
11
|
+
if (trimmed.startsWith(beginMarker) && endIndex >= 1) {
|
|
12
|
+
eat(value.substr(0, endIndex + endMarker.length));
|
|
13
|
+
}
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Run it just before `html`.
|
|
18
|
+
methods.splice(methods.indexOf('html'), 0, 'comments')
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports = plugin;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
const visit = require('unist-util-visit');
|
|
2
|
+
|
|
3
|
+
const styledMdTags = [
|
|
4
|
+
'p',
|
|
5
|
+
'ul',
|
|
6
|
+
'ul',
|
|
7
|
+
'ul',
|
|
8
|
+
'ol',
|
|
9
|
+
'li',
|
|
10
|
+
'dl',
|
|
11
|
+
'blockquote',
|
|
12
|
+
'small',
|
|
13
|
+
'hr',
|
|
14
|
+
'dt',
|
|
15
|
+
'code',
|
|
16
|
+
'table',
|
|
17
|
+
'img'
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
function styledTags() {
|
|
21
|
+
return tree => {
|
|
22
|
+
visit(tree, 'element', node => {
|
|
23
|
+
if (styledMdTags.includes(node.tagName)) {
|
|
24
|
+
node.properties.className = node.properties.className || '';
|
|
25
|
+
node.properties.className += node.properties.className ? ' ' : '';
|
|
26
|
+
node.properties.className += `ws-${node.tagName}`;
|
|
27
|
+
// Match pf-c-table implementation
|
|
28
|
+
// https://pf4.patternfly.org/components/table/html/basic-table/
|
|
29
|
+
if (node.tagName === 'table') {
|
|
30
|
+
node.properties.className += ' pf-c-table pf-m-grid-lg pf-m-compact';
|
|
31
|
+
node.properties.role = 'grid';
|
|
32
|
+
let columnHeaders = [];
|
|
33
|
+
for (let child of node.children) {
|
|
34
|
+
if (child.tagName === 'thead') {
|
|
35
|
+
// Find column headers
|
|
36
|
+
const tr = child.children.find(child => child.tagName === 'tr');
|
|
37
|
+
tr.properties.role = 'row';
|
|
38
|
+
tr.children
|
|
39
|
+
.filter(child => child.tagName === 'th')
|
|
40
|
+
.forEach(th => {
|
|
41
|
+
th.properties.className = th.properties.className || '';
|
|
42
|
+
th.properties.className += ' pf-m-wrap';
|
|
43
|
+
th.properties.role = 'columnheader';
|
|
44
|
+
th.properties.scope = 'col';
|
|
45
|
+
let colName = '';
|
|
46
|
+
visit(th, 'text', node2 => colName += node2.value);
|
|
47
|
+
columnHeaders.push(colName);
|
|
48
|
+
// Need to wrap in div for css-grid stuffs
|
|
49
|
+
th.children = [{
|
|
50
|
+
type: 'element',
|
|
51
|
+
tagName: 'div',
|
|
52
|
+
properties: {},
|
|
53
|
+
children: th.children
|
|
54
|
+
}];
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
else if (child.tagName === 'tbody') {
|
|
58
|
+
child.properties.role = 'rowgroup';
|
|
59
|
+
child.children
|
|
60
|
+
.filter(tr => tr.tagName === 'tr')
|
|
61
|
+
.forEach(tr => {
|
|
62
|
+
tr.properties.role = 'row';
|
|
63
|
+
tr.children
|
|
64
|
+
.filter(td => td.tagName === 'td')
|
|
65
|
+
.forEach((td, i) => {
|
|
66
|
+
td.properties.role = 'cell';
|
|
67
|
+
if (columnHeaders[i]) {
|
|
68
|
+
td.properties['data-label'] = columnHeaders[i];
|
|
69
|
+
}
|
|
70
|
+
// Need to wrap in div for css-grid stuffs
|
|
71
|
+
td.children = [{
|
|
72
|
+
type: 'element',
|
|
73
|
+
tagName: 'div',
|
|
74
|
+
properties: {},
|
|
75
|
+
children: td.children
|
|
76
|
+
}];
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = styledTags;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const glob = require('glob');
|
|
3
|
+
const ts = require('typescript');
|
|
4
|
+
const versions = require('../../versions.json');
|
|
5
|
+
|
|
6
|
+
// matches webpack.base.config.js
|
|
7
|
+
const defaultDeclarations = `
|
|
8
|
+
declare module '\\*.png' {
|
|
9
|
+
const content: string;
|
|
10
|
+
export default content;
|
|
11
|
+
}
|
|
12
|
+
declare module '\\*.jpg' {
|
|
13
|
+
const content: string;
|
|
14
|
+
export default content;
|
|
15
|
+
}
|
|
16
|
+
declare module '\\*.jpeg' {
|
|
17
|
+
const content: string;
|
|
18
|
+
export default content;
|
|
19
|
+
}
|
|
20
|
+
declare module '\\*.webp' {
|
|
21
|
+
const content: string;
|
|
22
|
+
export default content;
|
|
23
|
+
}
|
|
24
|
+
declare module '\\*.gif' {
|
|
25
|
+
const content: string;
|
|
26
|
+
export default content;
|
|
27
|
+
}
|
|
28
|
+
declare module '\\*.svg' {
|
|
29
|
+
const content: string;
|
|
30
|
+
export default content;
|
|
31
|
+
}
|
|
32
|
+
`;
|
|
33
|
+
|
|
34
|
+
const reactStylesDir = path.join(require.resolve('@patternfly/react-styles/package.json'), '../');
|
|
35
|
+
const reactStyles = glob.sync(path.join(reactStylesDir, 'css/**/*.d.ts'))
|
|
36
|
+
.map(f => f.replace(reactStylesDir, '@patternfly/react-styles/').replace(/\.d.ts$/, ''));
|
|
37
|
+
const files = {
|
|
38
|
+
'imports.ts': ['react', '@reach/router']
|
|
39
|
+
.concat(Object.keys(versions.Releases[0].versions))
|
|
40
|
+
.concat(reactStyles)
|
|
41
|
+
.filter(p => p !== '@patternfly/patternfly')
|
|
42
|
+
.map(p => `import '${p}';`)
|
|
43
|
+
.join('\n'),
|
|
44
|
+
'declarations.d.ts': defaultDeclarations
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const compilerOptions = {
|
|
48
|
+
target: 'ES2017',
|
|
49
|
+
noEmit: true,
|
|
50
|
+
skipLibCheck: true,
|
|
51
|
+
jsx: 'react',
|
|
52
|
+
esModuleInterop: true
|
|
53
|
+
};
|
|
54
|
+
const pfPackageRegex = /@patternfly\/([^\/]+)\/dist\/.*/;
|
|
55
|
+
|
|
56
|
+
const compilerHost = ts.createCompilerHost(compilerOptions);
|
|
57
|
+
compilerHost.readFile = fname => {
|
|
58
|
+
if (files[fname]) {
|
|
59
|
+
// console.log('readFile', fname, files[fname].text);
|
|
60
|
+
return files[fname];
|
|
61
|
+
}
|
|
62
|
+
return ts.sys.readFile(fname);
|
|
63
|
+
};
|
|
64
|
+
compilerHost.useCaseSensitiveFileNames = () => false; // saves 200ms on startup
|
|
65
|
+
|
|
66
|
+
const program = ts.createProgram(
|
|
67
|
+
Object.keys(files),
|
|
68
|
+
compilerOptions,
|
|
69
|
+
compilerHost
|
|
70
|
+
);
|
|
71
|
+
const defaultImportsFile = program.getSourceFile('imports.ts');
|
|
72
|
+
const typechecker = ts.createTypeChecker(program, true);
|
|
73
|
+
|
|
74
|
+
let fileCounter = 0;
|
|
75
|
+
function typecheck(fname, text) {
|
|
76
|
+
fname += fileCounter++; // Needs a new fname to bust diagnostics cache per-file :(
|
|
77
|
+
files[fname] = text;
|
|
78
|
+
const sourceFile = ts.createSourceFile(
|
|
79
|
+
fname,
|
|
80
|
+
text,
|
|
81
|
+
ts.ScriptTarget.ES2017,
|
|
82
|
+
true,
|
|
83
|
+
ts.ScriptKind.TSX
|
|
84
|
+
);
|
|
85
|
+
sourceFile.resolvedModules = defaultImportsFile.resolvedModules;
|
|
86
|
+
// Fix @patternfly/react-* imports so types resolve
|
|
87
|
+
sourceFile.statements
|
|
88
|
+
.filter(s => s.moduleSpecifier && s.moduleSpecifier.text.match(pfPackageRegex))
|
|
89
|
+
.forEach(s => {
|
|
90
|
+
s.moduleSpecifier.text = s.moduleSpecifier.text.replace(pfPackageRegex, (_, m) => `@patternfly/${m}`);
|
|
91
|
+
// Copied from a test file with `import { CogIcon } from '@patternfly/react-icons';`
|
|
92
|
+
if (!s.importClause.namedBindings) {
|
|
93
|
+
s.importClause.namedBindings = {
|
|
94
|
+
flags: 0,
|
|
95
|
+
modifierFlagsCache: 0,
|
|
96
|
+
kind: 265,
|
|
97
|
+
transformFlags: 0,
|
|
98
|
+
elements: [{
|
|
99
|
+
flags: 0,
|
|
100
|
+
modifierFlagsCache: 0,
|
|
101
|
+
kind: 266,
|
|
102
|
+
transformFlags: 0,
|
|
103
|
+
propertyName: undefined,
|
|
104
|
+
name: s.importClause.name
|
|
105
|
+
}]
|
|
106
|
+
};
|
|
107
|
+
s.importClause.name = undefined;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
ts.bindSourceFile(sourceFile, compilerOptions);
|
|
111
|
+
|
|
112
|
+
const allDiagnostics = [...typechecker.getDiagnostics(sourceFile)];
|
|
113
|
+
return allDiagnostics.map(diagnostic => {
|
|
114
|
+
if (diagnostic.file) {
|
|
115
|
+
let { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start);
|
|
116
|
+
let message = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
|
117
|
+
return {
|
|
118
|
+
line,
|
|
119
|
+
character: character + 1,
|
|
120
|
+
message
|
|
121
|
+
};
|
|
122
|
+
} else {
|
|
123
|
+
return {
|
|
124
|
+
message: ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = {
|
|
131
|
+
typecheck
|
|
132
|
+
};
|
|
133
|
+
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const reactDocgen = require('react-docgen');
|
|
3
|
+
const ts = require('typescript');
|
|
4
|
+
|
|
5
|
+
const annotations = [
|
|
6
|
+
{
|
|
7
|
+
regex: /@deprecated/,
|
|
8
|
+
name: 'deprecated',
|
|
9
|
+
type: 'Boolean'
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
regex: /@hide/,
|
|
13
|
+
name: 'hide',
|
|
14
|
+
type: 'Boolean'
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
regex: /@beta/,
|
|
18
|
+
name: 'beta',
|
|
19
|
+
type: 'Boolean'
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
regex: /@propType\s+(.*)/,
|
|
23
|
+
name: 'type',
|
|
24
|
+
type: 'String'
|
|
25
|
+
}
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
function addAnnotations(prop) {
|
|
29
|
+
if (prop.description) {
|
|
30
|
+
annotations.forEach(({ regex, name }) => {
|
|
31
|
+
const match = prop.description.match(regex);
|
|
32
|
+
if (match) {
|
|
33
|
+
prop.description = prop.description.replace(regex, '').trim();
|
|
34
|
+
if (name) {
|
|
35
|
+
prop[name] = match[2] || match[1] || true;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return prop;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getComponentMetadata(filename, sourceText) {
|
|
45
|
+
let parsedComponents = null;
|
|
46
|
+
try {
|
|
47
|
+
parsedComponents = reactDocgen.parse(
|
|
48
|
+
sourceText,
|
|
49
|
+
reactDocgen.resolver.findAllExportedComponentDefinitions,
|
|
50
|
+
null,
|
|
51
|
+
{ filename }
|
|
52
|
+
);
|
|
53
|
+
} catch (err) {
|
|
54
|
+
// eslint-disable-next-line no-console
|
|
55
|
+
// console.warn(`No component found in ${filename}:`, err);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return (parsedComponents || []).filter(parsed => parsed && parsed.displayName);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getInterfaceMetadata(filename, sourceText) {
|
|
62
|
+
const ast = ts.createSourceFile(
|
|
63
|
+
filename,
|
|
64
|
+
sourceText,
|
|
65
|
+
ts.ScriptTarget.Latest // languageVersion
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
function getText(node) {
|
|
69
|
+
if (!node || !node.pos || !node.end) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
return sourceText.substring(node.pos, node.end).trim();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const interfaces = [];
|
|
76
|
+
|
|
77
|
+
ast.statements
|
|
78
|
+
.filter(statement => statement.kind === ts.SyntaxKind.InterfaceDeclaration)
|
|
79
|
+
.forEach(statement => {
|
|
80
|
+
const props = statement.members.reduce((acc, member) => {
|
|
81
|
+
const name = (member.name && member.name.escapedText)
|
|
82
|
+
|| (member.parameters && `[${getText(member.parameters[0])}]`)
|
|
83
|
+
|| 'Unknown';
|
|
84
|
+
acc[name] = {
|
|
85
|
+
description: member.jsDoc
|
|
86
|
+
? member.jsDoc.map(doc => doc.comment).join('\n')
|
|
87
|
+
: null,
|
|
88
|
+
required: member.questionToken === undefined,
|
|
89
|
+
type: {
|
|
90
|
+
raw: getText(member.type).trim()
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
return acc;
|
|
94
|
+
}, {});
|
|
95
|
+
|
|
96
|
+
interfaces.push({
|
|
97
|
+
displayName: statement.name.escapedText,
|
|
98
|
+
props
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
return interfaces;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function normalizeProp([
|
|
105
|
+
name,
|
|
106
|
+
{
|
|
107
|
+
required,
|
|
108
|
+
annotatedType,
|
|
109
|
+
type,
|
|
110
|
+
tsType,
|
|
111
|
+
description,
|
|
112
|
+
defaultValue
|
|
113
|
+
}
|
|
114
|
+
]) {
|
|
115
|
+
const res = {
|
|
116
|
+
name,
|
|
117
|
+
type: annotatedType
|
|
118
|
+
|| (type && type.name)
|
|
119
|
+
|| (type && (type.raw || type.name))
|
|
120
|
+
|| (tsType && (tsType.raw || tsType.name))
|
|
121
|
+
|| 'No type info',
|
|
122
|
+
description
|
|
123
|
+
};
|
|
124
|
+
if (required) {
|
|
125
|
+
res.required = true;
|
|
126
|
+
}
|
|
127
|
+
if (defaultValue && defaultValue.value) {
|
|
128
|
+
res.defaultValue = defaultValue.value;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return res;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function tsDocgen(file) {
|
|
135
|
+
const sourceText = fs.readFileSync(file, 'utf8');
|
|
136
|
+
const componentMeta = getComponentMetadata(file, sourceText); // Array of components with props
|
|
137
|
+
const interfaceMeta = getInterfaceMetadata(file, sourceText); // Array of interfaces with props
|
|
138
|
+
|
|
139
|
+
return componentMeta
|
|
140
|
+
.concat(interfaceMeta)
|
|
141
|
+
.map(parsed => ({
|
|
142
|
+
name: parsed.displayName,
|
|
143
|
+
description: parsed.description || '',
|
|
144
|
+
props: Object.entries(parsed.props || {})
|
|
145
|
+
.map(normalizeProp)
|
|
146
|
+
.map(addAnnotations)
|
|
147
|
+
.filter(prop => !prop.hide)
|
|
148
|
+
.sort((p1, p2) => p1.name.localeCompare(p2.name))
|
|
149
|
+
}));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
module.exports = {
|
|
153
|
+
tsDocgen
|
|
154
|
+
};
|
|
155
|
+
|