Skip to content

Commit

Permalink
feat: ability to show source for Playground
Browse files Browse the repository at this point in the history
  • Loading branch information
atanasster committed Nov 13, 2020
1 parent f3c3b3b commit 08cba79
Show file tree
Hide file tree
Showing 35 changed files with 601 additions and 365 deletions.
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@
"program": "${workspaceFolder}/node_modules/.bin/jest",
"cwd": "${workspaceFolder}/core/instrument",
"args": [
"mdx-async",
"mdx-stories",
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
Expand Down
22 changes: 22 additions & 0 deletions core/instrument/src/babel/extract-attributes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,25 @@ export const extractAttributes = (
}
return undefined;
};

export const collectAttributes = (node: any): Record<string, string> => {
return node.attributes.reduce(
(acc: Record<string, unknown>, attribute: any) => {
if (!attribute.value) {
//console.log(attribute);
} else if (attribute.value.type === 'StringLiteral') {
return {
...acc,
[attribute.name.name]: attribute.value.value,
};
} else if (attribute.value.type === 'JSXExpressionContainer') {
return {
...acc,
[attribute.name.name]: extractAttributes(attribute.value.expression),
};
}
return acc;
},
{},
);
};
46 changes: 0 additions & 46 deletions core/instrument/src/babel/extract-function-parameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,49 +131,3 @@ export const extractFunctionParameters = (
path.skip();
},
});

const adjustCodeLocation = (
loc: CodeLocation,
line: number,
chars: number,
): CodeLocation => {
return {
start: {
line: loc.start.line,
column:
line === loc.start.line
? Math.max(0, loc.start.column - chars)
: loc.start.column,
},
end: {
line: loc.end.line,
column:
line === loc.end.line
? Math.max(0, loc.end.column - chars)
: loc.end.column,
},
};
};

// adjust source location, when timmiong white spaces
export const adjustArgumentsLocation = (
args: StoryArguments,
line: number,
chars: number,
): StoryArguments =>
args.map(arg => {
const ret = {
...arg,
loc: arg.loc ? adjustCodeLocation(arg.loc, line, chars) : undefined,
value: Array.isArray(arg.value)
? adjustArgumentsLocation(arg.value, line, chars)
: arg.value,
};
if (arg.usage) {
ret.usage = arg.usage.map(usage => ({
...usage,
loc: adjustCodeLocation(usage.loc, line, chars),
}));
}
return ret;
});
66 changes: 4 additions & 62 deletions core/instrument/src/babel/mdx-stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@ import {
import camelCase from 'camelcase';
import { File } from '@babel/types';
import traverse from '@babel/traverse';
import {
extractFunctionParameters,
adjustArgumentsLocation,
} from './extract-function-parameters';
import { extractFunctionParameters } from './extract-function-parameters';
import { followStoryImport } from './follow-imports';
import { extractAttributes } from './extract-attributes';
import { collectAttributes } from './extract-attributes';
import { sourceLocation } from '../misc/source-location';
import { ParseStorieReturnType, InstrumentOptions } from '../types';

import { extractSource } from '../misc/source-extract';
import { componentsFromParams } from '../misc/component-attributes';

type PartialStore = Required<
Expand All @@ -40,30 +37,6 @@ export const extractMDXStories: (
_options: Required<InstrumentOptions>,
{ source, filePath }: SourceFile,
): PartialStore | undefined => {
const collectAttributes = (node: any): Record<string, string> => {
return node.attributes.reduce(
(acc: Record<string, unknown>, attribute: any) => {
if (!attribute.value) {
//console.log(attribute);
} else if (attribute.value.type === 'StringLiteral') {
return {
...acc,
[attribute.name.name]: attribute.value.value,
};
} else if (attribute.value.type === 'JSXExpressionContainer') {
return {
...acc,
[attribute.name.name]: extractAttributes(
attribute.value.expression,
),
};
}
return acc;
},
{},
);
};

let components: { [key: string]: string | undefined } = {};
const collectComponent = (attributes: any) => {
const attrComponents = componentsFromParams(attributes);
Expand Down Expand Up @@ -186,38 +159,8 @@ export const extractMDXStories: (
);
// adjust for source code wraping issues
const storySource = getASTSource(source, loc);
const storyLines = storySource?.split('\n');
//remove as many spaces as there are in the line with the smallest amount of white spaces (but still some)
const whiteSpaces = storyLines?.reduce((spaces, line) => {
if (line.substring(0, 1) === ' ' && line.trim() !== '') {
const startSpaces = line.search(/\S/);
if (startSpaces && (spaces === 0 || startSpaces < spaces)) {
return startSpaces;
}
}
return spaces;
}, 0);
story.loc = sourceLocation(loc);
story.source =
storyLines && whiteSpaces
? storyLines
.map((line, lineIndex) => {
if (line.search(/\S/) >= whiteSpaces) {
//adjust location of arguments usage
if (story.arguments) {
story.arguments = adjustArgumentsLocation(
story.arguments,
lineIndex,
whiteSpaces,
);
}
return line.substr(whiteSpaces);
}
return line;
})
.join('\n')
.trim()
: storySource;
story.source = extractSource(storySource, story);
}

store.stories[id] = story;
Expand Down Expand Up @@ -254,7 +197,6 @@ export const extractMDXStories: (
collectComponent(attributes);
break;
}
// console.log(node.name.name, attributes);
}
},
});
Expand Down
57 changes: 55 additions & 2 deletions core/instrument/src/babel/transform-ast-tree.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,62 @@
import { TraverseOptions } from '@babel/traverse';

const babel = require('@babel/core');
import { CodeLocation, getASTSource } from '@component-controls/core';
import { extractSource } from '../misc/source-extract';
import { collectAttributes } from './extract-attributes';

export const transformTree = (): TraverseOptions => {
export const transformMDXAttributes = (source: string): TraverseOptions => {
return {
JSXElement: (path: any) => {
const node = path.node.openingElement;
switch (node.name.name) {
case 'Playground':
const playground = collectAttributes(node);
// extract playground source of not already a prop
if (!playground.source) {
const hasStory = path.node.children.some(
(el: any) =>
el.type === 'JSXElement' &&
el.openingElement?.name?.name === 'Story',
);
if (!hasStory) {
const loc: CodeLocation = path.node.children.length
? {
start: path.node.children[0].loc.start,
end:
path.node.children[path.node.children.length - 1].loc.end,
}
: path.node.loc;
const rawSource = getASTSource(source, loc);
const prettiedSource = extractSource(rawSource);
node.attributes.push({
type: 'JSXAttribute',
name: {
type: 'JSXIdentifier',
name: 'source',
},
value: {
type: 'JSXExpressionContainer',
expression: {
type: 'TemplateLiteral',
expressions: [],
quasis: [
{
type: 'TemplateElement',
value: {
raw: prettiedSource,
cooked: prettiedSource,
},
tail: true,
},
],
},
},
});
}
}
break;
}
},
JSXAttribute: (path: any) => {
const node = path.node;
switch (node.name?.name) {
Expand Down
22 changes: 13 additions & 9 deletions core/instrument/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {

import { extractCSFStories } from './babel/esm-stories';
import { extractMDXStories } from './babel/mdx-stories';
import { transformTree } from './babel/transform-ast-tree';
import { transformMDXAttributes } from './babel/transform-ast-tree';
import { extractStoreComponent } from './babel/extract-component';
import { packageInfo } from './misc/package-info';
import { extractStoryExports } from './misc/mdx-exports';
Expand Down Expand Up @@ -61,7 +61,7 @@ const parseSource = async (
filePath: string,
options: Required<InstrumentOptions>,
): Promise<ParseStorieReturnType | undefined> => {
const source = await prettify(code, filePath, options.prettier);
const source = await prettify(code, options.prettier, filePath);
const ast = parser.parse(source, options.parser);

const store = traverseFn(ast, options, { source, filePath });
Expand Down Expand Up @@ -158,16 +158,20 @@ export const parseStories = async (
} = mergedOptions.mdx;
if (test && filePath.match(test)) {
const { data, content } = matter(source);
const mdxParsed = await mdx(content, {
code = await mdx(content, {
filepath: filePath,
...otherMDXOptions,
});
const ast = parser.parse(mdxParsed, mergedOptions.parser) as any;
traverse(ast, transformTree());
({ code } = generate(ast, {
retainFunctionParens: true,
retainLines: true,
}));
const ast = parser.parse(code, mergedOptions.parser) as any;

if (transformMDX) {
//second pass transform - inject any necessary attributes
traverse(ast, transformMDXAttributes(code));
({ code } = generate(ast, {
retainFunctionParens: true,
retainLines: false,
}));
}
const store = await parseSource(
code,
extractMDXStories(data),
Expand Down
2 changes: 1 addition & 1 deletion core/instrument/src/misc/prettify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { PrettierOptions } from '../types';

export const prettify = async (
code: string,
filePath: string,
options: PrettierOptions | false,
filePath?: string,
): Promise<string> => {
if (options !== false) {
const { resolveConfigOptions, ...otherOptions } = options || {};
Expand Down
91 changes: 91 additions & 0 deletions core/instrument/src/misc/source-extract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { CodeLocation, StoryArguments, Story } from '@component-controls/core';

const adjustCodeLocation = (
loc: CodeLocation,
line: number,
chars: number,
): CodeLocation => {
return {
start: {
line: loc.start.line,
column:
line === loc.start.line
? Math.max(0, loc.start.column - chars)
: loc.start.column,
},
end: {
line: loc.end.line,
column:
line === loc.end.line
? Math.max(0, loc.end.column - chars)
: loc.end.column,
},
};
};

// adjust source location, when timmiong white spaces
export const adjustArgumentsLocation = (
args: StoryArguments,
line: number,
chars: number,
): StoryArguments =>
args.map(arg => {
const ret = {
...arg,
loc: arg.loc ? adjustCodeLocation(arg.loc, line, chars) : undefined,
value: Array.isArray(arg.value)
? adjustArgumentsLocation(arg.value, line, chars)
: arg.value,
};
if (arg.usage) {
ret.usage = arg.usage.map(usage => ({
...usage,
loc: adjustCodeLocation(usage.loc, line, chars),
}));
}
return ret;
});

export const removeMDXAttributes = (source: string): string =>
source.replace(/mdxType="[^"]*"\s?/, '');
export const extractSource = (
source?: string,
story?: Story,
): string | undefined => {
if (!source) {
return undefined;
}
const code = removeMDXAttributes(source);
const lines = code.split('\n');
//remove as many spaces as there are in the line with the smallest amount of white spaces (but still some)
const whiteSpacesList = lines
.map(line => {
if (line.substring(0, 1) === ' ' && line.trim() !== '') {
return line.search(/\S/);
}
return 999999;
})
.sort((a: number, b: number) => a - b);
const whiteSpaces = whiteSpacesList.length > 1 && whiteSpacesList[1];
return whiteSpaces
? lines
.map((line, lineIndex) => {
const spaces = line.search(/\S/);
const adjustSpaces = Math.min(spaces, whiteSpaces);
if (adjustSpaces > 0) {
//adjust location of arguments usage
if (story?.arguments) {
story.arguments = adjustArgumentsLocation(
story.arguments,
lineIndex,
adjustSpaces,
);
}
return line.substr(adjustSpaces);
}
return line;
})
.join('\n')
.trim()
: code;
};
Loading

0 comments on commit 08cba79

Please sign in to comment.