Skip to content

[bug] pkg.main, pkg.module are incompatible, so require("sortablejs") behaves differently under Rollup/Webpack #1891

Description

@andersk

In some environments like Node and Browserify, require("sortablejs") returns Sortable, but in others like Rollup and Webpack, require("sortablejs") returns a module { Sortable: Sortable, default: Sortable, … }. This means a SortableJS dependent can’t be compatible with these different environments without contortions.

$ npm i browserify rollup @rollup/plugin-commonjs @rollup/plugin-node-resolve sortablejs webpack webpack-cli
$ echo 'console.log(require("sortablejs"))' > src.js
$ node src.js
[Function: Sortable] {
  utils: { … },
  get: [Function (anonymous)],
  mount: [Function (anonymous)],
  create: [Function (anonymous)],
  version: '1.10.2'
}
$ npx browserify src.js > out.js
$ node out.js
[Function: Sortable] {
  utils: { … },
  get: [Function (anonymous)],
  mount: [Function (anonymous)],
  create: [Function (anonymous)],
  version: '1.10.2'
}
$ npx rollup src.js -p node-resolve -p commonjs -f cjs -o out.js
$ node out.js
[Object: null prototype] {
  default: [Function: Sortable] {
    utils: { … },
    get: [Function (anonymous)],
    mount: [Function (anonymous)],
    create: [Function (anonymous)],
    version: '1.10.2'
  },
  MultiDrag: [Function: MultiDragPlugin],
  Sortable: [Function: Sortable] {
    utils: { … },
    get: [Function (anonymous)],
    mount: [Function (anonymous)],
    create: [Function (anonymous)],
    version: '1.10.2'
  },
  Swap: [Function: SwapPlugin]
}
$ npx webpack -d
$ node dist/main.js
Object [Module] {
  MultiDrag: [Getter],
  Sortable: [Getter],
  Swap: [Getter],
  default: [Function: Sortable] {
    utils: { … },
    get: [Function (anonymous)],
    mount: [Function (anonymous)],
    create: [Function (anonymous)],
    version: '1.10.2'
  }
}

This is because Node and Browserify are using pkg.main (./Sortable.js), while Rollup and Webpack are using pkg.module (modular/sortable.esm.js), and the two files do not provide compatible interfaces.

This problem is called out in the Rollup documentation:

Note: There are some tools such as Babel, TypeScript, Webpack, and @rollup/plugin-commonjs that are capable of resolving a CommonJS require(...) call with an ES module. If you are generating CommonJS output that is meant to be interchangeable with ESM output for those tools, you should always use named export mode. The reason is that most of those tools will by default return the namespace of an ES module on require where the default export is the .default property.

Three potential solutions are:

  • use named export mode, so require("sortablejs").default or require("sortablejs").Sortable would work everywhere; or
  • add Sortable.default = Sortable and/or Sortable.Sortable = Sortable for compatibility; or
  • remove pkg.module.

Information

Versions - Look in your package.json for this information:
sortablejs = ^1.10.2

