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

fix: allow resolving package.json within packages #3413

Closed
wants to merge 1 commit into from

Conversation

paralin
Copy link

@paralin paralin commented Sep 29, 2023

Enables the following type of import:

const pkg = require('@mantine/core/package.json');

This is used to access the contents of package.json which is usually not exported by any of the exports within the package.json.

Reproduction of the issue:

package main

import (
	"errors"

	esbuild_api "github.com/evanw/esbuild/pkg/api"
)

func main() {
	res := esbuild_api.Build(esbuild_api.BuildOptions{
		EntryPoints: []string{"entry.js"},

		LogLevel: esbuild_api.LogLevelVerbose,
		Platform: esbuild_api.PlatformBrowser,
		Format:   esbuild_api.FormatESModule,

		Bundle:  true,
		Write:   true,
		Outfile: "out.js",

		Plugins: []esbuild_api.Plugin{buildPlugin()},
		Loader: map[string]esbuild_api.Loader{
			".json": esbuild_api.LoaderFile,
		},
	})
	if len(res.Errors) != 0 {
		panic(res.Errors[0].Text)
	}
}

func buildPlugin() esbuild_api.Plugin {
	return esbuild_api.Plugin{
		Name: "logger",
		Setup: func(pb esbuild_api.PluginBuild) {
			pb.OnResolve(esbuild_api.OnResolveOptions{
				Filter:    ".",
				Namespace: "file",
			}, func(ora esbuild_api.OnResolveArgs) (esbuild_api.OnResolveResult, error) {
				var result esbuild_api.OnResolveResult
				if ora.Importer == "logger" {
					return result, nil
				}

				resResult := pb.Resolve("@mantine/core/package.json", esbuild_api.ResolveOptions{
					// Importer: ora.Importer,
					// Namespace:  ora.Namespace,
					// Namespace:  "logger",
					Namespace:  "file",
					Importer:   "logger",
					ResolveDir: ora.ResolveDir,
					Kind:       esbuild_api.ResolveJSImportStatement,
				})
				if len(resResult.Errors) != 0 {
					return result, errors.New(resResult.Errors[0].Text)
				}

				return result, nil
			})
		},
	}
}

Importing package.json currently works with some packages (like react) but not others (like @mantine/core).

Enables the following type of import:

const pkg = require('@mantine/core/package.json');

This is used to access the contents of package.json which is usually not
exported by any of the exports within the package.json.

Signed-off-by: Christian Stewart <christian@aperture.us>
@evanw
Copy link
Owner

evanw commented Sep 29, 2023

The design of the exports feature intentionally makes it the package author’s decision which parts of their package are exported. Package consumers aren’t supposed to be able to override the package author’s decisions if they don’t like them. Sorry but that’s not how the exports feature is supposed to work. You can read the specification for yourself here. I’m closing this issue because it goes against the specification.

@evanw evanw closed this Sep 29, 2023
@paralin
Copy link
Author

paralin commented Sep 29, 2023

@evanw So it's impossible to import package.json? Why? It's in node_modules and always present. Regardless of what the developer exported.

If this isn't possible, is there any way at all to determine the root path (path to package.json parent dir) for an import? Because that seems impossible with esbuild and the above reasoning you have given seems arbitrary and unnecessarily restrictive.

Who does esbuild serve? The developer, or the person who published the package?...

@evanw
Copy link
Owner

evanw commented Sep 29, 2023

The reason esbuild does this is because it tries to follow already-established specifications and community conventions to have better interoperability with other tools. In this case the node team is the one who created the exports field in package.json so they get to say how it works (which they have said very precisely in a detailed specification). Other tools that interpret exports also all have the same behavior as esbuild here:

  • esbuild

    ✘ [ERROR] Could not resolve "@mantine/core/package.json"
    
        index.js:1:20:
          1 │ const pkg = require('@mantine/core/package.json');
            ╵                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
      The path "./package.json" is not exported by package "@mantine/core":
    
        node_modules/@mantine/core/package.json:8:13:
          8 │   "exports": {
            ╵              ^
    
  • node

    node:internal/modules/cjs/loader:553
          throw e;
          ^
    
    Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './package.json' is not defined by "exports" in ./node_modules/@mantine/core/package.json
        at new NodeError (node:internal/errors:405:5)
        at exportsNotFound (node:internal/modules/esm/resolve:260:10)
        at packageExportsResolve (node:internal/modules/esm/resolve:590:9)
        at resolveExports (node:internal/modules/cjs/loader:547:36)
        at Module._findPath (node:internal/modules/cjs/loader:621:31)
        at Module._resolveFilename (node:internal/modules/cjs/loader:1034:27)
        at Module._load (node:internal/modules/cjs/loader:901:27)
        at Module.require (node:internal/modules/cjs/loader:1115:19)
        at require (node:internal/modules/helpers:119:18)
        at Object.<anonymous> (./index.js:1:13) {
      code: 'ERR_PACKAGE_PATH_NOT_EXPORTED'
    }
    
  • webpack

    assets by status 267 bytes [cached] 1 asset
    ./index.js 50 bytes [built] [code generated]
    
    ERROR in ./index.js 1:12-49
    Module not found: Error: Package path ./package.json is not exported from package ./node_modules/@mantine/core (see exports field in ./node_modules/@mantine/core/package.json)
    
  • rollup

    index.js → stdout...
    import '@mantine/core/package.json';
    
    var temp = {};
    
    export { temp as default };
    (!) Plugin node-resolve: Could not resolve import "@mantine/core/package.json" in ./index.js using exports defined in ./node_modules/@mantine/core/package.json.
    (!) Plugin node-resolve: Could not resolve import "@mantine/core/package.json" in undefined using exports defined in ./node_modules/@mantine/core/package.json.
    (!) Unresolved dependencies
    

Who does esbuild serve? The developer, or the person who published the package?...

According to the specification for exports, in this situation esbuild is supposed to serve the person who published the package instead of the person using the package. Here you can see where the node developers have considered and rejected the same exception to the specification that you are proposing: nodejs/node#33460. So you are unlikely to be able to convince node to change the specification. Instead, I recommend reaching out to the author of that library to get them to allow access to package.json if they are open to it.

@paralin
Copy link
Author

paralin commented Sep 29, 2023

@evanw I understand the reasoning and the link you provided helps to clarify the situation quite a bit, thanks.

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

Successfully merging this pull request may close these issues.

None yet

2 participants