From 6df9c4e6611a30410610197dff295f1f323eaa22 Mon Sep 17 00:00:00 2001 From: David Anson Date: Mon, 18 Nov 2024 22:31:21 -0800 Subject: [PATCH] wip --- export-markdownlint-helpers.js | 2 +- export-markdownlint.js | 2 +- markdownlint-cli2.js | 53 +++++++-------- package.json | 1 + parsers/parsers.js | 2 +- .../rules/{first-line.js => first-line.cjs} | 0 test/fs-mock-test.js | 27 ++++---- test/fs-mock.js | 10 ++- test/fs-virtual-test.js | 12 ++-- test/markdownlint-cli2-test-exports.js | 9 ++- test/markdownlint-cli2-test.js | 65 ++++++++++--------- webworker/fs-virtual.js | 6 +- 12 files changed, 91 insertions(+), 98 deletions(-) rename test/customRules/rules/{first-line.js => first-line.cjs} (100%) diff --git a/export-markdownlint-helpers.js b/export-markdownlint-helpers.js index e68388d3..90d31e28 100644 --- a/export-markdownlint-helpers.js +++ b/export-markdownlint-helpers.js @@ -1,4 +1,4 @@ // @ts-check -import * as helpers from "markdownlint/helpers"; +import helpers from "markdownlint/helpers"; export default helpers; diff --git a/export-markdownlint.js b/export-markdownlint.js index 567023c8..a1e0f6bf 100644 --- a/export-markdownlint.js +++ b/export-markdownlint.js @@ -1,4 +1,4 @@ // @ts-check -import * as markdownlint from "markdownlint"; +import markdownlint from "markdownlint"; export default markdownlint; diff --git a/markdownlint-cli2.js b/markdownlint-cli2.js index 3be68519..19e57d49 100755 --- a/markdownlint-cli2.js +++ b/markdownlint-cli2.js @@ -1,17 +1,19 @@ #!/usr/bin/env node -// @ts-check - // @ts-ignore -// eslint-disable-next-line camelcase, no-inline-comments, no-undef -const dynamicRequire = (typeof __non_webpack_require__ === "undefined") ? require : /* c8 ignore next */ __non_webpack_require__; -// Capture native require implementation for dynamic loading of modules // Requires -const pathDefault = require("node:path"); +import fsx from "node:fs"; +import { createRequire } from 'node:module'; +const dynamicRequire = createRequire(import.meta.url); +import os from "node:os"; +import pathDefault from "node:path"; const pathPosix = pathDefault.posix; -const { pathToFileURL } = require("node:url"); -const markdownlintLibrary = require("markdownlint"); +import { pathToFileURL } from "node:url"; +import esMain from "es-main"; +import { globby } from "globby"; +import micromatch from "micromatch"; +import markdownlintLibrary from "markdownlint"; const { applyFixes, "getVersion": getLibraryVersion, @@ -22,6 +24,7 @@ const { "extendConfig": markdownlintExtendConfig, "readConfig": markdownlintReadConfig } = markdownlintPromises; +import { expandTildePath } from "markdownlint/helpers"; import appendToArray from "./append-to-array.js"; import mergeOptions from "./merge-options.js"; import resolveAndRequire from "./resolve-and-require.js"; @@ -39,13 +42,16 @@ const utf8 = "utf8"; const noop = () => null; // Gets a JSONC parser -const getJsoncParse = () => require("./parsers/jsonc-parse.js"); +import jsoncParse from "./parsers/jsonc-parse.js"; +const getJsoncParse = () => jsoncParse; // Gets a YAML parser -const getYamlParse = () => require("./parsers/yaml-parse.js"); +import yamlParse from "./parsers/yaml-parse.js"; +const getYamlParse = () => yamlParse; // Gets an ordered array of parsers -const getParsers = () => require("./parsers/parsers.js"); +import parsers from "./parsers/parsers.js"; +const getParsers = () => parsers; // Negates a glob const negateGlob = (glob) => `!${glob}`; @@ -61,15 +67,9 @@ const throwForConfigurationFile = (file, error) => { // Return a posix path (even on Windows) const posixPath = (p) => p.split(pathDefault.sep).join(pathPosix.sep); -// Expands a path with a tilde to an absolute path -const expandTildePath = (id) => { - const markdownlintRuleHelpers = require("markdownlint/helpers"); - return markdownlintRuleHelpers.expandTildePath(id, require("node:os")); -}; - // Resolves module paths relative to the specified directory const resolveModulePaths = (dir, modulePaths) => ( - modulePaths.map((path) => pathDefault.resolve(dir, expandTildePath(path))) + modulePaths.map((path) => pathDefault.resolve(dir, expandTildePath(path, os))) ); // Read a JSON(C) or YAML file and return the object @@ -93,7 +93,7 @@ const importOrRequireResolve = async (dirOrDirs, id, noRequire) => { return null; } const dirs = Array.isArray(dirOrDirs) ? dirOrDirs : [ dirOrDirs ]; - const expandId = expandTildePath(id); + const expandId = expandTildePath(id, os); const errors = []; try { return resolveAndRequire(dynamicRequire, expandId, dirs); @@ -212,7 +212,6 @@ const readOptionsOrConfig = async (configPath, fs, noRequire) => { // Filter a list of files to ignore by glob const removeIgnoredFiles = (dir, files, ignores) => { - const micromatch = require("micromatch"); return micromatch( files.map((file) => pathPosix.relative(dir, file)), ignores @@ -538,8 +537,6 @@ const enumerateFiles = async ( }) ); // Process glob patterns - // eslint-disable-next-line no-inline-comments - const { globby } = await import(/* webpackMode: "eager" */ "globby"); const files = [ ...await globby(expandedDirectories, globbyOptions), ...filteredLiteralFiles @@ -889,7 +886,7 @@ const outputSummary = async ( const dirs = [ dir, ...modulePaths ]; const formattersAndParams = outputFormatters ? await importOrRequireIdsAndParams(dirs, outputFormatters, noRequire) - : [ [ require("markdownlint-cli2-formatter-default") ] ]; + : [ [ (await import("markdownlint-cli2-formatter-default")).default ] ]; await Promise.all(formattersAndParams.map((formatterAndParams) => { const [ formatter, ...formatterParams ] = formatterAndParams; return formatter(formatterOptions, ...formatterParams); @@ -916,7 +913,7 @@ const main = async (params) => { } = params; const logMessage = params.logMessage || noop; const logError = params.logError || noop; - const fs = params.fs || require("node:fs"); + const fs = params.fs || fsx; const baseDirSystem = (directory && pathDefault.resolve(directory)) || process.cwd(); @@ -993,7 +990,7 @@ const main = async (params) => { // Add stdin as a non-file input if necessary if (useStdin) { const key = pathPosix.join(baseDir, "stdin"); - const { text } = require("node:stream/consumers"); + const { text } = await import("node:stream/consumers"); nonFileContents = { ...nonFileContents, [key]: await text(process.stdin) @@ -1080,13 +1077,13 @@ const main = async (params) => { return errorsPresent ? 1 : 0; }; -// Export functions -module.exports = { +// Exports +export { main }; // Run if invoked as a CLI -if (require.main === module) { +if (esMain(import.meta)) { const params = { "argv": process.argv.slice(2), "logMessage": console.log, diff --git a/package.json b/package.json index 04912415..2834a7dc 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "schema/ValidatingConfiguration.md" ], "dependencies": { + "es-main": "1.3.0", "globby": "14.0.2", "js-yaml": "4.1.0", "jsonc-parser": "3.3.1", diff --git a/parsers/parsers.js b/parsers/parsers.js index d4bddfd0..2fb66a32 100644 --- a/parsers/parsers.js +++ b/parsers/parsers.js @@ -1,7 +1,7 @@ // @ts-check import jsoncParse from "./jsonc-parse.js"; -import yamlParse from "./yaml-parse"; +import yamlParse from "./yaml-parse.js"; /** * Array of parser objects ordered by priority. diff --git a/test/customRules/rules/first-line.js b/test/customRules/rules/first-line.cjs similarity index 100% rename from test/customRules/rules/first-line.js rename to test/customRules/rules/first-line.cjs diff --git a/test/fs-mock-test.js b/test/fs-mock-test.js index f9734e64..10d70a05 100644 --- a/test/fs-mock-test.js +++ b/test/fs-mock-test.js @@ -1,19 +1,18 @@ // @ts-check -"use strict"; - -const path = require("node:path"); -const { promisify } = require("node:util"); -const test = require("ava").default; -const FsMock = require("./fs-mock"); +import fsNodePromises from "node:fs/promises"; +import path from "node:path"; +import { promisify } from "node:util"; +import test from "ava"; +import FsMock from "./fs-mock.js"; const mockPath = "/mock"; -const thisFile = path.basename(__filename); +const thisFile = path.basename(import.meta.filename); const testFile = path.join(mockPath, thisFile); test("fsMock.stat", async (t) => { t.plan(2); - const fs = new FsMock(__dirname); + const fs = new FsMock(import.meta.dirname); const fsStat = promisify(fs.stat); // @ts-ignore const stat = await fsStat(testFile); @@ -23,7 +22,7 @@ test("fsMock.stat", async (t) => { test("fsMock.lstat", async (t) => { t.plan(3); - const fs = new FsMock(__dirname); + const fs = new FsMock(import.meta.dirname); const fsLstat = promisify(fs.lstat); // @ts-ignore const stat = await fsLstat(testFile); @@ -34,7 +33,7 @@ test("fsMock.lstat", async (t) => { test("fsMock.lstat symbolic links", async (t) => { t.plan(3); - const fs = new FsMock(__dirname, true); + const fs = new FsMock(import.meta.dirname, true); const fsLstat = promisify(fs.lstat); // @ts-ignore const stat = await fsLstat(testFile); @@ -45,7 +44,7 @@ test("fsMock.lstat symbolic links", async (t) => { test("fsMock.readdir", async (t) => { t.plan(3); - const fs = new FsMock(__dirname); + const fs = new FsMock(import.meta.dirname); const fsReaddir = promisify(fs.readdir); // @ts-ignore const files = await fsReaddir(mockPath); @@ -56,7 +55,7 @@ test("fsMock.readdir", async (t) => { test("fsMock.*", async (t) => { t.plan(1); - const fs = new FsMock(__dirname); + const fs = new FsMock(import.meta.dirname); const fsAccess = promisify(fs.access); // @ts-ignore await fsAccess(testFile); @@ -74,7 +73,7 @@ test("fsMock.*", async (t) => { test("fsMock.promises.*", async (t) => { t.plan(2); - const fs = new FsMock(__dirname); + const fs = new FsMock(import.meta.dirname); const tempName = "fs-mock.tmp"; const tempFile = path.join(mockPath, tempName); await t.throwsAsync(() => fs.promises.access(tempFile)); @@ -82,5 +81,5 @@ test("fsMock.promises.*", async (t) => { await fs.promises.access(tempFile); await fs.promises.stat(tempFile); t.is(await fs.promises.readFile(tempFile, "utf8"), tempFile); - await require("node:fs").promises.unlink(path.join(__dirname, tempName)); + await fsNodePromises.unlink(path.join(import.meta.dirname, tempName)); }); diff --git a/test/fs-mock.js b/test/fs-mock.js index ad3a4580..7f9a0e57 100644 --- a/test/fs-mock.js +++ b/test/fs-mock.js @@ -1,12 +1,10 @@ // @ts-check -"use strict"; - -const fs = require("node:fs"); +import fs from "node:fs"; +import nodePath from "node:path"; const mapPath = (base, mockPath) => { - const path = require("node:path"); - return path.resolve(base, path.relative("/mock", mockPath)); + return nodePath.resolve(base, nodePath.relative("/mock", mockPath)); }; class fsMock { @@ -56,4 +54,4 @@ class fsMock { } } -module.exports = fsMock; +export default fsMock; diff --git a/test/fs-virtual-test.js b/test/fs-virtual-test.js index 1887bc70..8eec5376 100644 --- a/test/fs-virtual-test.js +++ b/test/fs-virtual-test.js @@ -1,14 +1,12 @@ // @ts-check -"use strict"; - -const path = require("node:path"); -const { promisify } = require("node:util"); -const test = require("ava").default; -const FsVirtual = require("../webworker/fs-virtual"); +import path from "node:path"; +import { promisify } from "node:util"; +import test from "ava"; +import FsVirtual from "../webworker/fs-virtual.js"; const mockPath = "/mock"; -const thisFile = path.basename(__filename); +const thisFile = path.basename(import.meta.filename); const testFile = path.join(mockPath, thisFile); const missingFile = `${mockPath}/missing`; diff --git a/test/markdownlint-cli2-test-exports.js b/test/markdownlint-cli2-test-exports.js index fdc6c557..7fd6b0bc 100644 --- a/test/markdownlint-cli2-test-exports.js +++ b/test/markdownlint-cli2-test-exports.js @@ -7,7 +7,7 @@ import { createRequire } from "node:module"; const require = createRequire(import.meta.url); const exportMappings = new Map([ - [ ".", ".." ], + [ ".", "../markdownlint-cli2.js" ], [ "./markdownlint", "markdownlint" ], [ "./markdownlint/helpers", "markdownlint/helpers" ], [ "./parsers", "../parsers/parsers.js" ], @@ -24,9 +24,12 @@ test("exportMappings", (t) => { for (const [ exportName, exportPath ] of exportMappings) { test(exportName, async (t) => { + const commonJs = !/.js$/.test(exportPath); + const importExportName = await import(exportName.replace(/^\./u, packageJson.name)); + const importExportPath = await import(exportPath); t.is( - await import(exportName.replace(/^\./u, packageJson.name)), - await import(exportPath) + commonJs ? importExportName.default : importExportName, + commonJs ? importExportPath.default : importExportPath ); }); } diff --git a/test/markdownlint-cli2-test.js b/test/markdownlint-cli2-test.js index 43c3f4b7..fda92323 100644 --- a/test/markdownlint-cli2-test.js +++ b/test/markdownlint-cli2-test.js @@ -2,19 +2,23 @@ "use strict"; -const fs = require("node:fs/promises"); -const path = require("node:path"); -const Ajv = require("ajv"); -const test = require("ava").default; -const { "main": markdownlintCli2 } = require("../markdownlint-cli2.js"); -const jsoncParse = require("../parsers/jsonc-parse.js"); -const yamlParse = require("../parsers/yaml-parse.js"); -const FsMock = require("./fs-mock"); -const FsVirtual = require("../webworker/fs-virtual"); +import nodeFs from "node:fs"; +import fs from "node:fs/promises"; +import path from "node:path"; +import Ajv from "ajv"; +import test from "ava"; +import { globby } from "globby"; +import packageJson from "../package.json" with { type: "json" }; +import { "main" as markdownlintCli2 } from "../markdownlint-cli2.js"; +import jsoncParse from "../parsers/jsonc-parse.js"; +import yamlParse from "../parsers/yaml-parse.js"; +import FsMock from "./fs-mock.js"; +import FsVirtual from "../webworker/fs-virtual.js"; +import firstLine from "./customRules/rules/first-line.cjs"; const schemaIdVersionRe = /^.*v(?\d+\.\d+\.\d+).*$/u; -const markdownlintConfigSchemaDefinition = require("../schema/markdownlint-config-schema.json"); -const markdownlintCli2ConfigSchemaDefinition = require("../schema/markdownlint-cli2-config-schema.json"); +import markdownlintConfigSchemaDefinition from "../schema/markdownlint-config-schema.json" with { type: "json" }; +import markdownlintCli2ConfigSchemaDefinition from "../schema/markdownlint-cli2-config-schema.json" with { type: "json" }; const outputFormatterLengthIs = (t, length) => (options) => { const { results } = options; @@ -23,7 +27,6 @@ const outputFormatterLengthIs = (t, length) => (options) => { test("name and version", (t) => { t.plan(3); - const packageJson = require("../package.json"); const logMessage = (msg) => { const match = (/^(?\S+)\sv(?\S+)\s/u).exec(msg); if (match) { @@ -77,7 +80,7 @@ test("validateMarkdownlintConfigSchema", async (t) => { const validateConfigSchema = ajv.compile(markdownlintConfigSchemaDefinition); t.is( markdownlintConfigSchemaDefinition.$id.replace(schemaIdVersionRe, "$"), - require("../package.json").dependencies.markdownlint + packageJson.dependencies.markdownlint ); t.is( markdownlintConfigSchemaDefinition.$id, @@ -85,7 +88,6 @@ test("validateMarkdownlintConfigSchema", async (t) => { ); // Validate instances - const { globby } = await import("globby"); const files = await globby( [ "**/*.markdownlint.(json|jsonc)", @@ -123,7 +125,7 @@ test("validateMarkdownlintCli2ConfigSchema", async (t) => { const validateConfigSchema = ajv.compile(markdownlintCli2ConfigSchemaDefinition); t.is( markdownlintCli2ConfigSchemaDefinition.$id.replace(schemaIdVersionRe, "$"), - require("../package.json").version + packageJson.version ); t.is( markdownlintCli2ConfigSchemaDefinition.$id, @@ -131,7 +133,6 @@ test("validateMarkdownlintCli2ConfigSchema", async (t) => { ); // Validate instances - const { globby } = await import("globby"); const files = await globby( [ "**/*.markdownlint-cli2.(json|jsonc)", @@ -200,7 +201,7 @@ test("main options default", (t) => { "directory": "test/main-options-default", "argv": [ "info.md" ], "optionsDefault": { - "customRules": [ require("./customRules/rules/first-line") ] + "customRules": [ firstLine ] } }) ]). @@ -294,7 +295,7 @@ test("alternate file contents with ignores", (t) => { test("extension scenario, file, no changes", (t) => { t.plan(2); return markdownlintCli2({ - "directory": __dirname, + "directory": import.meta.dirname, "argv": [ ":./markdownlint-json/viewme.md" ], "optionsOverride": { "outputFormatters": [ [ outputFormatterLengthIs(t, 4) ] ] @@ -306,7 +307,7 @@ test("extension scenario, file, no changes", (t) => { test("extension scenario, file, changes", (t) => { t.plan(2); return markdownlintCli2({ - "directory": __dirname, + "directory": import.meta.dirname, "argv": [ ":./markdownlint-json/viewme.md" ], "fileContents": { "./markdownlint-json/viewme.md": "# Title\n\n> Tagline \n\n\n" @@ -321,7 +322,7 @@ test("extension scenario, file, changes", (t) => { test("extension scenario, no file", (t) => { t.plan(2); return markdownlintCli2({ - "directory": __dirname, + "directory": import.meta.dirname, "argv": [], "nonFileContents": { "untitled-1": "# Title\n\nText\t\n" @@ -336,7 +337,7 @@ test("extension scenario, no file", (t) => { test("extension scenario, no file, empty", (t) => { t.plan(2); return markdownlintCli2({ - "directory": __dirname, + "directory": import.meta.dirname, "argv": [], "nonFileContents": { "untitled-1": "" @@ -360,7 +361,7 @@ test("extension scenario, ignores handled", (t) => { }; const argv = Object.keys(fileContents).map((key) => `:${key}`); return markdownlintCli2({ - "directory": path.join(__dirname, "extension-scenario-ignores"), + "directory": path.join(import.meta.dirname, "extension-scenario-ignores"), argv, fileContents, "optionsOverride": { @@ -372,7 +373,7 @@ test("extension scenario, ignores handled", (t) => { test("extension scenario, ignores handled, absolute paths", (t) => { t.plan(2); - const directory = path.join(__dirname, "extension-scenario-ignores"); + const directory = path.join(import.meta.dirname, "extension-scenario-ignores"); const fileContents = Object.fromEntries( Object.entries({ "viewme.md": "Heading", @@ -401,7 +402,7 @@ test("extension scenario, ignores handled, absolute paths", (t) => { test("extension scenario, globs ignored/filtered", (t) => { t.plan(2); return markdownlintCli2({ - "directory": path.join(__dirname, "extension-scenario-globs"), + "directory": path.join(import.meta.dirname, "extension-scenario-globs"), "argv": [ ":viewme.md", ":dir/viewme.md", @@ -425,14 +426,14 @@ test("extension scenario, globs ignored/filtered", (t) => { test("backslash translation", (t) => { t.plan(2); return markdownlintCli2({ - "directory": __dirname, + "directory": import.meta.dirname, "argv": [ "./markdownlint-json/viewme.md", "markdownlint-jsonc/viewme.md", - path.join(__dirname, "markdownlint-cli2-jsonc/viewme.md"), + path.join(import.meta.dirname, "markdownlint-cli2-jsonc/viewme.md"), ".\\markdownlint-yml\\viewme.md", "markdownlint-yaml\\viewme.md", - path.join(__dirname, "markdownlint-cli2-yaml\\viewme.md") + path.join(import.meta.dirname, "markdownlint-cli2-yaml\\viewme.md") ], "optionsOverride": { "outputFormatters": [ [ outputFormatterLengthIs(t, 24) ] ] @@ -563,7 +564,7 @@ test("custom fs, using node:fs", (t) => { "optionsOverride": { "outputFormatters": [ [ outputFormatterLengthIs(t, 10) ] ] }, - "fs": require("node:fs") + "fs": nodeFs }). then((exitCode) => { t.is(exitCode, 1); @@ -578,7 +579,7 @@ test("custom fs, using node:fs and noRequire=false", (t) => { "optionsOverride": { "outputFormatters": [ [ outputFormatterLengthIs(t, 11) ] ] }, - "fs": require("node:fs"), + "fs": nodeFs, "noRequire": false }). then((exitCode) => { @@ -594,7 +595,7 @@ test("custom fs, using node:fs and noRequire=true", (t) => { "optionsOverride": { "outputFormatters": [ [ outputFormatterLengthIs(t, 14) ] ] }, - "fs": require("node:fs"), + "fs": nodeFs, "noRequire": true }). then((exitCode) => { @@ -610,7 +611,7 @@ test("custom fs, using fsMock", (t) => { "optionsOverride": { "outputFormatters": [ [ outputFormatterLengthIs(t, 10) ] ] }, - "fs": new FsMock(path.join(__dirname, "markdownlint-cli2-jsonc")), + "fs": new FsMock(path.join(import.meta.dirname, "markdownlint-cli2-jsonc")), "noRequire": true }). then((exitCode) => { @@ -626,7 +627,7 @@ test("custom fs, using fsMock simulating symbolic links", (t) => { "optionsOverride": { "outputFormatters": [ [ outputFormatterLengthIs(t, 10) ] ] }, - "fs": new FsMock(path.join(__dirname, "markdownlint-cli2-jsonc"), true), + "fs": new FsMock(path.join(import.meta.dirname, "markdownlint-cli2-jsonc"), true), "noRequire": true }). then((exitCode) => { diff --git a/webworker/fs-virtual.js b/webworker/fs-virtual.js index 14d1ef3b..8435a5ff 100644 --- a/webworker/fs-virtual.js +++ b/webworker/fs-virtual.js @@ -1,7 +1,5 @@ // @ts-check -"use strict"; - const dirent = (path, directory) => { const name = path.replace(/^.*\//u, ""); return { @@ -99,6 +97,4 @@ class FsVirtual { } } -if (typeof module !== "undefined") { - module.exports = FsVirtual; -} +export default FsVirtual;