Skip to content

Commit 5520b55

Browse files
committed
Use import() only (no more require()) when loading modules for custom rules, plugins, output formatters, and configuration.
1 parent 62a2679 commit 5520b55

19 files changed

+168
-149
lines changed

markdownlint-cli2.mjs

Lines changed: 65 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// @ts-ignore
22

3-
// Requires
3+
// Imports
44
import fsNode from "node:fs";
55
import { createRequire } from "node:module";
66
const dynamicRequire = createRequire(import.meta.url);
@@ -15,7 +15,7 @@ import { lint, extendConfig, readConfig } from "markdownlint/promise";
1515
import { expandTildePath } from "markdownlint/helpers";
1616
import appendToArray from "./append-to-array.mjs";
1717
import mergeOptions from "./merge-options.mjs";
18-
import resolveAndRequire from "./resolve-and-require.mjs";
18+
import resolveModule from "./resolve-module.mjs";
1919
import parsers from "./parsers/parsers.mjs";
2020
import jsoncParse from "./parsers/jsonc-parse.mjs";
2121
import yamlParse from "./parsers/yaml-parse.mjs";
@@ -65,73 +65,65 @@ const readConfigFile = (fs, dir, name, otherwise) => () => {
6565
);
6666
};
6767

68-
// Import or resolve/require a module ID with a custom directory in the path
69-
const importOrRequireResolve = async (dirOrDirs, id, noRequire) => {
70-
if (typeof id === "string") {
71-
if (noRequire) {
72-
return null;
73-
}
74-
const dirs = Array.isArray(dirOrDirs) ? dirOrDirs : [ dirOrDirs ];
75-
const expandId = expandTildePath(id, os);
76-
const errors = [];
77-
// Try to load via require(...)
78-
try {
79-
const isModule = /\.mjs$/iu.test(expandId);
80-
if (!isModule) {
81-
// Try not to use require for modules due to breaking change in Node 22.12:
82-
// https://github.com/nodejs/node/releases/tag/v22.12.0
83-
return resolveAndRequire(dynamicRequire, expandId, dirs);
84-
}
85-
} catch (error) {
86-
errors.push(error);
87-
}
88-
// Try to load via import(...)
68+
// Import a module ID with a custom directory in the path
69+
const importModule = async (dirOrDirs, id, noImport) => {
70+
if (typeof id !== "string") {
71+
return id;
72+
} else if (noImport) {
73+
return null;
74+
}
75+
const dirs = Array.isArray(dirOrDirs) ? dirOrDirs : [ dirOrDirs ];
76+
const expandId = expandTildePath(id, os);
77+
const errors = [];
78+
let moduleName = null;
79+
try {
8980
try {
90-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
91-
const isURL = !pathDefault.isAbsolute(expandId) && URL.canParse(expandId);
92-
const urlString = (
93-
isURL ? new URL(expandId) : pathToFileURL(pathDefault.resolve(dirs[0], expandId))
94-
).toString();
95-
// eslint-disable-next-line no-inline-comments
96-
const module = await import(/* webpackIgnore: true */ urlString);
97-
return module.default;
81+
moduleName = pathToFileURL(resolveModule(dynamicRequire, expandId, dirs));
9882
} catch (error) {
9983
errors.push(error);
84+
moduleName =
85+
// eslint-disable-next-line n/no-unsupported-features/node-builtins
86+
(!pathDefault.isAbsolute(expandId) && URL.canParse(expandId))
87+
? new URL(expandId)
88+
: pathToFileURL(pathDefault.resolve(dirs[0], expandId));
10089
}
101-
// Give up
90+
// eslint-disable-next-line no-inline-comments
91+
const module = await import(/* webpackIgnore: true */ moduleName);
92+
return module.default;
93+
} catch (error) {
94+
errors.push(error);
10295
throw new AggregateError(
10396
errors,
104-
`Unable to require or import module '${id}'.`
97+
`Unable to import module '${id}'.`
10598
);
10699
}
107-
return id;
108100
};
109101

110-
// Import or require an array of modules by ID
111-
const importOrRequireIds = (dirs, ids, noRequire) => (
102+
// Import an array of modules by ID
103+
const importModuleIds = (dirs, ids, noImport) => (
112104
Promise.all(
113105
ids.map(
114-
(id) => importOrRequireResolve(dirs, id, noRequire)
106+
(id) => importModule(dirs, id, noImport)
115107
)
116108
).then((results) => results.filter(Boolean))
117109
);
118110

119-
// Import or require an array of modules by ID (preserving parameters)
120-
const importOrRequireIdsAndParams = (dirs, idsAndParams, noRequire) => (
111+
// Import an array of modules by ID (preserving parameters)
112+
const importModuleIdsAndParams = (dirs, idsAndParams, noImport) => (
121113
Promise.all(
122114
idsAndParams.map(
123-
(idAndParams) => importOrRequireResolve(dirs, idAndParams[0], noRequire).
115+
(idAndParams) => importModule(dirs, idAndParams[0], noImport).
124116
then((module) => module && [ module, ...idAndParams.slice(1) ])
125117
)
126118
).then((results) => results.filter(Boolean))
127119
);
128120

129-
// Import or require a JavaScript file and return the exported object
130-
const importOrRequireConfig = (fs, dir, name, noRequire, otherwise) => () => {
121+
// Import a JavaScript file and return the exported object
122+
const importConfig = (fs, dir, name, noImport, otherwise) => () => {
131123
const file = pathPosix.join(dir, name);
132124
return fs.promises.access(file).
133125
then(
134-
() => importOrRequireResolve(dir, name, noRequire),
126+
() => importModule(dir, name, noImport),
135127
otherwise
136128
);
137129
};
@@ -151,7 +143,7 @@ const getExtendedConfig = (config, configPath, fs) => {
151143
};
152144

153145
// Read an options or config file in any format and return the object
154-
const readOptionsOrConfig = async (configPath, fs, noRequire) => {
146+
const readOptionsOrConfig = async (configPath, fs, noImport) => {
155147
const basename = pathPosix.basename(configPath);
156148
const dirname = pathPosix.dirname(configPath);
157149
let options = null;
@@ -165,7 +157,7 @@ const readOptionsOrConfig = async (configPath, fs, noRequire) => {
165157
basename.endsWith(".markdownlint-cli2.cjs") ||
166158
basename.endsWith(".markdownlint-cli2.mjs")
167159
) {
168-
options = await importOrRequireResolve(dirname, basename, noRequire);
160+
options = await importModule(dirname, basename, noImport);
169161
} else if (
170162
basename.endsWith(".markdownlint.jsonc") ||
171163
basename.endsWith(".markdownlint.json") ||
@@ -177,7 +169,7 @@ const readOptionsOrConfig = async (configPath, fs, noRequire) => {
177169
basename.endsWith(".markdownlint.cjs") ||
178170
basename.endsWith(".markdownlint.mjs")
179171
) {
180-
config = await importOrRequireResolve(dirname, basename, noRequire);
172+
config = await importModule(dirname, basename, noImport);
181173
} else {
182174
throw new Error(
183175
"File name should be (or end with) one of the supported types " +
@@ -288,7 +280,7 @@ const getAndProcessDirInfo = (
288280
dirToDirInfo,
289281
dir,
290282
relativeDir,
291-
noRequire,
283+
noImport,
292284
allowPackageJson
293285
) => {
294286
// Create dirInfo
@@ -322,10 +314,10 @@ const getAndProcessDirInfo = (
322314
() => fs.promises.readFile(file, utf8).then(yamlParse),
323315
() => fs.promises.access(captureFile(markdownlintCli2Cjs)).
324316
then(
325-
() => importOrRequireResolve(dir, file, noRequire),
317+
() => importModule(dir, file, noImport),
326318
() => fs.promises.access(captureFile(markdownlintCli2Mjs)).
327319
then(
328-
() => importOrRequireResolve(dir, file, noRequire),
320+
() => importModule(dir, file, noImport),
329321
() => (allowPackageJson
330322
? fs.promises.access(captureFile(packageJson))
331323
// eslint-disable-next-line prefer-promise-reject-errors
@@ -379,16 +371,16 @@ const getAndProcessDirInfo = (
379371
fs,
380372
dir,
381373
".markdownlint.yml",
382-
importOrRequireConfig(
374+
importConfig(
383375
fs,
384376
dir,
385377
".markdownlint.cjs",
386-
noRequire,
387-
importOrRequireConfig(
378+
noImport,
379+
importConfig(
388380
fs,
389381
dir,
390382
".markdownlint.mjs",
391-
noRequire,
383+
noImport,
392384
noop
393385
)
394386
)
@@ -417,7 +409,7 @@ const getBaseOptions = async (
417409
options,
418410
fixDefault,
419411
noGlobs,
420-
noRequire
412+
noImport
421413
) => {
422414
const tasks = [];
423415
const dirToDirInfo = {};
@@ -427,7 +419,7 @@ const getBaseOptions = async (
427419
dirToDirInfo,
428420
baseDir,
429421
relativeDir,
430-
noRequire,
422+
noImport,
431423
true
432424
);
433425
await Promise.all(tasks);
@@ -468,7 +460,7 @@ const enumerateFiles = async (
468460
dirToDirInfo,
469461
gitignore,
470462
ignoreFiles,
471-
noRequire
463+
noImport
472464
) => {
473465
const tasks = [];
474466
/** @type {import("globby").Options} */
@@ -536,7 +528,7 @@ const enumerateFiles = async (
536528
dirToDirInfo,
537529
dir,
538530
null,
539-
noRequire,
531+
noImport,
540532
false
541533
);
542534
dirInfo.files.push(file);
@@ -549,7 +541,7 @@ const enumerateParents = async (
549541
fs,
550542
baseDir,
551543
dirToDirInfo,
552-
noRequire
544+
noImport
553545
) => {
554546
const tasks = [];
555547

@@ -578,7 +570,7 @@ const enumerateParents = async (
578570
dirToDirInfo,
579571
dir,
580572
null,
581-
noRequire,
573+
noImport,
582574
false
583575
);
584576
lastDirInfo.parent = dirInfo;
@@ -603,7 +595,7 @@ const createDirInfos = async (
603595
optionsOverride,
604596
gitignore,
605597
ignoreFiles,
606-
noRequire
598+
noImport
607599
) => {
608600
await enumerateFiles(
609601
fs,
@@ -613,13 +605,13 @@ const createDirInfos = async (
613605
dirToDirInfo,
614606
gitignore,
615607
ignoreFiles,
616-
noRequire
608+
noImport
617609
);
618610
await enumerateParents(
619611
fs,
620612
baseDir,
621613
dirToDirInfo,
622-
noRequire
614+
noImport
623615
);
624616

625617
// Merge file lists with identical configuration
@@ -650,10 +642,10 @@ const createDirInfos = async (
650642
);
651643
if (markdownlintOptions && markdownlintOptions.customRules) {
652644
tasks.push(
653-
importOrRequireIds(
645+
importModuleIds(
654646
[ effectiveDir, ...effectiveModulePaths ],
655647
markdownlintOptions.customRules,
656-
noRequire
648+
noImport
657649
).then((customRules) => {
658650
// Expand nested arrays (for packages that export multiple rules)
659651
markdownlintOptions.customRules = customRules.flat();
@@ -662,10 +654,10 @@ const createDirInfos = async (
662654
}
663655
if (markdownlintOptions && markdownlintOptions.markdownItPlugins) {
664656
tasks.push(
665-
importOrRequireIdsAndParams(
657+
importModuleIdsAndParams(
666658
[ effectiveDir, ...effectiveModulePaths ],
667659
markdownlintOptions.markdownItPlugins,
668-
noRequire
660+
noImport
669661
).then((markdownItPlugins) => {
670662
markdownlintOptions.markdownItPlugins = markdownItPlugins;
671663
})
@@ -858,7 +850,7 @@ const outputSummary = async (
858850
modulePaths,
859851
logMessage,
860852
logError,
861-
noRequire
853+
noImport
862854
) => {
863855
const errorsPresent = (summary.length > 0);
864856
if (errorsPresent || outputFormatters) {
@@ -871,7 +863,7 @@ const outputSummary = async (
871863
const dir = relativeDir || baseDir;
872864
const dirs = [ dir, ...modulePaths ];
873865
const formattersAndParams = outputFormatters
874-
? await importOrRequireIdsAndParams(dirs, outputFormatters, noRequire)
866+
? await importModuleIdsAndParams(dirs, outputFormatters, noImport)
875867
// eslint-disable-next-line no-inline-comments, unicorn/no-await-expression-member
876868
: [ [ (await import(/* webpackMode: "eager" */ "markdownlint-cli2-formatter-default")).default ] ];
877869
await Promise.all(formattersAndParams.map((formatterAndParams) => {
@@ -891,7 +883,7 @@ export const main = async (params) => {
891883
optionsDefault,
892884
optionsOverride,
893885
fileContents,
894-
noRequire,
886+
noImport,
895887
allowStdin
896888
} = params;
897889
let {
@@ -948,7 +940,7 @@ export const main = async (params) => {
948940
const resolvedConfigPath =
949941
posixPath(pathDefault.resolve(baseDirSystem, configPath));
950942
optionsArgv =
951-
await readOptionsOrConfig(resolvedConfigPath, fs, noRequire);
943+
await readOptionsOrConfig(resolvedConfigPath, fs, noImport);
952944
relativeDir = pathPosix.dirname(resolvedConfigPath);
953945
}
954946
// Process arguments and get base options
@@ -961,7 +953,7 @@ export const main = async (params) => {
961953
optionsArgv || optionsDefault,
962954
fixDefault,
963955
noGlobs,
964-
noRequire
956+
noImport
965957
);
966958
} finally {
967959
if (!baseOptions?.baseMarkdownlintOptions.noBanner) {
@@ -1020,7 +1012,7 @@ export const main = async (params) => {
10201012
optionsOverride,
10211013
gitignore,
10221014
ignoreFiles,
1023-
noRequire
1015+
noImport
10241016
);
10251017
// Output linting status
10261018
if (showProgress) {
@@ -1058,7 +1050,7 @@ export const main = async (params) => {
10581050
modulePaths,
10591051
logMessage,
10601052
logError,
1061-
noRequire
1053+
noImport
10621054
);
10631055
// Return result
10641056
return errorsPresent ? 1 : 0;

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
"playwright-test": "playwright test --config ./webworker/playwright.config.mjs",
4040
"playwright-test-docker": "docker run --rm --volume $PWD:/home/workdir --workdir /home/workdir --ipc=host mcr.microsoft.com/playwright:v1.49.1 npm run playwright-test",
4141
"schema": "cpy ./node_modules/markdownlint/schema/markdownlint-config-schema.json ./schema --flat",
42-
"test": "ava --timeout=1m test/append-to-array-test.mjs test/fs-mock-test.mjs test/fs-virtual-test.mjs test/markdownlint-cli2-test.mjs test/markdownlint-cli2-test-exec.mjs test/markdownlint-cli2-test-exports.mjs test/markdownlint-cli2-test-fs.mjs test/markdownlint-cli2-test-main.mjs test/merge-options-test.mjs test/resolve-and-require-test.mjs",
42+
"test": "ava --timeout=1m test/append-to-array-test.mjs test/fs-mock-test.mjs test/fs-virtual-test.mjs test/markdownlint-cli2-test.mjs test/markdownlint-cli2-test-exec.mjs test/markdownlint-cli2-test-exports.mjs test/markdownlint-cli2-test-fs.mjs test/markdownlint-cli2-test-main.mjs test/merge-options-test.mjs test/resolve-module-test.mjs",
4343
"test-cover": "c8 --100 npm test",
4444
"test-docker-hub-image": "VERSION=$(node -e \"process.stdout.write(require('./package.json').version)\") && docker image rm davidanson/markdownlint-cli2:v$VERSION davidanson/markdownlint-cli2:latest || true && docker run --rm -v $PWD:/workdir davidanson/markdownlint-cli2:v$VERSION \"*.md\" && docker run --rm -v $PWD:/workdir davidanson/markdownlint-cli2:latest \"*.md\"",
4545
"test-docker-hub-image-rules": "VERSION=$(node -e \"process.stdout.write(require('./package.json').version)\") && docker image rm davidanson/markdownlint-cli2-rules:v$VERSION davidanson/markdownlint-cli2-rules:latest || true && docker run --rm -v $PWD:/workdir davidanson/markdownlint-cli2-rules:v$VERSION \"*.md\" && docker run --rm -v $PWD:/workdir davidanson/markdownlint-cli2-rules:latest \"*.md\"",
@@ -68,7 +68,7 @@
6868
"parsers/jsonc-parse.mjs",
6969
"parsers/yaml-parse.mjs",
7070
"README.md",
71-
"resolve-and-require.mjs",
71+
"resolve-module.mjs",
7272
"schema/markdownlint-cli2-config-schema.json",
7373
"schema/markdownlint-config-schema.json",
7474
"schema/ValidatingConfiguration.md"

resolve-and-require.mjs

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)