Storybook: import markdown.md for addons "Cannot find module"

Created on 31 Jan 2018  ยท  29Comments  ยท  Source: storybookjs/storybook

Issue details

I want to use markdown.md with addons but I can't import it

Steps to reproduce

I got error import README1 from '../src/framework/content/README1.md';
error TS2307: Cannot find module '../src/framework/content/README1.md'.

Please specify which version of Storybook and optionally any affected addons that you're running

-storybook/addon-notes": "^3.3.11",
-storybook/angular": "^3.4.0-alpha.5",

Screenshots / Screencast / Code Snippets (Optional)

35622048-80ae5544-0697-11e8-9d4f-fd547f5f17cc

notes angular question / support

Most helpful comment

Try putting this in your TS code:

declare module '*.md';

All 29 comments

Can you please create a GitHub repo with minimal reproduction of your issue?

@Hypnosphi
The repo link on GitHub https://github.com/amorenew/travel-angular
Steps:
1-install packages >>yarn
2-run the angular app >>yarn start
3-run the storybook >>yarn storybook

@igor-dv mind taking a look?

๐Ÿ‘Œ Will check tomorrow

So, I've tried to figure this out and looks like there is some problem with importing non ts file when using typescript. I think it's not related to storybook. Maybe @alterx or @amcdnl can shed some light

Try putting this in your TS code:

declare module '*.md';

putting declare module '*.md'; in index.ts not working

image

I followed angular docs and then I made typings.d.ts file in root and I added in it

declare module "*.md" {
    const value: string;
    export default value;
}

and in tsconfig.json at compilerOptions I added

"typeRoots": [
      "node_modules/@types","./typings.d.ts"
    ],

it works no error but I don't think it returns value because withNotes addons doesn't show text

aassa
while without readme.md it show a value

image

hi @amorenew, I've used your approach and added 'marked' module
there is my story:

`import * as marked from 'marked';
import * as readme from '../app/components/popup/README.md';

storiesOf('comp', module)
.addDecorator(withKnobs)
.add(
'withNotes',
withNotes({ text: marked(readme) })(() => ({
...story there
}))
);
`

@vasinkevych
import * as readme from '../app/components/popup/README.md'; works

storiesOf('Component', module)
  .add('With Markdown', withNotes(readme)(() => ({
    component: HeadComponent,
    props: {
        text: '๐Ÿ˜€ ๐Ÿ˜Ž ๐Ÿ‘ ๐Ÿ’ฏ'
    }
})));

not
import README1 from '../src/framework/content/README1.md';

Thanks a lot all.

it works in normal case but not when I am trying to build
I use in building my storybook:
"storybook-build": "build-storybook -c .storybook -o dist/storybook"
and I got the following error:

ERR! ./src/framework/content/README1.md
ERR! Module parse failed: Unexpected character '#' (1:0)
ERR! You may need an appropriate loader to handle this file type.

image

I tried to use the following readme loader:
https://github.com/peerigon/markdown-loader

const marked = require("marked");
const renderer = new marked.Renderer();

return {
    module: {
        rules: [{
                test: /\.md$/,
                use: [
                    {
                        loader: "html-loader"
                    },
                    {
                        loader: "markdown-loader",
                        options: {
                            pedantic: true,
                            renderer
                        }
                    }
                ]
            }]
    }
}

but it gives me

E:\Cross\travelangular\node_modules\@storybook\angular\dist\server\config.js:75
      rules: [].concat((0, _toConsumableArray3.default)(config.module.rules), (0, _toConsumableArray3.default)(customConfig.module.rules ||
[]))
                                                                                                                                   ^
TypeError: Cannot read property 'rules' of undefined
    at exports.default (E:\Cross\travelangular\node_modules\@storybook\angular\dist\server\config.js:75:132)
    at Object.<anonymous> (E:\Cross\travelangular\node_modules\@storybook\angular\dist\server\build.js:76:35)

@vasinkevych Are you able to build not just run your storybook?

@amorenew , you are right. Just checked and seems like rules for md files are not used in the static build configuration. I'll fix it asap.

If the configuration you mentioned is a custom webpack.config you need to add it like this

moudle.exports = { /// <----
    module: {
        rules: [{
                     //...
            }]
    }
}

Also we are using this rule for md files:

        {
          test: /\.md$/,
          use: [
            {
              loader: 'html-loader',
            },
            {
              loader: 'markdown-loader',
            },
          ],
        }

in .storybook I added webpack.config.jswith your config and it works

const path = require('path');

module.exports = {
    module: {
        rules: [{
            test: /\.md$/,
            use: [
              {
                loader: 'html-loader',
              },
              {
                loader: 'markdown-loader',
              },
            ],
            include: path.resolve(__dirname, '../')
          }]
    }
}

@igor-dv
after you patch so in next alpha release I don't need to add anything to webpack.config. right?

@amorenew, right

really I don't see an open source repo with that much support before.
๐Ÿ˜ ๐Ÿ˜† ๐Ÿ˜… ๐Ÿ˜† ๐Ÿ˜

I'm using @storybook/[email protected], using @amorenew's webpack.config.js and importing markdown with

import * as docs from './button.md';

But I'm still getting error TS2307: Cannot find module './button.md'. Any thoughts why this isn't working?

@cwmrowe
check my tsconfig.json
I have

"typeRoots": [
      "node_modules/@types","./typings.d.ts"
    ],

and In typings.d.ts

declare module "*.md" {
    const value: string;
    export default value;
}

Full repo
https://github.com/amorenew/travel-angular/blob/master/.storybook/webpack.config.js
but I still on alpha.5, not 8

Thanks @amorenew

For those using Angular CLI with Storybook 3.4.0 release - you may have to add the typedef changes suggested by @amorenew in the typings.d.ts file _in the src directory_. That's what got this to work for me!

Thanks for saving me a night of headaches tracking this down.

@cwmrowe did you solve your question?
i use import * as docs from './button.md';
but still error TS2307: Cannot find module './button.md'

Did you add

declare module "*.md" {
    const value: string;
    export default value;
}

i encountered the same issue as @Yunnkii !
@cwmrowe did you solve your question?

Still have the warning event with the typings declaration.

Hi guys, I'm still getting errors here.
I'm making a simple documentation using Angular Material components.
Here's my brief Storybook configuration:

ng-cli

     _                      _                 ____ _     ___
    / \   _ __   __ _ _   _| | __ _ _ __     / ___| |   |_ _|
   / โ–ณ \ | '_ \ / _` | | | | |/ _` | '__|   | |   | |    | |
  / ___ \| | | | (_| | |_| | | (_| | |      | |___| |___ | |
 /_/   \_\_| |_|\__, |\__,_|_|\__,_|_|       \____|_____|___|
                |___/


Angular CLI: 7.2.1
Node: 11.9.0
OS: darwin x64
Angular: 7.2.0
... common, compiler, compiler-cli, core, forms
... language-service, platform-browser, platform-browser-dynamic
... router

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.12.0
@angular-devkit/build-angular     0.12.0
@angular-devkit/build-optimizer   0.12.0
@angular-devkit/build-webpack     0.12.0
@angular-devkit/core              7.2.1
@angular-devkit/schematics        7.2.1
@angular/animations               7.2.2
@angular/cdk                      7.3.0
@angular/cli                      7.2.1
@angular/material                 7.3.1
@ngtools/webpack                  7.2.0
@schematics/angular               7.2.1
@schematics/update                0.12.1
rxjs                              6.3.3
typescript                        3.2.2
webpack                           4.23.1

package.json

{
  "name": "test-storybook-angular",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "storybook": "start-storybook -p 9001 -c .storybook"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "7.2.2",
    "@angular/cdk": "7.3.0",
    "@angular/common": "7.2.0",
    "@angular/compiler": "7.2.0",
    "@angular/core": "7.2.0",
    "@angular/forms": "7.2.0",
    "@angular/material": "7.3.1",
    "@angular/platform-browser": "7.2.0",
    "@angular/platform-browser-dynamic": "7.2.0",
    "@angular/router": "7.2.0",
    "core-js": "2.5.4",
    "hammerjs": "^2.0.8",
    "rxjs": "6.3.3",
    "tslib": "1.9.0",
    "zone.js": "0.8.26"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "0.12.0",
    "@angular/cli": "7.2.1",
    "@angular/compiler-cli": "7.2.0",
    "@angular/language-service": "7.2.0",
    "@babel/core": "7.2.2",
    "@storybook/addon-actions": "4.1.11",
    "@storybook/addon-knobs": "4.1.11",
    "@storybook/addon-links": "4.1.11",
    "@storybook/addon-notes": "4.1.11",
    "@storybook/addon-options": "4.1.11",
    "@storybook/addons": "4.1.11",
    "@storybook/angular": "4.1.11",
    "@types/angular": "1.6.53",
    "@types/jasmine": "2.8.8",
    "@types/jasminewd2": "2.0.3",
    "@types/node": "8.9.4",
    "@types/storybook__addon-notes": "4.0.1",
    "autoprefixer": "9.4.7",
    "babel-loader": "8.0.5",
    "codelyzer": "4.5.0",
    "css-loader": "2.1.0",
    "html-loader": "0.5.5",
    "jasmine-core": "2.99.1",
    "jasmine-spec-reporter": "4.2.1",
    "karma": "3.1.1",
    "karma-chrome-launcher": "2.2.0",
    "karma-coverage-istanbul-reporter": "2.0.1",
    "karma-jasmine": "1.1.2",
    "karma-jasmine-html-reporter": "0.2.2",
    "markdown-loader": "5.0.0",
    "postcss-loader": "3.0.0",
    "protractor": "5.4.0",
    "sass-loader": "7.1.0",
    "ts-node": "7.0.0",
    "tslint": "5.11.0",
    "typescript": "3.2.2"
  }
}

tsconfig.json

{
  "compileOnSave": false,
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "module": "es2015",
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "importHelpers": true,
    "target": "es5",
    "typeRoots": ["node_modules/@types", "./typings.d.ts"],
    "lib": ["es2018", "dom"]
  }
}

