From 48d8025e1d1b607d95dd93ac261aa9990104851f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=94=AF=E7=84=B6?= Date: Sat, 18 Dec 2021 09:08:17 +0800 Subject: [PATCH] fix: import("eslint") doesn't work (#9) * fix: import("eslint") doesn't work fixes #8 * fix: failing tests * fix: safer import * Update lib/init/config-file.js Co-authored-by: Nicholas C. Zakas Co-authored-by: Nicholas C. Zakas --- .gitignore | 90 +----------------------------- lib/init/config-file.js | 20 ++++++- tests/fixtures/eslint/index.js | 3 - tests/fixtures/eslint/lib/api.js | 12 ++++ tests/fixtures/eslint/package.json | 3 +- tests/init/config-file.js | 2 +- tests/init/config-initializer.js | 17 ++++-- 7 files changed, 47 insertions(+), 100 deletions(-) delete mode 100644 tests/fixtures/eslint/index.js create mode 100644 tests/fixtures/eslint/lib/api.js diff --git a/.gitignore b/.gitignore index 050779a7..6f0c8f04 100644 --- a/.gitignore +++ b/.gitignore @@ -6,18 +6,6 @@ yarn-debug.log* yarn-error.log* lerna-debug.log* -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - # Coverage directory used by tools like istanbul coverage *.lcov @@ -25,82 +13,8 @@ coverage # nyc test coverage .nyc_output -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - # Dependency directories node_modules/ -jspm_packages/ - -# TypeScript v1 declaration files -typings/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variables file -.env -.env.test - -# parcel-bundler cache (https://parceljs.org/) -.cache - -# Next.js build output -.next - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and *not* Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port -.vscode \ No newline at end of file +.vscode +/tmp \ No newline at end of file diff --git a/lib/init/config-file.js b/lib/init/config-file.js index 77c2ea41..c5ec4e5b 100644 --- a/lib/init/config-file.js +++ b/lib/init/config-file.js @@ -12,8 +12,11 @@ import fs from "fs"; import path from "path"; import stringify from "json-stable-stringify-without-jsonify"; import debugEsm from "debug"; +import { pathToFileURL } from "url"; +import { createRequire } from "module"; const debug = debugEsm("eslint:config-file"); +const require = createRequire(path.join(process.cwd(), "./__placeholder__")); //------------------------------------------------------------------------------ // Helpers @@ -31,6 +34,17 @@ function sortByKey(a, b) { return a.key > b.key ? 1 : -1; } +/** + * import local-installed package + * @param {string} pkgName the package name to import + * @returns {Promise<*>} the exported module + */ +async function importLocalPackage(pkgName) { + const pkgPath = require.resolve(pkgName); + + return await import(pathToFileURL(pkgPath)); // eslint-disable-line node/no-unsupported-features/es-syntax +} + //------------------------------------------------------------------------------ // Private //------------------------------------------------------------------------------ @@ -84,8 +98,10 @@ async function writeJSConfigFile(config, filePath) { const stringifiedContent = `module.exports = ${stringify(config, { cmp: sortByKey, space: 4 })}\n`; try { - // eslint-disable-next-line node/no-unsupported-features/es-syntax - const eslint = (await import("eslint")); + + // import("eslint") won't work when running npx. + // as `@eslint/create-config` was installed in npm-cache, while eslint was installed in cwd. + const eslint = (await importLocalPackage("eslint")).default; const linter = new eslint.ESLint({ baseConfig: config, fix: true, useEslintrc: false }); const result = await linter.lintText(stringifiedContent); diff --git a/tests/fixtures/eslint/index.js b/tests/fixtures/eslint/index.js deleted file mode 100644 index eca4589e..00000000 --- a/tests/fixtures/eslint/index.js +++ /dev/null @@ -1,3 +0,0 @@ -"use strict"; - -module.exports = "fake eslint"; \ No newline at end of file diff --git a/tests/fixtures/eslint/lib/api.js b/tests/fixtures/eslint/lib/api.js new file mode 100644 index 00000000..edc25f33 --- /dev/null +++ b/tests/fixtures/eslint/lib/api.js @@ -0,0 +1,12 @@ +/** + * @fileoverview the file is to mock local-installed eslint for testing + */ +"use strict"; +const _module = require("module"); +const path = require("path"); + +const { createRequire } = _module; +const root = path.resolve(__dirname, "../../../../../../"); + +const realEslintPath = createRequire(root).resolve("eslint"); +module.exports = require(realEslintPath); diff --git a/tests/fixtures/eslint/package.json b/tests/fixtures/eslint/package.json index 85744f68..ffa290cc 100644 --- a/tests/fixtures/eslint/package.json +++ b/tests/fixtures/eslint/package.json @@ -1,4 +1,5 @@ { "name": "eslint", - "version": "3.18.0" + "version": "3.18.0", + "main": "./lib/api.js" } \ No newline at end of file diff --git a/tests/init/config-file.js b/tests/init/config-file.js index 21192186..08a32799 100644 --- a/tests/init/config-file.js +++ b/tests/init/config-file.js @@ -146,7 +146,7 @@ describe("ConfigFile", () => { const StubbedConfigFile = await esmock.p("../../lib/init/config-file.js", { fs: fakeFS, - eslint: { ESLint: fakeESLint } + eslint: { default: { ESLint: fakeESLint } } }); nodeAssert.rejects(async () => { diff --git a/tests/init/config-initializer.js b/tests/init/config-initializer.js index 8000a5ab..db643602 100644 --- a/tests/init/config-initializer.js +++ b/tests/init/config-initializer.js @@ -11,16 +11,15 @@ import chai from "chai"; import fs from "fs"; import path from "path"; -import os from "os"; import sinon from "sinon"; import sh from "shelljs"; -import * as npmUtils from "../../lib/init/npm-utils.js"; import esmock from "esmock"; +import { fileURLToPath } from "url"; +import * as npmUtils from "../../lib/init/npm-utils.js"; const originalDir = process.cwd(); const { assert } = chai; - //------------------------------------------------------------------------------ // Helpers //------------------------------------------------------------------------------ @@ -35,7 +34,13 @@ let localInstalledEslintDir; */ function setLocalInstalledEslint(version) { const eslintPkgPath = path.join(localInstalledEslintDir, "./package.json"); - const pkg = version ? { name: "eslint", version } : {}; + let pkg = JSON.parse(fs.readFileSync(eslintPkgPath, "utf8")); + + if (version) { + pkg.version = version; + } else { + pkg = {}; + } fs.writeFileSync(eslintPkgPath, JSON.stringify(pkg, null, 2), "utf8"); } @@ -60,7 +65,9 @@ describe("configInitializer", () => { // copy into clean area so as not to get "infected" by this project's .eslintrc files before(() => { - fixtureDir = path.join(os.tmpdir(), "eslint/fixtures/config-initializer"); + const __filename = fileURLToPath(import.meta.url); // eslint-disable-line no-underscore-dangle + + fixtureDir = path.join(__filename, "../../../tmp/eslint/fixtures/config-initializer"); localInstalledEslintDir = path.join(fixtureDir, "./node_modules/eslint"); sh.mkdir("-p", localInstalledEslintDir); sh.cp("-r", "./tests/fixtures/config-initializer/.", fixtureDir);