Activity

  1. changed the title [-][bug] pkg.main, pkg.module are incompatible, so require("sortablejs") behaves differently under Webpack[/-] [+][bug] pkg.main, pkg.module are incompatible, so require("sortablejs") behaves differently under Rollup/Webpack[/+] on Aug 29, 2020
  2. waynevanson commented on Aug 31, 2020

    @waynevanson
    Contributor

    I think option 3 sounds like the best option, but need to ensure no changes are breaking. I've also experienced troubles with this.

    So are main and module supposed to be the same?

    Some information:

  3. andersk commented on Sep 15, 2020

    @andersk
    Author

    Now with sortablejs@1.11.2-alpha.3 (did you mean to tag an alpha as latest?), require("sortablejs") doesn’t work at all in Node because pkg.main == "dist/sortable.js" is missing entirely.

    $ npm i sortablejs
    
    + sortablejs@1.11.2-alpha.3
    added 1 package and audited 1 package in 0.542s
    found 0 vulnerabilities
    
    $ node -e 'require("sortablejs")'
    internal/modules/cjs/loader.js:328
          throw err;
          ^
    
    Error: Cannot find module '/tmp/test/node_modules/sortablejs/dist/sortable.js'. Please verify that the package.json has a valid "main" entry
        at tryPackage (internal/modules/cjs/loader.js:320:19)
        at Function.Module._findPath (internal/modules/cjs/loader.js:533:18)
        at Function.Module._resolveFilename (internal/modules/cjs/loader.js:879:27)
        at Function.Module._load (internal/modules/cjs/loader.js:742:27)
        at Module.require (internal/modules/cjs/loader.js:964:19)
        at require (internal/modules/cjs/helpers.js:88:18)
        at [eval]:1:1
        at Script.runInThisContext (vm.js:132:18)
        at Object.runInThisContext (vm.js:309:38)
        at Object.<anonymous> ([eval]-wrapper:10:26) {
      code: 'MODULE_NOT_FOUND',
      path: '/tmp/test/node_modules/sortablejs/package.json',
      requestPath: 'sortablejs'
    }
    
    $ ls node_modules/sortablejs/dist
    sortable.umd.js  sortable.umd.js.map
  4. liuestc commented on Sep 16, 2020

    @liuestc

    I meet the same question , how to solve it?

  5. liuestc commented on Sep 16, 2020

    @liuestc

    I meet the same question , how to solve it?

    I solved it by
    import Sortable from 'sortablejs/dist/sortable.umd';

  6. waynevanson commented on Sep 18, 2020

    @waynevanson
    Contributor

    I'm pretty sure this was fixed by #1913, but looking closer it looks like I missed out on packaging Main.. Fixing now.

  7. waynevanson commented on Sep 18, 2020

    @waynevanson
    Contributor

    @liuestc Try it without the work around, it should work with version alpha-4.

    happens recently because of #1920

  8. andersk commented on Sep 19, 2020

    @andersk
    Author

    Still broken in 1.11.2-alpha.4 in a sort of reversed way:

    • in Node, require("sortablejs") returns a module {MultiDrag, Sortable, Swap, default};
    • in Browserify and Webpack, require("sortablejs") returns a function;
    • Rollup warns that sortablejs is an unresolved dependency and leaves in the require statement.

    This time pkg.module == "dist/sortable.module.js" doesn’t exist, and pkg.browser == "dist/sortable.umd.js" is incompatible with pkg.main == "dist/sortable.js".

  9. waynevanson commented on Sep 19, 2020

    @waynevanson
    Contributor

    Yeah okay thank you for your effort. I'll fix it tomorrow, don't have my computer one me right now.

  10. waynevanson commented on Sep 19, 2020

    @waynevanson
    Contributor

    Do we even need the main field?
    I think we can use module field and browser field.

    https://docs.npmjs.com/files/package.json#browser

  11. andersk commented on Sep 19, 2020

    @andersk
    Author

    Without the main field, it won’t be possible to require("sortablejs") in Node at all. You might assume there’s no use case for this, but it comes up when writing unit tests for parts of modules that also happen to load sortablejs somewhere in their recursive imports; this is the case in Zulip.

  12. waynevanson commented on Sep 20, 2020

    @waynevanson
    Contributor

    @andersk Thank you for claryifying.

    Almost done fixing the screw ups btw

  13. self-assigned this
    on Sep 21, 2020
  14. added a commit that references this issue on Sep 21, 2020
    7711207
  15. andersk commented on Sep 21, 2020

    @andersk
    Author

    Thanks for your attention to this, but it’s still broken in 1.12.0; it’s back to what I wrote in the original description. Please reopen this.

  16. waynevanson commented on Sep 21, 2020

    @waynevanson
    Contributor

    @andersk How about now? I removed the module and it now just Main.

  17. andersk commented on Sep 21, 2020

    @andersk
    Author

    I expect that will work.

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions