then / is-promise

Test whether an object looks like a promises-a+ promise
MIT License
282 stars 32 forks source link

"exports" config #20

Closed ForbesLindesay closed 4 years ago

ForbesLindesay commented 4 years ago

Please comment on this issue if you are still experiencing issues wen upgrading to 2.2.1

ljharb commented 4 years ago

Repeating my comment from another issue:

If in fact every single possible require path in v2.1 is available in v2.2, then it’s not breaking.

This includes:

is-promise is-promise/index is-promise/index.js is-promise/package is-promise/package.json

Adding exports is intended (by the node modules group) to be a breaking change in almost every case. Adding ESM after that is what’s not supposed to have to be breaking.

FritzTheDev commented 4 years ago

I'm still running into issues with 2.2.1: "No Valid Exports main found for " when I try running npx mrm lint-staged

I won't pretend to understand what the underlying issue is but I can confirm I'm on 2.2.1 at this point.

ForbesLindesay commented 4 years ago

@Rapdash mrm doesn't look like it has is-promise as a dependency

RyanZim commented 4 years ago

@Rapdash What's the output of npm list is-promise?

ForbesLindesay commented 4 years ago

@ljharb all those require paths are available.

FritzTheDev commented 4 years ago

Yarn Why gives me yarn why v1.22.4 [1/4] πŸ€” Why do we have the module "is-promise"...? [2/4] 🚚 Initialising dependency graph... [3/4] πŸ” Finding dependency... [4/4] 🚑 Calculating file sizes... => Found "is-promise@2.2.1" info Reasons this module exists

ljharb commented 4 years ago

@ForbesLindesay not in latest master - each of them would need to be an explicit key in the exports object. CJS resolution doesn’t apply to export paths.

ForbesLindesay commented 4 years ago

Ah, now I understand, not having package.json on that list by default seems like a pretty bad default.

RyanZim commented 4 years ago

@ljharb I get that technically, adding exports removes those import paths. However, it's my understanding that SemVer applies to the documented API. No import path other than is-promise was documented; and in reality, it's highly unlikely that anyone is importing anything other than is-promise.

FritzTheDev commented 4 years ago

@ForbesLindesay Is that last message directed at me a typo or are you actually saying that "is-promise" doesn't depend on "is-promise"?

WhiteJamer commented 4 years ago

I'm tried reinstall create-react-app with: npm install -g --force create-react-app and it fix them.

ForbesLindesay commented 4 years ago

2.2.2 removes "exports" so should fix this.

@Rapdash sorry, meant to say mrm doesn't depend on is-promise. Can you tell me if 2.2.2 fixes your problem.

ljharb commented 4 years ago

Everything reachable is part of the public api imo.

Either way, try requiring your package in node v13.0 and v13.1 and also v13.2. It takes a much more complex exports object to avoid breakage.

If you’re insistent on not reverting and saving it for a v3, I’ll be happy to make a PR in a few hours that exhaustively preserves back compat for all node versions.

FritzTheDev commented 4 years ago

@ForbesLindesay 2.2.2 did the trick! Thank you to everyone who moved so fast on this.

RyanZim commented 4 years ago

@ljharb Yeah, I can certainly see your side of the argument; not sure I 100% agree, but point taken. Will certainly keep that in mind when I add ESM to my own packages. BTW, feels like a blog post/gist about migrating a package from CJS to dual CJS/ESM might be useful to the community, if you're ever in the writing mood. :grinning:

RyanZim commented 4 years ago

@ForbesLindesay You're probably on it, but just in case you're not, https://github.com/then/is-promise/releases should have release notes for the new versions to note the removal of ESM support.

ForbesLindesay commented 4 years ago

@ljharb I've reverted the "exports" bit as a temporary fix. Not being able to add an esm export as a non-breaking change is a pretty big pain point. This could have been resolved with a simple flag to indicate whether all files were importable. Until the situation improves significantly. I will abandon any attempts to support es modules.

ljharb commented 4 years ago

Note that you can import CJS, so the only reason anyone ever needs to take any steps to support ESM is if they’d have named exports, in which case that’s a simple wrapper .mjs file.

Adding β€œexports” is a good idea separate from ESM - it’s just not trivially nonbreaking. I’m still happy to add it for you in a PR in a few hours, if you’re interested.

eemeli commented 4 years ago

If adding "exports" to your package.json, including a mapping "./": "./" will allow all package paths to be exported.

RyanZim commented 4 years ago

the only reason anyone ever needs to take any steps to support ESM is if they’d have named exports

In the far-distant future, I do forsee tooling that will only work with ESM, that will allow some cool static analysis optimization stuff, but that's way out there.

kopax commented 4 years ago

I agree with @RyanZim, ljharb I think a blog post explaining how to deal with ESM properly would be a benediction to everyone distributing packages for node. I've read quite a lot, and even after that, I am not sure what is the appropriate way to deal this properly.

ForbesLindesay commented 4 years ago

@ljharb it would be much better, if you have the time, to update the node.js documentation to add a clear warning about the possible breaking change, preferably before the long, incredibly detailed section on the "Dual Package Hazard". If @eemeli's suggestion is accurate, perhaps a note about it could be added as an example. I read the documentation, and then made this change thinking it was pretty safe, that's the problem that needs fixing. This package can stay as is because I don't want to risk another release at this time.

ForbesLindesay commented 4 years ago

some cool static analysis optimization stuff, but that's way out there.

This is exactly the kind of thing I had in mind. For example, I'm not sure how well Rollup handles CommonJS modules, it certainly didn't when it was first released.

fisker commented 4 years ago

@ForbesLindesay Can you unpublish 2.2.0? So people won't install the wrong version?

ForbesLindesay commented 4 years ago

@fisker done

badmus306 commented 4 years ago

npm install -g --force create-react-app

This fixed it

benwalio commented 4 years ago

just wanted to add my 2c here, this was my call as requested -

npm list -g is-promise
/usr/local/lib
β”œβ”€β”¬ create-react-app@3.4.1
β”‚ └─┬ inquirer@7.0.4
β”‚   └─┬ run-async@2.4.0
β”‚     └── is-promise@2.2.0 
└── is-promise@2.2.2

npm install -g --force create-react-app

this fixed it for me

ljharb commented 4 years ago

@RyanZim given dynamic import, and how rarely dynamic/conditional requires are used, i don't think that's true, since you can already do identical static analysis right now with CJS and ESM.

saparfriday commented 4 years ago

macOS Catalina 10.15.3 Node v14.0.0 is-promise@2.2.2

Unknown error: Error [ERR_INVALID_PACKAGE_TARGET]: Invalid "exports" main target "index.js" defined in the package config /usr/local/lib/node_modules/@angular/cli/node_modules/is-promise/package.json

ForbesLindesay commented 4 years ago

@SapaTech it looks like you still have the outdated is-promise. Can you try doing cat /usr/local/lib/node_modules/@angular/cli/node_modules/is-promise/package.json and checking that the version number is actually 2.2.2?

gnorbsl commented 4 years ago

Still have the issue @ForbesLindesay

$ cat /usr/lib/node_modules/@angular/cli/node_modules/is-promise/package.json
{
  "_from": "is-promise@^2.1.0",
  "_id": "is-promise@2.2.2",
  "_inBundle": false,
  "_integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==",
  "_location": "/@angular/cli/is-promise",
  "_phantomChildren": {},
  "_requested": {
    "type": "range",
    "registry": true,
    "raw": "is-promise@^2.1.0",
    "name": "is-promise",
    "escapedName": "is-promise",
    "rawSpec": "^2.1.0",
    "saveSpec": null,
    "fetchSpec": "^2.1.0"
  },
  "_requiredBy": [
    "/@angular/cli/run-async"
  ],
  "_resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
  "_shasum": "39ab959ccbf9a774cf079f7b40c7a26f763135f1",
  "_spec": "is-promise@^2.1.0",
  "_where": "/usr/lib/node_modules/@angular/cli/node_modules/run-async",
  "author": {
    "name": "ForbesLindesay"
  },
  "bugs": {
    "url": "https://github.com/then/is-promise/issues"
  },
  "bundleDependencies": false,
  "deprecated": false,
  "description": "Test whether an object looks like a promises-a+ promise",
  "devDependencies": {
    "better-assert": "^1.0.2",
    "mocha": "~1.7.4"
  },
  "files": [
    "index.js",
    "index.mjs"
  ],
  "homepage": "https://github.com/then/is-promise#readme",
  "license": "MIT",
  "main": "./index.js",
  "name": "is-promise",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/then/is-promise.git"
  },
  "scripts": {
    "test": "mocha -R spec"
  },
  "version": "2.2.2"
}
ForbesLindesay commented 4 years ago

