parcel-bundler/parcel

Use `package.json#exports` map

mischnic opened this issue Β· 55 comments

phaux commented

Also, conditional exports could be used while bundling to specify the dist paths. They could replace package.json#{main,module,browser} if the package.json#exports key exists.

Example:

{
  "exports": {
    ".": {
      "require": "./dist/node/index.js",
      "import": "./dist/module/index.js",
      "browser": "./dist/browser/index.js"
    }
  }
}

Would be the same as:

{
  "main": "./dist/node/index.js",
  "module": "./dist/module/index.js",
  "browser": "./dist/browser/index.js",
}

If nodejs/node#32869 lands, this likely should include support for NODE_ENV based resolution ("production" and "development").

Some links to implementations/details in other tools/bundlers (more to come):

rsthn commented

This would be actually awesome! I was recently refactoring code and used the "exports" field, and I didn't know parcel didn't support this.

yes i think as a work around i have to peek into the package and import from the global scope :(

Any plan for supporting this? There have been so many changes on the V2 branch that it is hard to track the progress.

At the moment we're focused on blockers for a stable v2 release in the next month or so. This will likely be sometime after that.

As a quick work around, I made this fallback resolver: @kkirbatski/parcel-resolver-require-resolve

import { Resolver } from '@parcel/plugin';

export default new Resolver({
	async resolve({ filePath }) {
		try {
			return {
				filePath: require.resolve(filePath),
			};
		} catch (e) {
			return null;
		}
	},
});

Now that we have Parcel 2, is this on the priority? Solid-js depends on this feature.

Packages like mem@9.0.1 don't contain a "main" (or module, ...) field at all, but only

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

So currently you can't import it with Parcel: Failed to resolve 'mem' from './index.js

Unfortunately mozilla/glean is also not usable with Parcel because of this.

Hey folks, this is very important: basically Parcel cannot use modules which support both common.js and ESM. We're forced to revert and drop support for ESM because of that.

FWIW, I looked into implementing this not long ago, using the library linked above. I couldn't figure out where it should go in the resolution algorithm though. Node has two different algorithms for CommonJS and ESM, but bundlers usually do more interop so we will have to diverge. In addition, there are many different conditions that are possible, and it's not obvious whether it should occur before or after other resolution features like aliases. More research is required to see how other bundlers behave so that we don't fragment the ecosystem even more.

basically Parcel cannot use modules which support both common.js and ESM

This has been possible for a long time, even without the exports field. The main and module field already work today. If that's the only thing you need it should work with Parcel and other tools just fine. If you need other kinds of conditions, then it's more complicated.

Vuetify 3, for example, won't work. (import 'vuetify/styles')
Thanks

I found a (manual) workaround to continue using Parcel even with these dependencies:

Add an alias entry in package.json

{
	"alias": {
 		"mem": "./node_modules/mem/dist/index.js"
	},
	"dependencies": {
		"mem": "^9.0.1"
	}
}

Live example: https://github.com/refined-github/refined-github/blob/fa2eb1c952da8d03672d19866782652474dae884/package.json#L129

Every time a new dependency starts using exports, you'll have add it here.

Alternatively, kkirby’s plugin also looks interesting, but I haven't tried it.

@devongovett

FWIW, I looked into implementing this not long ago, using the library linked above. I couldn't figure out where it should go in the resolution algorithm though.

The package I needed this for was swiper. The package.json contained an exports directive for resolving the files of the package. To get parcel to resolve it, I installed my plugin and created a .parcelrc file in my project root. Inside it contained this:

{
	"extends": "@parcel/config-default",
	"resolvers": [
		"...",
		"@kkirbatski/parcel-resolver-require-resolve"
	]
}

I'm not really familiar with these new mechanisms for resolving packages, but hopefully this helps a little.

This is causing issues with using Preact in the Parcel development server:

Server running at http://localhost:1234
🚨 Build failed.

@parcel/core: Failed to resolve 'preact/jsx-dev-runtime' from './src/app.jsx'

  /Users/joe/code/playground/parcel-preact-error/src/app.jsx:1:1
  > 1 | export default function App() {
  >   | ^
    2 |   return <div>This is content</div>;
    3 | }

Preact specifies what preact/jsx-dev-runtime resolves to via its package.json#exports.

Repo with reproduction.

The alias workaround mentioned in earlier comments works, but it's disappointing that Parcel's JSX docs say Preact works out of the box.

it's disappointing that Parcel's JSX docs say Preact works out of the box.

Well, it did work out of the box until Preact broke it I guess. πŸ˜‰

Hmm preact also has a package.json in the jsx-runtime directory, but not in jsx-dev-runtime: https://unpkg.com/browse/preact@10.6.5/jsx-runtime/package.json. Maybe we should only do this for react and not preact?

https://github.com/parcel-bundler/parcel/blob/v2/packages/transformers/js/src/JSTransformer.js#L673

@devongovett jsx-dev-runtime is just an alias to jsx-runtime on our end. The mapping happens via the exports field in package.json. The nested package.json files were a trick on our end to have multiple entries into an npm package before the exports field was made a standard a few years back. Afaik only browserify and unpkg make use of that as esbuild, vite, rollup and webpack resolve via the exports field.

I just tried Parcel 2 for the first time and was very impressed with how reasonably it self-configures using the information in package.json - but yes, I needed exports in order to make my package loadable both from Node and the browser. I got around it by just redundantly adding main and module to trigger the actual builds:

  "source": "src/index.ts",
  "main": "dist/index.js",
  "module": "dist/index.mjs",
  "types": "dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.js"
    }
  },

It's probably a bit fragile because of the repeated paths, but it seems to work for now.

I had to switch to another bundler because I really needed the exports field to work :( it's a large module with many entries. I was enjoying Parcel a lot though :(

I had to switch to another bundler because I really needed the exports field to work :( it's a large module with many entries. I was enjoying Parcel a lot though :(

Whatever you switched to, you're almost definitely going to have more configuration overall - not less. πŸ™‚

Which is why we're still waiting for for this issue to be resolved, even after two years and two months.

Anyone is welcome to contribute!

I don't know when the resolver plugin spec changed, but at some point the resolver passed a filePath but now it passes as specifier. So if you had issues with my plugin previously, it may work now on version 1.0.4. https://www.npmjs.com/package/@kkirbatski/parcel-resolver-require-resolve

Fwiw @kkirby, it changed at aa21e89 (i.e. ~2 months before the 2.0.0 release, but not in any beta or RC, so unpublished at the time you initially created your plugin ; I suppose you were still using such non-final version up until a couple weeks ago)

I just tried Parcel 2 for the first time and was very impressed with how reasonably it self-configures using the information in package.json - but yes, I needed exports in order to make my package loadable both from Node and the browser. I got around it by just redundantly adding main and module to trigger the actual builds:

  "source": "src/index.ts",
  "main": "dist/index.js",
  "module": "dist/index.mjs",
  "types": "dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.js"
    }
  },

It's probably a bit fragile because of the repeated paths, but it seems to work for now.

