15
15
*
16
16
* Warn for unexpected extensions.
17
17
*
18
- * > 👉 **Note**: does not warn when files have no file extensions (such as
19
- * > `AUTHORS` or `LICENSE`).
20
- *
21
18
* ###### Parameters
22
19
*
23
- * * `options` (`Array<string>` or `string`, default: `['mdx', 'md']`)
24
- * — allowed file extension(s)
20
+ * * `options` ([`Extensions`][api-extensions] or [`Options`][api-options],
21
+ * optional)
22
+ * — configuration
25
23
*
26
24
* ###### Returns
27
25
*
28
26
* Transform ([`Transformer` from `unified`][github-unified-transformer]).
29
27
*
28
+ * ### `Extensions`
29
+ *
30
+ * File extension(s) (TypeScript type).
31
+ *
32
+ * ###### Type
33
+ *
34
+ * ```ts
35
+ * type Extensions = Array<string> | string
36
+ * ```
37
+ *
38
+ * ### `Options`
39
+ *
40
+ * Configuration (TypeScript type).
41
+ *
42
+ * ###### Fields
43
+ *
44
+ * * `allowExtensionless` (`boolean`, default: `true`)
45
+ * — allow no file extension such as `AUTHORS` or `LICENSE`
46
+ * * `extensions` ([`Extensions`][api-extensions], default: `['mdx', 'md']`)
47
+ * — allowed file extension(s)
48
+ *
30
49
* ## Recommendation
31
50
*
32
51
* Use `md` as it’s the most common.
33
52
* Also use `md` when your markdown contains common syntax extensions (such as
34
53
* GFM, frontmatter, or math).
35
54
* Do not use `md` for MDX: use `mdx` instead.
36
55
*
56
+ * [api-extensions]: #extensions
57
+ * [api-options]: #options
37
58
* [api-remark-lint-file-extension]: #unifieduseremarklintfileextension-options
38
59
* [github-unified-transformer]: https://github.com/unifiedjs/unified#transformer
39
60
*
45
66
* {"name": "readme.md"}
46
67
*
47
68
* @example
69
+ * {"name": "readme.mdx"}
70
+ *
71
+ * @example
48
72
* {"name": "readme"}
49
73
*
50
74
* @example
51
- * {"name": "readme.mkd", "label": "output", "positionless": true}
75
+ * {"config": {"allowExtensionless": false}, "label": "output", "name": "readme", "positionless": true }
76
+ *
77
+ * 1:1: Incorrect extension: use `mdx` or `md`
78
+ *
79
+ * @example
80
+ * {"label": "output", "name": "readme.mkd", "positionless": true}
52
81
*
53
82
* 1:1: Incorrect extension: use `mdx` or `md`
54
83
*
55
84
* @example
56
- * {"name": "readme.mkd", "config": "mkd"}
85
+ * {"config": "mkd", "name": "readme.mkd"}
86
+ *
87
+ * @example
88
+ * {"config": ["mkd"], "name": "readme.mkd"}
57
89
*/
58
90
59
91
/**
60
92
* @typedef {import('mdast').Root } Root
61
93
*/
62
94
95
+ /**
96
+ * @typedef {ReadonlyArray<string> | string } Extensions
97
+ * File extension(s).
98
+ *
99
+ * @typedef Options
100
+ * Configuration.
101
+ * @property {boolean | null | undefined } [allowExtensionless=true]
102
+ * Allow no file extension such as `AUTHORS` or `LICENSE` (default: `true`).
103
+ * @property {Extensions | null | undefined } [extensions=['mdx', 'md']]
104
+ * Allowed file extension(s) (default: `['mdx', 'md']`).
105
+ */
106
+
63
107
import { lintRule } from 'unified-lint-rule'
64
108
import { quotation } from 'quotation'
65
109
@@ -76,18 +120,42 @@ const remarkLintFileExtension = lintRule(
76
120
/**
77
121
* @param {Root } _
78
122
* Tree.
79
- * @param {ReadonlyArray<string > | string | null | undefined } [options='md' ]
80
- * Configuration (default: `'md'` ).
123
+ * @param {Readonly<Extensions > | Readonly<Options> | null | undefined } [options]
124
+ * Configuration (optional ).
81
125
* @returns {undefined }
82
126
* Nothing.
83
127
*/
84
128
function ( _ , file , options ) {
85
- const extensions =
86
- typeof options === 'string' ? [ options ] : options || defaultExtensions
129
+ let extensions = defaultExtensions
130
+ let allowExtensionless = true
131
+ /** @type {Readonly<Extensions> | null | undefined } */
132
+ let extensionsValue
133
+
134
+ if ( Array . isArray ( options ) ) {
135
+ // TS fails on `isArray` w/ readonly.
136
+ extensionsValue = /** @type {ReadonlyArray<string> } */ ( options )
137
+ } else if ( typeof options === 'string' ) {
138
+ extensionsValue = options
139
+ } else if ( options ) {
140
+ // TS fails on `isArray` w/ readonly.
141
+ const settings = /** @type {Options } */ ( options )
142
+ extensionsValue = settings . extensions
143
+
144
+ if ( settings . allowExtensionless === false ) {
145
+ allowExtensionless = false
146
+ }
147
+ }
148
+
149
+ if ( Array . isArray ( extensionsValue ) ) {
150
+ extensions = /** @type {ReadonlyArray<string> } */ ( extensionsValue )
151
+ } else if ( typeof extensionsValue === 'string' ) {
152
+ extensions = [ extensionsValue ]
153
+ }
154
+
87
155
const extname = file . extname
88
156
const extension = extname ? extname . slice ( 1 ) : undefined
89
157
90
- if ( extension && ! extensions . includes ( extension ) ) {
158
+ if ( extension ? ! extensions . includes ( extension ) : ! allowExtensionless ) {
91
159
file . message (
92
160
'Incorrect extension: use ' +
93
161
listFormat . format ( quotation ( extensions , '`' ) )
0 commit comments