@gnorbsl It must be some kind of caching in angular then, since there is no exports listed in that package.json file, so it can't be causing the error message you're seeing.

jeremylima commented 4 years ago

I have the same issue creating a new vue js project. I just installed the latest vue js client npm i -g @vue/cli and when creating a project vue create test-project I got this error:

internal/modules/cjs/loader.js:584
            if (e.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') throw e;
                                                            ^

Error [ERR_INVALID_PACKAGE_TARGET]: Invalid "exports" main target "index.js" defined in the package config /usr/local/lib/node_modules/@vue/cli/node_modules/is-promise/package.json
    at resolveExportsTarget (internal/modules/cjs/loader.js:542:13)
    at resolveExportsTarget (internal/modules/cjs/loader.js:581:20)
    at applyExports (internal/modules/cjs/loader.js:455:14)
    at resolveExports (internal/modules/cjs/loader.js:508:23)
    at Function.Module._findPath (internal/modules/cjs/loader.js:632:31)
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:1001:27)
    at Function.Module._load (internal/modules/cjs/loader.js:884:27)
    at Module.require (internal/modules/cjs/loader.js:1074:19)
    at require (internal/modules/cjs/helpers.js:72:18)
    at Object.<anonymous> (/usr/local/lib/node_modules/@vue/cli/node_modules/run-async/index.js:3:17) {
  code: 'ERR_INVALID_PACKAGE_TARGET'
}
cat /usr/local/lib/node_modules/@vue/cli/node_modules/is-promise/package.json
{
  "_from": "is-promise@^2.1.0",
  "_id": "is-promise@2.2.0",
  "_inBundle": false,
  "_integrity": "sha512-N/4ZxZGjDLAWJQNtcq1/5AOiWTAAhDwnjlaGPaC2+p3pQ+Ka2Dl/EL29DppuoiZ8Xr1/p/9ywBGGzHRPoWKfGA==",
  "_location": "/@vue/cli/is-promise",
  "_phantomChildren": {},
  "_requested": {
    "type": "range",
    "registry": true,
    "raw": "is-promise@^2.1.0",
    "name": "is-promise",
    "escapedName": "is-promise",
    "rawSpec": "^2.1.0",
    "saveSpec": null,
    "fetchSpec": "^2.1.0"
  },
  "_requiredBy": [
    "/@vue/cli/listr",
    "/@vue/cli/lowdb",
    "/@vue/cli/run-async"
  ],
  "_resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.0.tgz",
  "_shasum": "3ebfc546cee7064c314686279cc9df7bc2724715",
  "_spec": "is-promise@^2.1.0",
  "_where": "/usr/local/lib/node_modules/@vue/cli/node_modules/lowdb",
  "author": {
    "name": "ForbesLindesay"
  },
  "bugs": {
    "url": "https://github.com/then/is-promise/issues"
  },
  "bundleDependencies": false,
  "deprecated": false,
  "description": "Test whether an object looks like a promises-a+ promise",
  "devDependencies": {
    "better-assert": "^1.0.2",
    "mocha": "^7.1.1"
  },
  "exports": {
    "import": "index.mjs",
    "require": "index.js"
  },
  "files": [
    "index.js"
  ],
  "homepage": "https://github.com/then/is-promise#readme",
  "license": "MIT",
  "main": "index.js",
  "name": "is-promise",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/then/is-promise.git"
  },
  "scripts": {
    "test": "mocha -R spec"
  },
  "type": "module",
  "version": "2.2.0"
}
npm list -g is-promise
/home/jeremy/.npm-global/lib
└─┬ @vue/cli@4.3.1
  β”œβ”€β”¬ @vue/cli-ui@4.3.1
  β”‚ β”œβ”€β”¬ lowdb@1.0.0
  β”‚ β”‚ └── is-promise@2.2.2 
  β”‚ └─┬ vue-cli-plugin-apollo@0.21.3
  β”‚   └─┬ apollo@2.27.3
  β”‚     └─┬ listr@0.14.3
  β”‚       └── is-promise@2.2.2  deduped
  └─┬ inquirer@7.1.0
    └─┬ run-async@2.4.0
      └── is-promise@2.2.2  deduped

