From 18e669f77ad2ab4521767fd3033068ff94211537 Mon Sep 17 00:00:00 2001 From: Titus Wormer Date: Sun, 17 Dec 2023 11:54:56 +0100 Subject: [PATCH] file-extension: add option to disallow extensionless --- packages/remark-lint-file-extension/index.js | 92 ++++++++++++++++--- packages/remark-lint-file-extension/readme.md | 63 +++++++++++-- 2 files changed, 137 insertions(+), 18 deletions(-) diff --git a/packages/remark-lint-file-extension/index.js b/packages/remark-lint-file-extension/index.js index 9742632..ee876c9 100644 --- a/packages/remark-lint-file-extension/index.js +++ b/packages/remark-lint-file-extension/index.js @@ -15,18 +15,37 @@ * * Warn for unexpected extensions. * - * > πŸ‘‰ **Note**: does not warn when files have no file extensions (such as - * > `AUTHORS` or `LICENSE`). - * * ###### Parameters * - * * `options` (`Array` or `string`, default: `['mdx', 'md']`) - * β€” allowed file extension(s) + * * `options` ([`Extensions`][api-extensions] or [`Options`][api-options], + * optional) + * β€” configuration * * ###### Returns * * Transform ([`Transformer` from `unified`][github-unified-transformer]). * + * ### `Extensions` + * + * File extension(s) (TypeScript type). + * + * ###### Type + * + * ```ts + * type Extensions = Array | string + * ``` + * + * ### `Options` + * + * Configuration (TypeScript type). + * + * ###### Fields + * + * * `allowExtensionless` (`boolean`, default: `true`) + * β€” allow no file extension such as `AUTHORS` or `LICENSE` + * * `extensions` ([`Extensions`][api-extensions], default: `['mdx', 'md']`) + * β€” allowed file extension(s) + * * ## Recommendation * * Use `md` as it’s the most common. @@ -34,6 +53,8 @@ * GFM, frontmatter, or math). * Do not use `md` for MDX: use `mdx` instead. * + * [api-extensions]: #extensions + * [api-options]: #options * [api-remark-lint-file-extension]: #unifieduseremarklintfileextension-options * [github-unified-transformer]: https://github.com/unifiedjs/unified#transformer * @@ -45,21 +66,44 @@ * {"name": "readme.md"} * * @example + * {"name": "readme.mdx"} + * + * @example * {"name": "readme"} * * @example - * {"name": "readme.mkd", "label": "output", "positionless": true} + * {"config": {"allowExtensionless": false}, "label": "output", "name": "readme", "positionless": true} * * 1:1: Incorrect extension: use `mdx` or `md` * * @example - * {"name": "readme.mkd", "config": "mkd"} + * {"label": "output", "name": "readme.mkd", "positionless": true} + * + * 1:1: Incorrect extension: use `mdx` or `md` + * + * @example + * {"config": "mkd", "name": "readme.mkd"} + * + * @example + * {"config": ["mkd"], "name": "readme.mkd"} */ /** * @typedef {import('mdast').Root} Root */ +/** + * @typedef {ReadonlyArray | string} Extensions + * File extension(s). + * + * @typedef Options + * Configuration. + * @property {boolean | null | undefined} [allowExtensionless=true] + * Allow no file extension such as `AUTHORS` or `LICENSE` (default: `true`). + * @property {Extensions | null | undefined} [extensions=['mdx', 'md']] + * Allowed file extension(s) (default: `['mdx', 'md']`). + */ + import {lintRule} from 'unified-lint-rule' import {quotation} from 'quotation' @@ -76,18 +120,42 @@ const remarkLintFileExtension = lintRule( /** * @param {Root} _ * Tree. - * @param {ReadonlyArray | string | null | undefined} [options='md'] - * Configuration (default: `'md'`). + * @param {Readonly | Readonly | null | undefined} [options] + * Configuration (optional). * @returns {undefined} * Nothing. */ function (_, file, options) { - const extensions = - typeof options === 'string' ? [options] : options || defaultExtensions + let extensions = defaultExtensions + let allowExtensionless = true + /** @type {Readonly | null | undefined} */ + let extensionsValue + + if (Array.isArray(options)) { + // TS fails on `isArray` w/ readonly. + extensionsValue = /** @type {ReadonlyArray} */ (options) + } else if (typeof options === 'string') { + extensionsValue = options + } else if (options) { + // TS fails on `isArray` w/ readonly. + const settings = /** @type {Options} */ (options) + extensionsValue = settings.extensions + + if (settings.allowExtensionless === false) { + allowExtensionless = false + } + } + + if (Array.isArray(extensionsValue)) { + extensions = /** @type {ReadonlyArray} */ (extensionsValue) + } else if (typeof extensionsValue === 'string') { + extensions = [extensionsValue] + } + const extname = file.extname const extension = extname ? extname.slice(1) : undefined - if (extension && !extensions.includes(extension)) { + if (extension ? !extensions.includes(extension) : !allowExtensionless) { file.message( 'Incorrect extension: use ' + listFormat.format(quotation(extensions, '`')) diff --git a/packages/remark-lint-file-extension/readme.md b/packages/remark-lint-file-extension/readme.md index b6035b8..c3f5c50 100644 --- a/packages/remark-lint-file-extension/readme.md +++ b/packages/remark-lint-file-extension/readme.md @@ -21,6 +21,8 @@ * [Use](#use) * [API](#api) * [`unified().use(remarkLintFileExtension[, options])`](#unifieduseremarklintfileextension-options) + * [`Extensions`](#extensions) + * [`Options`](#options) * [Recommendation](#recommendation) * [Examples](#examples) * [Compatibility](#compatibility) @@ -116,7 +118,9 @@ On the CLI in a config file (here a `package.json`): ## API This package exports no identifiers. -It exports no additional [TypeScript][typescript] types. +It exports the [TypeScript][typescript] types +[`Extensions`][api-extensions] and +[`Options`][api-options]. The default export is [`remarkLintFileExtension`][api-remark-lint-file-extension]. @@ -124,18 +128,37 @@ The default export is Warn for unexpected extensions. -> πŸ‘‰ **Note**: does not warn when files have no file extensions (such as -> `AUTHORS` or `LICENSE`). - ###### Parameters -* `options` (`Array` or `string`, default: `['mdx', 'md']`) - β€” allowed file extension(s) +* `options` ([`Extensions`][api-extensions] or [`Options`][api-options], + optional) + β€” configuration ###### Returns Transform ([`Transformer` from `unified`][github-unified-transformer]). +### `Extensions` + +File extension(s) (TypeScript type). + +###### Type + +```ts +type Extensions = Array | string +``` + +### `Options` + +Configuration (TypeScript type). + +###### Fields + +* `allowExtensionless` (`boolean`, default: `true`) + β€” allow no file extension such as `AUTHORS` or `LICENSE` +* `extensions` ([`Extensions`][api-extensions], default: `['mdx', 'md']`) + β€” allowed file extension(s) + ## Recommendation Use `md` as it’s the most common. @@ -151,12 +174,28 @@ Do not use `md` for MDX: use `mdx` instead. No messages. +##### `readme.mdx` + +###### Out + +No messages. + ##### `readme` ###### Out No messages. +##### `readme` + +When configured with `{ allowExtensionless: false }`. + +###### Out + +```text +1:1: Incorrect extension: use `mdx` or `md` +``` + ##### `readme.mkd` ###### Out @@ -173,6 +212,14 @@ When configured with `'mkd'`. No messages. +##### `readme.mkd` + +When configured with `[ 'mkd' ]`. + +###### Out + +No messages. + ## Compatibility Projects maintained by the unified collective are compatible with maintained @@ -198,6 +245,10 @@ abide by its terms. [MIT][file-license] Β© [Titus Wormer][author] +[api-extensions]: #extensions + +[api-options]: #options + [api-remark-lint-file-extension]: #unifieduseremarklintfileextension-options [author]: https://wooorm.com