Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support ESModule #9854

Closed
gsklee opened this issue Feb 14, 2020 · 40 comments
Closed

Support ESModule #9854

gsklee opened this issue Feb 14, 2020 · 40 comments

Comments

@gsklee
Copy link

gsklee commented Feb 14, 2020

I tried changing .storybook/main.js to .storybook/main.mjs and its content to:

export const stories = ['../src/**/*.stories.js'];
export const addons = [
  '@storybook/preset-create-react-app',
  '@storybook/addon-actions',
  '@storybook/addon-links',
];

This does not seem to work or is something supported as a $ yarn storybook command would bring up a broken Storybook interface.

Node version is v13.6.0.

@shilman
Copy link
Member

shilman commented Feb 14, 2020

cc @igor-dv @ndelangen

@igor-dv
Copy link
Member

igor-dv commented Feb 14, 2020

Main.js runs during the bootstrap of Storybook it's not something that is bundled with webpack. So if you want to use mjs, you need your node to understand ESM

@gsklee
Copy link
Author

gsklee commented Feb 17, 2020

@igor-dv as indicated I am using Node v13.6.0 which has out-of-box support for ESM. I already have a bunch of other Node CLI scripts in ESM format and they are all working.

@gsklee
Copy link
Author

gsklee commented Feb 17, 2020

Keep in mind for ESM, name of the script will end in .mjs. I am wondering if somewhere in the codebase we are making assumptions about file name extensions which could be the source of the issue.

@gsklee
Copy link
Author

gsklee commented Feb 17, 2020

Tried this and didn't work as well:

export const stories = ["../src/**/*.stories.js"];
export default { stories };

So in both cases (this and the original comment) instead of going to http://localhost:9009/?path=/story/welcome--to-storybook, I ended up with http://localhost:9009/?path=/story/* in an apparently stuck state.

@gsklee
Copy link
Author

gsklee commented Feb 17, 2020

@shilman
Copy link
Member

shilman commented Feb 17, 2020

@gsklee that sounds reasonable. Any chance you can get a dev setup on your local machine, test it out, and provide a PR if it works?

@igor-dv
Copy link
Member

igor-dv commented Feb 17, 2020

@gsklee, sorry, missed your node version.
adding the .mjs to the boost array probably won't work, this array just contains the most used extensions at first place. we use interpret in order to define how to import files with what extensions, and as far as I understand they don't have .mjs gulpjs/interpret#65.

you can probably add .mjs to the /interpret-files.js as a workaround, but the loading is happening here -

const moduleDescriptor = interpret.extensions[candidateExt];
, I am not sure what node will do while using require for mjs though.. this is something that should be checked 🤔

@stale
Copy link

stale bot commented Mar 9, 2020

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@stale stale bot added the inactive label Mar 9, 2020
@shilman
Copy link
Member

shilman commented Mar 9, 2020

cc @yannbf

@yannbf
Copy link
Member

yannbf commented Mar 10, 2020

Hey @gsklee, what @igor-dv said is correct. The boost array only holds the extension names, but interpret has loaders related to every extension, which would be required in order to load .mjs files. I have tried a workaround and it works, which is described in gulpjs/interpret#65 (comment).

However, that is not optimal for your project, given that every time you run npm or yarn install it will override that change. Also, I believe right now storybook only supports .js for their config files.

My suggestion to you is to try the following:
1 - rename your storybook config files (main.mjs, preview.mjs, etc) to use .js instead
2 - if necessary, add a loader or plugin to your webpack.config file that supports .mjs so it can interpret your .mjs components

@stale
Copy link

stale bot commented Apr 1, 2020

Hi everyone! Seems like there hasn't been much going on in this issue lately. If there are still questions, comments, or bugs, please feel free to continue the discussion. Unfortunately, we don't have time to get to every issue. We are always open to contributions so please send us a pull request if you would like to help. Inactive issues will be closed after 30 days. Thanks!

@stale stale bot added the inactive label Apr 1, 2020
@machineghost
Copy link

machineghost commented Apr 6, 2020

A much simpler way to solve ES Module issues is to not use Node, and instead use the "esm" module (from the creator of Lodash).

With that module and Node's -r argument, solving ES module code is as simple as:

npm i esm
node -r esm foo.stories.js

It seems to me the ideal solution here would be to just give the storybook command a --esm option that does that (or rather, the in-code equivalent), or a generic -r option that lets you require any module before running storybook.

EDIT

Actually, while the above would be nice to have, here's a simple workaround for anyone who wants to use ES Modules with Storybook.

Simply npm i esm then change your .storybook/main.js into:

require = require('esm')(module);
module.exports = require('./realMain.js');

Then just move whatever you used to have in main.js to realMain.js, and you're set: realMain.js can import other modules using ESM syntax, without any issues.

@stale stale bot removed the inactive label Apr 6, 2020
@shilman
Copy link
Member

shilman commented Apr 7, 2020

Ooh-la-la!! I just released https://github.com/storybookjs/storybook/releases/tag/v6.0.0-alpha.31 containing PR #10097 that references this issue. Upgrade today to try it out!

You can find this prerelease on the @next NPM tag.

Closing this issue. Please re-open if you think there's still more to do.

@kevinfaveri
Copy link

kevinfaveri commented Jun 25, 2021

Did we had a revert on this fix? It is not recognizing my "main.mjs"... And while we are at it, why not validate the package.json type use the appropriate loader now that all stable versions of node support ESM? Then just always try to get the "main.js"

@saiichihashimoto
Copy link
Contributor

Wanted to poke @shilman here, considering we don't have the ability to reopen this issue. Running into the same issue as @kevinfaveri.

@shilman shilman reopened this Apr 4, 2022
@shilman
Copy link
Member

shilman commented Apr 4, 2022

Cc @yannbf can you take a look?

@JustFly1984
Copy link

@shilman @yannbf I'm running the same issue, while trying to upgrade next.js + storybook + typescript + mdx-js/loader setup

mdx-js/loader uses 'remark-gfm', which is only ESM, and I need to import it in webpack config in main.js, but I can't require esm in cjs module.

@shilman shilman added the linear label Apr 17, 2022
@hieucd04
Copy link

+1

I'm having the same issue!!!

@nataliya-ioffe
Copy link

Same issue...

@aendra-rininsland
Copy link

I'm trying to use "type": "module" in my component library's package.json and not having much luck, I might have to revert and change my entire build setup back to using CJS, just because Storybook doesn't seem to be able to support ESM yet.

Things I've tried:

  • type: module and renaming .storybook/main.js to .storybook/main.cjs
    • Breaks Storyshots testing, gives the following error:
      info => Loading presets
      WARN Unable to find main.js: /Users/aendra.rininsland/Projects/g-components/.storybook/main
      info => Loading custom babel config
      WARN Unable to load config file: /Users/aendra.rininsland/Projects/g-components/.storybook/main
      
  • type: module and converting storybook/main.js to ESM (i.e., export default instead of module.exports =)
    • Gives me the following error:
      ERR! Error [ERR_REQUIRE_ESM]: require() of ES Module /Users/aendra.rininsland/Projects/g-    components/.storybook/main.js from /Users/aendra.rininsland/Projects/g-components/node_modules/@storybook/core-common/dist/cjs/utils/interpret-require.js not supported.
      ERR! Instead change the require of main.js in /Users/aendra.rininsland/Projects/g-components/node_modules/@storybook/core-common/dist/cjs/utils/interpret-require.js to a dynamic import() which is available in all CommonJS modules.
      

aendra-rininsland added a commit to Financial-Times/g-components that referenced this issue Apr 22, 2022
@JustFly1984
Copy link

@aendra-rininsland There is this new kid on the block called ladle. worth checking if they support esm.

@IanVS
Copy link
Member

IanVS commented Apr 25, 2022

@aendra-rininsland

type: module and renaming .storybook/main.js to .storybook/main.cjs
Breaks Storyshots testing, gives the following error:

Can you try updating to the 6.5.0 beta? I have been running my storybook with type: module for the last year with a main.cjs, although I don't use storyshots. But, #17524 should provide full support, which is included in the beta.

@Merri
Copy link

Merri commented Apr 27, 2022

What full support includes? Creating a build for production with 6.5.0 beta is still broken (with main.cjs + Webpack 5 + type: module).

@IanVS
Copy link
Member

IanVS commented Apr 27, 2022

I wasn't aware of that issue, thanks @Merri. I don't use webpack, but it would be awesome if someone could dig into this and find out why that error is happening, and how to fix it.

@aendra-rininsland
Copy link

@JustFly1984 That's not really an appropriate solution to this; even if I wanted to fully change out the infrastructure (I really don't), Ladle doesn't support the .mdx stories my library is written in.

@saiichihashimoto
Copy link
Contributor

@aendra-rininsland i tend to ignore “just use an entirely different product to solve this one use case” advice.

@The-Code-Monkey
Copy link
Contributor

Yeah i feel like storybook should have a solution to this. rather than just saying use something else.

@IanVS
Copy link
Member

IanVS commented May 2, 2022

@The-Code-Monkey the core storybook team is really quite small. If you're able to dig in and help to make a solution to provide better support for esmodules, it would be greatly appreciated!

@The-Code-Monkey
Copy link
Contributor

@IanVS I'm willing to have a look into it, could you possibly point me in the direction of where the main.js file gets loaded normally.

@IanVS
Copy link
Member

IanVS commented May 4, 2022

