Skip to content

Commit

Permalink
file-extension: add option to disallow extensionless
Browse files Browse the repository at this point in the history
  • Loading branch information
wooorm committed Dec 17, 2023
1 parent 655ba54 commit 18e669f
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 18 deletions.
92 changes: 80 additions & 12 deletions packages/remark-lint-file-extension/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,46 @@
*
* Warn for unexpected extensions.
*
* > 👉 **Note**: does not warn when files have no file extensions (such as
* > `AUTHORS` or `LICENSE`).
*
* ###### Parameters
*
* * `options` (`Array<string>` 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> | 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.
* Also use `md` when your markdown contains common syntax extensions (such as
* 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
*
Expand All @@ -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
* {"label": "output", "name": "readme.mkd", "positionless": true}
*
* 1:1: Incorrect extension: use `mdx` or `md`
*
* @example
* {"name": "readme.mkd", "config": "mkd"}
* {"config": "mkd", "name": "readme.mkd"}
*
* @example
* {"config": ["mkd"], "name": "readme.mkd"}
*/

/**
* @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 {quotation} from 'quotation'

Expand All @@ -76,18 +120,42 @@ const remarkLintFileExtension = lintRule(
/**
* @param {Root} _
* Tree.
* @param {ReadonlyArray<string> | string | null | undefined} [options='md']
* Configuration (default: `'md'`).
* @param {Readonly<Extensions> | Readonly<Options> | 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<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 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, '`'))
Expand Down
63 changes: 57 additions & 6 deletions packages/remark-lint-file-extension/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
* [Use](#use)
* [API](#api)
* [`unified().use(remarkLintFileExtension[, options])`](#unifieduseremarklintfileextension-options)
* [`Extensions`](#extensions)
* [`Options`](#options)
* [Recommendation](#recommendation)
* [Examples](#examples)
* [Compatibility](#compatibility)
Expand Down Expand Up @@ -116,26 +118,47 @@ 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].

### `unified().use(remarkLintFileExtension[, options])`

Warn for unexpected extensions.

> 👉 **Note**: does not warn when files have no file extensions (such as
> `AUTHORS` or `LICENSE`).
###### Parameters

* `options` (`Array<string>` 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> | 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.
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down

0 comments on commit 18e669f

Please sign in to comment.