Skip to content

Commit

Permalink
Remove import-lazy and change stylelint.rules Promise-based (#7279)
Browse files Browse the repository at this point in the history
This commit makes the following changes, including a breaking:

- Remove the `import-lazy` dependency
- Make the `stylelint.rules` object Promise-based (breaking)

Thanks to these changes, the test suite is executed for ESM rules (`lib/rules/*/index.mjs`), instead of CJS ones (`*.cjs`).
  • Loading branch information
ybiquitous committed Nov 6, 2023
1 parent 0f74d1e commit aa9be6f
Show file tree
Hide file tree
Showing 27 changed files with 1,069 additions and 651 deletions.
5 changes: 5 additions & 0 deletions .changeset/slow-planes-begin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"stylelint": major
---

Changed: Node.js API so that the `stylelint.rules` object has `Promise` functions
9 changes: 5 additions & 4 deletions docs/developer-guide/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,15 +297,16 @@ Here's an example of a plugin that runs `declaration-no-important` only if there

```js
module.exports = stylelint.createPlugin(ruleName, (expectation) => {
const runDeclarationNoImportant =
stylelint.rules["declaration-no-important"](expectation);
const runDeclarationNoImportant = stylelint.rules[
"declaration-no-important"
].then((rule) => rule(expectation));

return (root, result) => {
if (root.toString().indexOf("@@check-declaration-no-important") === -1) {
if (!root.toString().includes("@@check-declaration-no-important")) {
return;
}

runDeclarationNoImportant(root, result);
return runDeclarationNoImportant.then((rule) => rule(root, result));
};
});
```
Expand Down
1 change: 1 addition & 0 deletions docs/developer-guide/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ The final step is to add references to the new rule in the following places:

- [The rules `index.mjs` file](../../lib/rules/index.mjs)
- [The list of rules](../user-guide/rules.md)
- [The type definition of rules](../../types/stylelint/index.d.ts)

## Add an option to a rule

Expand Down
13 changes: 11 additions & 2 deletions docs/migration-guide/to-16.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,17 @@ If you use `stylelint.lint()` to lint files, the `code` property will always be
We've changed the `stylelint.formatters` object in the Node.js API so that every formatter is a `Promise` function.

```diff js
-stylelint.formatters.json(results);
+stylelint.formatters.json.then(formatter => formatter(results));
-const formatter = stylelint.formatters.json;
+const formatter = await stylelint.formatters.json;
```

## Changed Node.js API returned `rules` object

We've changed the `stylelint.rules` object in the Node.js API so that every rule is a `Promise` function.

```diff js
-const rule = stylelint.rules['block-no-empty'];
+const rule = await stylelint.rules['block-no-empty'];
```

## Changed CLI to print problems to stderr
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ const stylelint = require('../../index.cjs');
const ruleName = 'plugin/conditionally-check-color-named';

module.exports = stylelint.createPlugin(ruleName, (expectation, options, context) => {
const colorNamedRule = stylelint.rules['color-named'](expectation, options, context);
const colorNamedRule = stylelint.rules['color-named'].then((rule) =>
rule(expectation, options, context),
);

return (root, result) => {
if (!root.toString().includes('@@check-color-named')) return;

colorNamedRule(root, result);
return colorNamedRule.then((rule) => rule(root, result));
};
});
28 changes: 15 additions & 13 deletions lib/__tests__/normalizeRuleSettings.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ describe('rules whose primary option IS NOT an array', () => {
expect(actual).toEqual(expected);
});

it('arrayed number with secondary options returns same', () => {
const actual = normalizeRuleSettings([2, { severity: 'warning' }], rules['block-no-empty']);
it('arrayed number with secondary options returns same', async () => {
const rule = await rules['block-no-empty'];
const actual = normalizeRuleSettings([2, { severity: 'warning' }], rule);
const expected = [2, { severity: 'warning' }];

expect(actual).toEqual(expected);
Expand Down Expand Up @@ -75,8 +76,9 @@ describe('rules whose primary option IS NOT an array', () => {
expect(actual).toEqual(expected);
});

it('arrayed boolean with secondary options returns same', () => {
const actual = normalizeRuleSettings([true, { severity: 'warning' }], rules['block-no-empty']);
it('arrayed boolean with secondary options returns same', async () => {
const rule = await rules['block-no-empty'];
const actual = normalizeRuleSettings([true, { severity: 'warning' }], rule);
const expected = [true, { severity: 'warning' }];

expect(actual).toEqual(expected);
Expand All @@ -92,25 +94,25 @@ describe('rules whose primary option CAN BE an array', () => {
expect(normalizeRuleSettings([null], mockRule)).toBeNull();
});

it('solo primary option array is nested within an array', () => {
const actual = normalizeRuleSettings(['calc', 'rgba'], rules['function-allowed-list']);
it('solo primary option array is nested within an array', async () => {
const rule = await rules['function-allowed-list'];
const actual = normalizeRuleSettings(['calc', 'rgba'], rule);
const expected = [['calc', 'rgba']];

expect(actual).toEqual(expected);
});

it('primary option array in an array', () => {
const actual = normalizeRuleSettings([['calc', 'rgba']], rules['function-allowed-list']);
it('primary option array in an array', async () => {
const rule = await rules['function-allowed-list'];
const actual = normalizeRuleSettings([['calc', 'rgba']], rule);
const expected = [['calc', 'rgba']];

expect(actual).toEqual(expected);
});

it('nested primary option array returns same', () => {
const actual = normalizeRuleSettings(
[['calc', 'rgba'], { severity: 'warning' }],
rules['function-allowed-list'],
);
it('nested primary option array returns same', async () => {
const rule = await rules['function-allowed-list'];
const actual = normalizeRuleSettings([['calc', 'rgba'], { severity: 'warning' }], rule);
const expected = [['calc', 'rgba'], { severity: 'warning' }];

expect(actual).toEqual(expected);
Expand Down
20 changes: 7 additions & 13 deletions lib/__tests__/standalone-deprecations.test.mjs
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
import { createRequire } from 'node:module';
import readJSONFile from '../testUtils/readJSONFile.mjs';
import standalone from '../standalone.mjs';

import { jest } from '@jest/globals';

jest.mock('../rules/block-no-empty/index.cjs');

const require = createRequire(import.meta.url);
const blockNoEmpty = require('../rules/block-no-empty/index.cjs');
jest.unstable_mockModule('../rules/block-no-empty/index.mjs', () => ({
default() {
return (root, result) => {
result.warn('Some deprecation', { stylelintType: 'deprecation' });
};
},
}));

const configBlockNoEmpty = readJSONFile(
new URL('./fixtures/config-block-no-empty.json', import.meta.url),
);

blockNoEmpty.mockImplementation(() => {
return (root, result) => {
result.warn('Some deprecation', {
stylelintType: 'deprecation',
});
};
});

describe('standalone with deprecations', () => {
it('works', async () => {
const { output, results } = await standalone({
Expand Down
12 changes: 4 additions & 8 deletions lib/__tests__/standalone-quiet-deprecation-warnings.test.mjs
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import { createRequire } from 'node:module';
import { jest } from '@jest/globals';
import report from '../utils/report.mjs';
import standalone from '../standalone.mjs';

jest.mock('../rules/block-no-empty/index.cjs');

const require = createRequire(import.meta.url);
const deprecatedRule = require('../rules/block-no-empty/index.cjs');

deprecatedRule.mockImplementation(() => {
const deprecatedRule = () => {
return (root, result) => {
report({
ruleName: 'block-no-empty',
Expand All @@ -17,10 +11,12 @@ deprecatedRule.mockImplementation(() => {
result,
});
};
});
};

deprecatedRule.meta = { deprecated: true };

jest.unstable_mockModule('../rules/block-no-empty/index.mjs', () => ({ default: deprecatedRule }));

it('standalone does not silence deprecation warnings by default', async () => {
const config = {
rules: {
Expand Down
2 changes: 1 addition & 1 deletion lib/augmentConfig.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ async function augmentConfigFull(stylelint, filePath, cosmiconfigResult) {
);
}

augmentedConfig = normalizeAllRuleSettings(augmentedConfig);
augmentedConfig = await normalizeAllRuleSettings(augmentedConfig);

return {
config: augmentedConfig,
Expand Down
2 changes: 1 addition & 1 deletion lib/augmentConfig.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ export async function augmentConfigFull(stylelint, filePath, cosmiconfigResult)
);
}

augmentedConfig = normalizeAllRuleSettings(augmentedConfig);
augmentedConfig = await normalizeAllRuleSettings(augmentedConfig);

return {
config: augmentedConfig,
Expand Down
4 changes: 2 additions & 2 deletions lib/lintPostcssResult.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const index = require('./rules/index.cjs');
* @param {StylelintConfig} config
* @returns {Promise<any>}
*/
function lintPostcssResult(stylelintOptions, postcssResult, config) {
async function lintPostcssResult(stylelintOptions, postcssResult, config) {
postcssResult.stylelint.ruleSeverities = {};
postcssResult.stylelint.customMessages = {};
postcssResult.stylelint.ruleMetadata = {};
Expand Down Expand Up @@ -66,7 +66,7 @@ function lintPostcssResult(stylelintOptions, postcssResult, config) {
: [];

for (const ruleName of ruleNames) {
const ruleFunction = getStylelintRule(ruleName, config);
const ruleFunction = await getStylelintRule(ruleName, config);

if (ruleFunction === undefined) {
performRules.push(
Expand Down
4 changes: 2 additions & 2 deletions lib/lintPostcssResult.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import rules from './rules/index.mjs';
* @param {StylelintConfig} config
* @returns {Promise<any>}
*/
export default function lintPostcssResult(stylelintOptions, postcssResult, config) {
export default async function lintPostcssResult(stylelintOptions, postcssResult, config) {
postcssResult.stylelint.ruleSeverities = {};
postcssResult.stylelint.customMessages = {};
postcssResult.stylelint.ruleMetadata = {};
Expand Down Expand Up @@ -63,7 +63,7 @@ export default function lintPostcssResult(stylelintOptions, postcssResult, confi
: [];

for (const ruleName of ruleNames) {
const ruleFunction = getStylelintRule(ruleName, config);
const ruleFunction = await getStylelintRule(ruleName, config);

if (ruleFunction === undefined) {
performRules.push(
Expand Down
6 changes: 3 additions & 3 deletions lib/normalizeAllRuleSettings.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ const normalizeRuleSettings = require('./normalizeRuleSettings.cjs');

/**
* @param {StylelintConfig} config
* @return {StylelintConfig}
* @return {Promise<StylelintConfig>}
*/
function normalizeAllRuleSettings(config) {
async function normalizeAllRuleSettings(config) {
if (!config.rules) return config;

/** @type {StylelintConfig['rules']} */
const normalizedRules = {};

for (const [ruleName, rawRuleSettings] of Object.entries(config.rules)) {
const rule = getStylelintRule(ruleName, config);
const rule = await getStylelintRule(ruleName, config);

if (rule) {
normalizedRules[ruleName] = normalizeRuleSettings(rawRuleSettings, rule);
Expand Down
6 changes: 3 additions & 3 deletions lib/normalizeAllRuleSettings.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import normalizeRuleSettings from './normalizeRuleSettings.mjs';

/**
* @param {StylelintConfig} config
* @return {StylelintConfig}
* @return {Promise<StylelintConfig>}
*/
export default function normalizeAllRuleSettings(config) {
export default async function normalizeAllRuleSettings(config) {
if (!config.rules) return config;

/** @type {StylelintConfig['rules']} */
const normalizedRules = {};

for (const [ruleName, rawRuleSettings] of Object.entries(config.rules)) {
const rule = getStylelintRule(ruleName, config);
const rule = await getStylelintRule(ruleName, config);

if (rule) {
normalizedRules[ruleName] = normalizeRuleSettings(rawRuleSettings, rule);
Expand Down

0 comments on commit aa9be6f

Please sign in to comment.