From 121a34f8e9990ecde7202b4b44a7b30309855169 Mon Sep 17 00:00:00 2001 From: Masafumi Koba <473530+ybiquitous@users.noreply.github.com> Date: Sat, 13 Aug 2022 23:18:48 +0900 Subject: [PATCH] Fix `plugins.md` document and `createPlugin` type definition --- docs/developer-guide/plugins.md | 112 +++++++++--------- .../fixtures/plugin-warn-about-foo.js | 16 ++- lib/__tests__/plugins.test.js | 13 ++ lib/createPlugin.js | 12 +- types/stylelint/index.d.ts | 4 +- types/stylelint/type-test.ts | 10 +- 6 files changed, 96 insertions(+), 71 deletions(-) diff --git a/docs/developer-guide/plugins.md b/docs/developer-guide/plugins.md index b686e5304c..6d334a7636 100644 --- a/docs/developer-guide/plugins.md +++ b/docs/developer-guide/plugins.md @@ -26,33 +26,32 @@ const meta = { // deprecated: true, }; -module.exports = stylelint.createPlugin( - ruleName, - (primaryOption, secondaryOptionObject) => { - return (postcssRoot, postcssResult) => { - const validOptions = stylelint.utils.validateOptions( - postcssResult, - ruleName, - { - /* .. */ - } - ); - - if (!validOptions) { - return; +const ruleFunction = (primaryOption, secondaryOptionObject) => { + return (postcssRoot, postcssResult) => { + const validOptions = stylelint.utils.validateOptions( + postcssResult, + ruleName, + { + /* .. */ } + ); - // ... some logic ... - stylelint.utils.report({ - /* .. */ - }); - }; - } -); + if (!validOptions) { + return; + } + + // ... some logic ... + stylelint.utils.report({ + /* .. */ + }); + }; +}; -module.exports.ruleName = ruleName; -module.exports.messages = messages; -module.exports.meta = meta; +ruleFunction.ruleName = ruleName; +ruleFunction.messages = messages; +ruleFunction.meta = meta; + +module.exports = stylelint.createPlugin(ruleName, ruleFunction); ``` Your plugin's rule name must be namespaced, e.g. `your-namespace/your-rule-name`, to ensure it never clashes with the built-in rules. If your plugin provides only a single rule or you can't think of a good namespace, you can use `plugin/my-rule`. _You should document your plugin's rule name (and namespace) because users need to use them in their config._ @@ -89,39 +88,38 @@ const meta = { /* .. */ }; -module.exports = stylelint.createPlugin( - ruleName, - (primaryOption, secondaryOptionObject) => { - return (postcssRoot, postcssResult) => { - const validOptions = stylelint.utils.validateOptions( - postcssResult, - ruleName, - { - /* .. */ - } - ); - - if (!validOptions) { - return; +const ruleFunction = (primaryOption, secondaryOptionObject) => { + return (postcssRoot, postcssResult) => { + const validOptions = stylelint.utils.validateOptions( + postcssResult, + ruleName, + { + /* .. */ } + ); - return new Promise((resolve) => { - // some async operation - setTimeout(() => { - // ... some logic ... - stylelint.utils.report({ - /* .. */ - }); - resolve(); - }, 1); - }); - }; - } -); + if (!validOptions) { + return; + } + + return new Promise((resolve) => { + // some async operation + setTimeout(() => { + // ... some logic ... + stylelint.utils.report({ + /* .. */ + }); + resolve(); + }, 1); + }); + }; +}; + +ruleFunction.ruleName = ruleName; +ruleFunction.messages = messages; +ruleFunction.meta = meta; -module.exports.ruleName = ruleName; -module.exports.messages = messages; -module.exports.meta = meta; +module.exports = stylelint.createPlugin(ruleName, ruleFunction); ``` ## Testing @@ -132,7 +130,9 @@ For example: ```js // index.test.js -const { messages, ruleName } = require("."); +const { + rule: { ruleName, messages } +} = require("."); testRule({ plugins: ["./index.js"], @@ -153,7 +153,7 @@ testRule({ { code: ".myClass {}", fixed: ".my-class {}", - message: messages.expected(), + message: messages.expected, line: 1, column: 1, endLine: 1, diff --git a/lib/__tests__/fixtures/plugin-warn-about-foo.js b/lib/__tests__/fixtures/plugin-warn-about-foo.js index e5698a8342..7cb79b3e3d 100644 --- a/lib/__tests__/fixtures/plugin-warn-about-foo.js +++ b/lib/__tests__/fixtures/plugin-warn-about-foo.js @@ -4,11 +4,15 @@ const stylelint = require('../..'); const ruleName = 'plugin/warn-about-foo'; -const warnAboutFooMessages = stylelint.utils.ruleMessages('plugin/warn-about-foo', { +const warnAboutFooMessages = stylelint.utils.ruleMessages(ruleName, { found: 'found .foo', }); -module.exports = stylelint.createPlugin(ruleName, (expectation) => { +const meta = { + url: 'https://github.com/stylelint/stylelint-foo-plugin/warn-about-foo', +}; + +const warnAboutFoo = (expectation) => { return (root, result) => { root.walkRules((rule) => { if (rule.selector === '.foo' && expectation === 'always') { @@ -21,4 +25,10 @@ module.exports = stylelint.createPlugin(ruleName, (expectation) => { } }); }; -}); +}; + +warnAboutFoo.ruleName = ruleName; +warnAboutFoo.messages = warnAboutFooMessages; +warnAboutFoo.meta = meta; + +module.exports = stylelint.createPlugin(ruleName, warnAboutFoo); diff --git a/lib/__tests__/plugins.test.js b/lib/__tests__/plugins.test.js index e748dd023f..e862e5d6dc 100644 --- a/lib/__tests__/plugins.test.js +++ b/lib/__tests__/plugins.test.js @@ -67,6 +67,19 @@ it('another plugin runs', async () => { expect(result.warnings()[0].text).toBe('Unexpected empty block (block-no-empty)'); }); +it('plugin with rule metadata', async () => { + const result = await processorRelative.process(cssWithFoo, { from: undefined }); + + expect(result.stylelint).toHaveProperty('ruleMetadata', { + 'plugin/warn-about-foo': { + url: 'https://github.com/stylelint/stylelint-foo-plugin/warn-about-foo', + }, + 'block-no-empty': { + url: expect.stringMatching(/block-no-empty/), + }, + }); +}); + it('plugin with absolute path and no configBasedir', async () => { const result = await processorAbsolute.process(cssWithFoo, { from: undefined }); diff --git a/lib/createPlugin.js b/lib/createPlugin.js index 8a12480f55..0aa1f9b9f8 100644 --- a/lib/createPlugin.js +++ b/lib/createPlugin.js @@ -1,17 +1,11 @@ 'use strict'; -/** @typedef {import('stylelint').Rule} StylelintRule */ - /** - * @param {string} ruleName - * @param {StylelintRule} rule - * @returns {{ruleName: string, rule: StylelintRule}} + * @type {typeof import('stylelint').createPlugin} */ -function createPlugin(ruleName, rule) { +module.exports = function createPlugin(ruleName, rule) { return { ruleName, rule, }; -} - -module.exports = /** @type {typeof import('stylelint').createPlugin} */ (createPlugin); +}; diff --git a/types/stylelint/index.d.ts b/types/stylelint/index.d.ts index 29b53acfb6..1f301e2274 100644 --- a/types/stylelint/index.d.ts +++ b/types/stylelint/index.d.ts @@ -188,7 +188,7 @@ declare module 'stylelint' { meta?: RuleMeta; }; - export type Plugin

= RuleBase; + export type Plugin = RuleBase; export type CodeProcessor = (code: string, file: string | undefined) => string; @@ -440,7 +440,7 @@ declare module 'stylelint' { /** * Creates a Stylelint plugin. */ - createPlugin: (ruleName: string, plugin: Plugin) => { ruleName: string; rule: Rule }; + createPlugin: (ruleName: string, rule: Rule) => { ruleName: string; rule: Rule }; /** * Internal use only. Do not use or rely on this method. It may * change at any time. diff --git a/types/stylelint/type-test.ts b/types/stylelint/type-test.ts index 4d95de7bdd..9bc1e75d10 100644 --- a/types/stylelint/type-test.ts +++ b/types/stylelint/type-test.ts @@ -12,6 +12,8 @@ import type { LintResult, LinterResult, Plugin, + Rule, + RuleMeta, Warning, } from 'stylelint'; import stylelint from 'stylelint'; @@ -84,6 +86,7 @@ const messages = stylelint.utils.ruleMessages(ruleName, { warning: (reason) => `This is not allowed because ${reason}`, withNarrowedParam: (mixinName: string) => `Mixin not allowed: ${mixinName}`, }); +const meta: RuleMeta = { url: '...' }; const problemMessage: string = messages.problem; const problemFunc: (...reason: string[]) => string = messages.warning; @@ -115,7 +118,12 @@ const testPlugin: Plugin = (options) => { }; }; -stylelint.createPlugin(ruleName, testPlugin); +(testPlugin as Rule).ruleName = ruleName; +(testPlugin as Rule).messages = messages; +(testPlugin as Rule).primaryOptionArray = true; +(testPlugin as Rule).meta = meta; + +stylelint.createPlugin(ruleName, testPlugin as Rule); messages.withNarrowedParam('should work');