Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidAnson committed Dec 16, 2024
1 parent f72fac2 commit e2d5436
Show file tree
Hide file tree
Showing 14 changed files with 114 additions and 98 deletions.
99 changes: 44 additions & 55 deletions markdownlint-cli2.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { lint, extendConfig, readConfig } from "markdownlint/promise";
import { expandTildePath } from "markdownlint/helpers";
import appendToArray from "./append-to-array.mjs";
import mergeOptions from "./merge-options.mjs";
import resolveAndRequire from "./resolve-and-require.mjs";
import resolveModule from "./resolve-module.mjs";
import parsers from "./parsers/parsers.mjs";
import jsoncParse from "./parsers/jsonc-parse.mjs";
import yamlParse from "./parsers/yaml-parse.mjs";
Expand Down Expand Up @@ -65,73 +65,62 @@ const readConfigFile = (fs, dir, name, otherwise) => () => {
);
};

// Import or resolve/require a module ID with a custom directory in the path
const importOrRequireResolve = async (dirOrDirs, id, noRequire) => {
if (typeof id === "string") {
if (noRequire) {
return null;
}
const dirs = Array.isArray(dirOrDirs) ? dirOrDirs : [ dirOrDirs ];
const expandId = expandTildePath(id, os);
const errors = [];
// Try to load via require(...)
try {
const isModule = /\.mjs$/iu.test(expandId);
if (!isModule) {
// Try not to use require for modules due to breaking change in Node 22.12:
// https://github.com/nodejs/node/releases/tag/v22.12.0
return resolveAndRequire(dynamicRequire, expandId, dirs);
}
} catch (error) {
errors.push(error);
}
// Try to load via import(...)
// Import a module ID with a custom directory in the path
const importModule = async (dirOrDirs, id, noRequire) => {
if (typeof id !== "string") {
return id;
} else if (noRequire) {
return null;
}
const expandId = expandTildePath(id, os);
const dirs = Array.isArray(dirOrDirs) ? dirOrDirs : [ dirOrDirs ];
try {
let moduleName = null;
try {
// eslint-disable-next-line n/no-unsupported-features/node-builtins
const isURL = !pathDefault.isAbsolute(expandId) && URL.canParse(expandId);
const urlString = (
isURL ? new URL(expandId) : pathToFileURL(pathDefault.resolve(dirs[0], expandId))
).toString();
// eslint-disable-next-line no-inline-comments
const module = await import(/* webpackIgnore: true */ urlString);
return module.default;
} catch (error) {
errors.push(error);
moduleName = resolveModule(dynamicRequire, expandId, dirs);
} catch {
moduleName =
// eslint-disable-next-line n/no-unsupported-features/node-builtins
(!pathDefault.isAbsolute(expandId) && URL.canParse(expandId))
? new URL(expandId)
: pathToFileURL(pathDefault.resolve(dirs[0], expandId));
}
// Give up
throw new AggregateError(
errors,
`Unable to require or import module '${id}'.`
// eslint-disable-next-line no-inline-comments
const module = await import(/* webpackIgnore: true */ moduleName);
return module.default;
} catch (error) {
throw new Error(
`Unable to require or import module '${id}'.`,
{ "cause": error }
);
}
return id;
};

// Import or require an array of modules by ID
const importOrRequireIds = (dirs, ids, noRequire) => (
// Import an array of modules by ID
const importModuleIds = (dirs, ids, noRequire) => (
Promise.all(
ids.map(
(id) => importOrRequireResolve(dirs, id, noRequire)
(id) => importModule(dirs, id, noRequire)
)
).then((results) => results.filter(Boolean))
);

// Import or require an array of modules by ID (preserving parameters)
const importOrRequireIdsAndParams = (dirs, idsAndParams, noRequire) => (
// Import an array of modules by ID (preserving parameters)
const importModuleIdsAndParams = (dirs, idsAndParams, noRequire) => (
Promise.all(
idsAndParams.map(
(idAndParams) => importOrRequireResolve(dirs, idAndParams[0], noRequire).
(idAndParams) => importModule(dirs, idAndParams[0], noRequire).
then((module) => module && [ module, ...idAndParams.slice(1) ])
)
).then((results) => results.filter(Boolean))
);

// Import or require a JavaScript file and return the exported object
const importOrRequireConfig = (fs, dir, name, noRequire, otherwise) => () => {
// Import a JavaScript file and return the exported object
const importConfig = (fs, dir, name, noRequire, otherwise) => () => {
const file = pathPosix.join(dir, name);
return fs.promises.access(file).
then(
() => importOrRequireResolve(dir, name, noRequire),
() => importModule(dir, name, noRequire),
otherwise
);
};
Expand Down Expand Up @@ -165,7 +154,7 @@ const readOptionsOrConfig = async (configPath, fs, noRequire) => {
basename.endsWith(".markdownlint-cli2.cjs") ||
basename.endsWith(".markdownlint-cli2.mjs")
) {
options = await importOrRequireResolve(dirname, basename, noRequire);
options = await importModule(dirname, basename, noRequire);
} else if (
basename.endsWith(".markdownlint.jsonc") ||
basename.endsWith(".markdownlint.json") ||
Expand All @@ -177,7 +166,7 @@ const readOptionsOrConfig = async (configPath, fs, noRequire) => {
basename.endsWith(".markdownlint.cjs") ||
basename.endsWith(".markdownlint.mjs")
) {
config = await importOrRequireResolve(dirname, basename, noRequire);
config = await importModule(dirname, basename, noRequire);
} else {
throw new Error(
"File name should be (or end with) one of the supported types " +
Expand Down Expand Up @@ -322,10 +311,10 @@ const getAndProcessDirInfo = (
() => fs.promises.readFile(file, utf8).then(yamlParse),
() => fs.promises.access(captureFile(markdownlintCli2Cjs)).
then(
() => importOrRequireResolve(dir, file, noRequire),
() => importModule(dir, file, noRequire),
() => fs.promises.access(captureFile(markdownlintCli2Mjs)).
then(
() => importOrRequireResolve(dir, file, noRequire),
() => importModule(dir, file, noRequire),
() => (allowPackageJson
? fs.promises.access(captureFile(packageJson))
// eslint-disable-next-line prefer-promise-reject-errors
Expand Down Expand Up @@ -379,12 +368,12 @@ const getAndProcessDirInfo = (
fs,
dir,
".markdownlint.yml",
importOrRequireConfig(
importConfig(
fs,
dir,
".markdownlint.cjs",
noRequire,
importOrRequireConfig(
importConfig(
fs,
dir,
".markdownlint.mjs",
Expand Down Expand Up @@ -650,7 +639,7 @@ const createDirInfos = async (
);
if (markdownlintOptions && markdownlintOptions.customRules) {
tasks.push(
importOrRequireIds(
importModuleIds(
[ effectiveDir, ...effectiveModulePaths ],
markdownlintOptions.customRules,
noRequire
Expand All @@ -662,7 +651,7 @@ const createDirInfos = async (
}
if (markdownlintOptions && markdownlintOptions.markdownItPlugins) {
tasks.push(
importOrRequireIdsAndParams(
importModuleIdsAndParams(
[ effectiveDir, ...effectiveModulePaths ],
markdownlintOptions.markdownItPlugins,
noRequire
Expand Down Expand Up @@ -871,7 +860,7 @@ const outputSummary = async (
const dir = relativeDir || baseDir;
const dirs = [ dir, ...modulePaths ];
const formattersAndParams = outputFormatters
? await importOrRequireIdsAndParams(dirs, outputFormatters, noRequire)
? await importModuleIdsAndParams(dirs, outputFormatters, noRequire)
// eslint-disable-next-line no-inline-comments, unicorn/no-await-expression-member
: [ [ (await import(/* webpackMode: "eager" */ "markdownlint-cli2-formatter-default")).default ] ];
await Promise.all(formattersAndParams.map((formatterAndParams) => {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"playwright-test": "playwright test --config ./webworker/playwright.config.mjs",
"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",
"schema": "cpy ./node_modules/markdownlint/schema/markdownlint-config-schema.json ./schema --flat",
"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",
"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",
"test-cover": "c8 --100 npm test",
"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\"",
"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\"",
Expand Down Expand Up @@ -68,7 +68,7 @@
"parsers/jsonc-parse.mjs",
"parsers/yaml-parse.mjs",
"README.md",
"resolve-and-require.mjs",
"resolve-module.mjs",
"schema/markdownlint-cli2-config-schema.json",
"schema/markdownlint-config-schema.json",
"schema/ValidatingConfiguration.md"
Expand Down
17 changes: 0 additions & 17 deletions resolve-and-require.mjs

This file was deleted.

16 changes: 16 additions & 0 deletions resolve-module.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// @ts-check

/**
* Wrapper for calling Node's require.resolve with additional paths.
* @param {object} require Node's require implementation (or equivalent).
* @param {string} request Module path to require.
* @param {string[]} paths Paths to resolve module location from.
* @returns {string} Resolved file name.
*/
const resolveModule = (require, request, paths) => {
const resolvePaths = require.resolve.paths ? require.resolve.paths("") : [];
const allPaths = [ ...paths, ...resolvePaths ];
return require.resolve(request, { "paths": allPaths });
};

export default resolveModule;
2 changes: 1 addition & 1 deletion test/customRules/dir/subdir/.markdownlint-cli2.jsonc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"customRules": [
"../../rules/first-line.cjs",
"../../node_modules/markdownlint-rule-sample-commonjs",
"../../node_modules/markdownlint-rule-sample-commonjs/sample-rule.cjs",
"../../node_modules/markdownlint-rule-sample-module/sample-rule.mjs",
"markdownlint-rule-extended-ascii"
]
Expand Down
2 changes: 1 addition & 1 deletion test/customRules/dir2/.markdownlint-cli2.jsonc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"customRules": [
"markdownlint-rule-sample-commonjs",
"../node_modules/markdownlint-rule-sample-module/sample-rule.mjs"
"markdownlint-rule-sample-module"
]
}
2 changes: 1 addition & 1 deletion test/customRules/dir3/.markdownlint-cli2.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"customRules": [
"../rules/all-rules.cjs",
"../node_modules/markdownlint-rule-sample-commonjs",
"../node_modules/markdownlint-rule-sample-module/sample-rule.mjs"
"../node_modules/markdownlint-rule-sample-module"
]
}
4 changes: 4 additions & 0 deletions test/customRules/dir4/.markdownlint-cli2.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

const options = {
"customRules": [
(new URL(
"../node_modules/markdownlint-rule-sample-commonjs/sample-rule.cjs",
import.meta.url
)).toString(),
(new URL(
"../node_modules/markdownlint-rule-sample-module/sample-rule.mjs",
import.meta.url
Expand Down
2 changes: 2 additions & 0 deletions test/outputFormatters-module/.markdownlint-cli2.jsonc
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
{
"outputFormatters": [
[ "output-formatter-sample-commonjs" ],
[ "output-formatter-sample-module" ],
[ "./node_modules/output-formatter-sample-commonjs/sample-formatter.cjs" ],
[ "./node_modules/output-formatter-sample-module/sample-formatter.mjs" ]
]
}
38 changes: 19 additions & 19 deletions test/resolve-and-require-test.mjs → test/resolve-module-test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,32 @@
import test from "ava";
import path from "node:path";
import { __dirname } from "./esm-helpers.mjs";
import resolveAndRequire from "../resolve-and-require.mjs";
import resolve from "../resolve-module.mjs";

import { createRequire } from "node:module";
const require = createRequire(import.meta.url);

test("built-in module", (t) => {
t.plan(1);
t.deepEqual(
require("node:fs"),
resolveAndRequire(require, "fs", [ __dirname(import.meta) ])
require.resolve("node:fs"),
resolve(require, "node:fs", [ __dirname(import.meta) ])
);
});

test("locally-installed module", (t) => {
t.plan(1);
t.deepEqual(
require("micromatch"),
resolveAndRequire(require, "micromatch", [ __dirname(import.meta) ])
require.resolve("micromatch"),
resolve(require, "micromatch", [ __dirname(import.meta) ])
);
});

test("relative (to __dirname(import.meta)) path to module", (t) => {
t.plan(1);
t.deepEqual(
require("./customRules/node_modules/markdownlint-rule-sample-commonjs"),
resolveAndRequire(
require.resolve("./customRules/node_modules/markdownlint-rule-sample-commonjs"),
resolve(
require,
"./customRules/node_modules/markdownlint-rule-sample-commonjs",
[ __dirname(import.meta) ]
Expand All @@ -40,12 +40,12 @@ test("module in alternate node_modules", (t) => {
t.plan(2);
t.throws(
// @ts-ignore
() => require("markdownlint-rule-sample-commonjs"),
() => require.resolve("markdownlint-rule-sample-commonjs"),
{ "code": "MODULE_NOT_FOUND" }
);
t.deepEqual(
require("./customRules/node_modules/markdownlint-rule-sample-commonjs"),
resolveAndRequire(
require.resolve("./customRules/node_modules/markdownlint-rule-sample-commonjs"),
resolve(
require,
"markdownlint-rule-sample-commonjs",
[ path.join(__dirname(import.meta), "customRules") ]
Expand All @@ -59,12 +59,12 @@ test("module in alternate node_modules and no require.resolve.paths", (t) => {
delete require.resolve.paths;
t.throws(
// @ts-ignore
() => require("markdownlint-rule-sample-commonjs"),
() => require.resolve("markdownlint-rule-sample-commonjs"),
{ "code": "MODULE_NOT_FOUND" }
);
t.deepEqual(
require("./customRules/node_modules/markdownlint-rule-sample-commonjs"),
resolveAndRequire(
require.resolve("./customRules/node_modules/markdownlint-rule-sample-commonjs"),
resolve(
require,
"markdownlint-rule-sample-commonjs",
[ path.join(__dirname(import.meta), "customRules") ]
Expand All @@ -79,20 +79,20 @@ test("module local, relative, and in alternate node_modules", (t) => {
path.join(__dirname(import.meta), "customRules")
];
t.deepEqual(
require("micromatch"),
resolveAndRequire(require, "micromatch", dirs)
require.resolve("micromatch"),
resolve(require, "micromatch", dirs)
);
t.deepEqual(
require("./customRules/node_modules/markdownlint-rule-sample-commonjs"),
resolveAndRequire(
require.resolve("./customRules/node_modules/markdownlint-rule-sample-commonjs"),
resolve(
require,
"./customRules/node_modules/markdownlint-rule-sample-commonjs",
dirs
)
);
t.deepEqual(
require("./customRules/node_modules/markdownlint-rule-sample-commonjs"),
resolveAndRequire(
require.resolve("./customRules/node_modules/markdownlint-rule-sample-commonjs"),
resolve(
require,
"markdownlint-rule-sample-commonjs",
dirs
Expand Down
Loading

0 comments on commit e2d5436

Please sign in to comment.