I tried npm install -g --force @vue/cli but still doesn't work.

nodejs version: v14.0.0 npm: 6.14.4

MylesBorins commented 4 years ago

I've gone ahead and opened a docs change for exports

https://github.com/nodejs/node/pull/33074

FWIW the section just about the explicit note does mention that the interface will be broken, but I totally agree we should make this clearer / more obvious. A couple of people have gotten bit by the package.json one in the past. The biggest issue with creating any defaults, such as package.json, is that we would then need to introduce an explicit ignore if folks didn't want to include it. This would significantly complicate the parsing of the exports field.

Apologies that you got bit by this and thank you for taking new technology for a spin.

other may disagree but I think that adding a "./": "./" mapping is a very reasonable way to introduce exports as a Semver-Minor change (documented it in the PR), and removing it in a future Semver-Major is a great way to be more intentional about exposed API.

@RyanZim I can understand where you are coming from debating what parts of the interface are part of the "Contract" of an App. While introducing exports could technically be considered Semver-Minor I think practically there is no way to do it without the carve out I've mentioned before. Folks relying on internals sucks as a maintainer... but if you break someones workflow it is broken. I think that one could definitely have ground to stand on to say "we never supported this and if we broke you I'm sorry but thems the breaks"... but it is also easy to just revert the change and reland in a major, which is what it seems is happening here. Anyway, happy to debate Semver some other time, I don't even agree with myself half the time Β―_(ツ)_/Β―

bodograumann commented 4 years ago

For problems with build failures due to caching, I sometimes have to do:

rm ./node_modules/.cache -rf

Please try that if you are still experiencing problems, even if you are 100% sure that you have only v2.2.2 installed.

khaledosman commented 4 years ago

Ran into this issue when building an angular project after installing firebase via ng add @angular/fire

If you have the same issue, manually installing is-promise@2.2.2 fixed it for me via npm i is-promise@2.2.2

previous output of npm list is-promise

➜  craftnote git:(master) βœ— npm list is-promise
craftnote@0.0.0 /Users/khaledosman/Documents/github/craftnote
β”œβ”€β”¬ firebase-tools@8.1.1
β”‚ └─┬ cli-color@1.4.0
β”‚   └─┬ memoizee@0.4.14
β”‚     └── is-promise@2.2.2  deduped
β”œβ”€β”¬ UNMET PEER DEPENDENCY inquirer@7.1.0
β”‚ └─┬ run-async@2.4.0
β”‚   └── is-promise@2.2.2  deduped
└── is-promise@2.2.2

npm ERR! peer dep missing: inquirer@^5.0.0 || ^6.0.0, required by inquirer-autocomplete-prompt@1.0.2
joepie91 commented 4 years ago

@RyanZim

In the far-distant future, I do forsee tooling that will only work with ESM, that will allow some cool static analysis optimization stuff, but that's way out there.

The static analysis thing is commonly mentioned, but as of yet I've seen exactly zero credible arguments as to how ESM code would be more statically analyzable. It's still the same code, just using different import/export syntax; top-level requires are equally statically analyzable, and dynamic requires are just as unanalyzable as dynamic imports. Far as I can tell, it literally doesn't matter.

Really the only reason why tooling would "only work with ESM", is because it's currently the hip thing, the introduction of it has caused an (IMO completely unnecessary and undesirable) ecosystem split, and the author doesn't want to support two module syntaxes; not because of any actual technical reason.

(And I say this as someone who's working on a fair bit of static analysis tooling.)

ForbesLindesay commented 4 years ago

@joepie91 This is mostly true. There aren't any real issues that prevent statically analysing CommonJS, but it definitely requires two implementations, so there are going to be tools going forwards that don't support it.

If you are still having problems with this, you just need to clear your npm (or yarn) cache.

ForbesLindesay commented 4 years ago

Full post mortem writeup: https://medium.com/@forbeslindesay/is-promise-post-mortem-cab807f18dcc?source=friends_link&sk=8d71ecec159dd90f41ddb79b08fbea8d