Skip to content

Commit

Permalink
feat: mdx story templates
Browse files Browse the repository at this point in the history
  • Loading branch information
atanasster committed Nov 17, 2020
1 parent cad0ca1 commit d5a1ccd
Show file tree
Hide file tree
Showing 33 changed files with 873 additions and 493 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": [
"esm-template",
"mdx-template",
],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
Expand Down
8 changes: 4 additions & 4 deletions core/instrument/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@
},
"license": "MIT",
"dependencies": {
"@babel/generator": "^7.9.4",
"@babel/parser": "^7.9.4",
"@babel/traverse": "^7.9.0",
"@babel/generator": "^7.12.5",
"@babel/parser": "^7.12.5",
"@babel/traverse": "^7.12.5",
"@component-controls/core": "^1.40.2",
"@hutson/parse-repository-url": "^5.0.0",
"@mdx-js/loader": "^1.5.5",
Expand All @@ -51,7 +51,7 @@
"typescript": "^4.0.5"
},
"devDependencies": {
"@babel/types": "^7.9.0",
"@babel/types": "^7.12.6",
"@component-controls/ts-markdown-docs": "^1.37.0",
"@rollup/plugin-node-resolve": "^7.1.1",
"@types/find-cache-dir": "^3.2.0",
Expand Down
136 changes: 33 additions & 103 deletions core/instrument/src/babel/esm-stories.ts
Original file line number Diff line number Diff line change
@@ -1,107 +1,48 @@
import { Story, Document, Stories } from '@component-controls/core';
import { File } from '@babel/types';
import traverse from '@babel/traverse';
import { extractFunctionParameters } from './extract-function-parameters';
import traverse, { NodePath } from '@babel/traverse';
import { extractAttributes } from './extract-attributes';
import { followStoryImport } from './follow-imports';
import { componentsFromParams } from '../misc/component-attributes';
import { sourceLocation } from '../misc/source-location';
import {
LoadingDocStore,
ParseStorieReturnType,
InstrumentOptions,
} from '../types';

import {
extractFunction as extractFunctionTemplate,
extractVarFunction,
} from './extract-function';
export const extractCSFStories = (
ast: File,
_options: InstrumentOptions,
{ source, filePath }: { source: string; filePath: string },
): ParseStorieReturnType => {
const globals: Stories = {};
const localFunctions: Stories = {};
const locals: Stories = {};

const extractFunction = (
path: any,
declaration: any,
name: string,
): Story | undefined => {
if (declaration.init) {
switch (declaration.init.type) {
case 'ArrowFunctionExpression': {
const el = declaration.init.body;
const story: Story = {
loc: sourceLocation(el.loc),
name,
id: name,
};
traverse(path.node, extractFunctionParameters(story), path.scope);
return story;
}
case 'Identifier': {
return followStoryImport(
name,
declaration.init.name,
filePath,
source,
_options,
ast,
);
}
case 'ObjectExpression': {
if (store.doc?.template) {
const template = store.doc.template;
const story: Story = {
name,
id: name,
loc: template.loc,
arguments: template.arguments,
};
return story;
}
break;
}
case 'CallExpression': {
//template.bind
if (declaration.init.callee?.property?.name === 'bind') {
const callee = declaration.init.callee?.object;
if (callee?.name) {
const template = localFunctions[callee.name];
if (template) {
const story: Story = {
loc: template.loc,
name,
id: name,
arguments: template.arguments,
};
return story;
}
}
}
}
}
} else if (declaration.type === 'FunctionDeclaration') {
const story: Story = {
loc: sourceLocation(declaration.body.loc),
name,
id: name,
};
traverse(path.node, extractFunctionParameters(story), path.scope);
return story;
}
return undefined;
};
let components: { [key: string]: string | undefined } = {};
const store: LoadingDocStore = {
stories: {},
doc: undefined,
components: {},
packages: {},
};
traverse(ast as any, {
const extractFunction = (path: NodePath, declaration: any, name: string) =>
extractFunctionTemplate(
ast,
_options,
{ source, filePath },
path,
declaration,
name,
store.doc?.template,
locals,
);
traverse(ast, {
ExportDefaultDeclaration: (path: any) => {
const { declaration } = path.node;
const attributes = extractAttributes(declaration);
const template = declaration?.expression?.properties.find(
const template = declaration.expression?.properties.find(
(prop: any) =>
prop.key?.name === 'template' &&
prop.value?.type === 'ArrowFunctionExpression',
Expand All @@ -128,7 +69,7 @@ export const extractCSFStories = (
}
if (template) {
doc.template = extractFunction(
path,
path as NodePath,
{ init: template.value },
template.key.name,
) as Document['template'];
Expand Down Expand Up @@ -171,35 +112,24 @@ export const extractCSFStories = (
}
},
VariableDeclaration: (path: any) => {
const { declarations } = path.node;

if (Array.isArray(declarations) && declarations.length > 0) {
const declaration = declarations[0];
if (declaration) {
const name = declaration.id.name;
//check if it was a named export
if (!store.stories[name]) {
const story = extractFunction(path, declaration, name);
if (story && story.name) {
localFunctions[story.name] = story;
}
}
}
const story = extractVarFunction(
ast,
_options,
{ source, filePath },
path,
store.doc?.template,
locals,
);
if (story && story.name) {
locals[story.name] = story;
}
},
ExportSpecifier: (path: any) => {
const { node } = path;
const localName = node.local.name;
const exportedName = node.exported.name;
const story = localFunctions[localName];
const story = locals[localName];
if (story) {
const global = globals[localName];
if (global) {
localFunctions[localName] = {
...story,
...global,
};
}
store.stories[exportedName] = story;
}
},
Expand All @@ -210,7 +140,7 @@ export const extractCSFStories = (
if (declarations) {
if (Array.isArray(declarations) && declarations.length > 0) {
const story = extractFunction(
path,
path as NodePath,
declarations[0],
declarations[0].id.name,
);
Expand All @@ -225,7 +155,7 @@ export const extractCSFStories = (
} else {
if (declaration.type === 'FunctionDeclaration') {
const story = extractFunction(
path,
path as NodePath,
declaration,
declaration.id.name,
);
Expand Down
50 changes: 42 additions & 8 deletions core/instrument/src/babel/extract-function-parameters.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
import traverse, { TraverseOptions } from '@babel/traverse';
import traverse, { TraverseOptions, NodePath } from '@babel/traverse';
import {
ArrowFunctionExpression,
CallExpression,
JSXElement,
FunctionDeclaration,
} from '@babel/types';
import generate from '@babel/generator';
import { Story, CodeLocation, StoryArguments } from '@component-controls/core';
import {
Story,
CodeLocation,
StoryArguments,
Stories,
} from '@component-controls/core';
import { adjustSourceLocation } from '../misc/source-location';
import {
extractArgumentsUsage,
Expand Down Expand Up @@ -106,18 +117,41 @@ const extractPatameters = (
export const extractFunctionParameters = (
story: Story,
exports?: MDXExportType,
locals?: Stories,
): TraverseOptions => ({
ArrowFunctionExpression: (path: any) => {
CallExpression: (path: NodePath<CallExpression>) => {
const methodName = (path.node.callee as { property: { name: string } })
.property.name;
if (methodName === 'bind' && locals) {
const name = (path.node.callee as { object: { name: string } }).object
.name;
if (locals[name]) {
const local = locals[name];
story.loc = local.loc;
story.arguments = local.arguments;
const { code } = generate(path.node, {
retainFunctionParens: true,
retainLines: true,
});
if (exports) {
exports.render = code.trim();
}
}
}
path.skip();
},
ArrowFunctionExpression: (path: NodePath<ArrowFunctionExpression>) => {
extractPatameters(path, story, exports);
path.skip();
},
FunctionDeclaration: (path: any) => {
FunctionDeclaration: (path: NodePath<FunctionDeclaration>) => {
extractPatameters(path, story, exports);
path.skip();
},
JSXElement: (path: any) => {
if (exports && path.parent.children) {
const children = path.parent.children
JSXElement: (path: NodePath<JSXElement>) => {
const children = (path.parent as { children: any }).children;
if (exports && children) {
const childStr = children
.map((child: any) => {
const { code } = generate(child, {
retainFunctionParens: true,
Expand All @@ -126,7 +160,7 @@ export const extractFunctionParameters = (
return code.trim();
})
.join('\n');
exports.render = `() => (<>\n${children}\n</>)`;
exports.render = `() => (<>\n${childStr}\n</>)`;
}
path.skip();
},
Expand Down
Loading

0 comments on commit d5a1ccd

Please sign in to comment.