Skip to content

Commit

Permalink
Merge pull request #30 from mansona/hash-bang
Browse files Browse the repository at this point in the history
Support shebang files
  • Loading branch information
mansona authored Oct 3, 2024
2 parents 3c68650 + fe37dc6 commit 4fd6001
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 176 deletions.
164 changes: 3 additions & 161 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,164 +1,6 @@
/* eslint-disable prettier/prettier */
const { readFileSync, writeFileSync, lstatSync } = require('fs');
const { join } = require('path');
const importCwd = require('import-cwd');
const walkSync = require('walk-sync');
const semver = require('semver');

function ignoreError(error) {
const ruleIds = error.messages.map((message) => message.ruleId);
let uniqueIds = [...new Set(ruleIds)];

const file = readFileSync(error.filePath, 'utf8');

const firstLine = file.split('\n')[0];

// The whitespace after `eslint-disable` is important so `eslint-disable-next-line` and variants
// aren't picked up.
const matched = firstLine.match(/eslint-disable (.*)\*\//);

if (matched) {
const existing = matched[1].split(',').map((item) => item.trim());
uniqueIds = [...new Set([...ruleIds, ...existing])];
uniqueIds.sort((a, b) => a.localeCompare(b));

writeFileSync(error.filePath, file.replace(/^.*\n/, `/* eslint-disable ${uniqueIds.join(', ')} */\n`));
} else {
uniqueIds.sort((a, b) => a.localeCompare(b));
writeFileSync(error.filePath, `/* eslint-disable ${uniqueIds.join(', ')} */\n${file}`);
}
}

function removeIgnore(filePath, rulename) {
const file = readFileSync(filePath, 'utf8');

const firstLine = file.split('\n')[0];

// The whitespace after `eslint-disable` is important so `eslint-disable-next-line` and variants
// aren't picked up.
const matched = firstLine.match(/eslint-disable (.*)\*\//);

if (matched) {
const existing = matched[1].split(',').map((item) => item.trim()).filter((item) => item !== rulename);

if (existing.length) {
writeFileSync(filePath, file.replace(/^.*\n/, `/* eslint-disable ${existing.join(', ')} */\n`));
} else {
writeFileSync(filePath, file.replace(/^.*\n/, ''));
}
}
}

function getFiles(cwd, providedGlob) {
let ignoreFile;

try {
ignoreFile = readFileSync(join(cwd, '.gitignore'), 'utf8')
.split('\n')
.filter((line) => line.length)
.filter((line) => !line.startsWith('#'))
// walkSync can't handle these
.filter((line) => !line.startsWith('!'))
.map((line) => line.replace(/^\//, ''))
.map((line) => line.replace(/\/$/, '/*'));
} catch (e) {
// noop
}

let globs;

if (providedGlob) {
globs = [providedGlob];
} else {
globs = ['**/*.js', '**/*.ts'];
}

return walkSync(cwd, {
globs,
ignore: ignoreFile || ['**/node_modules/*'],
directories: false,
});
}

async function ignoreAll(cwd = process.cwd()) {
const currentPackageJSON = require(join(cwd, 'package.json'));

const eslintVersion = currentPackageJSON.devDependencies.eslint;

let cli;
let report;
let results;

const eslint = importCwd('eslint');

if (semver.intersects(eslintVersion, '8')) {
// this won't use the version in the repo but it will still use v8 because
// that is installed in this repo
const { ESLint } = eslint;
cli = new ESLint();
results = await cli.lintFiles([cwd]);

// remove warnings
results = ESLint.getErrorResults(results);
} else {
const { CLIEngine, ESLint } = eslint;
cli = new CLIEngine();
report = cli.executeOnFiles([cwd]);
results = report.results;

// remove warnings
results = ESLint.getErrorResults(results);
}

const errors = results.filter((result) => result.errorCount > 0);

errors.forEach(ignoreError);
}

function list(cwd = process.cwd()) {
const files = getFiles(cwd);

const output = {};

files.forEach((relativeFilePath) => {
const filePath = join(cwd, relativeFilePath);
// prevent odd times when directories might end with `.js` or `.ts`;
if (!lstatSync(filePath).isFile()) {
return;
}

const file = readFileSync(filePath, 'utf8');
const firstLine = file.split('\n')[0];
if (!firstLine.includes('eslint-disable ')) {
return;
}

const matched = firstLine.match(/eslint-disable (.*)\*\//);
const ignoreRules = matched[1].split(',').map((item) => item.trim());

ignoreRules.forEach((rule) => {
if (output[rule]) {
output[rule].push(filePath);
} else {
output[rule] = [filePath];
}
});
});

return output;
}

function remove({name, filter} = {}, cwd = process.cwd()) {
if (!name) {
throw new Error('No rulename was passed to `remove()` in lint-to-the-future-eslint plugin');
}

const files = getFiles(cwd, filter);

files.forEach((relativeFilePath) => {
removeIgnore(join(cwd, relativeFilePath), name);
});
}
const ignoreAll = require('./lib/ignore-all');
const list = require('./lib/list');
const remove = require('./lib/remove');

module.exports = {
ignoreAll,
Expand Down
34 changes: 34 additions & 0 deletions lib/get-files.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const { readFileSync } = require('fs');
const walkSync = require('walk-sync');
const { join } = require('path');

module.exports = function getFiles(cwd, providedGlob) {
let ignoreFile;

try {
ignoreFile = readFileSync(join(cwd, '.gitignore'), 'utf8')
.split('\n')
.filter((line) => line.length)
.filter((line) => !line.startsWith('#'))
// walkSync can't handle these
.filter((line) => !line.startsWith('!'))
.map((line) => line.replace(/^\//, ''))
.map((line) => line.replace(/\/$/, '/*'));
} catch (e) {
// noop
}

let globs;

if (providedGlob) {
globs = [providedGlob];
} else {
globs = ['**/*.js', '**/*.ts'];
}

return walkSync(cwd, {
globs,
ignore: ignoreFile || ['**/node_modules/*'],
directories: false,
});
};
77 changes: 77 additions & 0 deletions lib/ignore-all.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
const { join } = require('path');
const importCwd = require('import-cwd');
const semver = require('semver');
const { readFileSync, writeFileSync } = require('fs');

function ignoreError(error) {
const ruleIds = error.messages.map((message) => message.ruleId);
let uniqueIds = [...new Set(ruleIds)].sort((a, b) => a.localeCompare(b));

const file = readFileSync(error.filePath, 'utf8');

const splitFile = file.split('\n');

let firstLine = splitFile[0];

// shebang https://nodejs.org/en/learn/command-line/run-nodejs-scripts-from-the-command-line
let shebangFile = firstLine.startsWith('#!');

// the logical first line of the file is after the shebang
if (shebangFile) {
firstLine = splitFile[1];
}

// The whitespace after `eslint-disable` is important so `eslint-disable-next-line` and variants
// aren't picked up.
const matched = firstLine.match(/eslint-disable (.*)\*\//);

if (matched) {
const existing = matched[1].split(',').map((item) => item.trim());
uniqueIds = [...new Set([...ruleIds, ...existing])].sort((a, b) =>
a.localeCompare(b),
);
}

splitFile.splice(
shebangFile ? 1 : 0, // if it's a shebangFile we start at the 2nd line of the file
matched ? 1 : 0, // if it's already got an eslint-disable we replace the line (i.e. delete one line before splicing)
`/* eslint-disable ${uniqueIds.join(', ')} */`,
);

writeFileSync(error.filePath, splitFile.join('\n'));
}

module.exports = async function ignoreAll(cwd = process.cwd()) {
const currentPackageJSON = require(join(cwd, 'package.json'));

const eslintVersion = currentPackageJSON.devDependencies.eslint;

let cli;
let report;
let results;

const eslint = importCwd('eslint');

if (semver.intersects(eslintVersion, '8')) {
// this won't use the version in the repo but it will still use v8 because
// that is installed in this repo
const { ESLint } = eslint;
cli = new ESLint();
results = await cli.lintFiles([cwd]);

// remove warnings
results = ESLint.getErrorResults(results);
} else {
const { CLIEngine, ESLint } = eslint;
cli = new CLIEngine();
report = cli.executeOnFiles([cwd]);
results = report.results;

// remove warnings
results = ESLint.getErrorResults(results);
}

const errors = results.filter((result) => result.errorCount > 0);

errors.forEach(ignoreError);
};
45 changes: 45 additions & 0 deletions lib/list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const { readFileSync, lstatSync } = require('fs');
const { join } = require('path');
const getFiles = require('./get-files');

module.exports = function list(cwd = process.cwd()) {
const files = getFiles(cwd);

const output = {};

files.forEach((relativeFilePath) => {
const filePath = join(cwd, relativeFilePath);
// prevent odd times when directories might end with `.js` or `.ts`;
if (!lstatSync(filePath).isFile()) {
return;
}

const file = readFileSync(filePath, 'utf8');
const splitFile = file.split('\n');
let firstLine = splitFile[0];

// shebang https://nodejs.org/en/learn/command-line/run-nodejs-scripts-from-the-command-line
let shebangFile = firstLine.startsWith('#!');

// the logical first line of the file is after the shebang
if (shebangFile) {
firstLine = splitFile[1];
}
if (!firstLine.includes('eslint-disable ')) {
return;
}

const matched = firstLine.match(/eslint-disable (.*)\*\//);
const ignoreRules = matched[1].split(',').map((item) => item.trim());

ignoreRules.forEach((rule) => {
if (output[rule]) {
output[rule].push(filePath);
} else {
output[rule] = [filePath];
}
});
});

return output;
};
59 changes: 59 additions & 0 deletions lib/remove.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
const { readFileSync, writeFileSync } = require('fs');
const getFiles = require('./get-files');
const { join } = require('path');

function removeIgnore(filePath, rulename) {
const file = readFileSync(filePath, 'utf8');

const splitFile = file.split('\n');
let firstLine = splitFile[0];

// shebang https://nodejs.org/en/learn/command-line/run-nodejs-scripts-from-the-command-line
let shebangFile = firstLine.startsWith('#!');

// the logical first line of the file is after the shebang
if (shebangFile) {
firstLine = splitFile[1];
}

// The whitespace after `eslint-disable` is important so `eslint-disable-next-line` and variants
// aren't picked up.
const matched = firstLine.match(/eslint-disable (.*)\*\//);

if (matched) {
const existing = matched[1]
.split(',')
.map((item) => item.trim())
.filter((item) => item !== rulename);

if (existing.length) {
splitFile.splice(
shebangFile ? 1 : 0, // if it's a shebangFile we start at the 2nd line of the file
1, // replacing the existing eslint-disable
`/* eslint-disable ${existing.join(', ')} */`,
);
} else {
// if we removed the last one we just remove the eslint-disable line
splitFile.splice(
shebangFile ? 1 : 0, // if it's a shebangFile we start at the 2nd line of the file
1,
);
}

writeFileSync(filePath, splitFile.join('\n'));
}
}

module.exports = function remove({ name, filter } = {}, cwd = process.cwd()) {
if (!name) {
throw new Error(
'No rulename was passed to `remove()` in lint-to-the-future-eslint plugin',
);
}

const files = getFiles(cwd, filter);

files.forEach((relativeFilePath) => {
removeIgnore(join(cwd, relativeFilePath), name);
});
};
Loading

0 comments on commit 4fd6001

Please sign in to comment.