diff --git a/index.js b/index.js index 594497c3..0c2e078f 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ import pMap from 'p-map'; import {cosmiconfig, defaultLoaders} from 'cosmiconfig'; import defineLazyProperty from 'define-lazy-prop'; import pFilter from 'p-filter'; +import slash from 'slash'; import {CONFIG_FILES, MODULE_NAME, DEFAULT_IGNORES} from './lib/constants.js'; import { normalizeOptions, @@ -94,9 +95,9 @@ const runEslint = async (paths, options, processorOptions) => { const globFiles = async (patterns, {ignores, extensions, cwd}) => ( await globby( - patterns.length === 0 ? [`**/*.{${extensions.join(',')}}`] : arrify(patterns), - {ignore: ignores, gitignore: true, cwd}, - )).filter(file => extensions.includes(path.extname(file).slice(1))).map(file => path.resolve(cwd, file)); + patterns.length === 0 ? [`**/*.{${extensions.join(',')}}`] : arrify(patterns).map(pattern => slash(pattern)), + {ignore: ignores, gitignore: true, absolute: true, cwd}, + )).filter(file => extensions.includes(path.extname(file).slice(1))); const getConfig = async options => { const {options: foundOptions, prettierOptions} = mergeWithFileConfig(normalizeOptions(options)); @@ -149,8 +150,8 @@ const lintFiles = async (patterns, inputOptions = {}) => { const configFiles = (await Promise.all( (await globby( CONFIG_FILES.map(configFile => `**/${configFile}`), - {ignore: DEFAULT_IGNORES, gitignore: true, cwd: inputOptions.cwd}, - )).map(async configFile => configExplorer.load(path.resolve(inputOptions.cwd, configFile))), + {ignore: DEFAULT_IGNORES, gitignore: true, absolute: true, cwd: inputOptions.cwd}, + )).map(configFile => configExplorer.load(configFile)), )).filter(Boolean); const paths = configFiles.length > 0 diff --git a/package.json b/package.json index fb5f8e0f..0138723d 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "find-up": "^5.0.0", "fs-extra": "^10.0.0", "get-stdin": "^9.0.0", - "globby": "^9.2.0", + "globby": "^11.0.4", "imurmurhash": "^0.1.4", "is-path-inside": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", @@ -117,9 +117,13 @@ }, "eslintIgnore": [ "test/fixtures", + "test/temp", "coverage" ], "ava": { + "files": [ + "!test/temp" + ], "timeout": "1m" }, "nyc": { diff --git a/test/cli.js b/test/cli.js index 2a75fdc8..9623bc94 100644 --- a/test/cli.js +++ b/test/cli.js @@ -3,7 +3,6 @@ import path from 'node:path'; import test from 'ava'; import execa from 'execa'; import slash from 'slash'; -import tempWrite from 'temp-write'; import createEsmUtils from 'esm-utils'; const {__dirname} = createEsmUtils(import.meta); @@ -12,8 +11,10 @@ process.chdir(__dirname); const main = (arguments_, options) => execa(path.join(__dirname, '../cli.js'), arguments_, options); test('fix option', async t => { - const filepath = await tempWrite('console.log()\n', 'x.js'); - await main(['--fix', filepath]); + const cwd = await fs.promises.mkdtemp(path.join(__dirname, './temp/')); + const filepath = path.join(cwd, 'x.js'); + await fs.promises.writeFile(filepath, 'console.log()\n'); + await main(['--fix', filepath], {cwd}); t.is(fs.readFileSync(filepath, 'utf8').trim(), 'console.log();'); }); @@ -33,10 +34,12 @@ test('stdin-filename option with stdin', async t => { }); test('reporter option', async t => { - const filepath = await tempWrite('console.log()\n', 'x.js'); + const cwd = await fs.promises.mkdtemp(path.join(__dirname, './temp/')); + const filepath = path.join(cwd, 'x.js'); + await fs.promises.writeFile(filepath, 'console.log()\n'); const error = await t.throwsAsync(() => - main(['--reporter=compact', filepath]), + main(['--reporter=compact', filepath], {cwd}), ); t.true(error.stdout.includes('Error - ')); }); @@ -98,15 +101,19 @@ test('supports being extended with a shareable config', async t => { }); test('quiet option', async t => { - const filepath = await tempWrite('// TODO: quiet\nconsole.log()\n', 'x.js'); - const error = await t.throwsAsync(main(['--quiet', '--reporter=json', filepath])); + const cwd = await fs.promises.mkdtemp(path.join(__dirname, './temp/')); + const filepath = path.join(cwd, 'x.js'); + await fs.promises.writeFile(filepath, '// TODO: quiet\nconsole.log()\n'); + const error = await t.throwsAsync(main(['--quiet', '--reporter=json', filepath], {cwd})); const [report] = JSON.parse(error.stdout); t.is(report.warningCount, 0); }); test('invalid node-engine option', async t => { - const filepath = await tempWrite('console.log()\n', 'x.js'); - const error = await t.throwsAsync(main(['--node-version', 'v', filepath])); + const cwd = await fs.promises.mkdtemp(path.join(__dirname, './temp/')); + const filepath = path.join(cwd, 'x.js'); + await fs.promises.writeFile(filepath, 'console.log()\n'); + const error = await t.throwsAsync(main(['--node-version', 'v', filepath], {cwd})); t.is(error.exitCode, 1); }); diff --git a/test/fixtures/custom-extension/readme.md b/test/fixtures/custom-extension/readme.md new file mode 120000 index 00000000..2f969b9f --- /dev/null +++ b/test/fixtures/custom-extension/readme.md @@ -0,0 +1 @@ +../../../readme.md \ No newline at end of file diff --git a/test/lint-files.js b/test/lint-files.js index 575b834a..3fcd84cd 100644 --- a/test/lint-files.js +++ b/test/lint-files.js @@ -13,21 +13,23 @@ const hasRule = (results, filePath, ruleId) => { test('only accepts allowed extensions', async t => { // Markdown files will always produce linter errors and will not be going away - const mdGlob = path.join(__dirname, '..', '*.md'); + const cwd = path.join(__dirname, 'fixtures/custom-extension'); + const mdGlob = '*.md'; // No files should be linted = no errors - const noOptionsResults = await xo.lintFiles(mdGlob, {}); + const noOptionsResults = await xo.lintFiles(mdGlob, {cwd}); t.is(noOptionsResults.errorCount, 0); // Markdown files linted (with no plugin for it) = errors - const moreExtensionsResults = await xo.lintFiles(mdGlob, {extensions: ['md']}); + const moreExtensionsResults = await xo.lintFiles(mdGlob, {extensions: ['md'], cwd}); t.true(moreExtensionsResults.errorCount > 0); }); test('ignores dirs for empty extensions', async t => { { - const glob = path.join(__dirname, 'fixtures/nodir/*'); - const results = await xo.lintFiles(glob, {extensions: ['', 'js']}); + const cwd = path.join(__dirname, 'fixtures/nodir'); + const glob = '*'; + const results = await xo.lintFiles(glob, {extensions: ['', 'js'], cwd}); const {results: [fileResult]} = results; // Only `fixtures/nodir/noextension` should be linted @@ -38,8 +40,9 @@ test('ignores dirs for empty extensions', async t => { } { - const glob = path.join(__dirname, 'fixtures/nodir/nested/*'); - const results = await xo.lintFiles(glob); + const cwd = path.join(__dirname, 'fixtures/nodir'); + const glob = 'nested/*'; + const results = await xo.lintFiles(glob, {cwd}); const {results: [fileResult]} = results; // Ensure `nodir/nested` **would** report if globbed @@ -48,6 +51,19 @@ test('ignores dirs for empty extensions', async t => { t.is(actual, expected); t.is(results.errorCount, 1); } + + { + const cwd = path.join(__dirname, 'fixtures/nodir'); + // Check Windows-style paths are working + const glob = 'nested\\*'; + const results = await xo.lintFiles(glob, {cwd}); + const {results: [fileResult]} = results; + + const expected = 'fixtures/nodir/nested/index.js'.split('/').join(path.sep); + const actual = path.relative(__dirname, fileResult.filePath); + t.is(actual, expected); + t.is(results.errorCount, 1); + } }); test.serial('cwd option', async t => { @@ -59,7 +75,7 @@ test.serial('cwd option', async t => { test('do not lint gitignored files', async t => { const cwd = path.join(__dirname, 'fixtures/gitignore'); - const glob = path.posix.join(cwd, '**/*'); + const glob = '**/*'; const ignored = path.resolve('fixtures/gitignore/test/foo.js'); const {results} = await xo.lintFiles(glob, {cwd}); @@ -68,7 +84,7 @@ test('do not lint gitignored files', async t => { test('do not lint gitignored files in file with negative gitignores', async t => { const cwd = path.join(__dirname, 'fixtures/negative-gitignore'); - const glob = path.posix.join(cwd, '*'); + const glob = '*'; const ignored = path.resolve('fixtures/negative-gitignore/bar.js'); const {results} = await xo.lintFiles(glob, {cwd}); @@ -77,7 +93,7 @@ test('do not lint gitignored files in file with negative gitignores', async t => test('lint negatively gitignored files', async t => { const cwd = path.join(__dirname, 'fixtures/negative-gitignore'); - const glob = path.posix.join(cwd, '*'); + const glob = '*'; const negative = path.resolve('fixtures/negative-gitignore/foo.js'); const {results} = await xo.lintFiles(glob, {cwd}); @@ -86,7 +102,7 @@ test('lint negatively gitignored files', async t => { test('do not lint inapplicable negatively gitignored files', async t => { const cwd = path.join(__dirname, 'fixtures/negative-gitignore'); - const glob = path.posix.join(cwd, 'bar.js'); + const glob = 'bar.js'; const negative = path.resolve('fixtures/negative-gitignore/foo.js'); const {results} = await xo.lintFiles(glob, {cwd}); @@ -125,7 +141,7 @@ test('enable rules based on nodeVersion', async t => { test('do not lint eslintignored files', async t => { const cwd = path.join(__dirname, 'fixtures/eslintignore'); - const glob = path.posix.join(cwd, '*'); + const glob = '*'; const positive = path.resolve('fixtures/eslintignore/foo.js'); const negative = path.resolve('fixtures/eslintignore/bar.js'); const {results} = await xo.lintFiles(glob, {cwd}); @@ -216,7 +232,7 @@ test('typescript no semicolon option', async t => { test('webpack import resolver is used if webpack.config.js is found', async t => { const cwd = 'fixtures/webpack/with-config/'; - const {results} = await xo.lintFiles(path.resolve(cwd, 'file1.js'), { + const {results} = await xo.lintFiles('file1.js', { cwd, rules: { 'import/no-unresolved': 2, @@ -233,7 +249,7 @@ test('webpack import resolver is used if webpack.config.js is found', async t => test('webpack import resolver config can be passed through webpack option', async t => { const cwd = 'fixtures/webpack/no-config/'; - const {results} = await xo.lintFiles(path.resolve(cwd, 'file1.js'), { + const {results} = await xo.lintFiles('file1.js', { cwd, webpack: { config: { @@ -256,7 +272,7 @@ test('webpack import resolver config can be passed through webpack option', asyn test('webpack import resolver is used if {webpack: true}', async t => { const cwd = 'fixtures/webpack/no-config/'; - const {results} = await xo.lintFiles(path.resolve(cwd, 'file3.js'), { + const {results} = await xo.lintFiles('file3.js', { cwd, webpack: true, rules: { diff --git a/test/temp/.gitignore b/test/temp/.gitignore new file mode 100644 index 00000000..355164c1 --- /dev/null +++ b/test/temp/.gitignore @@ -0,0 +1 @@ +*/