pesy
pesy copied to clipboard
New conventions based on feedback from @jaredly
@prometheansacrifice
@jaredly gave some good feedback about how many moving parts there are even with pesy. In some cases, there's some ways to make those much less noisy or require less explanation.
In other cases, there's not much we can do:
Why do packages need to be named
package-nameand then the namespaces/files be namedPackageName?
The reason is that packages end up in file paths, and not all file systems are case sensitive. If publishing to npm they even mandate that everything be lowercase. With some changes to esy, we could maybe fix that. But it would be weird and different than npm. Not necessarily bad.
How about making the public library name the same as the module name so they're both
LikeThis?
I think that has the same problem. The library name ends up in file paths so it has to be like-this when the module name is LikeThis
Okay, what can we do to simplify?
One great simplification is to use pesy to hide public library names and make people basically think they're just supplying require paths! We do that by making the public library names correspond directly to their paths inside their packages.
{
"name": "my-package",
"build": "dune build -p #{self.name}",
"buildDirs": {
"lib/here": {
"namespace": "Foo",
"require": ["my-package/lib/there"]
},
"lib/there": { },
"bin/my-binary.exe": {
"require": ["my-package/lib/here", "@reason-native/console"]
}
}
}
You wouldn't even have to specify a namespace most of the time. You could even infer it from the library path (so "lib/this-path" could automatically have namespace ThisPath).
So what's going on is that packages at "lib/there" are actually getting a dune public_nameofmy-package.lib.there`.
This is really nice because it makes it feel a lot like npm - they're just require paths like they've always been using. The only difference is that they have to specify them in the package.json per directory instead of per file which is actually kind of nice because it gives you a bird's eye view of your project.
I think using my-package/lib/there instead of my-package.lib.there. I clearly remember spending sometime how subpackages where named relative to one another because I hadn't read findlib docs. This is so much more intuitive. Easier onboarding! I'm glad that the next set of users will have on less thing to know/read just to get started.
About bin/my-binary.exe: I have a slight discomfort looking at this. Is the .exe supposed to help us internally figure if the package is a lib or a binary? Also, this shouldn't prompt the user to use .lib similarly for libraries. As users, we tend to generalise and consistent patterns are easy to understand, isn't it?
Good feedback. Please specify what you would rather have instead of that for bin conventions.
"path/to/exe-package": {
"main": "MyBinary.re",
"require": ["my-package/lib/here", "@reason-native/console"]
}
MyBinary is the name of the main file ie MyBinary.re which creates MyBinary.exe. This way keys always have path to the package.
Rationale: libraries are a collection of .re files (unless there's single entry that can abstract/make some re files private, which needs to be handled differently I think). Executables are a collection of .re files that are bundled with one particular .re acting as the main. Sort of like C's main() function or the file mentioned as bin in package.json in npm packages.
Considering that we are used to referring to main file in npm binaries as bin and libraries as main, we should consider the following too
"path/to/lib-package": {
"main": "LibPackage.re",
"require": ["my-package/lib/here", "@reason-native/console"]
}
"path/to/exe-package": {
"bin": "SomeMainFile.re",
"require": ["my-package/lib/here", "@reason-native/console"]
}
This inverts things a bit: what is considered main in the native world is entry point for libs in npm packages. While I don't think we should force users to specify a main file, some might want to hide/privatise a couple .re files. In that case, we expect them to specify the entry file with main.
hmmm I think I'd prefer having a canonical "default main filename", similar to how nodejs has index.js. rust has mod.rs, for example. I'd be happy with Index.re being the "entry point" for both libraries and executables
@jaredly How do we differentiate between exe and a lib?
I like that the config would look simpler
"path/to/exe-package": {
"require": ["my-package/lib/here", "@reason-native/console"]
}
"path/to/lib-package": {
"require": ["my-package/lib/here", "@reason-native/console"]
}
Correct me if I'm wrong, the bin and lib would be installed as exe-package.exe and lib-package.lib behind the scenes, right?. So the package that wants to consume the lib would look like
"path/to/consuming-package": {
"require": ["path/to/lib-package"]
}
Sounds great to me. And I'm guessing its okay for the user to override this behaviour with a main or a bin field.
Taking this one step further: I think we could allow relative paths too
"lib/here": {
"namespace": "Foo",
"require": ["../there"]
},
"lib/there": { },
For libraries, I think the best defaulting is that if you don't specify an entrypoint, then one is created for you out of all the modules. If you want to mark things private then you make an entrypoint.
"path/to/exe-package": { "require": ["my-package/lib/here", "@reason-native/console"] } "path/to/lib-package": { "require": ["my-package/lib/here", "@reason-native/console"] }Correct me if I'm wrong, the bin and lib would be installed as exe-package.exe and lib-package.lib behind the scenes, right?. So the package that wants to consume the lib would look like
The library would actually be installed as package-name.path.to.lib-package behind the scenes. I don't know what the binary would be installed as behind the scenes because I think we need some way to specify the name of the binary or use a convention which is what we're discussing here.
Taking this one step further: I think we could allow relative paths too
Yes, I do think that would be nice.
If libraries are defaulted to a bundling of all the modules then they won't always have a "main" field, then we get in a situation where binaries must have some way to be distinguished as binaries:
In this case, we can't know which are libraries and which are binaries.
"path/to/exe-package": {
"require": ["my-package/lib/here", "@reason-native/console"]
}
"path/to/lib-package": {
"require": ["my-package/lib/here", "@reason-native/console"]
We can introduce the bin property like npm as we discussed.
Okay so it would be like:
"path/to/exe-package/": {
"bin": "SomeEntrypoint.re",
"require": ["my-package/lib/here", "@reason-native/console"]
}
"path/to/lib-package": {
"require": ["my-package/lib/here", "@reason-native/console"]
}
And maybe you could do "bin": "SomeEntrypoint.re as binary-name.exe" if you want to be able to customize the binary name instead of SomeEntrypoint.exe?
In this proposal the bin field is required every time you want to mark something a binary.
SomeEntrypoint.re as binary-name.exe
:+1:
In this proposal the bin field is required every time you want to mark something a binary.
Agreed.
SomeEntrypoint.re as binary-name.exe
While it can lead to confusion in a case insensitve file system, I do believe we should allow SomeEntrypoint.exe by default without having to specify the alternative kebab cased binary-name.exe