Closed remcohaszing closed 3 years ago
I’m willing to fix this by the way :smile:
Some loose thoughts!
plugins && presets are essentially the same thing btw!
See also: https://github.com/unifiedjs/unified/issues/121#issuecomment-792326646.
Some work would also be in load-plugin
first, because we’d need to figure our what 'remark-gfm'
points to. Supporting export maps, .cjs
, .mjs
, and package.json#type
.
Then there’s work in https://github.com/unifiedjs/unified-engine/blob/53be1502aa05c6b4618402d3d9fa848d594739bf/lib/configuration.js#L19.
We’d probably have to add .mjs
and .cjs
.
We can detect whether .js
is ESM or CJS based on the nearest package.json#type
field.
And indeed, breaking. So we can also drop other features in the engine.
Some more thoughts on this…
Currently undocumented is that remark-cli
supports YAML based configurations as well. Should we
keep this? I personally think JavaScript configuration files offer much more flexibility, i.e. the
use of a custom unified
processor for remark-retext
.
If we could drop support for YAML, we could also drop support for string based plugin references, which removes the need to resolve plugins based on named exports. IMO this is too much magic anyway, although I get tools such as ESLint and Prettier do this as well.
import dictionary from 'dictionary-en';
import remarkFrontmatter from 'remark-frontmatter';
import remarkGfm from 'remark-gfm';
import { remarkMermaid } from 'remark-mermaid';
import remarkPresetWooorm from 'remark-preset-wooorm';
import remarkRetext from 'remark-retext';
import retextSpell from 'retext-spell';
import unified from 'unified';
export const presets = [remarkPresetWooorm];
export const plugins = [
remarkFrontmatter,
remarkGfm,
// Not sure why one would do this, but this is just to show named exports may be used as well.
remarkMermaid,
[remarkRetext, unified().use(retextSpell, { dictionary })],
];
I do realize this is very verbose though. Optionally we could allow an array of promises in the plugins array, which would allow to write configuration files like this:
import dictionary from 'dictionary-en';
import retextSpell from 'retext-spell';
import unified from 'unified';
export const presets = [import('remark-preset-wooorm')];
export const plugins = [
import('remark-frontmatter'),
import('remark-gfm'),
[import('remark-retext'), unified().use(retextSpell, { dictionary })],
];
If we do want to keep string based configurations, why not just use
cosmiconfig
? This is a popular package dedicated to
solving this exact issue, used by for example Prettier and Stylelint. They already support YAML,
JSON, and CJS configuration files and there’s a
draft pull request for ESM.
Also if we want to keep support for this, I believe there’s value in supporting this for
remark-retext
as well.
import dictionary from 'dictionary-en';
export const presets = ['remark-preset-wooorm'];
export const plugins = [
'remark-frontmatter',
'remark-gfm',
['remark-retext', [['retext-spell', { dictionary }]]],
];
This also means we need to determine what a string plugin points to for ESM. Probably the default export.
Another though I was having is: What’s the goal of remark-cli
? In my experience it’s a CLI for
running a bunch or remark-lint-*
whilst the remark-lint
package is fairly useless (it just adds
support for <!--lint ignore-->
comments).
In the beginning this was confusing to me. I just wanted to lint some markdown files, so why can’t I
just yarn add remark-lint
and run remark-lint
from the command line? This is how ESLint,
Stylelint, Prettier, and other linters I’ve used work. I also think this may be the main reason why
markdownlint has more downloads than remark-cli
.
Sorry if I got off topic too much. 😅
But I do think these considerations may affect how this is going to be implemented.
Tangentially related with new formats being considered, it may be worth revisiting if https://github.com/davidtheclark/cosmiconfig or a similar config manager could be a good fit for the unified ecosystem.
Currently undocumented is that remark-cli supports YAML
It’s documented in the engine docs, linked to in the remark-cli and rehype-cli readmes!
If we could drop support for YAML, we could also drop support for string based plugin references,
There’s still JSON. .remarkrc
, .remarkrc.json
, and package.json
are all JSON.
which removes the need to resolve plugins based on named exports. IMO this is too much magic anyway,
ESLint, Babel, xo, postcss, etc, also all allow package.json
or other JSON config files, to load packages through names. I don’t think we should drop that.
I believe ESLint also always supported YAML.
Anyway, I don’t care much for YAML.
Having a JS based config file is actually rather bad for caching, because well, it could do Math.random()
, or other things to define the structure. That means the config can’t be cashed. And is the main reason why Parcel does not allow JS to define a config file. We could also instead ban JS and go all in on JSON.
If we do want to keep string based configurations, why not just use cosmiconfig? This is a popular package dedicated to solving this exact issue, used by for example Prettier and Stylelint. They already support YAML, JSON, and CJS configuration files and there’s a draft pull request for ESM.
Yeah we can switch to that. It wasn’t around, or at least in those tools, when I made remark-cli and the engine
Another though I was having is: What’s the goal of remark-cli? In my experience it’s a CLI for running a bunch or remark-lint-* whilst the remark-lint package is fairly useless (it just adds support for
<!--lint ignore-->
comments).In the beginning this was confusing to me. I just wanted to lint some markdown files, so why can’t I just yarn add remark-lint and run remark-lint from the command line? This is how ESLint, Stylelint, Prettier, and other linters I’ve used work. I also think this may be the main reason why markdownlint has more downloads than remark-cli.
Because unified is the only project that does both linting and compiling. That doesn’t exist in other languages, which are all fragmented. Rome is one solution that’s trying to fix it. I think unified has already solved all that while being modular instead of monolithic. All of my 500+ projects use remark-cli for both linting and formatting btw, and I quite like it 😅
My preference is JavaScript (supports all values and allows comments) > YAML (better caching and allows comments) > JSON (better caching, but not comments)
I think this shows users have different preferences for this and we should keep support for JSON, YAML, and JavaScript, meaning we should add support for ESM.
I do believe cosmiconfig is the way to go. This means we should wait for cosmiconfig to support ESM, which they want to postpone until NodeJS 10 is EOL (2021-04-30)
So it’s a two part thing.
remark --use plugin
or from JSON/YAML). We could work on that earlier/laterSecond part is now done. Should we wait on cosmic config for the config files or get something in the engine for now (minor?)?
That leaves load-plugin as an optional thing, sort of outside the scope of this issue, but nice to have to actually match export maps?
If this can be done easily, I’d say go for it.
Note that some people are migrating from cosmiconfig
to lilconfig
, because it's lighter:
I haven't tried it myself yet, but something to keep in mind.
@jaydenseric Interesting .Although, that does seem to miss ESM support (cosmiconfig does too, but at least is working on it)
Thanks @jaydenseric, lilconfig
looks a lot better.
@wooorm It looks like lilconfig
follows major versions of cosmiconfig
very closely. So when v8 lands, lilconfig
should update accordingly.
lilconfig
is buggy and lack of maintenance than cosmiconfig
.
This was solved btw, forgot about this issue. And I kept with the existing custom loading—it’s pretty fast and works well and it was relatively fine to add ESM!
Hi, sorry for posting to an old thread but it's the closest I can find that might add context to my question!
@wooorm, I'm too stupid to understand whether ESM configuration would allow me to override the cli's --use mechanism to look for an export other than the default. I'm trying to --use remark-code-import
and it doesn't export a default.
You can open new questions!
That’s a bug in that plugin. Plugins must export the plugin as the default.
Thanks for getting back to me so quickly :)
I posted here as I had an inkling some of the code snippets above were pertinent but I'm not a JS programmer and couldn't tell.
In this case I find that version 0.4.0 of remark-code-import works as I need so I'm good, and I've opened an issue in that repository for cli support.
Subject of the feature
I would like to add support for ESM
.remarkrc
files in remark-cli. :smile:Problem
Since @wooorm is eager to ESMify (this is a verb now) a lot of packages, I believe
remark-cli
should support this, so users don’t run into compatibility issues when importing packages in their remark configuration files.I believe this is a breaking change, because users may already have their build these configuration files. I’m not aware of any examples, but there’s probably 1 person on the world for whom this change would be breaking.
Considering this is already a breaking change and ESM files may import CJS files, I believe CJS support may be dropped.
This requires some upstream changes, I believe all of them are within the unified ecosystem.
Expected behavior
Users can create
.remarkrc.mjs
or.remarkrc.js
files using ESM named exports.I.e.:
Alternatives
A default export could be used, but personally I prefer named exports.