Open abdes opened 4 years ago
It remains however a bit unclear in the current documentation and in the current behavior of yarnpkg-builder how this feature fits into the larger ecosystem
That's true! It's because we didn't put much thought into that yet 😅
Given the already large number of changes in the v2, we preferred to implement low-level tools and figure out the next high-level layers based on community feedback during the next months. Plugins are one of those things that are a bit rough at the moment until we can figure out a better user story.
builder build plugin always normalizes the plugin name to '@yarnpkg/xxxxx'. It's not clear today if this is intended or if there is something more that we need to know,
It's intended...ish. The normalization was made so that people could in theory override the builtin plugins (like essentials
) with their own implementations. In practice I'm not sure it works, and I'm even less sure it's a valid use case. I'd be ok with removing this limitation.
yarn plugin import has different treatment depending on whether the plugin is hosted on yarnpkg repo or not. If not, it has to be a downloadable single file bundle. Why we can't just ship third-party plugins as npm modules, add them as dev deps, and then yarn can just import from there, like usual?
Because, just like the Yarn bundle itself, plugins cannot have dependencies (chicken and egg problem; if you have dependencies you need to run an install, but to run an install you need the plugins to be registered).
So I figured the easiest way to distribute them would be as single JS files (just like the regular CLI), and thus using the npm registry didn't really provide anything of value (since you need to download a tgz which may contain many files).
even when a plugin is imported from the local filesystem, yarn plugin import builds a different cjs file. Not clear how is that working with builder and if we (as plugin developers) need to know about it.
Not sure what you mean - iirc we just copy it into the local directory and add it to the configuration.
Thank you @arcanis for such a quick turnaround!
even when a plugin is imported from the local filesystem, yarn plugin import builds a different cjs file. Not clear how is that working with builder and if we (as plugin developers) need to know about it.
Not sure what you mean - iirc we just copy it into the local directory and add it to the configuration.
Actually you are right, only the bundle file name is changed from .js
to .cjs
. Not sure again if it is intended or not that builder produces .js
and plugin import
makes it .cjs
.
I'm starting a repo in which I will be experimenting with writing a plugin to analyze and visualize the dependency graph inside a multi-workspace berry project. It will be taking the output from workspaces list
as json and then starting a local web server to serve an app that will visualize and offer analysis tools for that graph. I chose a complex scenario involved more than the manifests and the core API in order to push the plugin model to the edge :-). I will share my findings and challenges as I make progress on it.
The initial thoughts on the structure are as follows:
Let me know if something seems wrong to you in the approach described above.
Again, really appreciate the work done on berry and your feedback.
I'm not sure why you want to have both a plugin and a package - I'd suggest to avoid that and simply include everything into the plugin. If you want to have a package to use it with other package managers too, imo you should just bundle it inside the plugin (the builder will do this) and just distribute the Yarn plugin to users.
I'd also avoid the postinstall and the unplug. I don't know how many statics you have, but with the current single-file model you'd rather bundle the assets inside the JS - it's a bit weird, I agree, but usually there aren't that many images within plugins 😄
- I'm not sure why you want to have both a plugin and a package - I'd suggest to avoid that and simply include everything into the plugin. If you want to have a package to use it with other package managers too, imo you should just bundle it inside the plugin (the builder will do this) and just distribute the Yarn plugin to users.
The first issue is how to distribute the plugin to the users. People just do git clone
and yarn install
and expect everything to be ready. Now we will expect them to do another step as well: yarn plugin import https://xxxxx/plugin.js
.
- I'd also avoid the postinstall and the unplug. I don't know how many statics you have, but with the current single-file model you'd rather bundle the assets inside the JS - it's a bit weird, I agree, but usually there aren't that many images within plugins 😄
yarn plugin import
on behalf of the user. so we just stay with yarn install
The app packing can be done outside in a separate workspace though. We can bundle everything inside a webpack library single chunk and pass it as a dependency to the plugin so builder
can still produce a single file. The impacts of that approach is that I need to find a way to serve the app script to the browser out of the plugin bundle :-). Not sure how...
I will play around this weekend with both approaches and let you know after.
The first issue is how to distribute the plugin to the users. People just do
git clone
andyarn install
and expect everything to be ready. Now we will expect them to do another step as well:yarn plugin import https://xxxxx/plugin.js
.
Note that plugins are meant to be checked-in (same as the Yarn binary itself), so cloning is all that's required to have a working environment.
Can a plugin access the files inside a package in the cache?
I tried the example at: https://yarnpkg.com/features/pnp#frequently-asked-questions
const {readFileSync} = require(`fs`);
// Looks similar to `/path/to/.yarn/cache/lodash-npm-4.17.11-1c592398b2-8b49646c65.zip/node_modules/lodash/ceil.js`
const lodashCeilPath = require.resolve(`lodash/ceil`);
console.log(readFileSync(lodashCeilPath));
I got the following error:
TypeError: Cannot read property '1' of undefined
at isFileType (fs.js:177:16)
at Object.readFileSync (fs.js:363:16)
at l._serveStaticContent (E:\dev\projects\workspaces\yarn-plugins\.yarn\plugins\@yarnpkg\plugin-workspaces-analyze.cjs:5:890)
at Server.<anonymous> (E:\dev\projects\workspaces\yarn-plugins\.yarn\plugins\@yarnpkg\plugin-workspaces-analyze.cjs:5:1277) at Server.emit (events.js:315:20)
at parserOnIncoming (_http_server.js:790:12)
at HTTPParser.parserOnHeadersComplete (_http_common.js:119:17)
There are a number of issues related with that access model anyway:
eval('require')
, eval('require.resolve')
, __non_webpack_require__
but undefined, ... .js
files?Is there another way to acess the zip file for a package in the cache other then using require?
After more tests, I came to the conclusion that because plugins cannot have dependencies and pretty much won't work unless they are really limited to a single file bundle, the most appropriate use cases for them is to execute commands that only use @yarnpkg
modules. Any more complicated operations that require external dependencies are better done in separate cli from yarn.
The node fs implementation when running the cli with yarn node
works fine with yarn PnP.
After more tests, I came to the conclusion that because plugins cannot have dependencies and pretty much won't work unless they are really limited to a single file bundle, the most appropriate use cases for them is to execute commands that only use
@yarnpkg
modules. Any more complicated operations that require external dependencies are better done in separate cli from yarn.The node fs implementation when running the cli with
yarn node
works fine with yarn PnP.
So, yeah much simpler to keep complicated stuff out of yarn plugins and just reuse as much as possible the existing plugins. So far I've not encountered the need yet to build an extra plugin :-)
You can have a look at the web UI for project dependencies simply by using yarn dlx from any project root:
yarn dlx -p @lerepo/cli lrt web
The cli brings the web app as a dependency and serves static files seamlessly through node fs provided by yarn. Works 100% with PnP, no unplugging needed.
@arcanis is the intention for yarn plugins only to be those that strictly interact with the @yarnpkg/*
ecosystem and a select set of modules exposed by the require
function that factory exposes?
I am trying to assess whether yarn 2 is going to be a good fit for projects or if we should just migrate to npm. The differentiating factor for us is plugins, as the other features are either already there in npm (especially now that it supports workspaces) or are quickly being developed, but no other package manager has this concept of plugins. I like that they're fat 'binaries' if you will, but not being able to cleanly package outside modules is a real problem. We want to leverage this functionality to have a set of core plugins that take over various things (like prettier, eslint etc) so we can just share these plugins between projects instead of sharing a config + ensuring the correct dependencies are installed (and importantly versions). Right now we have to manage versions and consistency and its really easy for things to fall out of date, where is shipping a plugin and updating that plugging infrastructure wise, would be really easy (distribution isn't the problem we have, its time and effort). For us, not having to run updates against the package.json, but instead just importing a plugin, is something that fits really well with the mental model.
I think its yarns killer feature now, frankly, and I'd like to know more about this because it would be really great to package up these kind of tools as part of the package manager rather than building separate tools to manage it all.
is the intention for yarn plugins only to be those that strictly interact with the
@yarnpkg/*
ecosystem and a select set of modules exposed by therequire
function that factory exposes?
Yes and no - plugins can't access third-party packages out of the box because it would mean that they wouldn't be able to execute until after the project has been installed (and thus after the plugins have already been executed).
That being said, you can bundle your plugin sources into a single binary (like we do for Yarn), in which case you can use whatever third-party you want since they'll be stored inside the plugin anyway. This is what @yarnpkg/builder
does for you, although it's not well enough documented at the moment.
Plugins are one of the best features introduced to berry. It remains however a bit unclear in the current documentation and in the current behavior of yarnpkg-builder how this feature fits into the larger ecosystem (i.e. outside the internal use of plugins by berry itself).
builder build plugin
always normalizes the plugin name to '@yarnpkg/xxxxx'. It's not clear today if this is intended or if there is something more that we need to know,yarn plugin
command line, which behaves slightly differently from the tutorial example. Here again we may need a bit more clarity on the best practice of how plugins get added to a berry project,yarn plugin import
has different treatment depending on whether the plugin is hosted on yarnpkg repo or not. If not, it has to be a downloadable single file bundle. Why we can't just ship third-party plugins as npm modules, add them as dev deps, and then yarn can just import from there, like usual?yarn plugin import
builds a different cjs file. Not clear how is that working with builder and if we (as plugin developers) need to know about it.