Migrating to 16.0.0
This release contains significant or breaking changes.
We've migrated our source code to ECMAScript modules (ESM) — a year-long effort to allow ESM plugins, custom syntaxes and formatters, and a step towards updating our pure ESM dependencies.
To give the community time to migrate to ESM, we'll publish a hybrid package to support the (now deprecated) CommonJS Node.js API until our next major release.
Significant changes
We've:
- added support for ESM plugins
- added support for ESM custom syntaxes
- added support for ESM custom formatters
- deprecated the CommonJS Node.js API
- refactored to use
.mjs
and.cjs
extensions internally
Added support for ESM plugins
You can now create ESM plugins
.
For example:
import stylelint from "stylelint";
const {
createPlugin,
utils: { report, ruleMessages, validateOptions }
} = stylelint;
const ruleName = "foo-org/foo-bar";
const messages = ruleMessages(ruleName, {
rejected: (selector) => `Unexpected "foo"`
});
const meta = {
url: "https://foo.org/foo-bar"
};
const ruleFunction = (primary, secondaryOptions) => {
return (root, result) => {
const validOptions = validateOptions(/* .. */);
if (!validOptions) return;
/* .. */
report({
/* .. */
});
};
};
ruleFunction.ruleName = ruleName;
ruleFunction.messages = messages;
ruleFunction.meta = meta;
export default createPlugin(ruleName, ruleFunction);
We've updated our plugin developer guide with more examples of ESM syntax.
Added support for ESM custom syntaxes
You can now create ESM custom syntaxes.
For example:
import postcss from "postcss";
function parse(css, opts) {
/* .. */
}
function stringify(node, builder) {
/* .. */
}
export default { parse, stringify };
See the custom syntaxes section of our developer guide for more examples.
Added support for ESM custom formatters
You can now create ESM custom formatters.
For example:
export default function yourOwnFormatter(results) {
/* .. */
}
See the custom formatters section of our developer guide for more examples.
Deprecated CommonJS API
We've deprecated the CommonJS Node.js API and will remove it in the next major release so that we can update our pure ESM dependencies.
The deprecation will impact you if you're a plugin author or use stylelint.lint()
to lint files. Custom syntaxes and formatters are not affected as they don't consume the Node.js API.
To migrate to ESM you should:
- replace all
require()/module.export
withimport/export
- add
"type": "module"
topackage.json
if you are not using the.mjs
extension - use only full relative file paths for imports, e.g.,
import x from '.';
→import x from './index.js';
We also recommend that you:
- update the
"engines"
field inpackage.json
to"node": ">=18.12.0"
- remove
'use strict';
from all files - add
"exports": "./index.js"
inpackage.json
- use the
node:
protocol for Node.js built-in imports
For more details, see the Node.js documentation for ESM.
Plugins
For example, to migrate your plugin to use import
and export
:
-const stylelint = require("stylelint");
+import stylelint from "stylelint";
const {
createPlugin,
utils: { report, validateOptions },
} = stylelint;
const ruleFunction = (primary, secondaryOptions) => { /* .. */ };
ruleFunction.ruleName = ruleName;
ruleFunction.messages = messages;
ruleFunction.meta = meta;
-module.exports = createPlugin(ruleName, ruleFunction);
+export default createPlugin(ruleName, ruleFunction);
If you test your plugin using our testRule
schema, you can either:
- update to the last version of our
jest-preset-stylelint
package - try our new
stylelint-test-rule-node
package that usesnode:test|assert
jest-preset-stylelint
The preset needs the --experimental-vm-modules
Node.js flag to support ESM. You can use the cross-env
package to set the NODE_OPTIONS
environment variable in your npm-run-script:
{
"scripts": {
- "test": "jest",
+ "test": "cross-env NODE_OPTIONS=\"--experimental-vm-modules --no-warnings\" jest --runInBand",
}
}
(cross-env
is in maintenance-mode as it's considered finished.)
If you get an error (e.g. a segmentation fault while running the preset on Node.js 18), you can try using jest-light-runner
in your Jest config:
export default {
preset: "jest-preset-stylelint",
setupFiles: ["./jest.setup.js"],
+ runner: "jest-light-runner",
};
The runner has limited coverage support.
stylelint-test-rule-node
To try our new stylelint-test-rule-node
package, you should import it into your test files:
+import { testRule } from "stylelint-test-rule-node";
testRule({
/* .. */
});
And update your npm-run-script:
{
"scripts": {
- "test": "jest"
+ "test": "node --test"
}
}
The package uses node:test
which is experimental within Node.js 18, but stable in 20. Coverage support is also experimental.
If you have other Jest tests, you'll need to adapt them to use node:test
, e.g. expect()
becomes assert()
.
stylelint.lint()
For example, to migrate your code that uses stylelint.lint()
to use import
and top-level await
:
-const stylelint = require("stylelint");
+import stylelint from "stylelint";
-stylelint.lint(options).then((result) => { console.log(result); });
+const result = await stylelint.lint(options);
+console.log(result);
You'll find more details about the ESM Node.js API in the user guide.
Using the CommonJS Node.js API will trigger a deprecation warning. If you're not quite ready to migrate to ESM yet, you can use the quietDeprecationWarnings
option to hide the warning.
const stylelint = require("stylelint");
const resultPromise = stylelint.lint({
/* .. */
+ quietDeprecationWarnings: true
});