Description
Nickel package management
A package manager (PM) is an indispensable part of the tooling of any programming language out there. By PM, we mean a way to distribute and depend on external Nickel code (aka libraries).
This need is already present in e.g. nickel-lang/organist where they need to distribute a common library (the current solution is a mix of relying on a Nix flake and a home-made lock mechanism, cf nickel-lang/organist#63). The JSON schema to Nickel converter needs to distribute a base library as well, and currently has to inline this library in each generated contract to avoid bothering users with an additional setup (nickel-lang/json-schema-to-nickel#29).
We also anticipate that many users will write or generate comprehensive contract suites for particular use-cases (Kubernetes, GitHub actions, etc.). Those contract suites need to be distributed to other users to be useful: once again, this requires a package manager of some sort.
Expected outcome
A working package manager is a basic, indispensable tool for Nickel adoption to continue growing.
Technical aspects
There are several aspects to such a package manager:
- the tool itself, e.g. a
metalist install json-schema-to-nickel/predicates
that would make a library available in a project. At a high-level, this tool needs to handle version selection, to pull a distant source in a local directory and to map an abstract name "predicates" to this local directory. This might involve e.g. a lockfile. - a standardized way of describing a Nickel library, and in particular what values it (and types) it exports. This doesn't have to be a full fledged module system, and can be as simple as saying that importing a library automatically imports the root file
main.ncl
orlib.ncl
. It might be more sophisticated as well, with a way of exporting static types, hiding symbols, etc. - A new syntax to import a non local file. See Handle non-local dependencies #329 where some proposals have been discussed already.
Proposal
While not set in stone, the current plan of the Nickel team is to avoid re-implementing full package management from scratch. This is done for each and every new language out there, while a lot of the features are language-agnostic (lockfiles, version selection, dependency resolution, project description, etc.). Especially for an interpreted language, which doesn't require the PM to be aware of a build system.
Our inclination is to follow the discussion of #329:
- Nickel is meant to be used in various contexts. If you use it to configure a webapp in JavaScript, you might have NPM and NodeJS already installed. If you use it for your Rust webapp, you might have cargo installed. For domain-specific libraries tied to one toolchain, it makes sense to reuse the existing PM.
- Still, it's useful to have a blessed way of distributing Nickel libraries that aren't necessarily tied to one toolchain (or might be useful beyond one). The idea here would be to bless one language PM, e.g. by writing a wrapper around it, and support it as if it were Nickel's own PM (ensure that it always work correctly with Nickel's use-case).
We propose to write small extensions to make Nickel understand the lockfile (or the equivalent notion) of several PMs, so that it can map names to local directories. All the package management part (installing, updating, etc.) would be handled by the external PM, although for the blessed one, we would provide a specific wrapper for it. Users would then simply write something like (the syntax is not part of proposal, just for the sake of this example):
let pred_lib = import <json-schema-to-nickel:predicates> from nix in
# or
let pred_lib = import <json-schema-to-nickel:predicates> from npm in
Mixed PMs
Things can get hairy if you need to depend on libraries from various PMs. It's probably not an issue (although it's a bit heavy) for a private project, but for libraries, it's more difficult. As a first step, we would require that a library distributed with PM XXX must ensure that all of its transitive dependencies use XXX as well.
Default PM
How to select the default, blessed PM? Here is a list of criterion:
- Multi-platform (in particular, it must be Windows-compatible, which excludes Nix)
- Mature
- Easy to use
- Compatible with our use-case (not too coupled to its specific language)
- (Not required, but a plus) Widely used and installed by default
- Amongst “equal” candidates, take the lighter/smaller one if possible
Related: