storybookjs / storybook

Storybook is the industry standard workshop for building, documenting, and testing UI components in isolation
https://storybook.js.org
MIT License
84.54k stars 9.3k forks source link

storybook cannot import `.js` paths in `.tsx?` files. #15962

Open lifeiscontent opened 3 years ago

lifeiscontent commented 3 years ago

Describe the bug I'm creating a UI Library that is distributed as a ES Module.

in typescript, to create an ES Module compliant package you must import/export all paths as .js as outlined here: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c

storybook does not currently infer these paths and will fail to find the .ts files during development.

in order to provide a great developer experience, storybook should support this functionality.

To Reproduce

https://github.com/lifeiscontent/storybook-repro

System

Environment Info:

  System:
    OS: Linux 5.11 Zorin OS 16
    CPU: (4) x64 Intel(R) Core(TM) i3-10110U CPU @ 2.10GHz
  Binaries:
    Node: 14.17.4 - ~/.asdf/installs/nodejs/14.17.4/bin/node
    Yarn: 3.0.1 - ~/.asdf/installs/nodejs/14.17.4/.npm/bin/yarn
    npm: 6.14.14 - ~/.asdf/installs/nodejs/14.17.4/bin/npm
  Browsers:
    Chrome: 93.0.4577.63
    Firefox: 91.0.2
  npmPackages:
    @storybook/addon-actions: ^6.4.0-alpha.33 => 6.4.0-alpha.33 
    @storybook/addon-docs: ^6.4.0-alpha.33 => 6.4.0-alpha.33 
    @storybook/addon-essentials: ^6.4.0-alpha.33 => 6.4.0-alpha.33 
    @storybook/addon-links: ^6.4.0-alpha.33 => 6.4.0-alpha.33 
    @storybook/react: ^6.4.0-alpha.33 => 6.4.0-alpha.33 

Additional context

thanks for your help ❤️

alvis commented 3 years ago

@lifeiscontent ESM output is genuinely a challenge for many package maintainers, including me.

The biggest challenge is that, perhaps, for esm outputs, all the import/export statements must include the annoying 'js' extension. But if you use .js in your typescript source, it's not quite typescript anymore and I doubt why typescript does not complain about that before transpilation.

My solution to that is to add .js extension via babel with babel-plugin-add-import-extension and babel-plugin-transform-default-named-imports.

As for storybook, I'm not quite sure about your intention? For viewing stories or publishing storybook as a website, you don't need to add '.js'. It only becomes an issue if you want to export stories as ES module for other projects. But it's unrelated to storybook, but just your build system.

lifeiscontent commented 3 years ago

Hey @alvis, then intention here is that I have stories in the package I'm publishing, so when I import the typescript source of that package, storybook doesn't know how to handle the import path.

since storybook is a tool for development I'd expect it to know how to resolve these paths since it supports typescript.

alvis commented 3 years ago

@lifeiscontent In this case, what you need is a tool that transpile your tsx files to ES modules. Pure tsc doesn't work, but rollup or babel with the plugins I mentioned above can do the job.

Storybook can only generate you a static web site.

lifeiscontent commented 3 years ago

@alvis the doc types for storybook wouldn't work if I did that, there's a known bug that only allows you to import typescript source, outside of node_modules.

alvis commented 3 years ago

It could be the case, I'm not sure, even with declaration file emitted? Maybe you can link the known issue here too.

lifeiscontent commented 2 years ago

CC: @shilman do you happen to know where the issue is for storybook not reading d.ts (compiled ts source for controls)

as-zlynn-philipps commented 2 years ago

For others that wind up here, this is the answer: https://github.com/softwareventures/resolve-typescript-plugin

If you use baseUrl / pathMapping, you will need to redefine your aliases using webpackFinal & config.resolve.alias since tsconfig-paths-webpack plugin doesn't play nicely with resolve-typescript-plugin. Example:

{
  webpackFinal: async (config) => {
    config.module.rules.push({
      test: /\.tsx?$/,
      use: 'ts-loader',
    })

    /**
     * Used in lieu of tsconfig-paths-webpack plugin since that doesn't
     * play nicely with resolve-typescript-plugin. If a new folder is added (direct
     * child of `src/`), it will need to be added here. :/ (but not tsconfig.json)
     */
    config.resolve.alias = {
      components: path.resolve(__dirname, '../src/components/'),
      hocs: path.resolve(__dirname, '../src/hocs/'),
      hooks: path.resolve(__dirname, '../src/hooks/'),
      utils: path.resolve(__dirname, '../src/utils/'),
    }

    config.resolve.extensions = ['.js', '.jsx', '.json']

    config.resolve.plugins = [
      ...(config.resolve.plugins || []),

      /**
       * See https://github.com/storybookjs/storybook/issues/15962
       * and https://github.com/softwareventures/resolve-typescript-plugin
       */
      new ResolveTypeScriptPlugin(),

      /**
       * Needed to support TS aliases.
       */
      // new TsconfigPathsPlugin({
      //   extensions: config.resolve.extensions,
      // }),
    ]

    return config
  },
}
wKich commented 2 years ago

I have a similar problem, but in my case I have a preset in .ts which imports es module with .js extension. So I use this preset like this:

// .storybook/main.ts
export default {
  stories: ['../stories/**/*.stories.@(md|ts)x'],
  addons: [
    '@storybook/addon-postcss',
    '@storybook/addon-essentials',
    {
      name: './../src/client/addon/preset',
      options: { clientPort: 8000 },
    },
  ],
  features: {
    previewCsfV3: true,
  },
};
eric-burel commented 2 years ago

Solution from @as-zlynn-philipps sounds great, but this is a fix at Storybook level. Eventhough TS seems to accept ".js" even when the source a ".ts", you probably want to improve the bundle process instead to automatically add the ".js" extension. This ticket shows a possible solution with Esbuild: https://github.com/evanw/esbuild/issues/622

Edit: after more research, fix at Storybook level is the right solution. This issue may also affect Jest or other Webpack based development tooling that loads the TypeScript source.

advl commented 9 months ago

This problem is still present - would be great if someone had the time to look at it !

I confirm that @as-zlynn-philipps 's answer still works, and the comment around the path resolution is also pertinent. I also ended up having to comment out the TsConfigPathsPlugin and find an alternative.

A potential easy fix would be to upgrade to webpack 5.74.0 that includes the fix. On storybook's side, we could provide a safe default to map .js to .ts when typescript is being used. Would there be any downsides to that ?