typings.d.ts

declare module '*.md' {
  const content: string;
  export default content;
}

.storybook/addons.js

/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */

import '@storybook/addon-options/register';
import '@storybook/addon-knobs/register'
import '@storybook/addon-actions/register';
import '@storybook/addon-notes/register';

.storybook/config.js

/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions */
import { configure, addDecorator } from '@storybook/angular';
import { withOptions } from '@storybook/addon-options';
import { withKnobs } from '@storybook/addon-knobs';
import { action } from '@storybook/addon-actions';

import '@angular/material/prebuilt-themes/deeppurple-amber.css';

addDecorator(
  withOptions({
    name: 'Storybook Angular Material',
    addonPanelInRight: true
  })
);

addDecorator(withKnobs);

addDecorator(withNotes);

// import all files ending in *.stories.ts
const req = require.context('../src/', true, /.stories.ts$/);
function loadStories() {
  req.keys().forEach(filename => req(filename));
}

configure(loadStories, module);

src/app/material/mat-button.stories.ts

import { storiesOf, moduleMetadata } from '@storybook/angular';
import { MatButtonModule, MatIconModule } from '@angular/material';
import basicDoc from '../notes/mat-button-basic.md';

import { boolean, select, text } from '@storybook/addon-knobs';

import { withNotes } from '@storybook/addon-notes';

storiesOf('Button', module)
  .addDecorator(
    moduleMetadata({
      imports: [MatButtonModule, MatIconModule],
      providers: []
    })
  )
  .add('Basic', () => {
    return {
      template: `<button mat-raised-button [disabled]="disabled" [disableRipple]="disableRipple" [color]="color">
        <span>{{ label }}</span>
        <mat-icon
          *ngIf="!!icon"
          [attr.aria-label]="'Icon ' + icon"
          style="margin-left: 0.25em;">
            {{ icon }}
          </mat-icon>
      </button>`,
      props: {
        label: text('label', 'Basic'),
        disabled: boolean('disabled', false),
        disableRipple: boolean('disableRipple', false),
        color: select(
          'color',
          {
            none: '',
            primary: 'primary',
            accent: 'accent',
            warn: 'warn'
          },
          ''
        ),
        icon: select(
          'icon',
          {
            none: '',
            favorite: 'favorite',
            home: 'home',
            weekend: 'weekend',
            sort: 'sort',
            gesture: 'gesture'
          },
          ''
        )
      },
      notes: { markdown: basicDoc }
    };
  });

src/app/notes/mat-button-basic.md

# Button

Lorem ipsum dolor sit amet consectetur adipliscig elit...

Still getting error when compiling:

ERROR in /Users/ifthenelse/Sites/test/test-storybook-angular-material/src/app/material/mat-button.stories.ts
ERROR in /Users/ifthenelse/Sites/test/test-storybook-angular-material/src/app/material/mat-button.stories.ts(3,22):
TS2307: Cannot find module '../notes/mat-button-basic.md'.

Am I missing something?

For me the errors go away if i switch the extension from 'stories.tsx' to just 'tsx'

edit: actually any other extension that 'stories.tsx' works, even 'story.tsx'

We were having trouble with this exact same issue because we had "**/*.stories.*", in as an element in exclude in our tsconfig.json. We had originally placed it there to prevent stories files from being compiled into our bundles in our component library, but it was also causing this issue.

Removing it and separately adding *.stories* into our .npmignore fixed the issue while continuing to prevent the story files from getting bundled with our published package.

Hope this helps someone having the same issue!

@haldunanil this is exactly what i found too. If you have exclude the stories in your tsconfig, typescript will ignore your typings for those files.

Thanks for the .npmignore tip!

image
Try adding .md in extension field at .stroybook/webpack.config.js

An alternative solution for it - at the root of the project:
npm i marked

On your story - Import the MD file and 'marked' as following:

import * as marked from 'marked';
const componentNotes = require('./1-HeroSearch.stories.md').default;

Use 'marked' to read/parse for you and display as markdown:

    {
      notes: {
        markdown: marked(componentNotes)
      }
    }

Ta-da it works! :smiley:
Good luck :+1:

in CRA with typescript, can support markdown by editing react-app-dev.d.ts, add the declaration

declare module '*.md' {
  const value: string;
  export default value;
}

and in your component

import tReadme from '../readme.md';
Was this page helpful?
0 / 5 - 0 ratings

Related issues

alexanbj picture alexanbj  ยท  3Comments

Jonovono picture Jonovono  ยท  3Comments

zvictor picture zvictor  ยท  3Comments

rpersaud picture rpersaud  ยท  3Comments

ZigGreen picture ZigGreen  ยท  3Comments