Open justinmk opened 1 year ago
pkg.json
{
"name" : "lspconfig", // OPTIONAL cosmetic name, not used for resolution nor filesystem locations.
"description" : "Quickstart configurations for the Nvim-lsp client", // OPTIONAL
"engines": {
"nvim": "^0.10.0",
"vim": "^9.1.0"
},
"repository": { // REQUIRED
"type": "git", // reserved for future use
"url": "https://github.com/neovim/nvim-lspconfig"
},
"dependencies" : { // OPTIONAL
"https://github.com/neovim/neovim" : "0.6.1",
"https://github.com/lewis6991/gitsigns.nvim" : "0.3"
},
}
pkg.json
file. Only required for "downstream".
pkg.json
can declare a dependency on any random artifact fetchable by URL. The upstream dependency doesn't need a pkg.json
.dependencies
follow the NPM version range spec vim.version.range()
."HEAD"
means git HEAD. (npm version spec defines ""
and "*"
as latest stable version.)pkg.json
is decentralized. Publishing a package means pushing it to a git repo with a top-level pkg.json
.packspec.json
to pkg.json
deps.json
package.json
)"version" : "0.1.2",
because package version is provided by the .git
repo infoexternal_dependencies
specification_version
. The lack of a "spec version" field means the spec version is 1.0.0
. If breaking changes are ever needed then we could introduce a "spec version" field."source" : "git:…",
to repository.url
package
to name
(to align with NPM)description
from object to string (to align with NPM)dependencies
shape to align with NPM. Except the keys are URLs.
scripts
(lifecycle)
scripts
item is a path to a Lua file on the Nvim 'runtimepath'.
"scripts.prepare": "lua/my_statusline/prepare.lua"
preinstall
install
postinstall
client
, user
, metadata
, ...?) for use by clients (package managers)?
pkg.json
allows arbitrary application-defined fields, as package.json
does.pkg.json
is redundant. pkg.json
is only useful for ecosystems that don't have centralized package management.git
commands instead of editing a json file)engines
field: how will aggregators build a package list?version
field mean that a manifest file always tracks HEAD of the git repo?
pkg.json
to repeat that information. The reason that package.json
and other package formats need a version
field is because they don't require a .git
repo to be present.repository.type
is available for future use if we want to deal with that.dependencies
could look like { "dependencies": { "plenary.nvim": "1.0.0" } }
name
be removed? Because repository.url
already defines the "name" (which can be prettified in UIs).
name
as OPTIONAL and strictly cosmetic (not used for programmatic decisions or filesystem paths).package.json
has an engines
field that declares what software can run the package. Example:
"engines": {
"vscode": "^1.71.0"
},
pkg.json
support "fallback" URLs?
package.json
pkg.json
be a strict subset of NPM package.json
? The ability to validate it with https://www.npmjs.com/package/read-package-json is attractive..."https://www.leonerd.org.uk/code/libvterm/libvterm-0.3.2.tar.gz": { "type": "tar+gzip", "version": "…" }
https://github.com/.../foo
and https://sr.ht/.../foo
are in the dependency tree?
.local/share/nvim/site/pack/github.com/start/
.local/share/nvim/site/pack/sr.ht/start/
pack/
dir, update existing dir, ...)removed "version" : "0.1.2", because package version is provided by the .git repo info
based on tag/release ?
should name be removed? because repository.url already defines the "name" (which can be prettified in UIs)
I think the name
attribute can stay IMO
Should we perhaps have a way of describing why a dependency exists? What about optional dependencies? Should that be within scope for this package spec, and if so, how would it work?
Should we perhaps have a way of describing why a dependency exists?
You mean like pseudo "comments"? No, that doesn't sound like something needed in a minimal "P0" approach.
What about optional dependencies?
Why is that needed in the minimal, initial spec?
Should we perhaps have a way of describing why a dependency exists?
You mean like pseudo "comments"? No, that doesn't sound like something needed in a minimal "P0" approach.
Makes sense.
What about optional dependencies?
Why is that needed in the minimal, initial spec?
I don't think it is "needed", I was just asking if it should be included or not, just in case the thought of optional deps may have slipped your mind.
Cool! I'll add some of my thoughts, hope you don't mind;
- removed
"version" : "0.1.2",
because package version is provided by the.git
repo info
Does this mean that a manifest file always tracks HEAD of the git repo? Given a manifest file as input, how would one know which version of the package it describes? I think the version number in the manifest file is very much needed, and should be the canonical version identifier. I also feel like the absence of the version number would violate the 4th principle?
- renamed
specification_version
tospecversion
I think excluding this field would help with overall ergonomics. If the spec starts out small with only fundamental requirements and additions are carefully considered I don't see the spec schema changing in such a way it'd require a spec version bump. Bumping the spec version should imo be a last resort. I think a strict non-breaking change policy would make sense. Consumers would simply check the presence of a field to enable that "feature".
changed
dependencies
shape to align with NPM. Except the keys are URLs.
- Leaves the door open for non-URL keys in the future.
Should consumers of dependencies need to control how a dependency is resolved? I think it'd be nice if the spec enforces globally unique names instead which central registries would have to enforce. The dependency schema could then simply be:
{
"dependencies": {
"plenary.nvim": "1.0.0"
}
}
As for describing version ranges, it looks like the suggested syntax employs npm's syntax, which I think is entirely proprietary to npm. I think cargo's approach is a bit simpler, where:
1.2.3 := >=1.2.3, <2.0.0
1.2 := >=1.2.0, <2.0.0
1 := >=1.0.0, <2.0.0
0.2.3 := >=0.2.3, <0.3.0
0.2 := >=0.2.0, <0.3.0
0.0.3 := >=0.0.3, <0.0.4
0.0 := >=0.0.0, <0.1.0
0 := >=0.0.0, <1.0.0
I feel like this alone is enough? In the future it could be extended with the additional modifiers ~
, ^
and *
(which have different meaning than in npm).
Dependencies aren't required to have a packspec.json file. That's only required for the "leaf nodes".
I think the entire dependency tree should be "packspec-enabled". Having plugin dependencies is in my experience pretty rare (maybe only a contemporary consequence of the lack of a manifest file), and those who do tend to depend on plugins that are intended to be dependent on (e.g. plenary
) - it'd be in everyone's interest for them to provide a manifest. Having this requirement could also act as a motivator for authors to include one. It's also a hygiene and stability factor as it'd hinder "nonserious" plugins from making their way into the package ecosystem - it's a good proxy for signaling stability.
Finally I think it'd also be good to explicitly define which fields are required. I don't see it being mentioned here but I feel like there's an implication that some of them currently are?
Great feedback!
removed
"version" : "0.1.2",
Does this mean that a manifest file always tracks HEAD of the git repo?
(Added to "Closed questions") The dependents declare what version they need, which must be available as a git tag in the dependency. Thus there is no need for packspec.json
to repeat that information. The reason that package.json
and other package formats need a version
field is because they don't require a .git
repo to be present.
excluding
specversion
field would help with overall ergonomics. ... Bumping the spec version should be a last resort.
Agreed, updated spec. The absence of specversion
means "spec version 1.0".
Should consumers of dependencies need to control how a dependency is resolved?
(Added to "Closed questions") repository.type
is available for future use if we want to deal with that.
it'd be nice if the spec enforces globally unique names
(Added to "Closed questions") Requiring URIs achieves that.
globally unique names ... which central registries would have to enforce. The dependency schema could then simply be:
{ "dependencies": { "plenary.nvim": "1.0.0"
(Added to "Closed questions") That looks nice but it makes the protocol more complicated and less "distributed".
As for describing version ranges, it looks like the suggested syntax employs npm's syntax, which I think is entirely proprietary to npm. I think cargo's approach is a bit simpler
Good idea, updated spec.
I think the entire dependency tree should be "packspec-enabled". Having plugin dependencies is in my experience pretty rare
packspec.json
.It's also a hygiene and stability factor as it'd hinder "nonserious" plugins from making their way into the package ecosystem - it's a good proxy for signaling stability.
That just isn't a goal (and it's the "RMS" approach to software 😏: cathedral instead of crazy, infectious bazaar) . The original scope was to be able to declare and resolve dependencies. Not anything else.
Those git urls git://github.com/neovim/neovim.git
are not valid.
Should be just https
or any other git supported url format.
scripts
and "build-time" tasks (lifecycle)
- Scripts must be array of strings (unlike npm package.json).
- Scripts are run from the root of the package folder, regardless of what the current working directory is.
- Predefined script names and lifecycle order:
- These all run after fetching and writing the package contents to the engine-defined package path, in order.
preinstall
install
postinstall
On the topic on uninstall scripts: Is it sure that we don't want that? Clean uninstall would be a really cool feature.
For example for something like mason
automatically removing all installed lsp-binaries would be nice.
I read this and am not sure, why it would be necessary to give context for uninstall. Like there isn't context given for install, so why should it be needed for uninstall.
I tried research on the exact reasons why it was removed for npm, but haven't really found anything.
- Version specifiers in
dependencies
follow the NPM version range spec ~cargo spec~- Supported by Nvim
vim.version.range()
.- Extensions to npm version spec:
"HEAD"
means git HEAD. (npm version spec defines""
and"*"
as latest stable version.)- ~Do NOT support "Combined ranges".~
- Treat any string of length >=7 and lacking "." as a commit-id.
- Only support commit-id, tags, and HEAD.
- Tags must contain a non-alphanumeric char.
Wouldn't it be better if there was a more explicit way to define non-semver dependency versions? Like if it's just a string it is interpreted as either semver or HEAD. If there is an object it depends on the keys used.
{
...
"dependencies" : { // OPTIONAL
...
"<dependency-url>" : {
"branch": "<branch-name>"
},
"<dependency-url>" : {
"tag": "<tag-name>"
},
"<dependency-url>" : {
"ref": "<commit-id>"
},
"<dependency-url>" : { // This is equivalent to the normal "<dependency>": "<semver>"
"version": "<semver>"
},
},
}
Another idea would be to support optional dependencies that can be installed to support additional features.
{
...
"dependencies" : { // OPTIONAL
...
"<dependency-url>" : {
"version": "<semver>",
"optional": true,
// OPTIONAL
"description": "Optional textual description describing the features enabled by using the optional dependency"
},
},
}
Other ideas:
On the topic on uninstall scripts: Is it sure that we don't want that?
Yes
Clean uninstall would be a really cool feature.
It would. And yet...
optional dependencies
Why do people keep mentioning this? What in the world is an "optional" dependency? Either it's a dependency or it isn't.
path dependencies? ... OS specific dependencies?
Out of scope. Out of scope. Can wait until v2. Can wait until v2.
optional dependencies
Why do people keep mentioning this? What in the world is an "optional" dependency? Either it's a dependency or it isn't.
I mean lots of package managers support optional dependencies. In my experience it's often a way to make integrations with other packages more discoverable.
Like it works without the dependency, but has additional features if the dependency is added.
optional dependencies
Why do people keep mentioning this? What in the world is an "optional" dependency? Either it's a dependency or it isn't.
I mean lots of package managers support optional dependencies. In my experience it's often a way to make integrations with other packages more discoverable.
Like it works without the dependency, but has additional features if the dependency is added.
Not needed for v1 imo, could be looked into for v2 though.
Via https://twitter.com/oilsforunix/status/1680957458431213569 :
I have been lightly working on almost exactly this, many notes on https://oilshell.zulipchat.com (please join). Working name is "Silo" for dumb artifacts; "medo"/meadow for git-versioned trees. It's a "meta" package manager because it invokes containerized apt, pip, etc.
❓ metadata probably makes sense, NPM itself allows arbitrary fields in package.json
I think having a metadata field reserved for client defined data is a pretty good idea. Especially when considering that this spec is likely to change in the future and breaking old packages because they used fields that collide with fields that were added in new versions would be bad.
Also validation and deserialization is much easier if you know what to expect.
whether the plugin needs/supports setup()
VSCode handles this in the package.json. actually we could rip off quite a few of their contribution points although the rest of them have less obvious value.
configuration
and configurationDefaults
maps most cleanly to vim.g
instead of setup()
. I guess setup()
could be a bool on top of that.
Contribution points can be used for anything lua_ls annotations are currently used for (completions, hover, diagnostics, vimdoc) plus:
vim.g
vim.g
option nameTechnically lua_ls annotations could also be contorted to do some of the above (not sure about vim.g
or vimscript support) although the annotated setup()
"standard" is too tacit I think. But if contribution points were added to the spec then I imagine someone would make a generator. Similar to how you can go schema-first or code-first in GraphQL and OpenAPI. (But schema-first is the way 😤)
(Note: all feedback is captured in this description + the first comment.)
To add some momentum to this effort, I suggest:
all Lua-supporting versions of Vim/Nvimall "assets" or "artifacts" of any kind in the spec should be a requirement.Only support one format, Lua (because it allows comments). Vim must be compiled with+lua
or must havelua
available on $PATH.package.json
, where possible (assuming this reduces confusion rather than increasing it)pkg.json
should be totally sandboxed (evaluating it must have no side-effects, only input and output).(Does #7 address this?)Client requirements
git
(packages can live at any git URL)Package server requirements
git
repoWhy NPM package.json?
NPM is special because it's ubiquitous--and lots of discussion hasn't yielded a strong case for a novel format. Using things that are ubiquitous means you gain their tooling, docs, validators (and possibly even... infrastructure).
node and NPM aren't perfect, but that doesn't matter. Choosing to be a subset of that ecosystem provides optionality: it's almost entirely upside with limited downside. We could also choose PHP's package format. The point is to surf on something massive and immortal.
What about LuaRocks?
I've advocated for LuaRocks as the Nvim plugin manager, but defining a
"plugin spec""federated package spec" also makes sense because:pkg.json
is a fairly "cheap" approach. LuaRocksReferences