Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ export const Example = ({
// Content that appears between h3 and code block to explain example
children,
// Show dark theme switcher on full page examples
hasDarkThemeSwitcher = process.env.hasDarkThemeSwitcher
hasDarkThemeSwitcher = process.env.hasDarkThemeSwitcher,
// Map of relative imports matched to their npm package import path (passed to Codesandbox)
relativeImports
}) => {
if (isFullscreenPreview) {
isFullscreen = false;
Expand All @@ -98,9 +100,9 @@ export const Example = ({

const [editorCode, setEditorCode] = React.useState(code);
const loc = useLocation();

const scope = {
...liveContext,
// These 2 are in the bundle anyways for the site since we dogfood
...reactCoreModule,
...reactTableModule,
...(source === 'react-next' ? reactCoreNextModule : {})
Expand Down Expand Up @@ -158,7 +160,7 @@ export const Example = ({
const codeBoxParams = getParameters(
lang === 'html'
? getStaticParams(title, editorCode)
: getReactParams(title, editorCode, scope, lang)
: getReactParams(title, editorCode, scope, lang, relativeImports)
);
const fullscreenLink = loc.pathname.replace(/\/$/, '')
+ (loc.pathname.endsWith(source) ? '' : `/${source}`)
Expand Down
15 changes: 13 additions & 2 deletions packages/documentation-framework/helpers/codesandbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const { parse } = require('@patternfly/ast-helpers');
const versions = require('../versions.json');
const overpass = require('./fonts');
const { capitalize } = require('./capitalize');
const pathPrefix = process.env.pathPrefix;

const getStaticParams = (title, html) => {
const imgAssetRegex = /['"](\/assets\/images\/.*)['"]/g;
Expand Down Expand Up @@ -106,7 +107,7 @@ function prettyExampleCode(title, code, declaration, identifier) {
}

// TODO: Make React examples work and use a template that has our assets.
function getReactParams(title, code, scope, lang) {
function getReactParams(title, code, scope, lang, relativeImports) {
let toRender = null;
try {
let declaration = getExampleDeclaration(code);
Expand All @@ -129,14 +130,24 @@ function getReactParams(title, code, scope, lang) {
catch (err) {
// Ignore
}

// Update image imports to point to pf.org
const imgImportRegex = /import\s*(\w*).*['"](.*)(\.(png|jpe?g|webp|gif|svg))['"]/g;
let imgImportMatch;
while ((imgImportMatch = imgImportRegex.exec(code))) {
const imgName = imgImportMatch[1];
code = code.replace(imgImportMatch[0], `const ${imgName} = "https://www.patternfly.org/v4${scope[imgName]}"`);
}

const relImportRegex = /(?<=import[\s*{])([\w*{}\n\r\t, ]+)(?=[\s*]from\s["']([\.\/]+.*)["'])/gm;
let relImportMatch;
while (relImportMatch = relImportRegex.exec(code)) {
const [ relImportName, _name, relImportPath ] = relImportMatch;
if (relativeImports[relImportName]) {
code = code.replace(relImportPath, relativeImports[relImportName]);
}
}


const dependencies = {
'@patternfly/react-core': versions.Releases[0].versions['@patternfly/react-core']
};
Expand Down
40 changes: 35 additions & 5 deletions packages/documentation-framework/scripts/md/mdx-hast-to-jsx.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,34 +69,64 @@ function serializeRoot(node, options) {

const importStatements = groups.import
.map(node => node.value)
.map(imp => imp.replace(/(['"])\./g, (_, match) => `${match}${getRelPath()}${path.posix.sep}\.`))
.map(imp => imp.replace(/(['"])\./g, (_, match) => `${match}${getRelPath()}${path.posix.sep}\.`));

// Map relative import name to '@package...'
const relativeImportsRegex = /(?:[\.\/]+.*)(@.*)['"]/gm;
let relativeImportMatch;
let relativeImportMatches = {};
while (relativeImportMatch = relativeImportsRegex.exec(importStatements[0])) {
const [_match, absoluteImportPath] = relativeImportMatch;
if (absoluteImportPath && !absoluteImportPath.includes('srcImport')) {
// `@patternfly/react-core/src/demos/./examples/DashboardWrapper` to `DashboardWrapper`
let relativeFileImport = /(?<=\/)(\.+\/.*)/gm.exec(absoluteImportPath);
if (relativeFileImport) {
// Build map of relative imports (from example.js code) to npm package import path (used in codesandbox.js)
const relativeFilePath = relativeFileImport[0];
const relativeImportName = relativeFilePath
.split('/')
.pop()
.split('.')
.shift();
relativeImportMatches[relativeImportName] = absoluteImportPath;
}
}
}

const importStatementsWithThumbnails = importStatements
.concat(thumbnailImports)
.join('\n')
.join('\n');

// https://astexplorer.net/#/gist/9c531dd372dfc57e194c13c2889d31c3/03f2d6e889db1a733c6a079554e8af7784863739
options.importSpecifiers = parse(importStatements).body
options.importSpecifiers = parse(importStatementsWithThumbnails).body
.map(node => node.specifiers)
.flat(1)
.map(spec => spec.local ? spec.local.name : null)
.filter(Boolean);
const liveContext = options.importSpecifiers
.filter(localName => !/srcImport.*/.test(localName)) // Images in MD like [!img](./src)
.join(',\n ');

const childNodes = groups.rest
.map(childNode => toJSX(childNode, node, options))
.join('');

let res = `import React from 'react';
import { AutoLinkHeader, Example, Link as PatternflyThemeLink } from '@patternfly/documentation-framework/components';
${importStatements}
${importStatementsWithThumbnails}
const pageData = ${JSON.stringify(pageData, null, 2)};
`;
if (liveContext) {
res += `pageData.liveContext = {\n${
' ' + liveContext
}\n};\n`
}
if (relativeImportMatches) {
res += `pageData.relativeImports = {\n${
' ' + Object.entries(relativeImportMatches)
.map(([key, val]) => `'${key}': '${val}'`)
.join(',\n ')
}\n};\n`;
}
if (examples) {
res += `pageData.examples = {\n${
' ' + Object.entries(examples)
Expand Down