file-extension: add option to disallow extensionless

This commit is contained in:
Titus Wormer 2023-12-17 11:54:56 +01:00
parent 655ba54202
commit 18e669f77a
No known key found for this signature in database
GPG Key ID: E6E581152ED04E2E
2 changed files with 137 additions and 18 deletions

View File

@ -15,18 +15,37 @@
* *
* Warn for unexpected extensions. * Warn for unexpected extensions.
* *
* > 👉 **Note**: does not warn when files have no file extensions (such as
* > `AUTHORS` or `LICENSE`).
*
* ###### Parameters * ###### Parameters
* *
* * `options` (`Array<string>` or `string`, default: `['mdx', 'md']`) * * `options` ([`Extensions`][api-extensions] or [`Options`][api-options],
* allowed file extension(s) * optional)
* configuration
* *
* ###### Returns * ###### Returns
* *
* Transform ([`Transformer` from `unified`][github-unified-transformer]). * Transform ([`Transformer` from `unified`][github-unified-transformer]).
* *
* ### `Extensions`
*
* File extension(s) (TypeScript type).
*
* ###### Type
*
* ```ts
* type Extensions = Array<string> | 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 * ## Recommendation
* *
* Use `md` as its the most common. * Use `md` as its the most common.
@ -34,6 +53,8 @@
* GFM, frontmatter, or math). * GFM, frontmatter, or math).
* Do not use `md` for MDX: use `mdx` instead. * Do not use `md` for MDX: use `mdx` instead.
* *
* [api-extensions]: #extensions
* [api-options]: #options
* [api-remark-lint-file-extension]: #unifieduseremarklintfileextension-options * [api-remark-lint-file-extension]: #unifieduseremarklintfileextension-options
* [github-unified-transformer]: https://github.com/unifiedjs/unified#transformer * [github-unified-transformer]: https://github.com/unifiedjs/unified#transformer
* *
@ -45,21 +66,44 @@
* {"name": "readme.md"} * {"name": "readme.md"}
* *
* @example * @example
* {"name": "readme.mdx"}
*
* @example
* {"name": "readme"} * {"name": "readme"}
* *
* @example * @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` * 1:1: Incorrect extension: use `mdx` or `md`
* *
* @example * @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 {import('mdast').Root} Root
*/ */
/**
* @typedef {ReadonlyArray<string> | 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 {lintRule} from 'unified-lint-rule'
import {quotation} from 'quotation' import {quotation} from 'quotation'
@ -76,18 +120,42 @@ const remarkLintFileExtension = lintRule(
/** /**
* @param {Root} _ * @param {Root} _
* Tree. * Tree.
* @param {ReadonlyArray<string> | string | null | undefined} [options='md'] * @param {Readonly<Extensions> | Readonly<Options> | null | undefined} [options]
* Configuration (default: `'md'`). * Configuration (optional).
* @returns {undefined} * @returns {undefined}
* Nothing. * Nothing.
*/ */
function (_, file, options) { function (_, file, options) {
const extensions = let extensions = defaultExtensions
typeof options === 'string' ? [options] : options || defaultExtensions let allowExtensionless = true
/** @type {Readonly<Extensions> | null | undefined} */
let extensionsValue
if (Array.isArray(options)) {
// TS fails on `isArray` w/ readonly.
extensionsValue = /** @type {ReadonlyArray<string>} */ (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<string>} */ (extensionsValue)
} else if (typeof extensionsValue === 'string') {
extensions = [extensionsValue]
}
const extname = file.extname const extname = file.extname
const extension = extname ? extname.slice(1) : undefined const extension = extname ? extname.slice(1) : undefined
if (extension && !extensions.includes(extension)) { if (extension ? !extensions.includes(extension) : !allowExtensionless) {
file.message( file.message(
'Incorrect extension: use ' + 'Incorrect extension: use ' +
listFormat.format(quotation(extensions, '`')) listFormat.format(quotation(extensions, '`'))

View File

@ -21,6 +21,8 @@
* [Use](#use) * [Use](#use)
* [API](#api) * [API](#api)
* [`unified().use(remarkLintFileExtension[, options])`](#unifieduseremarklintfileextension-options) * [`unified().use(remarkLintFileExtension[, options])`](#unifieduseremarklintfileextension-options)
* [`Extensions`](#extensions)
* [`Options`](#options)
* [Recommendation](#recommendation) * [Recommendation](#recommendation)
* [Examples](#examples) * [Examples](#examples)
* [Compatibility](#compatibility) * [Compatibility](#compatibility)
@ -116,7 +118,9 @@ On the CLI in a config file (here a `package.json`):
## API ## API
This package exports no identifiers. 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 The default export is
[`remarkLintFileExtension`][api-remark-lint-file-extension]. [`remarkLintFileExtension`][api-remark-lint-file-extension].
@ -124,18 +128,37 @@ The default export is
Warn for unexpected extensions. Warn for unexpected extensions.
> 👉 **Note**: does not warn when files have no file extensions (such as
> `AUTHORS` or `LICENSE`).
###### Parameters ###### Parameters
* `options` (`Array<string>` or `string`, default: `['mdx', 'md']`) * `options` ([`Extensions`][api-extensions] or [`Options`][api-options],
— allowed file extension(s) optional)
— configuration
###### Returns ###### Returns
Transform ([`Transformer` from `unified`][github-unified-transformer]). Transform ([`Transformer` from `unified`][github-unified-transformer]).
### `Extensions`
File extension(s) (TypeScript type).
###### Type
```ts
type Extensions = Array<string> | 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 ## Recommendation
Use `md` as its the most common. Use `md` as its the most common.
@ -151,12 +174,28 @@ Do not use `md` for MDX: use `mdx` instead.
No messages. No messages.
##### `readme.mdx`
###### Out
No messages.
##### `readme` ##### `readme`
###### Out ###### Out
No messages. No messages.
##### `readme`
When configured with `{ allowExtensionless: false }`.
###### Out
```text
1:1: Incorrect extension: use `mdx` or `md`
```
##### `readme.mkd` ##### `readme.mkd`
###### Out ###### Out
@ -173,6 +212,14 @@ When configured with `'mkd'`.
No messages. No messages.
##### `readme.mkd`
When configured with `[ 'mkd' ]`.
###### Out
No messages.
## Compatibility ## Compatibility
Projects maintained by the unified collective are compatible with maintained Projects maintained by the unified collective are compatible with maintained
@ -198,6 +245,10 @@ abide by its terms.
[MIT][file-license] © [Titus Wormer][author] [MIT][file-license] © [Titus Wormer][author]
[api-extensions]: #extensions
[api-options]: #options
[api-remark-lint-file-extension]: #unifieduseremarklintfileextension-options [api-remark-lint-file-extension]: #unifieduseremarklintfileextension-options
[author]: https://wooorm.com [author]: https://wooorm.com