diff --git a/test/esm-helpers.mjs b/test/esm-helpers.mjs new file mode 100644 index 0000000..fdf0b6e --- /dev/null +++ b/test/esm-helpers.mjs @@ -0,0 +1,19 @@ +// @ts-check + +import fs from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +// Shims import.meta.filename on Node 18 +// eslint-disable-next-line no-underscore-dangle +export const __filename = (meta) => fileURLToPath(meta.url); + +// Shims import.meta.dirname on Node 18 +// eslint-disable-next-line no-underscore-dangle +export const __dirname = (meta) => path.dirname(__filename(meta)); + +// Avoids "ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time" +export const importWithTypeJson = async (file) => ( + // @ts-ignore + JSON.parse(await fs.readFile(path.resolve(__dirname(import.meta), file))) +); diff --git a/test/fs-mock-test.mjs b/test/fs-mock-test.mjs index f8f5cde..4b25de9 100644 --- a/test/fs-mock-test.mjs +++ b/test/fs-mock-test.mjs @@ -4,15 +4,16 @@ import fsNodePromises from "node:fs/promises"; import path from "node:path"; import { promisify } from "node:util"; import test from "ava"; +import { __dirname, __filename } from "./esm-helpers.mjs"; import FsMock from "./fs-mock.mjs"; const mockPath = "/mock"; -const thisFile = path.basename(import.meta.filename); +const thisFile = path.basename(__filename(import.meta)); const testFile = path.join(mockPath, thisFile); test("fsMock.stat", async (t) => { t.plan(2); - const fs = new FsMock(import.meta.dirname); + const fs = new FsMock(__dirname(import.meta)); const fsStat = promisify(fs.stat); // @ts-ignore const stat = await fsStat(testFile); @@ -22,7 +23,7 @@ test("fsMock.stat", async (t) => { test("fsMock.lstat", async (t) => { t.plan(3); - const fs = new FsMock(import.meta.dirname); + const fs = new FsMock(__dirname(import.meta)); const fsLstat = promisify(fs.lstat); // @ts-ignore const stat = await fsLstat(testFile); @@ -33,7 +34,7 @@ test("fsMock.lstat", async (t) => { test("fsMock.lstat symbolic links", async (t) => { t.plan(3); - const fs = new FsMock(import.meta.dirname, true); + const fs = new FsMock(__dirname(import.meta), true); const fsLstat = promisify(fs.lstat); // @ts-ignore const stat = await fsLstat(testFile); @@ -44,7 +45,7 @@ test("fsMock.lstat symbolic links", async (t) => { test("fsMock.readdir", async (t) => { t.plan(3); - const fs = new FsMock(import.meta.dirname); + const fs = new FsMock(__dirname(import.meta)); const fsReaddir = promisify(fs.readdir); // @ts-ignore const files = await fsReaddir(mockPath); @@ -55,7 +56,7 @@ test("fsMock.readdir", async (t) => { test("fsMock.*", async (t) => { t.plan(1); - const fs = new FsMock(import.meta.dirname); + const fs = new FsMock(__dirname(import.meta)); const fsAccess = promisify(fs.access); // @ts-ignore await fsAccess(testFile); @@ -73,7 +74,7 @@ test("fsMock.*", async (t) => { test("fsMock.promises.*", async (t) => { t.plan(2); - const fs = new FsMock(import.meta.dirname); + const fs = new FsMock(__dirname(import.meta)); const tempName = "fs-mock.tmp"; const tempFile = path.join(mockPath, tempName); await t.throwsAsync(() => fs.promises.access(tempFile)); @@ -81,5 +82,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 fsNodePromises.unlink(path.join(import.meta.dirname, tempName)); + await fsNodePromises.unlink(path.join(__dirname(import.meta), tempName)); }); diff --git a/test/fs-virtual-test.mjs b/test/fs-virtual-test.mjs index cf50407..88860c7 100644 --- a/test/fs-virtual-test.mjs +++ b/test/fs-virtual-test.mjs @@ -3,10 +3,11 @@ import path from "node:path"; import { promisify } from "node:util"; import test from "ava"; +import { __filename } from "./esm-helpers.mjs"; import FsVirtual from "../webworker/fs-virtual.mjs"; const mockPath = "/mock"; -const thisFile = path.basename(import.meta.filename); +const thisFile = path.basename(__filename(import.meta)); const testFile = path.join(mockPath, thisFile); const missingFile = `${mockPath}/missing`; diff --git a/test/import-with-type-json.mjs b/test/import-with-type-json.mjs deleted file mode 100644 index 60521e2..0000000 --- a/test/import-with-type-json.mjs +++ /dev/null @@ -1,12 +0,0 @@ -// @ts-check - -import fs from "node:fs/promises"; -import path from "node:path"; - -// Avoids "ExperimentalWarning: Importing JSON modules is an experimental feature and might change at any time" -const importWithTypeJson = async (file) => ( - // @ts-ignore - JSON.parse(await fs.readFile(path.resolve(import.meta.dirname, file))) -); - -export default importWithTypeJson; diff --git a/test/markdownlint-cli2-test-cases.mjs b/test/markdownlint-cli2-test-cases.mjs index b4c3453..ac654fa 100644 --- a/test/markdownlint-cli2-test-cases.mjs +++ b/test/markdownlint-cli2-test-cases.mjs @@ -4,6 +4,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import test from "ava"; +import { __dirname } from "./esm-helpers.mjs"; const noop = () => null; const empty = () => ""; @@ -11,7 +12,7 @@ const sanitize = (str) => str. replace(/\r/gu, ""). replace(/\bv\d+\.\d+\.\d+\b/gu, "vX.Y.Z"). replace(/ :.+[/\\]sentinel/gu, " :[PATH]"); -const sameFileSystem = (path.relative(os.homedir(), import.meta.dirname) !== import.meta.dirname); +const sameFileSystem = (path.relative(os.homedir(), __dirname(import.meta)) !== __dirname(import.meta)); const isModule = (file) => file.endsWith(".cjs") || file.endsWith(".mjs"); const testCases = ({ @@ -55,7 +56,7 @@ const testCases = ({ } test(`${name} (${host})`, (t) => { t.plan(3); - const directory = path.join(import.meta.dirname, cwd || name); + const directory = path.join(__dirname(import.meta), cwd || name); return ((pre || noop)(name, shadow) || Promise.resolve()). then(invoke(directory, args, noRequire, env, script)). then((result) => Promise.all([ @@ -141,13 +142,13 @@ const testCases = ({ const copyDirectory = (dir, alt) => import("cpy").then((cpy) => ( cpy.default( - path.join(import.meta.dirname, (alt || dir), "**"), - path.join(import.meta.dirname, directoryName(dir)) + path.join(__dirname(import.meta), (alt || dir), "**"), + path.join(__dirname(import.meta), directoryName(dir)) ) )); const deleteDirectory = (dir) => import("del").then((del) => ( - del.deleteAsync(path.join(import.meta.dirname, directoryName(dir))) + del.deleteAsync(path.join(__dirname(import.meta), directoryName(dir))) )); testCase({ @@ -586,7 +587,7 @@ const testCases = ({ }); const literalFilesAbsoluteFile = absolute( - path.join(import.meta.dirname, "literal-files"), + path.join(__dirname(import.meta), "literal-files"), "sentinel/dir(1)/(view)me.md" ). split(path.sep). @@ -668,7 +669,7 @@ const testCases = ({ "name": `config-files-${configFile}-absolute-arg`, "args": [ "--config", - path.join(import.meta.dirname, "config-files", `cfg/${configFile}`), + path.join(__dirname(import.meta), "config-files", `cfg/${configFile}`), "**/*.md" ], "exitCode": 1, diff --git a/test/markdownlint-cli2-test-exec.mjs b/test/markdownlint-cli2-test-exec.mjs index 78f5420..5285c55 100644 --- a/test/markdownlint-cli2-test-exec.mjs +++ b/test/markdownlint-cli2-test-exec.mjs @@ -4,9 +4,10 @@ import fs from "node:fs/promises"; import path from "node:path"; import test from "ava"; import testCases from "./markdownlint-cli2-test-cases.mjs"; +import { __dirname } from "./esm-helpers.mjs"; const absolute = (rootDir, file) => path.join(rootDir, file); -const repositoryPath = (name) => path.join(import.meta.dirname, "..", name); +const repositoryPath = (name) => path.join(__dirname(import.meta), "..", name); const invoke = (directory, args, noRequire, env, script) => async () => { await fs.access(directory); @@ -158,7 +159,7 @@ test("- parameter uses base directory configuration", (t) => { return invokeStdin( [ "-" ], invalidInput, - path.join(import.meta.dirname, "stdin") + path.join(__dirname(import.meta), "stdin") ). then(() => t.fail()). catch((error) => { @@ -172,7 +173,7 @@ test("- parameter not treated as stdin in configuration file globs", (t) => { return invokeStdin( [], invalidInput, - path.join(import.meta.dirname, "stdin-globs") + path.join(__dirname(import.meta), "stdin-globs") ). then(() => t.pass()). catch(() => t.fail()); diff --git a/test/markdownlint-cli2-test-exports.mjs b/test/markdownlint-cli2-test-exports.mjs index 4934515..951ab87 100644 --- a/test/markdownlint-cli2-test-exports.mjs +++ b/test/markdownlint-cli2-test-exports.mjs @@ -1,7 +1,7 @@ // @ts-check import test from "ava"; -import importWithTypeJson from "./import-with-type-json.mjs"; +import { importWithTypeJson } from "./esm-helpers.mjs"; const packageJson = await importWithTypeJson("../package.json"); const exportMappings = new Map([ diff --git a/test/markdownlint-cli2-test.mjs b/test/markdownlint-cli2-test.mjs index b4f2554..fbf43b6 100644 --- a/test/markdownlint-cli2-test.mjs +++ b/test/markdownlint-cli2-test.mjs @@ -6,7 +6,7 @@ import path from "node:path"; import Ajv from "ajv"; import test from "ava"; import { globby } from "globby"; -import importWithTypeJson from "./import-with-type-json.mjs"; +import { __dirname, importWithTypeJson } from "./esm-helpers.mjs"; const packageJson = await importWithTypeJson("../package.json"); import { "main" as markdownlintCli2 } from "../markdownlint-cli2.mjs"; import jsoncParse from "../parsers/jsonc-parse.mjs"; @@ -294,7 +294,7 @@ test("alternate file contents with ignores", (t) => { test("extension scenario, file, no changes", (t) => { t.plan(2); return markdownlintCli2({ - "directory": import.meta.dirname, + "directory": __dirname(import.meta), "argv": [ ":./markdownlint-json/viewme.md" ], "optionsOverride": { "outputFormatters": [ [ outputFormatterLengthIs(t, 4) ] ] @@ -306,7 +306,7 @@ test("extension scenario, file, no changes", (t) => { test("extension scenario, file, changes", (t) => { t.plan(2); return markdownlintCli2({ - "directory": import.meta.dirname, + "directory": __dirname(import.meta), "argv": [ ":./markdownlint-json/viewme.md" ], "fileContents": { "./markdownlint-json/viewme.md": "# Title\n\n> Tagline \n\n\n" @@ -321,7 +321,7 @@ test("extension scenario, file, changes", (t) => { test("extension scenario, no file", (t) => { t.plan(2); return markdownlintCli2({ - "directory": import.meta.dirname, + "directory": __dirname(import.meta), "argv": [], "nonFileContents": { "untitled-1": "# Title\n\nText\t\n" @@ -336,7 +336,7 @@ test("extension scenario, no file", (t) => { test("extension scenario, no file, empty", (t) => { t.plan(2); return markdownlintCli2({ - "directory": import.meta.dirname, + "directory": __dirname(import.meta), "argv": [], "nonFileContents": { "untitled-1": "" @@ -360,7 +360,7 @@ test("extension scenario, ignores handled", (t) => { }; const argv = Object.keys(fileContents).map((key) => `:${key}`); return markdownlintCli2({ - "directory": path.join(import.meta.dirname, "extension-scenario-ignores"), + "directory": path.join(__dirname(import.meta), "extension-scenario-ignores"), argv, fileContents, "optionsOverride": { @@ -372,7 +372,7 @@ test("extension scenario, ignores handled", (t) => { test("extension scenario, ignores handled, absolute paths", (t) => { t.plan(2); - const directory = path.join(import.meta.dirname, "extension-scenario-ignores"); + const directory = path.join(__dirname(import.meta), "extension-scenario-ignores"); const fileContents = Object.fromEntries( Object.entries({ "viewme.md": "Heading", @@ -401,7 +401,7 @@ test("extension scenario, ignores handled, absolute paths", (t) => { test("extension scenario, globs ignored/filtered", (t) => { t.plan(2); return markdownlintCli2({ - "directory": path.join(import.meta.dirname, "extension-scenario-globs"), + "directory": path.join(__dirname(import.meta), "extension-scenario-globs"), "argv": [ ":viewme.md", ":dir/viewme.md", @@ -425,14 +425,14 @@ test("extension scenario, globs ignored/filtered", (t) => { test("backslash translation", (t) => { t.plan(2); return markdownlintCli2({ - "directory": import.meta.dirname, + "directory": __dirname(import.meta), "argv": [ "./markdownlint-json/viewme.md", "markdownlint-jsonc/viewme.md", - path.join(import.meta.dirname, "markdownlint-cli2-jsonc/viewme.md"), + path.join(__dirname(import.meta), "markdownlint-cli2-jsonc/viewme.md"), ".\\markdownlint-yml\\viewme.md", "markdownlint-yaml\\viewme.md", - path.join(import.meta.dirname, "markdownlint-cli2-yaml\\viewme.md") + path.join(__dirname(import.meta), "markdownlint-cli2-yaml\\viewme.md") ], "optionsOverride": { "outputFormatters": [ [ outputFormatterLengthIs(t, 24) ] ] @@ -610,7 +610,7 @@ test("custom fs, using fsMock", (t) => { "optionsOverride": { "outputFormatters": [ [ outputFormatterLengthIs(t, 10) ] ] }, - "fs": new FsMock(path.join(import.meta.dirname, "markdownlint-cli2-jsonc")), + "fs": new FsMock(path.join(__dirname(import.meta), "markdownlint-cli2-jsonc")), "noRequire": true }). then((exitCode) => { @@ -626,7 +626,7 @@ test("custom fs, using fsMock simulating symbolic links", (t) => { "optionsOverride": { "outputFormatters": [ [ outputFormatterLengthIs(t, 10) ] ] }, - "fs": new FsMock(path.join(import.meta.dirname, "markdownlint-cli2-jsonc"), true), + "fs": new FsMock(path.join(__dirname(import.meta), "markdownlint-cli2-jsonc"), true), "noRequire": true }). then((exitCode) => { diff --git a/test/resolve-and-require-test.mjs b/test/resolve-and-require-test.mjs index cfeb23e..97897f6 100644 --- a/test/resolve-and-require-test.mjs +++ b/test/resolve-and-require-test.mjs @@ -2,6 +2,7 @@ import test from "ava"; import path from "node:path"; +import { __dirname } from "./esm-helpers.mjs"; import resolveAndRequire from "../resolve-and-require.mjs"; import { createRequire } from "node:module"; @@ -11,7 +12,7 @@ test("built-in module", (t) => { t.plan(1); t.deepEqual( require("node:fs"), - resolveAndRequire(require, "fs", [ import.meta.dirname ]) + resolveAndRequire(require, "fs", [ __dirname(import.meta) ]) ); }); @@ -19,18 +20,18 @@ test("locally-installed module", (t) => { t.plan(1); t.deepEqual( require("markdownlint"), - resolveAndRequire(require, "markdownlint", [ import.meta.dirname ]) + resolveAndRequire(require, "markdownlint", [ __dirname(import.meta) ]) ); }); -test("relative (to import.meta.dirname) path to module", (t) => { +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, "./customRules/node_modules/markdownlint-rule-sample-commonjs", - [ import.meta.dirname ] + [ __dirname(import.meta) ] ) ); }); @@ -47,7 +48,7 @@ test("module in alternate node_modules", (t) => { resolveAndRequire( require, "markdownlint-rule-sample-commonjs", - [ path.join(import.meta.dirname, "customRules") ] + [ path.join(__dirname(import.meta), "customRules") ] ) ); }); @@ -66,7 +67,7 @@ test("module in alternate node_modules and no require.resolve.paths", (t) => { resolveAndRequire( require, "markdownlint-rule-sample-commonjs", - [ path.join(import.meta.dirname, "customRules") ] + [ path.join(__dirname(import.meta), "customRules") ] ) ); }); @@ -74,8 +75,8 @@ test("module in alternate node_modules and no require.resolve.paths", (t) => { test("module local, relative, and in alternate node_modules", (t) => { t.plan(3); const dirs = [ - import.meta.dirname, - path.join(import.meta.dirname, "customRules") + __dirname(import.meta), + path.join(__dirname(import.meta), "customRules") ]; t.deepEqual( require("markdownlint"),