-
Notifications
You must be signed in to change notification settings - Fork 30.5k
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
Add mainDir
to module resolution algorithm
#14970
Comments
Some prior art: #3953. That issue was closed, but the reasons given were focused on the fact that it wanted to change the existing semantics of the |
I created a package that simulates the semantics I'm proposing via monkey-patching Module, for the curious: https://github.com/davewasmer/main-dir |
It seems that you could also be served well by some sort of opt in towards using You could prepend to your files |
@Slayer95 - I wasn't aware of Let's say I have a Node script I run via |
Oh, it seems I missed some bits in your explanation of the problem. Indeed, the feature I mention would only be useful at the application / child process level. Without intention to hijack the thread, as I'd prefer mainDir, an alternative feature proposal could be a require.packageMain module reference. |
Would definitely love to see this happen! |
This may help. For a typescript package I'm working on, I executed the following workaround and it seems to work but is messier than I would like. tbh, I was prety disappointed (and frankly shocked) that this wasn't well supported already.
The net net is that I can npm install ../my-package and that works AND I can npm install git://blah and that works. I have not tried publishing to npm but I imagine if the git works, that'll work. tsconfig |
I'm using typescript, compile all file from ./src to ./dist in package.json, run a script when prePublish:
BOOM. |
This would indeed VERY nice to have |
Wouldn't this make conflicts with node_modules paths? |
since you can now create arbitrary require functions (https://nodejs.org/api/modules.html#modules_module_createrequirefrompath_filename) can this be considered fixed? |
The problem isn't you using require, it's having other people use require for your module. |
This is a really common issue. Each time you need to access some functions that ain't directly exposed by the main field, you end with with pretty ugly imports from the The current solutions then are:
The problem I can see for the Also, there is the use of the So, I think the more straightforward solution to this problem would be to introduce a
The fact that it concerns only sub paths is important so that one requiring WDYT? |
Yeah, it'd be nice to have this feature so that we can write What I've been doing to publish built files so that it works like we want, is just outputting the build output into the root of my package (mixing them with the root files). It sounds messy, but the following is what I'm doing to manage it (here's the project for reference, infamous (EDIT: this is not longer true for that project, and now I publish from First, I set up .gitignore to ignore everything in the project (this ignores build output files in the root of the project) and then I whitelist the top-level things I don't want to ignore: ### ignore everything
/*
# including hidden files
/**/.*
### except for the following top-level whitelisted items:
# folders
!/src
!/tests
!/docs
!/examples
!/website
# global build, to be published
!/global.js
!/global.js.map
# config files
!/.all-contributorsrc
!/.builderrc
!/.editorconfig
!/.gitignore
!/.npmignore
!/.npmrc
!/bower.json
!/builder.config.js
!/package.json
# the basics!
!/README.md
!/LICENSE Then, I simply build everything into the root of my project (gotta be careful not to name any files inside my So, with that gitignore configuration applied, when I build the project the file structure looks like the following, with the grayed-out items being the ignored build output: With this setup we can write |
why not just publish the dist folder instead of publishing the root folder? |
@devsnek, because the package.json is located in root directory. |
so why not just add a cp to your build script? |
@devsnek I's ugly. |
@devsnek, and you cannot simply run |
@devsnek if you want to be cross platform, this introduces new dependencies. E.g. copy or copy-webpack-plugin |
that doesn't seem like the end of the world. i'm just trying to think of solutions that move the performance overhead to build time instead of runtime. |
Maybe that the simpler would be to allow the files field to be a map instead of an array where, the sources in the project and their destinations in the package would be set ? That way, no need to cp anything. Something like: {
files: {
'dist/**': '.'
}
} |
@nfroidure Maybe, but that would be a feature request for npm. |
Agreed this sounds like a publish-time feature to me not an install-time one. Perhaps make a feature request to npm or yarn for a |
@guybedford This is not publish-time feature. If this is done only during publish then common workflows like npm link and monorepo scenarios would be broken. See below issues for reference: Above feature request is intended to solve three common problems:
I will update to provide more examples for above. |
seems like subdirectory publishing works well enough for sindre https://github.com/sindresorhus/np |
Maybe the new |
using exports field in package.json can we map lib folder like this? "exports": {
"./": "./lib/"
} |
As a reminder to those on the thread: If your solution is talking about an Yes, it is possible to copy files around and get a package published in a desired format. But because this hack is not built into |
@chyzwar that does in fact work! Here is a slightly expanded answer.
package.json {
"type": "module",
"main": "./build/index.js",
"exports": {
"./": "./build/"
}
} then, in a project which uses this module: // main alias:
import { main } from 'nice-exports-sample'
// exports aliases:
import { main as mainAgain } from 'nice-exports-sample/index.js'
import { another } from 'nice-exports-sample/another.js'
// nested folders do not resolve, this line would fail
// import { hidden } from 'nice-exports/hidden/module.js' I ran this code with node [edit] I know this isn't everyones intended use case (many users want to avoid leaking internal modules). This example is really just a way to avoid copying package.json into your build folder and publishing from there. |
Publish-time workaround doesn't work in a monorepo with symlinks :\ |
EDIT: this comment is wrong. The package.json The new {
"type": "module",
"main": "./dist/index.js",
"exports": {
"./": "./dist/"
}
} then we can not To make it work, we'd need to write {
"type": "module",
"main": "./dist/index.js",
"exports": {
"./": "./dist/",
"./foo": "./dist/foo",
"./foo/bar": "./dist/foo/bar",
"./foo/bar/baz": "./dist/foo/bar/baz",
"./lorem": "./dist/lorem",
// ...
}
} It is ironic that this new |
deleted |
deleted |
@BridgeAR There's a large number of up votes for this in the comments after you closed this. Can you please re-open this? |
@trusktr I'm a bit confused by #14970 (comment) Once you map a folder you can deeply traverse it, you shouldn't have to map each subsequent folder... afaict the requested feature here is 100% implementable /w export maps. Photos attached of an example |
Reopened for further discussion. |
@MylesBorins Your example is using CommonJS modules. I made a small example of how it fails with ES Modules here: https://github.com/trusktr/es-modules-exports-alias-unable-to-import-subfolder |
@trusktr I just tested on my local machine and both examples work fine. Are you using Node.js 13? There is no difference in how export maps works between ESM and CJS and they are built to model how import maps will work. As mentioned above the behavior you are asking for already exists... and afaict your demo works fine. |
barring any new evidence I think that this issue should be clsoed |
Ah, you're right! From Node 13.1 combined with working example (in Node 13.2 or higher): https://github.com/trusktr/node-es-modules-alias-root-to-distI was assuming that it didn't work because the latest docs (13.6) still say the opposite in the Package Exports section:
|
Maybe the docs weren't clear enough. I realized that if Let me see if I can update wording to make it more clear. EDIT: I'm not sure I understand it well enough to update wording yet, but after playing with it, the following "exports": {
// this allows people to `import {...} from 'the-package'` directly
// This replaces the `main` field, though the `main` field can be kept for backwards compatibility with Node versions that don't yet read the `exports` field.
".": "./dist/index.js",
// this allows people to `import {...} from 'the-package/any/subfolder'
// where `the-pacakge/dist/` contains `any/subfolder/`
"./": "./dist/"
},
// The `main` field can be used as a fallback for older versions of Node:
"main": "./dist/index.cjs", @MylesBorins Yes indeed the issue can be closed. Thanks for showing that. |
@trusktr Your comment just saved me a lot of heartache and stress. Thank you for that explanation, this should be in the documentation! |
closing per #14970 (comment) |
I'm getting an warning saying
|
@chathu-novade, use |
This approach does not work. At least with the turbo repo setup - trying to use this approach to export from dist forlder in this repo - https://github.com/react18-tools/turborepo-template/blob/main/lib/fork-me/package.json |
First off, I realize that the Module API is frozen, so I understand if this proposal will be rejected outright. However, I figured I'd give it a shot anyway.
Many node packages today are written in a source language other than JavaScript, and compiled before publishing. The
main
field in the package.json file allows these compiled packages to specify a different file as the entry point (i.e.dist/index.js
).However, the module resolution algorithm allows for loading files within a module via path syntax, i.e.
require('foo/bar/quux')
would loadbar/quux.js
from thefoo
module. Currently, the only way to ensure this kind of path syntax works (without needing to includedist/
in the path) is to publish only thedist
folder. This approach requires the author to remember to publish the subdirectory every time, which can be mistake prone.I'd like to propose supporting a new field in the
package.json
spec calledmainDir
. If present, the module resolution algorithm would treat thatmainDir
path as the root path for that package, so compiled packages could specify"mainDir": "dist"
, and easily support sub-path module loading (i.e.require('foo/bar/quux')
) without having to remember to publish from the subdirectory every time.I think this would be a relatively straightforward change on the implementation side, happy to PR it, but I wanted to test the waters first. The only trouble I can see is if people are already using
mainDir
in package.json files for something else. However, a quick Github search reveals zero public instances of usingmainDir
in a package.json file. This of course doesn't preclude it's use in private repos, but I think it's a strong indicator that we wouldn't be trampling on too much, if any, existing code.The text was updated successfully, but these errors were encountered: