diff --git a/__mocks__/inject-decorator.angular-stories.txt b/__mocks__/inject-decorator.angular-stories.txt
new file mode 100644
index 000000000000..5baa440d797b
--- /dev/null
+++ b/__mocks__/inject-decorator.angular-stories.txt
@@ -0,0 +1,15 @@
+import { Component } from '@angular/core';
+import { storiesOf } from '@storybook/angular';
+
+@Component({
+ selector: 'storybook-with-ng-content',
+ template: `
`,
+})
+class WithNgContentComponent {}
+
+storiesOf('Custom|ng-content', module).add('Default', () => ({
+ template: `This is rendered in ng-content `,
+ moduleMetadata: {
+ declarations: [WithNgContentComponent],
+ },
+}));
diff --git a/__mocks__/inject-decorator.no-stories.txt b/__mocks__/inject-decorator.no-stories.txt
new file mode 100644
index 000000000000..f0124e61691c
--- /dev/null
+++ b/__mocks__/inject-decorator.no-stories.txt
@@ -0,0 +1,3 @@
+while(true) {
+ console.log("it's a kind of magic");
+}
\ No newline at end of file
diff --git a/__mocks__/inject-decorator.stories.txt b/__mocks__/inject-decorator.stories.txt
new file mode 100644
index 000000000000..c4f0c4d53e5d
--- /dev/null
+++ b/__mocks__/inject-decorator.stories.txt
@@ -0,0 +1,153 @@
+import React from 'react';
+import { storiesOf } from '@storybook/react';
+import { withInfo } from '@storybook/addon-info';
+import { action } from '@storybook/addon-actions';
+
+import DocgenButton from '../components/DocgenButton';
+import FlowTypeButton from '../components/FlowTypeButton';
+import BaseButton from '../components/BaseButton';
+import TableComponent from '../components/TableComponent';
+
+storiesOf('Addons|Info.React Docgen', module)
+ .add(
+ 'Comments from PropType declarations',
+ withInfo(
+ 'Comments above the PropType declarations should be extracted from the React component file itself and rendered in the Info Addon prop table'
+ )(() => )
+ )
+ .add(
+ 'Comments from Flow declarations',
+ withInfo(
+ 'Comments above the Flow declarations should be extracted from the React component file itself and rendered in the Info Addon prop table'
+ )(() => )
+ )
+ .add(
+ 'Comments from component declaration',
+ withInfo(
+ 'Comments above the component declaration should be extracted from the React component file itself and rendered below the Info Addon heading'
+ )(() => )
+ );
+
+const markdownDescription = `
+#### You can use markdown in your withInfo() description.
+
+Sometimes you might want to manually include some code examples:
+~~~js
+const Button = () => ;
+~~~
+
+Maybe include a [link](http://storybook.js.org) to your project as well.
+`;
+
+storiesOf('Addons|Info.Markdown', module).add(
+ 'Displays Markdown in description',
+ withInfo(markdownDescription)(() => )
+);
+
+storiesOf('Addons|Info.Options.inline', module).add(
+ 'Inlines component inside story',
+ withInfo({
+ text: 'Component should be inlined between description and PropType table',
+ inline: true, // Displays info inline vs click button to view
+ })(() => )
+);
+
+storiesOf('Addons|Info.Options.header', module).add(
+ 'Shows or hides Info Addon header',
+ withInfo({
+ text: 'The Info Addon header should be hidden',
+ header: false, // Toggles display of header with component name and description
+ })(() => )
+);
+
+storiesOf('Addons|Info.Options.source', module).add(
+ 'Shows or hides Info Addon source',
+ withInfo({
+ text: 'The Info Addon source section should be hidden',
+ source: false, // Displays the source of story Component
+ })(() => )
+);
+
+storiesOf('Addons|Info.Options.propTables', module).add(
+ 'Shows additional component prop tables',
+ withInfo({
+ text: 'There should be a prop table added for a component not included in the story',
+ propTables: [FlowTypeButton],
+ })(() => )
+);
+
+storiesOf('Addons|Info.Options.propTablesExclude', module).add(
+ 'Exclude component from prop tables',
+ withInfo({
+ text: 'This can exclude extraneous components from being displayed in prop tables.',
+ propTablesExclude: [FlowTypeButton],
+ })(() => (
+
+
+
+
+ ))
+);
+
+storiesOf('Addons|Info.Options.styles', module)
+ .add(
+ 'Extend info styles with an object',
+ withInfo({
+ styles: {
+ button: {
+ base: {
+ background: 'purple',
+ },
+ },
+ header: {
+ h1: {
+ color: 'green',
+ },
+ },
+ },
+ })(() => )
+ )
+ .add(
+ 'Full control over styles using a function',
+ withInfo({
+ styles: stylesheet => ({
+ ...stylesheet,
+ header: {
+ ...stylesheet.header,
+ h1: {
+ ...stylesheet.header.h1,
+ color: 'red',
+ },
+ },
+ }),
+ })(() => )
+ );
+
+storiesOf('Addons|Info.Options.TableComponent', module).add(
+ 'Use a custom component for the table',
+ withInfo({
+ TableComponent,
+ })(() => )
+);
+
+storiesOf('Addons|Info.Decorator', module)
+ .addDecorator((story, context) =>
+ withInfo('Info could be used as a global or local decorator as well.')(story)(context)
+ )
+ .add('Use Info as story decorator', () => );
+
+const hoc = WrapComponent => ({ ...props }) => ;
+
+const Input = hoc(() => );
+
+const TextArea = hoc(({ children }) => );
+
+storiesOf('Addons|Info.GitHub issues', module).add(
+ '#1814',
+ withInfo('Allow Duplicate DisplayNames for HOC #1814')(() => (
+
+
+
+
+ ))
+);
diff --git a/addons/storysource/README.md b/addons/storysource/README.md
new file mode 100644
index 000000000000..f4daf4f1f074
--- /dev/null
+++ b/addons/storysource/README.md
@@ -0,0 +1,33 @@
+# Storybook Storysource Addon
+
+This addon is used to show stories source in the addon panel.
+
+## Getting Started
+
+First, install the addon
+
+```sh
+npm install -D @storybook/addon-storysource
+```
+
+Add this line to your `addons.js` file
+
+```js
+import '@storybook/addon-storysource/register';
+```
+
+Use this hook to a custom webpack.config. This will generate a decorator call in every story:
+
+```js
+module.exports = {
+ module: {
+ rules: [
+ {
+ test: /\.stories\.jsx?$/,
+ loaders: [require.resolve('@storybook/addon-storysource/loader')],
+ enforce: 'pre',
+ },
+ ],
+ },
+};
+```
\ No newline at end of file
diff --git a/addons/storysource/loader.js b/addons/storysource/loader.js
new file mode 100644
index 000000000000..6cc4e38b8d14
--- /dev/null
+++ b/addons/storysource/loader.js
@@ -0,0 +1 @@
+module.exports = require('./dist/loader');
diff --git a/addons/storysource/package.json b/addons/storysource/package.json
new file mode 100644
index 000000000000..a867b047996a
--- /dev/null
+++ b/addons/storysource/package.json
@@ -0,0 +1,37 @@
+{
+ "name": "@storybook/addon-storysource",
+ "version": "3.4.0-alpha.8",
+ "description": "Stories addon for storybook",
+ "keywords": [
+ "storybook"
+ ],
+ "homepage": "https://github.com/storybooks/storybook/tree/master/addons/storysource",
+ "bugs": {
+ "url": "https://github.com/storybooks/storybook/issues"
+ },
+ "license": "MIT",
+ "main": "dist/index.js",
+ "jsnext:main": "src/index.js",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/storybooks/storybook.git"
+ },
+ "scripts": {
+ "prepare": "node ../../scripts/prepare.js"
+ },
+ "dependencies": {
+ "acorn": "^5.3.0",
+ "acorn-es7": "^0.1.0",
+ "acorn-jsx": "^4.1.1",
+ "acorn-stage3": "^0.4.0",
+ "estraverse": "^4.2.0",
+ "line-column": "^1.0.2",
+ "prop-types": "^15.5.10",
+ "react-syntax-highlighter": "^7.0.0"
+ },
+ "peerDependencies": {
+ "@storybook/addons": "^3.3.0",
+ "react": "*",
+ "react-dom": "*"
+ }
+}
diff --git a/addons/storysource/register.js b/addons/storysource/register.js
new file mode 100644
index 000000000000..e69edbea3ed1
--- /dev/null
+++ b/addons/storysource/register.js
@@ -0,0 +1 @@
+require('./dist').register();
diff --git a/addons/storysource/src/StoryPanel.js b/addons/storysource/src/StoryPanel.js
new file mode 100644
index 000000000000..06c3bccfcb3a
--- /dev/null
+++ b/addons/storysource/src/StoryPanel.js
@@ -0,0 +1,84 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import jsx from 'react-syntax-highlighter/languages/prism/jsx';
+import { darcula } from 'react-syntax-highlighter/styles/prism';
+import SyntaxHighlighter, { registerLanguage } from 'react-syntax-highlighter/prism-light';
+import { createElement } from 'react-syntax-highlighter';
+import { EVENT_ID } from './';
+
+registerLanguage('jsx', jsx);
+
+export default class StoryPanel extends Component {
+ constructor(props) {
+ super(props);
+
+ this.state = { source: '// Here will be dragons 🐉' };
+
+ const { channel } = props;
+
+ channel.on(EVENT_ID, ({ source, location }) => {
+ this.setState({
+ source,
+ location,
+ });
+ });
+
+ this.lineRenderer = this.lineRenderer.bind(this);
+ }
+
+ createPart(rows, stylesheet, useInlineStyles) {
+ return rows.map((node, i) =>
+ createElement({
+ node,
+ stylesheet,
+ useInlineStyles,
+ key: `code-segement${i}`,
+ })
+ );
+ }
+
+ lineRenderer({ rows, stylesheet, useInlineStyles }) {
+ const { location } = this.state;
+
+ if (location) {
+ const first = location.startLoc.line - 1;
+ const last = location.endLoc.line;
+
+ const start = this.createPart(rows.slice(0, first), stylesheet, useInlineStyles);
+ const selected = this.createPart(rows.slice(first, last), stylesheet, useInlineStyles);
+ const end = this.createPart(rows.slice(last), stylesheet, useInlineStyles);
+
+ return (
+
+ {start}
+ {selected}
+ {end}
+
+ );
+ }
+
+ return this.createPart(rows, stylesheet, useInlineStyles);
+ }
+
+ render() {
+ return (
+
+ {this.state.source}
+
+ );
+ }
+}
+
+StoryPanel.propTypes = {
+ channel: PropTypes.shape({
+ emit: PropTypes.func,
+ on: PropTypes.func,
+ removeListener: PropTypes.func,
+ }).isRequired,
+};
diff --git a/addons/storysource/src/index.js b/addons/storysource/src/index.js
new file mode 100644
index 000000000000..d24b2a4f9853
--- /dev/null
+++ b/addons/storysource/src/index.js
@@ -0,0 +1,6 @@
+export const ADDON_ID = 'storybook/stories';
+export const PANEL_ID = `${ADDON_ID}/stories-panel`;
+export const EVENT_ID = `${ADDON_ID}/story-event`;
+
+export { register } from './manager';
+export { withStorySource } from './preview';
diff --git a/addons/storysource/src/loader/__snapshots__/inject-decorator.test.js.snap b/addons/storysource/src/loader/__snapshots__/inject-decorator.test.js.snap
new file mode 100644
index 000000000000..065b5599ec2c
--- /dev/null
+++ b/addons/storysource/src/loader/__snapshots__/inject-decorator.test.js.snap
@@ -0,0 +1,343 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`inject-decorator positive - angular calculates "adds" map 1`] = `
+Object {
+ "Custom|ng-content@Default": Object {
+ "endLoc": Object {
+ "col": 3,
+ "line": 15,
+ },
+ "startLoc": Object {
+ "col": 44,
+ "line": 10,
+ },
+ },
+}
+`;
+
+exports[`inject-decorator positive - angular injects stories decorator after the all "storiesOf" functions 1`] = `
+"import { Component } from '@angular/core';
+import { storiesOf } from '@storybook/angular';
+
+@Component({
+ selector: 'storybook-with-ng-content',
+ template: \`
\`,
+})
+class WithNgContentComponent {}
+
+storiesOf('Custom|ng-content', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add('Default', () => ({
+ template: \`This is rendered in ng-content \`,
+ moduleMetadata: {
+ declarations: [WithNgContentComponent],
+ },
+}));
+"
+`;
+
+exports[`inject-decorator positive calculates "adds" map 1`] = `
+Object {
+ "Addons|Info.Decorator@Use Info as story decorator": Object {
+ "endLoc": Object {
+ "col": 74,
+ "line": 137,
+ },
+ "startLoc": Object {
+ "col": 8,
+ "line": 137,
+ },
+ },
+ "Addons|Info.GitHub issues@#1814": Object {
+ "endLoc": Object {
+ "col": 5,
+ "line": 152,
+ },
+ "startLoc": Object {
+ "col": 3,
+ "line": 146,
+ },
+ },
+ "Addons|Info.Markdown@Displays Markdown in description": Object {
+ "endLoc": Object {
+ "col": 97,
+ "line": 44,
+ },
+ "startLoc": Object {
+ "col": 3,
+ "line": 43,
+ },
+ },
+ "Addons|Info.Options.TableComponent@Use a custom component for the table": Object {
+ "endLoc": Object {
+ "col": 42,
+ "line": 130,
+ },
+ "startLoc": Object {
+ "col": 3,
+ "line": 127,
+ },
+ },
+ "Addons|Info.Options.header@Shows or hides Info Addon header": Object {
+ "endLoc": Object {
+ "col": 42,
+ "line": 60,
+ },
+ "startLoc": Object {
+ "col": 3,
+ "line": 56,
+ },
+ },
+ "Addons|Info.Options.inline@Inlines component inside story": Object {
+ "endLoc": Object {
+ "col": 42,
+ "line": 52,
+ },
+ "startLoc": Object {
+ "col": 3,
+ "line": 48,
+ },
+ },
+ "Addons|Info.Options.propTables@Shows additional component prop tables": Object {
+ "endLoc": Object {
+ "col": 42,
+ "line": 76,
+ },
+ "startLoc": Object {
+ "col": 3,
+ "line": 72,
+ },
+ },
+ "Addons|Info.Options.propTablesExclude@Exclude component from prop tables": Object {
+ "endLoc": Object {
+ "col": 5,
+ "line": 89,
+ },
+ "startLoc": Object {
+ "col": 3,
+ "line": 80,
+ },
+ },
+ "Addons|Info.Options.source@Shows or hides Info Addon source": Object {
+ "endLoc": Object {
+ "col": 42,
+ "line": 68,
+ },
+ "startLoc": Object {
+ "col": 3,
+ "line": 64,
+ },
+ },
+ "Addons|Info.Options.styles@Extend info styles with an object": Object {
+ "endLoc": Object {
+ "col": 44,
+ "line": 108,
+ },
+ "startLoc": Object {
+ "col": 5,
+ "line": 94,
+ },
+ },
+ "Addons|Info.Options.styles@Full control over styles using a function": Object {
+ "endLoc": Object {
+ "col": 44,
+ "line": 123,
+ },
+ "startLoc": Object {
+ "col": 5,
+ "line": 111,
+ },
+ },
+ "Addons|Info.React Docgen@Comments from Flow declarations": Object {
+ "endLoc": Object {
+ "col": 86,
+ "line": 22,
+ },
+ "startLoc": Object {
+ "col": 5,
+ "line": 19,
+ },
+ },
+ "Addons|Info.React Docgen@Comments from PropType declarations": Object {
+ "endLoc": Object {
+ "col": 80,
+ "line": 16,
+ },
+ "startLoc": Object {
+ "col": 5,
+ "line": 13,
+ },
+ },
+ "Addons|Info.React Docgen@Comments from component declaration": Object {
+ "endLoc": Object {
+ "col": 71,
+ "line": 28,
+ },
+ "startLoc": Object {
+ "col": 5,
+ "line": 25,
+ },
+ },
+}
+`;
+
+exports[`inject-decorator positive injects stories decorator after the all "storiesOf" functions 1`] = `
+"import React from 'react';
+import { storiesOf } from '@storybook/react';
+import { withInfo } from '@storybook/addon-info';
+import { action } from '@storybook/addon-actions';
+
+import DocgenButton from '../components/DocgenButton';
+import FlowTypeButton from '../components/FlowTypeButton';
+import BaseButton from '../components/BaseButton';
+import TableComponent from '../components/TableComponent';
+
+storiesOf('Addons|Info.React Docgen', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__))
+ .add(
+ 'Comments from PropType declarations',
+ withInfo(
+ 'Comments above the PropType declarations should be extracted from the React component file itself and rendered in the Info Addon prop table'
+ )(() => )
+ )
+ .add(
+ 'Comments from Flow declarations',
+ withInfo(
+ 'Comments above the Flow declarations should be extracted from the React component file itself and rendered in the Info Addon prop table'
+ )(() => )
+ )
+ .add(
+ 'Comments from component declaration',
+ withInfo(
+ 'Comments above the component declaration should be extracted from the React component file itself and rendered below the Info Addon heading'
+ )(() => )
+ );
+
+const markdownDescription = \`
+#### You can use markdown in your withInfo() description.
+
+Sometimes you might want to manually include some code examples:
+~~~js
+const Button = () => ;
+~~~
+
+Maybe include a [link](http://storybook.js.org) to your project as well.
+\`;
+
+storiesOf('Addons|Info.Markdown', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add(
+ 'Displays Markdown in description',
+ withInfo(markdownDescription)(() => )
+);
+
+storiesOf('Addons|Info.Options.inline', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add(
+ 'Inlines component inside story',
+ withInfo({
+ text: 'Component should be inlined between description and PropType table',
+ inline: true, // Displays info inline vs click button to view
+ })(() => )
+);
+
+storiesOf('Addons|Info.Options.header', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add(
+ 'Shows or hides Info Addon header',
+ withInfo({
+ text: 'The Info Addon header should be hidden',
+ header: false, // Toggles display of header with component name and description
+ })(() => )
+);
+
+storiesOf('Addons|Info.Options.source', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add(
+ 'Shows or hides Info Addon source',
+ withInfo({
+ text: 'The Info Addon source section should be hidden',
+ source: false, // Displays the source of story Component
+ })(() => )
+);
+
+storiesOf('Addons|Info.Options.propTables', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add(
+ 'Shows additional component prop tables',
+ withInfo({
+ text: 'There should be a prop table added for a component not included in the story',
+ propTables: [FlowTypeButton],
+ })(() => )
+);
+
+storiesOf('Addons|Info.Options.propTablesExclude', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add(
+ 'Exclude component from prop tables',
+ withInfo({
+ text: 'This can exclude extraneous components from being displayed in prop tables.',
+ propTablesExclude: [FlowTypeButton],
+ })(() => (
+
+
+
+
+ ))
+);
+
+storiesOf('Addons|Info.Options.styles', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__))
+ .add(
+ 'Extend info styles with an object',
+ withInfo({
+ styles: {
+ button: {
+ base: {
+ background: 'purple',
+ },
+ },
+ header: {
+ h1: {
+ color: 'green',
+ },
+ },
+ },
+ })(() => )
+ )
+ .add(
+ 'Full control over styles using a function',
+ withInfo({
+ styles: stylesheet => ({
+ ...stylesheet,
+ header: {
+ ...stylesheet.header,
+ h1: {
+ ...stylesheet.header.h1,
+ color: 'red',
+ },
+ },
+ }),
+ })(() => )
+ );
+
+storiesOf('Addons|Info.Options.TableComponent', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add(
+ 'Use a custom component for the table',
+ withInfo({
+ TableComponent,
+ })(() => )
+);
+
+storiesOf('Addons|Info.Decorator', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__))
+ .addDecorator((story, context) =>
+ withInfo('Info could be used as a global or local decorator as well.')(story)(context)
+ )
+ .add('Use Info as story decorator', () => );
+
+const hoc = WrapComponent => ({ ...props }) => ;
+
+const Input = hoc(() => );
+
+const TextArea = hoc(({ children }) => );
+
+storiesOf('Addons|Info.GitHub issues', module).addDecorator(withStorySource(__STORY__, __ADDS_MAP__)).add(
+ '#1814',
+ withInfo('Allow Duplicate DisplayNames for HOC #1814')(() => (
+
+
+
+
+ ))
+);
+"
+`;
+
+exports[`inject-decorator will not change the source when there are no "storiesOf" functions 1`] = `
+"while(true) {
+ console.log(\\"it's a kind of magic\\");
+}"
+`;
diff --git a/addons/storysource/src/loader/index.js b/addons/storysource/src/loader/index.js
new file mode 100644
index 000000000000..cb7ac63d2810
--- /dev/null
+++ b/addons/storysource/src/loader/index.js
@@ -0,0 +1,27 @@
+import injectDecorator from './inject-decorator';
+
+const ADD_DECORATOR_STATEMENT = '.addDecorator(withStorySource(__STORY__, __ADDS_MAP__))';
+
+function transform(source) {
+ const result = injectDecorator(source, ADD_DECORATOR_STATEMENT);
+
+ if (!result.changed) {
+ return source;
+ }
+
+ const sourceJson = JSON.stringify(source)
+ .replace(/\u2028/g, '\\u2028')
+ .replace(/\u2029/g, '\\u2029');
+
+ const addsMap = JSON.stringify(result.addsMap);
+
+ return `
+ var withStorySource = require('@storybook/addon-storysource').withStorySource;
+ var __STORY__ = ${sourceJson};
+ var __ADDS_MAP__ = ${addsMap};
+
+ ${result.source}
+ `;
+}
+
+export default transform;
diff --git a/addons/storysource/src/loader/inject-decorator.js b/addons/storysource/src/loader/inject-decorator.js
new file mode 100644
index 000000000000..fe0d4774b45b
--- /dev/null
+++ b/addons/storysource/src/loader/inject-decorator.js
@@ -0,0 +1,73 @@
+import lineColumn from 'line-column';
+import { handleADD, handleSTORYOF } from './parse-helpers';
+
+const estraverse = require('estraverse');
+const acorn = require('acorn');
+
+require('acorn-stage3/inject')(acorn);
+require('acorn-jsx/inject')(acorn);
+require('acorn-es7')(acorn);
+
+const acornConfig = {
+ ecmaVersion: '6',
+ sourceType: 'module',
+ plugins: {
+ jsx: true,
+ stage3: true,
+ es7: true,
+ },
+};
+
+function calculateLocations(source, adds) {
+ const addsKeys = Object.keys(adds);
+
+ if (!addsKeys.length) {
+ return {};
+ }
+
+ const lineColumnFinder = lineColumn(source);
+
+ return addsKeys.reduce((map, key) => {
+ const value = adds[key];
+
+ // eslint-disable-next-line no-param-reassign
+ map[key] = {
+ startLoc: lineColumnFinder.fromIndex(value.start),
+ endLoc: lineColumnFinder.fromIndex(value.end),
+ };
+
+ return map;
+ }, {});
+}
+
+function inject(source, decorator) {
+ const ast = acorn.parse(source, acornConfig);
+
+ let lastIndex = 0;
+ const parts = [source];
+ const adds = {};
+
+ estraverse.traverse(ast, {
+ fallback: 'iteration',
+ enter: (node, parent) => {
+ if (node.type === 'MemberExpression') {
+ handleADD(node, parent, adds);
+ }
+
+ if (node.type === 'CallExpression') {
+ lastIndex = handleSTORYOF(node, parts, source, lastIndex);
+ }
+ },
+ });
+
+ const addsMap = calculateLocations(source, adds);
+ const newSource = parts.join(decorator);
+
+ return {
+ changed: lastIndex > 0,
+ source: newSource,
+ addsMap,
+ };
+}
+
+export default inject;
diff --git a/addons/storysource/src/loader/inject-decorator.test.js b/addons/storysource/src/loader/inject-decorator.test.js
new file mode 100644
index 000000000000..5fe68ddb8d02
--- /dev/null
+++ b/addons/storysource/src/loader/inject-decorator.test.js
@@ -0,0 +1,50 @@
+import fs from 'fs';
+import injectDecorator from './inject-decorator';
+
+const ADD_DECORATOR_STATEMENT = '.addDecorator(withStorySource(__STORY__, __ADDS_MAP__))';
+
+describe('inject-decorator', () => {
+ describe('positive', () => {
+ const source = fs.readFileSync('./__mocks__/inject-decorator.stories.txt', 'utf-8');
+ const result = injectDecorator(source, ADD_DECORATOR_STATEMENT);
+
+ it('returns "changed" flag', () => {
+ expect(result.changed).toBeTruthy();
+ });
+
+ it('injects stories decorator after the all "storiesOf" functions', () => {
+ expect(result.source).toMatchSnapshot();
+ });
+
+ it('calculates "adds" map', () => {
+ expect(result.addsMap).toMatchSnapshot();
+ });
+ });
+
+ describe('positive - angular', () => {
+ const source = fs.readFileSync('./__mocks__/inject-decorator.angular-stories.txt', 'utf-8');
+ const result = injectDecorator(source, ADD_DECORATOR_STATEMENT);
+
+ it('returns "changed" flag', () => {
+ expect(result.changed).toBeTruthy();
+ });
+
+ it('injects stories decorator after the all "storiesOf" functions', () => {
+ expect(result.source).toMatchSnapshot();
+ });
+
+ it('calculates "adds" map', () => {
+ expect(result.addsMap).toMatchSnapshot();
+ });
+ });
+
+ it('will not change the source when there are no "storiesOf" functions', () => {
+ const source = fs.readFileSync('./__mocks__/inject-decorator.no-stories.txt', 'utf-8');
+
+ const result = injectDecorator(source, ADD_DECORATOR_STATEMENT);
+
+ expect(result.changed).toBeFalsy();
+ expect(result.addsMap).toEqual({});
+ expect(result.source).toMatchSnapshot();
+ });
+});
diff --git a/addons/storysource/src/loader/parse-helpers.js b/addons/storysource/src/loader/parse-helpers.js
new file mode 100644
index 000000000000..d89764f5f1c7
--- /dev/null
+++ b/addons/storysource/src/loader/parse-helpers.js
@@ -0,0 +1,82 @@
+const STORIES_OF = 'storiesOf';
+
+function pushParts(source, parts, from, to) {
+ const start = source.slice(from, to);
+ parts.push(start);
+
+ const end = source.slice(to);
+ parts.push(end);
+}
+
+function getKindFromStoryOfNode(object) {
+ if (object.arguments.length < 1) {
+ return '';
+ }
+
+ const kindArgument = object.arguments[0];
+
+ if (kindArgument.type === 'Literal') {
+ return kindArgument.value;
+ }
+
+ if (kindArgument.type === 'TemplateLiteral') {
+ // we can generate template, but it will not be a real value
+ // until the full template compilation. probably won't fix.
+ return '';
+ }
+
+ // other options may include some complex usages.
+ return '';
+}
+
+function findRelatedKind(object) {
+ if (!object || !object.callee) {
+ return '';
+ }
+
+ if (object.callee.name === STORIES_OF) {
+ return getKindFromStoryOfNode(object);
+ }
+
+ return findRelatedKind(object.callee.object);
+}
+
+export function handleADD(node, parent, adds) {
+ if (!node.property || !node.property.name || node.property.name.indexOf('add') !== 0) {
+ return;
+ }
+
+ const addArgs = parent.arguments;
+
+ if (!addArgs || addArgs.length < 2) {
+ return;
+ }
+
+ const storyName = addArgs[0];
+ const lastArg = addArgs[addArgs.length - 1];
+
+ if (storyName.type !== 'Literal') {
+ // if story name is not literal, it much harder to extract it
+ return;
+ }
+
+ const kind = findRelatedKind(node.object) || '';
+ const key = `${kind}@${storyName.value}`;
+
+ // eslint-disable-next-line no-param-reassign
+ adds[key] = {
+ // Debug: code: source.slice(storyName.start, lastArg.end),
+ start: storyName.start,
+ end: lastArg.end,
+ };
+}
+
+export function handleSTORYOF(node, parts, source, lastIndex) {
+ if (!node.callee || !node.callee.name || node.callee.name !== STORIES_OF) {
+ return lastIndex;
+ }
+
+ parts.pop();
+ pushParts(source, parts, lastIndex, node.end);
+ return node.end;
+}
diff --git a/addons/storysource/src/manager.js b/addons/storysource/src/manager.js
new file mode 100644
index 000000000000..bdfd4b4d5db4
--- /dev/null
+++ b/addons/storysource/src/manager.js
@@ -0,0 +1,14 @@
+import React from 'react';
+import addons from '@storybook/addons';
+import StoryPanel from './StoryPanel';
+import { ADDON_ID, PANEL_ID } from './';
+
+export function register() {
+ addons.register(ADDON_ID, () => {
+ const channel = addons.getChannel();
+ addons.addPanel(PANEL_ID, {
+ title: 'Story',
+ render: () => ,
+ });
+ });
+}
diff --git a/addons/storysource/src/preview.js b/addons/storysource/src/preview.js
new file mode 100644
index 000000000000..76ecc883effc
--- /dev/null
+++ b/addons/storysource/src/preview.js
@@ -0,0 +1,19 @@
+import addons from '@storybook/addons';
+import { EVENT_ID } from './';
+
+function setStorySource(source, map, context) {
+ const channel = addons.getChannel();
+ const location = map[`${context.kind}@${context.story}`] || map[`@${context.story}`];
+
+ channel.emit(EVENT_ID, {
+ source,
+ location,
+ });
+}
+
+export function withStorySource(source, map) {
+ return (story, context) => {
+ setStorySource(source, map, context);
+ return story();
+ };
+}
diff --git a/examples/angular-cli/.storybook/addons.js b/examples/angular-cli/.storybook/addons.js
index 44a1313c8f0d..59f97cdaf0f9 100644
--- a/examples/angular-cli/.storybook/addons.js
+++ b/examples/angular-cli/.storybook/addons.js
@@ -1,3 +1,4 @@
+import '@storybook/addon-storysource/register';
import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';
import '@storybook/addon-notes/register';
diff --git a/examples/angular-cli/.storybook/webpack.config.js b/examples/angular-cli/.storybook/webpack.config.js
new file mode 100644
index 000000000000..3aa4a3be868d
--- /dev/null
+++ b/examples/angular-cli/.storybook/webpack.config.js
@@ -0,0 +1,15 @@
+const path = require('path');
+
+module.exports = baseConfig => {
+ baseConfig.module.rules.push({
+ test: [
+ /\.stories\.tsx?$/,
+ /index\.ts$/
+ ],
+ loaders: [require.resolve('@storybook/addon-storysource/loader')],
+ include: [path.resolve(__dirname, '../src')],
+ enforce: 'pre',
+ });
+
+ return baseConfig;
+};
diff --git a/examples/angular-cli/package.json b/examples/angular-cli/package.json
index 73465f1df4e7..1eb599bcc4a0 100644
--- a/examples/angular-cli/package.json
+++ b/examples/angular-cli/package.json
@@ -35,6 +35,7 @@
"@storybook/addon-links": "^3.4.0-alpha.8",
"@storybook/addon-notes": "^3.4.0-alpha.8",
"@storybook/addon-options": "^3.4.0-alpha.8",
+ "@storybook/addon-storysource": "^3.4.0-alpha.8",
"@storybook/addon-storyshots": "^3.4.0-alpha.8",
"@storybook/addons": "^3.4.0-alpha.8",
"@storybook/angular": "^3.4.0-alpha.8",
diff --git a/examples/official-storybook/addons.js b/examples/official-storybook/addons.js
index e8ffd5ee2812..8d78d9d8216c 100644
--- a/examples/official-storybook/addons.js
+++ b/examples/official-storybook/addons.js
@@ -1,3 +1,4 @@
+import '@storybook/addon-storysource/register';
import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';
import '@storybook/addon-events/register';
diff --git a/examples/official-storybook/config.js b/examples/official-storybook/config.js
index e7a90c2bceef..e53df3b35d7e 100644
--- a/examples/official-storybook/config.js
+++ b/examples/official-storybook/config.js
@@ -11,16 +11,20 @@ setOptions({
hierarchyRootSeparator: /\|/,
});
+function importAll(req) {
+ req.keys().forEach(filename => req(filename));
+}
+
function loadStories() {
let req;
req = require.context('../../lib/ui/src', true, /\.stories\.js$/);
- req.keys().forEach(filename => req(filename));
+ importAll(req);
req = require.context('../../lib/components/src', true, /\.stories\.js$/);
- req.keys().forEach(filename => req(filename));
+ importAll(req);
req = require.context('./stories', true, /\.stories\.js$/);
- req.keys().forEach(filename => req(filename));
+ importAll(req);
}
configure(loadStories, module);
diff --git a/examples/official-storybook/package.json b/examples/official-storybook/package.json
index 23bb10983100..7c6dc11e991f 100644
--- a/examples/official-storybook/package.json
+++ b/examples/official-storybook/package.json
@@ -21,6 +21,7 @@
"@storybook/addon-links": "^3.4.0-alpha.8",
"@storybook/addon-notes": "^3.4.0-alpha.8",
"@storybook/addon-options": "^3.4.0-alpha.8",
+ "@storybook/addon-storysource": "^3.4.0-alpha.8",
"@storybook/addon-storyshots": "^3.4.0-alpha.8",
"@storybook/addon-viewport": "^3.4.0-alpha.8",
"@storybook/addons": "^3.4.0-alpha.8",
diff --git a/examples/official-storybook/webpack.config.js b/examples/official-storybook/webpack.config.js
index 0a6124501f5b..e0a1cfbbb3ae 100644
--- a/examples/official-storybook/webpack.config.js
+++ b/examples/official-storybook/webpack.config.js
@@ -3,6 +3,16 @@ const path = require('path');
module.exports = {
module: {
rules: [
+ {
+ test: /\.stories\.jsx?$/,
+ loaders: [require.resolve('@storybook/addon-storysource/loader')],
+ include: [
+ path.resolve(__dirname, './stories'),
+ path.resolve(__dirname, '../../lib/ui/src'),
+ path.resolve(__dirname, '../../lib/components/src'),
+ ],
+ enforce: 'pre',
+ },
{
test: /\.js/,
loaders: ['babel-loader'],
diff --git a/examples/polymer-cli/.storybook/addons.js b/examples/polymer-cli/.storybook/addons.js
index 40db03219095..aa7f0807eda1 100644
--- a/examples/polymer-cli/.storybook/addons.js
+++ b/examples/polymer-cli/.storybook/addons.js
@@ -1,3 +1,4 @@
+import '@storybook/addon-storysource/register';
import '@storybook/addon-actions/register';
import '@storybook/addon-notes/register';
import '@storybook/addon-knobs/register';
diff --git a/examples/polymer-cli/.storybook/webpack.config.js b/examples/polymer-cli/.storybook/webpack.config.js
new file mode 100644
index 000000000000..3ba8cbcf5fd7
--- /dev/null
+++ b/examples/polymer-cli/.storybook/webpack.config.js
@@ -0,0 +1,15 @@
+const path = require('path');
+
+module.exports = (storybookBaseConfig, configType, defaultConfig) => {
+ defaultConfig.module.rules.push({
+ test: [
+ /\.stories\.js$/,
+ /index\.js$/
+ ],
+ loaders: [ require.resolve('@storybook/addon-storysource/loader') ],
+ include: [ path.resolve(__dirname, '../src') ],
+ enforce: 'pre',
+ });
+
+ return defaultConfig;
+};
\ No newline at end of file
diff --git a/examples/polymer-cli/package.json b/examples/polymer-cli/package.json
index da064a39a126..468b439b22b0 100644
--- a/examples/polymer-cli/package.json
+++ b/examples/polymer-cli/package.json
@@ -12,6 +12,7 @@
"@storybook/addon-knobs": "^3.4.0-alpha.8",
"@storybook/addon-notes": "^3.4.0-alpha.8",
"@storybook/addon-options": "^3.4.0-alpha.8",
+ "@storybook/addon-storysource": "^3.4.0-alpha.8",
"@storybook/addon-viewport": "^3.4.0-alpha.8",
"@storybook/polymer": "^3.4.0-alpha.8",
"@webcomponents/webcomponentsjs": "^1.1.0",
diff --git a/examples/vue-kitchen-sink/.storybook/addons.js b/examples/vue-kitchen-sink/.storybook/addons.js
index 392c585c9c6a..d6145bf315b8 100644
--- a/examples/vue-kitchen-sink/.storybook/addons.js
+++ b/examples/vue-kitchen-sink/.storybook/addons.js
@@ -1,3 +1,4 @@
+import '@storybook/addon-storysource/register';
import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';
import '@storybook/addon-notes/register';
diff --git a/examples/vue-kitchen-sink/.storybook/webpack.config.js b/examples/vue-kitchen-sink/.storybook/webpack.config.js
index 0d8d7a63c6c3..52dac06c46c8 100644
--- a/examples/vue-kitchen-sink/.storybook/webpack.config.js
+++ b/examples/vue-kitchen-sink/.storybook/webpack.config.js
@@ -1,11 +1,18 @@
+const path = require('path');
const webpack = require('webpack');
module.exports = (storybookBaseConfig, configType, defaultConfig) => {
- // configType has a value of 'DEVELOPMENT' or 'PRODUCTION'
- // You can change the configuration based on that.
- // 'PRODUCTION' is used when building the static version of storybook.
- // Make whatever fine-grained changes you need
+ defaultConfig.module.rules.push({
+ test: [
+ /\.stories\.js$/,
+ /index\.js$/
+ ],
+ loaders: [require.resolve('@storybook/addon-storysource/loader')],
+ include: [path.resolve(__dirname, '../src')],
+ enforce: 'pre',
+ });
+
defaultConfig.plugins.push(
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
@@ -17,6 +24,5 @@ module.exports = (storybookBaseConfig, configType, defaultConfig) => {
})
);
- // Return the altered config
return defaultConfig;
};
diff --git a/examples/vue-kitchen-sink/package.json b/examples/vue-kitchen-sink/package.json
index 6f0e5d57140a..0b82b8c2d6c2 100644
--- a/examples/vue-kitchen-sink/package.json
+++ b/examples/vue-kitchen-sink/package.json
@@ -19,6 +19,7 @@
"@storybook/addon-links": "^3.4.0-alpha.8",
"@storybook/addon-notes": "^3.4.0-alpha.8",
"@storybook/addon-options": "^3.4.0-alpha.8",
+ "@storybook/addon-storysource": "^3.4.0-alpha.8",
"@storybook/addon-storyshots": "^3.4.0-alpha.8",
"@storybook/addon-viewport": "^3.4.0-alpha.8",
"@storybook/addons": "^3.4.0-alpha.8",
diff --git a/integration/__image_snapshots__/angular-cli-snap.png b/integration/__image_snapshots__/angular-cli-snap.png
index 50073148dab8..163db3912c57 100644
Binary files a/integration/__image_snapshots__/angular-cli-snap.png and b/integration/__image_snapshots__/angular-cli-snap.png differ
diff --git a/integration/__image_snapshots__/vue-kitchen-sink-snap.png b/integration/__image_snapshots__/vue-kitchen-sink-snap.png
index 0ad942f7f5dc..122e2060e53c 100644
Binary files a/integration/__image_snapshots__/vue-kitchen-sink-snap.png and b/integration/__image_snapshots__/vue-kitchen-sink-snap.png differ
diff --git a/yarn.lock b/yarn.lock
index b4cc6c751e9b..3ce6bc6aac63 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -407,12 +407,36 @@ accepts@~1.3.0, accepts@~1.3.4:
mime-types "~2.1.16"
negotiator "0.6.1"
-acorn-dynamic-import@^2.0.0:
+acorn-async-iteration@^0.2.0:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/acorn-async-iteration/-/acorn-async-iteration-0.2.2.tgz#cc7346d5088b98c186bb16206af0264b1f8b1fab"
+ dependencies:
+ acorn "^5.3.0"
+
+acorn-bigint@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/acorn-bigint/-/acorn-bigint-0.2.0.tgz#0f45a5290537799a3b07085689a186881cb53784"
+ dependencies:
+ acorn "^5.2.1"
+
+acorn-class-fields@^0.1.0:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/acorn-class-fields/-/acorn-class-fields-0.1.2.tgz#20782f304af42257feff5bd4a5c335291473bf58"
+ dependencies:
+ acorn "^5.3.0"
+
+acorn-dynamic-import@^2.0.0, acorn-dynamic-import@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4"
dependencies:
acorn "^4.0.3"
+acorn-es7@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/acorn-es7/-/acorn-es7-0.1.0.tgz#4a6de4522faacb4c31209e1b73b5f301ed2bb30a"
+ dependencies:
+ acorn "^2.6.4"
+
acorn-globals@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf"
@@ -425,12 +449,67 @@ acorn-globals@^4.0.0:
dependencies:
acorn "^5.0.0"
+acorn-import-meta@^0.2.0:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/acorn-import-meta/-/acorn-import-meta-0.2.1.tgz#ac91e06e00facece7e96ff76a0fe9ec7b1cb5b5c"
+ dependencies:
+ acorn "^5.4.1"
+
acorn-jsx@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b"
dependencies:
acorn "^3.0.4"
+acorn-jsx@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-4.1.1.tgz#e8e41e48ea2fe0c896740610ab6a4ffd8add225e"
+ dependencies:
+ acorn "^5.0.3"
+
+acorn-numeric-separator@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/acorn-numeric-separator/-/acorn-numeric-separator-0.1.1.tgz#aa455a1d95ae887231de97e0681abbe28b065e8d"
+ dependencies:
+ acorn "^5.2.1"
+
+acorn-optional-catch-binding@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/acorn-optional-catch-binding/-/acorn-optional-catch-binding-0.1.0.tgz#d9a1874dbffce1eb3495836e5e75b2ad9c300868"
+ dependencies:
+ acorn "^5.2.1"
+
+acorn-private-methods@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/acorn-private-methods/-/acorn-private-methods-0.1.1.tgz#32c13cf24d05bf1c9be04914b41491c59d75a195"
+ dependencies:
+ acorn "^5.4.0"
+
+acorn-stage3@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/acorn-stage3/-/acorn-stage3-0.4.0.tgz#f0313a21cde0fdaddddaa2d0dc79c373b4d00b77"
+ dependencies:
+ acorn "^5.3.0"
+ acorn-async-iteration "^0.2.0"
+ acorn-bigint "^0.2.0"
+ acorn-class-fields "^0.1.0"
+ acorn-dynamic-import "^2.0.2"
+ acorn-import-meta "^0.2.0"
+ acorn-numeric-separator "^0.1.0"
+ acorn-optional-catch-binding "^0.1.0"
+ acorn-private-methods "^0.1.0"
+ acorn5-object-spread "^5.1.0"
+
+acorn5-object-spread@^5.1.0:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/acorn5-object-spread/-/acorn5-object-spread-5.1.2.tgz#fbd6c8f483867bd859f479c1396aa495c9b07458"
+ dependencies:
+ acorn "^5.2.1"
+
+acorn@^2.6.4:
+ version "2.7.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-2.7.0.tgz#ab6e7d9d886aaca8b085bc3312b79a198433f0e7"
+
acorn@^3.0.4:
version "3.3.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
@@ -443,6 +522,10 @@ acorn@^5.0.0, acorn@^5.1.2, acorn@^5.2.1:
version "5.3.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.3.0.tgz#7446d39459c54fb49a80e6ee6478149b940ec822"
+acorn@^5.0.3, acorn@^5.3.0, acorn@^5.4.0, acorn@^5.4.1:
+ version "5.4.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.4.1.tgz#fdc58d9d17f4a4e98d102ded826a9b9759125102"
+
add-stream@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa"
@@ -3199,6 +3282,14 @@ cli@~1.0.0:
exit "0.1.2"
glob "^7.1.1"
+clipboard@^1.7.1:
+ version "1.7.1"
+ resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-1.7.1.tgz#360d6d6946e99a7a1fef395e42ba92b5e9b5a16b"
+ dependencies:
+ good-listener "^1.2.2"
+ select "^1.1.2"
+ tiny-emitter "^2.0.0"
+
cliui@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1"
@@ -3395,6 +3486,12 @@ combined-stream@^1.0.5, combined-stream@~1.0.5:
dependencies:
delayed-stream "~1.0.0"
+comma-separated-tokens@^1.0.0:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.4.tgz#72083e58d4a462f01866f6617f4d98a3cd3b8a46"
+ dependencies:
+ trim "0.0.1"
+
command-join@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/command-join/-/command-join-2.0.0.tgz#52e8b984f4872d952ff1bdc8b98397d27c7144cf"
@@ -4422,6 +4519,10 @@ delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+delegate@^3.1.2:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
+
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
@@ -6504,6 +6605,12 @@ glogg@^1.0.0:
dependencies:
sparkles "^1.0.0"
+good-listener@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
+ dependencies:
+ delegate "^3.1.2"
+
got@^5.0.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/got/-/got-5.7.1.tgz#5f81635a61e4a6589f180569ea4e381680a51f35"
@@ -6834,6 +6941,20 @@ hash.js@^1.0.0, hash.js@^1.0.3:
inherits "^2.0.3"
minimalistic-assert "^1.0.0"
+hast-util-parse-selector@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/hast-util-parse-selector/-/hast-util-parse-selector-2.1.0.tgz#b55c0f4bb7bb2040c889c325ef87ab29c38102b4"
+
+hastscript@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/hastscript/-/hastscript-3.1.0.tgz#66628ba6d7f1ad07d9277dd09028aba7f4934599"
+ dependencies:
+ camelcase "^3.0.0"
+ comma-separated-tokens "^1.0.0"
+ hast-util-parse-selector "^2.0.0"
+ property-information "^3.0.0"
+ space-separated-tokens "^1.0.0"
+
hawk@3.1.3, hawk@~3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
@@ -6856,6 +6977,10 @@ he@1.1.x, he@^1.1.0, he@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
+highlight.js@~9.12.0:
+ version "9.12.0"
+ resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e"
+
hipchat-notifier@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/hipchat-notifier/-/hipchat-notifier-1.1.0.tgz#b6d249755437c191082367799d3ba9a0f23b231e"
@@ -9156,6 +9281,13 @@ lie@~3.1.0:
dependencies:
immediate "~3.0.5"
+line-column@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/line-column/-/line-column-1.0.2.tgz#d25af2936b6f4849172b312e4792d1d987bc34a2"
+ dependencies:
+ isarray "^1.0.0"
+ isobject "^2.0.0"
+
linkify-it@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.0.3.tgz#d94a4648f9b1c179d64fa97291268bdb6ce9434f"
@@ -9663,6 +9795,12 @@ lowercase-keys@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
+lowlight@~1.9.1:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.9.1.tgz#ed7c3dffc36f8c1f263735c0fe0c907847c11250"
+ dependencies:
+ highlight.js "~9.12.0"
+
lru-cache@2.2.x:
version "2.2.4"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d"
@@ -11933,6 +12071,18 @@ pretty-format@^4.2.1:
version "4.3.1"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-4.3.1.tgz#530be5c42b3c05b36414a7a2a4337aa80acd0e8d"
+prismjs@^1.8.4:
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.11.0.tgz#297aef33eb79421bfdb19273a5092ca515970d29"
+ optionalDependencies:
+ clipboard "^1.7.1"
+
+prismjs@~1.10.0:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.10.0.tgz#77e5187c2ae6b3253fcc313029cf25fe53778721"
+ optionalDependencies:
+ clipboard "^1.7.1"
+
private@^0.1.6, private@^0.1.7, private@~0.1.5:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
@@ -12002,6 +12152,10 @@ prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.7,
loose-envify "^1.3.1"
object-assign "^4.1.1"
+property-information@^3.0.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/property-information/-/property-information-3.2.0.tgz#fd1483c8fbac61808f5fe359e7693a1f48a58331"
+
proto-list@~1.2.1:
version "1.2.4"
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
@@ -12593,6 +12747,16 @@ react-style-proptype@^3.0.0:
dependencies:
prop-types "^15.5.4"
+react-syntax-highlighter@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/react-syntax-highlighter/-/react-syntax-highlighter-7.0.0.tgz#a522ccf95f272178b8f25de2184383e9bf647155"
+ dependencies:
+ babel-runtime "^6.18.0"
+ highlight.js "~9.12.0"
+ lowlight "~1.9.1"
+ prismjs "^1.8.4"
+ refractor "^2.0.0"
+
react-test-renderer@^16.0.0-0, react-test-renderer@^16.1.0, react-test-renderer@^16.2.0:
version "16.2.0"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.2.0.tgz#bddf259a6b8fcd8555f012afc8eacc238872a211"
@@ -12937,6 +13101,13 @@ reflect-metadata@^0.1.10, reflect-metadata@^0.1.2:
version "0.1.10"
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.10.tgz#b4f83704416acad89988c9b15635d47e03b9344a"
+refractor@^2.0.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/refractor/-/refractor-2.3.0.tgz#b8cade8f88020f8836ca3622c6ef87fd2444d76a"
+ dependencies:
+ hastscript "^3.1.0"
+ prismjs "~1.10.0"
+
regenerate@^1.2.1:
version "1.3.3"
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.3.tgz#0c336d3980553d755c39b586ae3b20aa49c82b7f"
@@ -13684,6 +13855,10 @@ select-hose@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
+select@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
+
selenium-webdriver@3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz#2ba87a1662c020b8988c981ae62cb2a01298eafc"
@@ -14240,6 +14415,12 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+space-separated-tokens@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.1.tgz#9695b9df9e65aec1811d4c3f9ce52520bc2f7e4d"
+ dependencies:
+ trim "0.0.1"
+
sparkles@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.0.tgz#1acbbfb592436d10bbe8f785b7cc6f82815012c3"
@@ -14975,6 +15156,10 @@ timespan@2.3.x:
version "2.3.0"
resolved "https://registry.yarnpkg.com/timespan/-/timespan-2.3.0.tgz#4902ce040bd13d845c8f59b27e9d59bad6f39929"
+tiny-emitter@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c"
+
tinycolor2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.1.tgz#f4fad333447bc0b07d4dc8e9209d8f39a8ac77e8"