Xstate: ESM loading fails for native [email protected] loader

Created on 27 Jan 2020  ·  11Comments  ·  Source: davidkpiano/xstate

Description
I'm trying to import xstate using Node@13 native support for ES Modules.

Expected Result
Should be able to import as specified is the docs

Actual Result

The requested module 'xstate' does not provide an export named 'Machine'

Reproduction

Simply run the following code after installing xstate and run it with Node >= 13.0.0

import { Machine } from 'xstate';

Additional context
I've tried both setting the "type": "module" in my package.json and renaming the script file to *.mjs, but the error still persists.

Out of curiosity, I decided to use the import-star syntax to see what happened:

import * as xs from 'xstate';

console.log(xs);

It outputs:

[Module] {
  default: {
    matchesState: [Function: matchesState],
    mapState: [Function: mapState],
    StateNode: [Function: StateNode],
    State: [Function: State] {
      from: [Function (anonymous)],
      create: [Function (anonymous)],
      inert: [Function (anonymous)]
    },
    Machine: [Function: Machine],
    createMachine: [Function: createMachine],
    send: [Function: send],
    sendParent: [Function: sendParent],
    sendUpdate: [Function: sendUpdate],
    assign: [Function (anonymous)],
    doneInvoke: [Function: doneInvoke],
    forwardTo: [Function: forwardTo],
    interpret: [Function: interpret],
    Interpreter: [Function: Interpreter] {
      defaultOptions: [Object],
      interpret: [Function: interpret]
    },
    spawn: [Function: spawn],
    matchState: [Function: matchState],
    actions: {
      raise: [Function: raise],
      send: [Function: send],
      sendParent: [Function: sendParent],
      sendUpdate: [Function: sendUpdate],
      log: [Function: log],
      cancel: [Function (anonymous)],
      start: [Function: start],
      stop: [Function: stop],
      assign: [Function (anonymous)],
      after: [Function: after],
      done: [Function: done],
      respond: [Function: respond],
      forwardTo: [Function: forwardTo],
      escalate: [Function: escalate]
    },
    ActionTypes: {
      Start: 'xstate.start',
      Stop: 'xstate.stop',
      Raise: 'xstate.raise',
      Send: 'xstate.send',
      Cancel: 'xstate.cancel',
      NullEvent: '',
      Assign: 'xstate.assign',
      After: 'xstate.after',
      DoneState: 'done.state',
      DoneInvoke: 'done.invoke',
      Log: 'xstate.log',
      Init: 'xstate.init',
      Invoke: 'xstate.invoke',
      ErrorExecution: 'error.execution',
      ErrorCommunication: 'error.communication',
      ErrorPlatform: 'error.platform',
      ErrorCustom: 'xstate.error',
      Update: 'xstate.update',
      Pure: 'xstate.pure'
    },
    SpecialTargets: { Parent: '#_parent', Internal: '#_internal' }
  }
}

So it looks like even though state has an ESM target, it doesn't seem it's being properly loaded, because this looks like the CommonJS exported module.

For reference, here's my package.json:

{
  "name": "xstate-labs",
  "version": "1.0.0",
  "module": "index.js",
  "license": "MIT",
  "type": "module",
  "dependencies": {
    "xstate": "^4.7.7"
  }
}

Tested with [email protected].

bug

Most helpful comment

Just an update - we'll be providing "exports" field in the future in the XState 5. It's not yet implemented and nor v5 is ready to be shipped even as a prerelease but at least I have a plan now to make this happen. At this point in time, pkg.json#exports semantics seem to be pretty stable as webpack is going to ship support for it soon-ish, so it should be safe for us to start using that in the future.

All 11 comments

Thanks for the report - I will take a look later to see what's the best way to support this.

Just a little more context:

I tried to pair it up with typescript@3.7.5 to see if I could get rid of the error.

However, after running ts-node index.ts with node@13.7.0, I got the following error:

(node:4678) ExperimentalWarning: The ESM module loader is experimental.
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /home/henrique/labs/xstate-labs/src/index.ts
    at Loader.defaultGetFormat [as _getFormat] (internal/modules/esm/get_format.js:71:15)
    at Loader.resolve (internal/modules/esm/loader.js:98:42)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at Loader.getModuleJob (internal/modules/esm/loader.js:188:29)
    at Loader.import (internal/modules/esm/loader.js:163:17)

Then I downgraded to node@12.14.1 it works just fine with TypeScript.

I'm guessing this is more of a general problem with node@13 ESM loader.

I've educated myself about ESM support in node@13 and I think I understand its intricacies now. It's not as easy to support it as I have thought - when you also want to support older nodes, bundlers etc (and we do).

I will explore making it work in v5, but at the moment I don't think it's worth it to include this in v4.

If you want to patch your node_modules and xstate in it then I think this should work if you add this to pkg.json

"type": "module",
"exports": "./es/index.js"

I get the same problem with node v13.9.0 and xstate 4.8.0.

SyntaxError: The requested module 'xstate' does not provide an export named 'interpret'

Any developments on this issue?

Literally the previous comment says that we wont work on this right now and that we are going to tackle this in upcoming v5

With that said, I'd accept a PR from someone who wants to fix this for the current version.

Problem is that this would be a breaking change for all of our ESM-node consumers - im not sure if it would be a good thing to “fix” this within v4 for that reason because we know that this would break all of those consumers immediately.

So, in the meantime what is your advice to keep things gong? Revert to a previous version of node.js? Which, by the way?

In the meantime, found this workaround:

npm install esm
node -r esm xstateTest.js

Also added this line to launch.json node xstateTest.js launch configuration:
"runtimeArgs": ["-r", "esm"]

Sorry for not responding sooner, I've missed your reply.

So, in the meantime what is your advice to keep things gong?

  1. use CJS
  2. or use default export for now
  3. or use esm loader like you have done.

Just an update - we'll be providing "exports" field in the future in the XState 5. It's not yet implemented and nor v5 is ready to be shipped even as a prerelease but at least I have a plan now to make this happen. At this point in time, pkg.json#exports semantics seem to be pretty stable as webpack is going to ship support for it soon-ish, so it should be safe for us to start using that in the future.

Was this page helpful?
0 / 5 - 0 ratings