Skip to content

Commit 6e153ec

Browse files
authored
chore!: convert to ESM (#97)
* chore!: convert to ESM BREAKING CHANGE: This package is now ESM-only and requires Node.js >= 20.16.0 || >= 22.4.0 * chore: convert custom rules to .cjs extension * chore: convert custom markdownlint rules to ESM
1 parent 75ccb94 commit 6e153ec

17 files changed

+431
-515
lines changed

.github/workflows/test.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@ jobs:
1717
strategy:
1818
matrix:
1919
node-version:
20-
- '22.1'
21-
- '20.10'
22-
- '18.18'
20+
- '22.4'
21+
- '20.16'
2322
os:
2423
- macos-latest
2524
- ubuntu-latest

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
11
dist
22
node_modules
3-
markdownlint-rules/emd002.js
4-
markdownlint-rules/emd003.js

bin/lint-markdown-api-history.ts

Lines changed: 75 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,20 @@
22

33
import { access, constants, readFile } from 'node:fs/promises';
44
import { resolve } from 'node:path';
5+
import { fileURLToPath } from 'node:url';
6+
import { parseArgs } from 'node:util';
57

68
import Ajv, { ValidateFunction } from 'ajv';
7-
import type { fromHtml as FromHtmlFunction } from 'hast-util-from-html';
8-
import type { HTML, Heading } from 'mdast';
9-
import type { fromMarkdown as FromMarkdownFunction } from 'mdast-util-from-markdown';
10-
import * as minimist from 'minimist';
11-
import type { Literal, Node } from 'unist';
12-
import type { visit as VisitFunction } from 'unist-util-visit';
9+
import { fromHtml } from 'hast-util-from-html';
10+
import { fromMarkdown } from 'mdast-util-from-markdown';
11+
import { visit } from 'unist-util-visit';
1312
import { URI } from 'vscode-uri';
1413
import { parseDocument, visit as yamlVisit } from 'yaml';
1514

16-
import { dynamicImport } from '../lib/helpers';
17-
import { DocsWorkspace } from '../lib/markdown';
15+
import type { HTML, Heading } from 'mdast';
16+
import type { Literal, Node } from 'unist';
17+
18+
import { DocsWorkspace } from '../lib/markdown.js';
1819

1920
// "<any char>: <match group>"
2021
const possibleStringRegex = /^[ \S]+?: *?(\S[ \S]+?)$/gm;
@@ -35,19 +36,19 @@ interface ApiHistory {
3536

3637
interface Options {
3738
// Check if the API history block is preceded by a heading
38-
checkPlacement: boolean;
39+
checkPlacement?: boolean;
3940
// Check if the 'breaking-changes-header' heading id's in the API history block exist in the breaking changes file at this filepath
40-
breakingChangesFile: string;
41+
breakingChangesFile?: string;
4142
// Check if the API history block contains strings that might cause issues when parsing the YAML
42-
checkStrings: boolean;
43+
checkStrings?: boolean;
4344
// Check if the API history block contains descriptions that aren't surrounded by double quotation marks
44-
checkDescriptions: boolean;
45+
checkDescriptions?: boolean;
4546
// Check if the API history block contains comments
46-
disallowComments: boolean;
47+
disallowComments?: boolean;
4748
// Array of glob patterns to ignore when processing files
48-
ignoreGlobs: string[];
49+
ignoreGlobs?: string[];
4950
// Check if the API history block's YAML adheres to the JSON schema at this filepath
50-
schema: string;
51+
schema?: string;
5152

5253
// TODO: Implement this when GH_TOKEN isn't needed to fetch PR release versions anymore
5354
// checkPullRequestLinks: boolean;
@@ -65,12 +66,6 @@ function isHTML(node: Node): node is HTML {
6566
export async function findPossibleApiHistoryBlocks(
6667
content: string,
6768
): Promise<PossibleHistoryBlock[]> {
68-
const { fromMarkdown } = (await dynamicImport('mdast-util-from-markdown')) as {
69-
fromMarkdown: typeof FromMarkdownFunction;
70-
};
71-
const { visit } = (await dynamicImport('unist-util-visit')) as {
72-
visit: typeof VisitFunction;
73-
};
7469
const tree = fromMarkdown(content);
7570
const codeBlocks: PossibleHistoryBlock[] = [];
7671

@@ -119,13 +114,6 @@ async function main(
119114
let warningCounter = 0;
120115

121116
try {
122-
const { fromHtml } = (await dynamicImport('hast-util-from-html')) as {
123-
fromHtml: typeof FromHtmlFunction;
124-
};
125-
const { fromMarkdown } = (await dynamicImport('mdast-util-from-markdown')) as {
126-
fromMarkdown: typeof FromMarkdownFunction;
127-
};
128-
129117
const workspace = new DocsWorkspace(workspaceRoot, globs, ignoreGlobs);
130118

131119
let validateAgainstSchema: ValidateFunction<ApiHistory> | null = null;
@@ -407,47 +395,71 @@ async function main(
407395
}
408396

409397
function parseCommandLine() {
410-
const showUsage = (arg?: string): boolean => {
411-
if (!arg || arg.startsWith('-')) {
412-
console.log(
413-
'Usage: lint-roller-markdown-api-history [--root <dir>] <globs>' +
414-
' [-h|--help]' +
415-
' [--check-placement] [--breaking-changes-file <path>] [--check-strings] [--check-descriptions] [--disallow-comments]' +
416-
' [--schema <path>]' +
417-
' [--ignore <globs>] [--ignore-path <path>]',
418-
);
419-
process.exit(1);
420-
}
421-
422-
return true;
398+
const showUsage = (): never => {
399+
console.log(
400+
'Usage: lint-roller-markdown-api-history [--root <dir>] <globs>' +
401+
' [-h|--help]' +
402+
' [--check-placement] [--breaking-changes-file <path>] [--check-strings] [--check-descriptions] [--disallow-comments]' +
403+
' [--schema <path>]' +
404+
' [--ignore <globs>] [--ignore-path <path>]',
405+
);
406+
process.exit(1);
423407
};
424408

425-
const opts = minimist(process.argv.slice(2), {
426-
boolean: [
427-
'help',
428-
'check-placement',
429-
'check-strings',
430-
'check-descriptions',
431-
'disallow-comments',
432-
],
433-
string: ['root', 'ignore', 'ignore-path', 'schema', 'breaking-changes-file'],
434-
unknown: showUsage,
435-
default: {
436-
'check-placement': true,
437-
'check-strings': true,
438-
'check-descriptions': true,
439-
'disallow-comments': true,
440-
},
441-
});
409+
try {
410+
const opts = parseArgs({
411+
allowNegative: true,
412+
allowPositionals: true,
413+
options: {
414+
'check-placement': {
415+
type: 'boolean',
416+
default: true,
417+
},
418+
'check-strings': {
419+
type: 'boolean',
420+
default: true,
421+
},
422+
'check-descriptions': {
423+
type: 'boolean',
424+
default: true,
425+
},
426+
'disallow-comments': {
427+
type: 'boolean',
428+
default: true,
429+
},
430+
root: {
431+
type: 'string',
432+
},
433+
ignore: {
434+
type: 'string',
435+
multiple: true,
436+
},
437+
'ignore-path': {
438+
type: 'string',
439+
},
440+
schema: {
441+
type: 'string',
442+
},
443+
'breaking-changes-file': {
444+
type: 'string',
445+
},
446+
help: {
447+
type: 'boolean',
448+
},
449+
},
450+
});
442451

443-
if (opts.help || !opts._.length) showUsage();
452+
if (opts.values.help || !opts.positionals.length) return showUsage();
444453

445-
return opts;
454+
return opts;
455+
} catch {
456+
return showUsage();
457+
}
446458
}
447459

448460
async function init() {
449461
try {
450-
const opts = parseCommandLine();
462+
const { values: opts, positionals } = parseCommandLine();
451463

452464
if (!opts.root) {
453465
opts.root = '.';
@@ -477,7 +489,7 @@ async function init() {
477489

478490
const { historyBlockCounter, documentCounter, errorCounter, warningCounter } = await main(
479491
resolve(process.cwd(), opts.root),
480-
opts._,
492+
positionals,
481493
{
482494
checkPlacement: opts['check-placement'],
483495
breakingChangesFile: opts['breaking-changes-file'],
@@ -500,7 +512,7 @@ async function init() {
500512
}
501513
}
502514

503-
if (require.main === module) {
515+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
504516
init().catch((error) => {
505517
console.error(error);
506518
process.exit(1);

bin/lint-markdown-links.ts

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
import * as fs from 'node:fs';
44
import * as path from 'node:path';
5+
import { fileURLToPath } from 'node:url';
6+
import { parseArgs } from 'node:util';
57

68
import {
79
createLanguageService,
@@ -10,11 +12,10 @@ import {
1012
ILogger,
1113
LogLevel,
1214
} from '@dsanders11/vscode-markdown-languageservice';
13-
import * as minimist from 'minimist';
1415
import { CancellationTokenSource } from 'vscode-languageserver';
1516
import { URI } from 'vscode-uri';
1617

17-
import { DocsWorkspace, MarkdownLinkComputer, MarkdownParser } from '../lib/markdown';
18+
import { DocsWorkspace, MarkdownLinkComputer, MarkdownParser } from '../lib/markdown.js';
1819

1920
class NoOpLogger implements ILogger {
2021
readonly level = LogLevel.Off;
@@ -144,31 +145,50 @@ async function main(
144145
}
145146

146147
function parseCommandLine() {
147-
const showUsage = (arg?: string): boolean => {
148-
if (!arg || arg.startsWith('-')) {
149-
console.log(
150-
'Usage: lint-roller-markdown-links [--root <dir>] <globs> [-h|--help] [--fetch-external-links] ' +
151-
'[--check-redirects] [--ignore <globs>]',
152-
);
153-
process.exit(1);
154-
}
155-
156-
return true;
148+
const showUsage = (): never => {
149+
console.log(
150+
'Usage: lint-roller-markdown-links [--root <dir>] <globs> [-h|--help] [--fetch-external-links] ' +
151+
'[--check-redirects] [--ignore <globs>]',
152+
);
153+
process.exit(1);
157154
};
158155

159-
const opts = minimist(process.argv.slice(2), {
160-
boolean: ['help', 'fetch-external-links', 'check-redirects'],
161-
string: ['root', 'ignore', 'ignore-path'],
162-
unknown: showUsage,
163-
});
156+
try {
157+
const opts = parseArgs({
158+
allowPositionals: true,
159+
options: {
160+
'fetch-external-links': {
161+
type: 'boolean',
162+
},
163+
'check-redirects': {
164+
type: 'boolean',
165+
},
166+
root: {
167+
type: 'string',
168+
},
169+
ignore: {
170+
type: 'string',
171+
multiple: true,
172+
},
173+
'ignore-path': {
174+
type: 'string',
175+
},
176+
help: {
177+
type: 'boolean',
178+
},
179+
},
180+
});
164181

165-
if (opts.help || !opts._.length) showUsage();
182+
if (opts.values.help || !opts.positionals.length) return showUsage();
166183

167-
return opts;
184+
return opts;
185+
} catch {
186+
return showUsage();
187+
}
168188
}
169189

170-
if (require.main === module) {
171-
const opts = parseCommandLine();
190+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
191+
const { values: opts, positionals } = parseCommandLine();
172192

173193
if (!opts.root) {
174194
opts.root = '.';
@@ -188,7 +208,7 @@ if (require.main === module) {
188208
}
189209
}
190210

191-
main(path.resolve(process.cwd(), opts.root), opts._, {
211+
main(path.resolve(process.cwd(), opts.root), positionals, {
192212
fetchExternalLinks: opts['fetch-external-links'],
193213
checkRedirects: opts['check-redirects'],
194214
ignoreGlobs: opts.ignore,

0 commit comments

Comments
 (0)