@mindplay-dk How would you do it to have the ability to import multiple items from the same NPM module? In my case, I need to be able to import a lot of different files from the package directly & independently (it's a UI library). That's the reason I'm using exports like:

"exports": {
    ".": "./_dist/index.js",
    "./*": "./_dist/*"
}

Which unfortunately doesn't seem to work with Parcel. It does work with Webpack, but I would much rather be using Parcel.

How would you do it to have the ability to import multiple items from the same NPM module?

You can't. That's part of the reason the exports field was added. It is a more powerful alternative.

I'm using the work-around using an alias for this problem but I'm so far about 50 aliases in and I have no idea when I'm done. This is bonkers.

This issue is also blocking my team’s ability to upgrade a key package (https://www.npmjs.com/package/@popeindustries/lit-html). Since exports is now part of the package.json standard I expect these problems will multiply over time as more libraries start using the field. The lack of support in Parcel will very likely force my team to switch to a different bundler that supports the standard, despite us being big fans of Parcel.

You are also welcome to contribute.

I am once again attempting to work on this along with some other resolver features (e.g. tsconfig paths). There are a lot of edge cases though, and every tool I've tried has subtle bugs and differences. Will share more updates as I progress.

@devongovett I was made aware of https://github.com/privatenumber/resolve-pkg-maps today, might be worth a look? It explicitly verifies its results against node's results and includes tests against at least resolve.exports's known bugs.

/cc @privatenumber

(this is assuming your resolver is written in JS and not Rust πŸ˜… haven't checked)

Oh nice. It is in Rust but I might be able to reuse some tests from that.

@devongovett I also had issues regarding exports declarations in a package.json file. The small plugin from @kkirby ist working very well. Maybe you should consider to implement this fallback to the default parcel config.

Example

packages/shared:

  "name": "@shared",
  "exports": {
    "./utils": "./build/utils/index.js",
  }

packages/app:

  "name": "@app",
  "dependencies": {
    "@shared": "1.0.0"
  }
  // somewhere in the app
  import { foo } from "@shared/utils"

#8807 implements package exports support, and will be included in the next release soon.

This is now released in nightly. Would be great if you all could try it out in your projects and report back if it is working before the full release!

This is now released in nightly. Would be great if you all could try it out in your projects and report back if it is working before the full release!

Great news!

I'll add some comments here if I experience issues with it.

I'm not quite sure yet how big of a pain point this will be for us and if it's worth the effort to report this as a proper issue with a reproduction, but one issue that I encountered already is the following:

daniel@Daniels-MacBook-Pro vanilla-js % yarn dev
Server running at http://localhost:3000
🚨 Build failed.

@parcel/core: Failed to resolve '@depict-ai/ui/locales' from '/Users/daniel/Documents/depict.ai/browser-tags-v2/packages/js-ui/locales/index.js'

  /Users/daniel/Documents/depict.ai/browser-tags-v2/packages/js-ui/locales/index.js:1:15
  > 1 | export * from "@depict-ai/ui/locales";
  >   |               ^^^^^^^^^^^^^^^^^^^^^^^^
    2 | 

@parcel/resolver-default: Could not find extended tsconfig

  /Users/daniel/Documents/depict.ai/browser-tags-v2/packages/js-ui/tsconfig.json:2:14
    1 | {
  > 2 |   "extends": "../../tsconfig_base",
  >   |              ^^^^^^^^^^^^^^^^^^^^^
    3 |   "compilerOptions": {
    4 |     "composite": true,

@parcel/resolver-default: Cannot load file '../../tsconfig_base' in '../../browser-tags-v2/packages/js-ui'.
πŸ’‘ Did you mean './tsconfig'?

This worked before (on 2.8.3).

The setup is the following:

  1. /Users/daniel/Documents/depict.ai/browser-tags-v2 is a yarn workspace with different packages in the packages folder
  2. The parcel instance that runs in this example from vanilla-js is not part of the browser-tags-v2 workspace. It uses yarn link though to link all packages in the yarn workspace so that the versions on the filesystem are used instead of the published ones on npm.
  3. We're using yarn 3.3.1 everywhere
  4. In /Users/daniel/Documents/depict.ai/browser-tags-v2/packages/js-ui/package.json (the package where parcel complains about not being able to find @depict-ai/ui/locales the UI package is included like this: "@depict-ai/ui": "workspace:^" in the dependencies section
  5. When importing @depict-ai/ui from vanilla-js directly (import * as locales from "@depict-ai/ui/locales";) it works

So parcel now has issues resolving workspace packages in workspaces it isn't ran in if that makes sense.

Next issue:

@devongovett I'd love to hear from you if this is a supported use case or if I'm just doing illegal stuff here (also contains actual regression:

We're publishing a package that exports both TypeScript code and a SCSS module (for styling).

It would be really nice to be able to just import the TypeScript stuff as import { SearchPage } from "@depict-ai/js-ui"; and the styling as @use "@depict-ai/js-ui";.

To do that, I've put this in the package.json file of @depict-ai/js-ui:

"sass": "index.scss",
"exports": {
    ".": {
      "import": "./dist/module.js",
      "require": "./dist/main.js",
      "types": "./dist/index.d.ts",
      "default": "./dist/module.js",
      "sass": "./index.scss"
    },
    "./locales": {
      "types": "./locales/index.d.ts",
      "import": "./locales/index.js",
      "require": "./locales/require.js",
      "default": "./locales/index.js"
    }
  },

(I got the inspiration for the sass entry from bootstrap)

Unfortunately though, it doesn't work - parcel tries to import the JavaScript into SCSS instead (it does work on packages though that only export scss):

daniel@Daniels-MacBook-Pro vanilla-js % yarn dev
Server running at http://localhost:3000
🚨 Build failed.

@parcel/transformer-sass: expected "{".
  β•·
1 β”‚ import {add_to_version_info as $gXbnb$add_to_version_info, catchify as $gXbnb$catchify, href_change_ipns as $gXbnb$href_change_ipns, Tenant as $gXbnb$Tenant, GenericSliderArrowButton as $gXbnb$GenericSliderArrowButton, RecommendationSlider as $gXbnb$RecommendationSlider, make_placeholder_rr as 
$gXbnb$make_placeholder_rr} from "@depict-ai/utilishared";
  β”‚                                                                                                                                                                                                                                                                                                               
                   ^
  β•΅
  ../../browser-tags-v2/packages/js-ui/dist/module.js 1:322  @use
  src/depict-ui.scss 2:1                                     @use
  src/styling.scss 1:1                                       root stylesheet

  Error: expected "{".
    β•·
  1 β”‚ import {add_to_version_info as $gXbnb$add_to_version_info, catchify as $gXbnb$catchify, href_change_ipns as $gXbnb$href_change_ipns, Tenant as $gXbnb$Tenant, GenericSliderArrowButton as $gXbnb$GenericSliderArrowButton, RecommendationSlider as $gXbnb$RecommendationSlider, make_placeholder_rr as 
  $gXbnb$make_placeholder_rr} from "@depict-ai/utilishared";
    β”‚                                                                                                                                                                                                                                                                                                             
                       ^
    β•΅
    ../../browser-tags-v2/packages/js-ui/dist/module.js 1:322  @use
    src/depict-ui.scss 2:1                                     @use
    src/styling.scss 1:1                                       root stylesheet
      at Object.wrapException (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:1250:17)
      at Object.throwWithTrace0 (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:23399:15)
      at ScssParser0.wrapSpanFormatException$1$1 (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:91071:13)
      at ScssParser0.wrapSpanFormatException$1 (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:91077:19)
      at ScssParser0.parse$0 (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:95148:19)
      at Object.Stylesheet_Stylesheet$parse0 (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:22778:56)
      at /Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:71275:63
      at _wrapJsFunctionForAsync_closure.$protected (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:3841:15)
      at _wrapJsFunctionForAsync_closure.call$2 (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:28694:12)
      at _awaitOnObject_closure.call$1 (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:28682:32)



Regression in 2.0.0-nightly.1251

A regression of the new exports system is that it's not possible anymore to import the styling by explicitly specifying a file name (to workaround the error message shown above):

Let me know if you need a better reproduction for this.

I will try to create a styling.scss file in the meantime and see if that works instead.

daniel@Daniels-MacBook-Pro vanilla-js % yarn dev
Server running at http://localhost:3000
🚨 Build failed.

@parcel/transformer-sass: Can't find stylesheet to import.
  β•·
2 β”‚ @use "@depict-ai/js-ui/index.scss" as plp-styling;
  β”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  β•΅
  src/depict-ui.scss 2:1  @use
  src/styling.scss 1:1    root stylesheet

  Error: Can't find stylesheet to import.
    β•·
  2 β”‚ @use "@depict-ai/js-ui/index.scss" as plp-styling;
    β”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    β•΅
    src/depict-ui.scss 2:1  @use
    src/styling.scss 1:1    root stylesheet
      at Object.wrapException (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:1250:17)
      at /Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:71174:25
      at _wrapJsFunctionForAsync_closure.$protected (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:3841:15)
      at _wrapJsFunctionForAsync_closure.call$2 (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:28694:12)
      at _awaitOnObject_closure.call$1 (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:28682:32)
      at Object._rootRunUnary (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:4216:18)
      at StaticClosure.<anonymous> (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:100086:16)
      at _CustomZone.runUnary$2$2 (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:30091:39)
      at _Future__propagateToListeners_handleValueCallback.call$0 (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:29163:51)
      at Object._Future__propagateToListeners (/Users/daniel/Documents/depict.ai/storefronts/vanilla-js/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:4012:93)

@danieltroger thanks so much for testing! Really great to get feedback from real-world projects – it's really hard to cover all possible project setups in our integration tests so this is very helpful.

You have some interesting cases here. The first one looks related to tsconfig. It's trying to find the "../../tsconfig_base" file referenced in the "extends" field from /Users/daniel/Documents/depict.ai/browser-tags-v2/packages/js-ui/tsconfig.json, so /Users/daniel/Documents/depict.ai/browser-tags-v2/tsconfig_base. I think this is perhaps a bug where we need to append .json to that if it is omitted.

The second issue about SASS is because with package.json exports, only files declared in the exports map are allowed to be imported, so where it would previously have resolved to index.scss it won't anymore. The "sass" export condition isn't yet supported, so it resolves to the "default" export. We could probably support the "sass" condition though, not sure if other tools do.

Third issue is the same as the second. Importing directly from index.scss won't work with package.json exports unless it is explicitly declared as an export. This is also how node and other tools work, but does make me a little nervous about introducing this change in a minor version...

The first one looks related to tsconfig. It's trying to find the "../../tsconfig_base" file referenced in the "extends" field from /Users/daniel/Documents/depict.ai/browser-tags-v2/packages/js-ui/tsconfig.json, so /Users/daniel/Documents/depict.ai/browser-tags-v2/tsconfig_base. I think this is perhaps a bug where we need to append .json to that if it is omitted.

Yes, the tsconfig error looks like that's probably it.
Do you think it's unable to find the locales export though because it can't correctly read the tsconfig? Why does it need to understand the tsconfig?

We could probably support the "sass" condition though, not sure if other tools do.

Not sure, but some tool should because bootstrap does it, I assume?
I think it would be awesome if you could support it.

The second issue about SASS is because with package.json exports, only files declared in the exports map are allowed to be imported

Why does this not work then?
parcel-repro.zip

(Unzip, yarn install, yarn build)

It yields:

daniel@Daniels-MacBook-Pro parcel-repro % yarn build
🚨 Build failed.

@parcel/transformer-sass: Can't find stylesheet to import.
  β•·
1 β”‚ @use "@depict-ai/react-ui/styles/index.scss" as search-styling;
  β”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  β•΅
  src/styling.scss 1:1  root stylesheet

  Error: Can't find stylesheet to import.
    β•·
  1 β”‚ @use "@depict-ai/react-ui/styles/index.scss" as search-styling;
    β”‚ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    β•΅
    src/styling.scss 1:1  root stylesheet
      at Object.wrapException (/private/tmp/parcel-repro/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:1250:17)
      at /private/tmp/parcel-repro/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:71174:25
      at _wrapJsFunctionForAsync_closure.$protected (/private/tmp/parcel-repro/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:3841:15)
      at _wrapJsFunctionForAsync_closure.call$2 (/private/tmp/parcel-repro/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:28694:12)
      at _awaitOnObject_closure.call$1 (/private/tmp/parcel-repro/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:28682:32)
      at Object._rootRunUnary (/private/tmp/parcel-repro/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:4216:18)
      at StaticClosure.<anonymous> (/private/tmp/parcel-repro/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:100086:16)
      at _CustomZone.runUnary$2$2 (/private/tmp/parcel-repro/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:30091:39)
      at _Future__propagateToListeners_handleValueCallback.call$0 (/private/tmp/parcel-repro/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:29163:51)
      at Object._Future__propagateToListeners (/private/tmp/parcel-repro/.yarn/cache/sass-npm-1.58.3-8afd137ed0-35a2b98c03.zip/node_modules/sass/sass.dart.js:4012:93)

Why I think this should work:

@depict-ai/react-ui@0.0.78 has

"exports": {
   "./styles/": "./dist/styles/",
}

in its package.json.
Also I'd love @use "@depict-ai/react-ui/styles/" as search-styling; to work.

How would you recommend me solve my sass exporting use case?

This is also how node and other tools work, but does make me a little nervous about introducing this change in a minor version...

Indeed. I think it could break lots of things and there doesn't seem to be a workaround for that breakage other than changing the imported package?

Why does it need to understand the tsconfig?

This is to support another resolver feature, which respects the baseUrl and paths declared in tsconfig.json. Kind of unrelated to package exports, just something else that happens while trying to resolve.

Not sure, but some tool should because bootstrap does it, I assume?

Bootstrap uses the top-level "sass" field in package.json it seems: https://github.com/twbs/bootstrap/blob/5241b988c0156dbcc2c6bdaeb29967141bea567d/package.json#L39 I don't see any exports in their package.json. But I do see that sass-loader in webpack does support the "sass" condition, so probably worth supporting.

Why I think this should work:

"exports": {
   "./styles/": "./dist/styles/",
}

Ah this is using a deprecated syntax in package exports for exporting directories. Node deprecated this syntax in Node 14, and fully removed it in Node 17: https://nodejs.org/api/deprecations.html#DEP0148. I thought since Parcel's support for this is brand new we could get away without supporting it. The new syntax is:

"exports": {
   "./styles/*": "./dist/styles/*",
}

See https://nodejs.org/api/packages.html#subpath-patterns.

How would you recommend me solve my sass exporting use case?

I think supporting the "sass" export condition is probably the right way. Alternatively you could add "./index.scss" to your package exports.

Indeed. I think it could break lots of things and there doesn't seem to be a workaround for that breakage other than changing the imported package?

Well, it's a bit of a strange case because most packages on npm would already have had to deal with this if they supported node or other tools like webpack for a while. For internal packages only ever loaded by parcel, package exports were never supported before so it is unlikely that they would have them.

This is to support another resolver feature, which respects the baseUrl and paths declared in tsconfig.json. Kind of unrelated to package exports, just something else that happens while trying to resolve.

I see. I tried adding .json everywhere after tsconfig_base and can confirm that I now no longer have any workspace related issues, so it seems to just be that lil bug!

Another thing I had to do was changing the following but now everything works again. Just FYI. Change to fix:
diff --git a/storefronts/vanilla-js/babel.config.json b/storefronts/vanilla-js/babel.config.json
index 733ceb3e41..b002b1838a 100644
--- a/storefronts/vanilla-js/babel.config.json
+++ b/storefronts/vanilla-js/babel.config.json
@@ -5,7 +5,7 @@
       {
         "throwIfNamespace": false,
         "runtime": "automatic",
-        "importSource": "~/"
+        "importSource": "~"
       }
     ]
   ]

Error message without it:

🚨 Build failed.

@parcel/core: Failed to resolve '~//jsx-runtime' from './src/scripts/productCard.tsx'

  /Users/daniel/Documents/depict.ai/storefronts/vanilla-js/src/scripts/productCard.tsx:2:45
    1 | import { ContainedPlaceholderImage, Placeholder } from "@depict-ai/utilishared";
  > 2 | import { StrongerDisplay } from "./display";
  >   |                                             ^
    3 | 
    4 | const img_aspect_ratio = 2 / 3;

@parcel/resolver-default: Cannot load file './/jsx-runtime' in './'.
πŸ’‘ Did you mean './jsx-runtime'?

But I do see that sass-loader in webpack does support the "sass" condition, so probably worth supporting.

Yay, that'd be amazing πŸŽ‰

Ah this is using a deprecated syntax in package exports for exporting directories.

Ah, thanks. Thanks a lot for the links to the docs too. What do you think about warning about this? I think that could save people time and pain.

I think supporting the "sass" export condition is probably the right way. Alternatively you could add "./index.scss" to your package exports.

Right, so just "./index.scss": "./index.scss" I presume.

On a second thought

Importing directly from index.scss won't work with package.json exports unless it is explicitly declared as an export.

Why doesn't @use "@depict-ai/js-ui/styles" as plp-styling;

work given

"exports": {
    ".": {
      "import": "./dist/module.js",
      "require": "./dist/main.js",
      "types": "./dist/index.d.ts",
      "default": "./dist/module.js",
      "sass": "./styles.scss"
    },
    "./locales": {
      "types": "./locales/index.d.ts",
      "import": "./locales/index.js",
      "require": "./locales/require.js",
      "default": "./locales/index.js"
    }
  },

I'd consider it explicitly declared when it says "sass": "./styles.scss". Maybe it makes sense to allow importing things where the key is not yet supported by parcel?

Right, so just "./index.scss": "./index.scss" I presume.

This works, but only when importing @depict-ai/js-ui/index.scss and not when importing @depict-ai/js-ui/index like it normally would in sass

#8844 implements support for the sass, less, stylus, and style conditions supported by webpack. It also opens the door for plugins to support other custom conditions. Note that in your example, "sass" would need to come before the "default" condition for this to work, since default always matches and matching follows the key order in package.json.

That PR also fixes the tsconfig extends bug.

ukstv commented

I regret to post to a closed issue, but I think the story is not over yet. Apparently, parcel can not correctly resolve uint8arrays or multiformats/bases/base36 for example. I guess, the culprit is the default config actually ignores exports map and looks for main or whatever. I manually modified package.json for uint8arrays to set main field, and it works fine. Surprisingly, "exports": {".": "./dist/src/index.js"} did not work.

ukstv commented

Oh, never mind. It works in nightly. I hope it is released soon enough.

Yes, sorry it's taken longer than expected to get released. We have run into some backward compatibility issues. Unfortunately, I think we are going to have to make exports support opt-in until Parcel 3 as adding it is a breaking change for some packages. Once that is done we can release it soon.

For future reference, here's how to opt-in to this before parcel 3 is released https://parceljs.org/blog/v2-9-0/#new-resolver

char0n commented

Hi everybody,

When bundling CommonJS code and one of the dependencies is using using imports field, we'll end up hanging on the following error:

@parcel/core: Failed to resolve '#swagger-ui' from 
'./node_modules/swagger-ui-react/index.cjs'

  /swagger-ui-parcel/node_modules/swagger-ui-react/index.cjs:7:49
    6 | var _propTypes = _interopRequireDefault(require("prop-types"));
  > 7 | var _swaggerUi = _interopRequireDefault(require("#swagger-ui"));

New resolver is explicitly enabled in root package.json:

  "@parcel/resolver-default": {
    "packageExports": true
  }

I've tried to use alias in root package.json, but with no effect:

  "alias": {
    "./node_modules/swagger-ui-react/index.mjs": "./node_modules/swagger-ui-react/index.cjs",
    "#swagger-ui": "./node_modules/swagger-ui-react/swagger-ui.js"
  }

Any idea how to resolve this, or do I need to write my own resolver?

Note: when using ESM, everything works as expected.

mgol commented

I’m testing Parcel exports implementation against jQuery (which introduces exports in its recently released 4.0.0-beta) and I struggle to make it work without duplicating jQuery when both import & require are used in the code base. More info in the jQuery issue jquery/jquery#5416; I’d appreciate input from Parcel maintainers there.

Since this is the top search result, here's what you need to enable support for the exports map in Parcel v2.

Add this to your package.json:

{
  "dependencies": {
    // your stuff here
  },
  "devDependencies": {
    // your stuff here
  },
  "@parcel/resolver-default": {
     "packageExports": true
  }
}

Oh, and 2 cents from me regarding monorepo. If you use monorepo, @parcel/resolver-default (see above) clause should be set on the root package.json. Otherwise, parcel does not pick it up.