Awesome! The main file is referenced and loaded in a number of different places, including presets, plugins, and frameworks. Searching for 'main' will be a good way to find them. Feel free to hop into discord and ping me there, if you have questions, and I can try to help out where I can. I think having a good reproduction to work through will be key as well. So far I don't see any here in this issue.

@The-Code-Monkey
Copy link
Contributor

I've actually found the solution is to name the main.js file main.cjs and storybook then handles it correctly

@Hideman85
Copy link

Still not working for me neither, I have the builder-vite, my config in TypeScript and my project in ESM and I cannot start storybook. Whatever I do I run into a babel regenerator esm import stuff that storybook dont like.

(node:10329) UnhandledPromiseRejectionWarning: Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: node_modules/@babel/runtime/helpers/esm/regeneratorRuntime.js
require() of ES modules is not supported.
require() of node_modules/@babel/runtime/helpers/esm/regeneratorRuntime.js from .storybook/main.ts is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename regeneratorRuntime.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from node_modules/@babel/runtime/helpers/esm/package.json.

@shilman
Copy link
Member

shilman commented Jul 25, 2022

Zoinks!! I just released https://github.com/storybookjs/storybook/releases/tag/v7.0.0-alpha.16 containing PR #18648 that references this issue. Upgrade today to the @future NPM tag to try it out!

npx sb upgrade --prerelease

Closing this issue. Please re-open if you think there's still more to do.

@shilman shilman closed this as completed Jul 25, 2022
@shilman
Copy link
Member

shilman commented Jul 26, 2022

ZOMG!! I just released https://github.com/storybookjs/storybook/releases/tag/v6.5.10-alpha.0 containing PR #18648 that references this issue. Upgrade today to the @prerelease NPM tag to try it out!

npx sb upgrade --prerelease

@hrishikesh-k
Copy link

hrishikesh-k commented Jul 29, 2022

EDIT: Fixed this!

I enabled babelModeV7, added a .babelrc file in the root of the project with content:

{
  "presets": [["@babel/preset-env", {
    "modules": "cjs"
  }],"@babel/preset-typescript"]
}

and renamed the file to main.ts. This works.

Note that, I'm using the current stable version with this setup, that is 6.5.9.


Original issue:

Did someone get it working with the pre-release version? It still fails for me.

In my new Vue 3 + Vite project, tried:

npx storybook@future init

which failed as it had some issues with the framework.

Logs for that if it helps:
npx storybook@future init
Need to install the following packages:
  storybook@future
Ok to proceed? (y) y
npm WARN deprecated source-map-url@0.4.1: See https://github.com/lydell/source-map-url#deprecated
npm WARN deprecated stable@0.1.8: Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility
npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
npm WARN deprecated rollup-plugin-inject@3.0.2: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.
npm WARN deprecated source-map-resolve@0.5.3: See https://github.com/lydell/source-map-resolve#deprecated

 storybook init - the simplest way to add a Storybook to your project. 

 • Detecting project type. ✓
    Detected vite project, setting builder to @storybook/builder-vite
 • Adding Storybook support to your "Vue 3" app

attention => Storybook now collects completely anonymous telemetry regarding usage.
This information is used to shape Storybook's roadmap and prioritize features.
You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting the following URL:
https://storybook.js.org/telemetry


     Error: Sorry, for now, you can not do this, please use a framework such as @storybook/react-webpack5

https://github.com/storybookjs/storybook/issues/18360
    at baseGenerator (/Users/hrishikesh/.npm/_npx/10f435f127cf8f73/node_modules/@storybook/cli/dist/cjs/generators/baseGenerator.js:142:11)
    at async generator (/Users/hrishikesh/.npm/_npx/10f435f127cf8f73/node_modules/@storybook/cli/dist/cjs/generators/VUE3/index.js:11:3)
    at async initiate (/Users/hrishikesh/.npm/_npx/10f435f127cf8f73/node_modules/@storybook/cli/dist/cjs/initiate.js:260:3)

So I tried:

npx storybook@prerelease init

Which installed v6.15.10-alpha.0 and created .cjs files automatically inside the .storybook folder. So far, all good.

I tried renaming main.cjs to main.ts and added:

import type {StorybookViteConfig} from '@storybook/builder-vite'

which fails with:

SyntaxError: /Users/hrishikesh/Desktop/project/.storybook/main.ts: Unexpected token, expected "from" (1:12)
ERR! 
ERR! > 1 | import type {StorybookViteConfig} from '@storybook/builder-vite'
ERR!     |             ^
ERR!   2 | module.exports = {
ERR!   3 |   "stories": [
ERR!   4 |     "../src/**/*.stories.mdx",
ERR!     at instantiate (/Users/hrishikesh/Desktop/netlify-support-2/node_modules/@babel/parser/lib/index.js:72:32)
ERR!     at constructor (/Users/hrishikesh/Desktop/netlify-support-2/node_modules/@babel/parser/lib/index.js:359:12)
ERR!     at Parser.raise (/Users/hrishikesh/Desktop/netlify-support-2/node_modules/@babel/parser/lib/index.js:3339:19)
ERR!     at Parser.unexpected (/Users/hrishikesh/Desktop/netlify-support-2/node_modules/@babel/parser/lib/index.js:3377:16)
ERR!     at Parser.expectContextual (/Users/hrishikesh/Desktop/netlify-support-2/node_modules/@babel/parser/lib/index.js:3977:18)
ERR!     at Parser.parseImport (/Users/hrishikesh/Desktop/netlify-support-2/node_modules/@babel/parser/lib/index.js:16310:12)
ERR!     at Parser.parseStatementContent (/Users/hrishikesh/Desktop/netlify-support-2/node_modules/@babel/parser/lib/index.js:14753:27)
ERR!     at Parser.parseStatement (/Users/hrishikesh/Desktop/netlify-support-2/node_modules/@babel/parser/lib/index.js:14643:17)
ERR!     at Parser.parseBlockOrModuleBlockBody (/Users/hrishikesh/Desktop/netlify-support-2/node_modules/@babel/parser/lib/index.js:15286:25)
ERR!     at Parser.parseBlockBody (/Users/hrishikesh/Desktop/netlify-support-2/node_modules/@babel/parser/lib/index.js:15277:10)
ERR!  SyntaxError: /Users/hrishikesh/Desktop/netlify-support-2/.storybook/main.ts: Unexpected token, expected "from" (1:12)

I tried removing type keyword:

ERR! /Users/hrishikesh/Desktop/project/storybook/main.ts:1
ERR! import { StorybookViteConfig } from '@storybook/builder-vite';
ERR! ^^^^^^
ERR! 
ERR! SyntaxError: Cannot use import statement outside a module
ERR!     at Object.compileFunction (node:vm:352:18)
ERR!     at wrapSafe (node:internal/modules/cjs/loader:1033:15)
ERR!     at Module._compile (node:internal/modules/cjs/loader:1069:27)
ERR!     at Module._compile (/Users/hrishikesh/Desktop/netlify-support-2/node_modules/pirates/lib/index.js:136:24)
ERR!     at Module._extensions..js (node:internal/modules/cjs/loader:1159:10)
ERR!     at Object.newLoader [as .ts] (/Users/hrishikesh/Desktop/netlify-support-2/node_modules/pirates/lib/index.js:141:7)
ERR!     at Module.load (node:internal/modules/cjs/loader:981:32)
ERR!     at Function.Module._load (node:internal/modules/cjs/loader:822:12)
ERR!     at Module.require (node:internal/modules/cjs/loader:1005:19)
ERR!     at require (node:internal/modules/cjs/helpers:102:18)

I tried renaming it to main.mjs and now it complains about cannot require ESM.

Note that, my top-level package.json has type: "module".

@shilman
Copy link
Member

shilman commented Aug 4, 2022

Jiminy cricket!! I just released https://github.com/storybookjs/storybook/releases/tag/v6.5.10 containing PR #18648 that references this issue. Upgrade today to the @latest NPM tag to try it out!

npx sb upgrade

@yannbf
Copy link
Member

yannbf commented Aug 4, 2022

Hey peeps! This ☝️ release does not add support for ESM but rather adds a small workaround for CLI starters. If you are looking into having actual ESM support, Storybook v7.0.0-alpha.12 does support it.

@kmannislands
Copy link

I have a limited work-around to import an ESM dependency from the storybook config (CJS, typescript) while waiting on first-class support from storybook.

This should work if your node version is new enough to support async import() of ESM from CJS and you can work with the import being async (in my case this was fine as the dependency was needed in the vite builder integration, which is an async method).

Using import() directly in main.ts gets compiled in my project to require so add this code to your main.ts

/**
 * Use to import deps that are ESM-only. We need to use dynamic/async `import()` to load ESM packages.
 * However, the ts-node embedded in storybook can't disambiguate this case from other imports (and it only works in "moduleResolution":
 * "nodenext") and compiles it to a sync `require` call.
 *
 * So, use this work-around to preserve the native `import()` method by tricking typescript with an eval.
 *
 * @nb This can be removed if storybook starts supporting ESM for its config: https://github.com/storybookjs/storybook/issues/9854
 *
 * @see https://github.com/microsoft/TypeScript/issues/43329#issuecomment-811606238
 */
const dynamicImport = new Function('modulePath', 'return import(modulePath)');

And then use that method to import the ESM dependencies. Don't forget to await the result!

@IanVS
Copy link
Member

IanVS commented May 4, 2023

@kmannislands Storybook 7 includes first-class support for ESM config files!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests