-
Notifications
You must be signed in to change notification settings - Fork 235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Convert to ESM #2262
Convert to ESM #2262
Changes from all commits
dca354e
c8a1da5
a6ac218
e941271
b380487
1e2712a
52a2c39
5b737e4
65b3856
052aad0
2af478f
662f026
87659bb
b343eb3
aa0a1b4
ac11ee6
8a7082e
2915d00
af34b38
0b679a6
ab3e2e4
fa1ccee
65ab235
2e6e880
3ef3381
cbf9079
8d892af
e92578f
5de4747
fbad6c9
ed1b32f
8a44de4
14fe0ca
f2ed7bb
2f6b7ee
4fec95b
9419b21
f33308a
ef5befc
963257f
5948f73
d5cc369
08b44c3
71b3b46
22637bc
b7daa1f
1e27c8e
f2a5ddc
f3c2f19
595ad6d
6b7ba73
30103e4
b629856
5d20713
d89d10c
d4917c5
8c533e9
8da9528
42c1591
5e21d7c
3b295e0
7b868ba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
module.exports = { | ||
parser: '@babel/eslint-parser', | ||
parserOptions: { | ||
ecmaVersion: 2021, | ||
sourceType: 'script', | ||
sourceType: 'module', | ||
requireConfigFile: false, | ||
}, | ||
env: { | ||
node: true, | ||
|
@@ -82,7 +84,7 @@ module.exports = { | |
'filenames/match-regex': ['error', '^.?[a-z0-9-]+$'], // Kebab-case. | ||
|
||
// Optional import rules: | ||
'import/extensions': 'error', | ||
'import/extensions': ['error', 'always'], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This enforces that we include file extensions in all import statements. |
||
'import/first': 'error', | ||
'import/newline-after-import': 'error', | ||
'import/no-absolute-path': 'error', | ||
|
@@ -111,6 +113,14 @@ module.exports = { | |
}, | ||
], | ||
|
||
// Node rules: | ||
'node/no-unsupported-features/es-syntax': [ | ||
'error', | ||
{ | ||
ignores: ['dynamicImport', 'modules'], // False positives: https://github.com/mysticatea/eslint-plugin-node/issues/250 | ||
}, | ||
], | ||
|
||
// Unicorn rules: | ||
'unicorn/consistent-destructuring': 'off', | ||
'unicorn/consistent-function-scoping': 'off', | ||
|
@@ -120,14 +130,19 @@ module.exports = { | |
'unicorn/no-lonely-if': 'off', | ||
'unicorn/no-null': 'off', | ||
'unicorn/no-useless-undefined': 'off', | ||
'unicorn/prefer-module': 'off', | ||
'unicorn/prefer-ternary': 'off', | ||
'unicorn/prevent-abbreviations': 'off', | ||
}, | ||
|
||
overrides: [ | ||
{ | ||
files: ['bin/**/*.js'], | ||
files: ['**/*.cjs'], | ||
parserOptions: { | ||
sourceType: 'script', | ||
}, | ||
}, | ||
{ | ||
files: ['bin/**/*.js', 'lib/helpers/cli.js'], | ||
rules: { | ||
'no-console': 'off', | ||
}, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,25 @@ | ||
#!/usr/bin/env node | ||
|
||
'use strict'; | ||
/* eslint node/shebang:"off" -- shebang needed so this script can be run directly */ | ||
|
||
// Use V8's code cache to speed up instantiation time: | ||
require('v8-compile-cache'); // eslint-disable-line import/no-unassigned-import | ||
|
||
const fs = require('fs'); | ||
const path = require('path'); | ||
const { promisify } = require('util'); | ||
import 'v8-compile-cache'; // eslint-disable-line import/no-unassigned-import | ||
|
||
const { | ||
import { | ||
compactTodoStorageFile, | ||
getTodoStorageFilePath, | ||
getTodoConfig, | ||
validateConfig, | ||
} = require('@lint-todo/utils'); | ||
const ci = require('ci-info'); | ||
const getStdin = require('get-stdin'); | ||
const globby = require('globby'); | ||
const isGlob = require('is-glob'); | ||
const micromatch = require('micromatch'); | ||
|
||
const Linter = require('../lib'); | ||
const camelize = require('../lib/helpers/camelize'); | ||
const processResults = require('../lib/helpers/process-results'); | ||
} from '@lint-todo/utils'; | ||
import getStdin from 'get-stdin'; | ||
import fs from 'node:fs'; | ||
import path from 'node:path'; | ||
import process from 'node:process'; | ||
import { promisify } from 'node:util'; | ||
|
||
import Printer from '../lib/formatters/default.js'; | ||
import { parseArgv, getFilesToLint } from '../lib/helpers/cli.js'; | ||
import processResults from '../lib/helpers/process-results.js'; | ||
import Linter from '../lib/linter.js'; | ||
|
||
const readFile = promisify(fs.readFile); | ||
|
||
|
@@ -55,233 +51,6 @@ async function buildLinterOptions(workingDir, filePath, filename = '', isReading | |
} | ||
} | ||
|
||
function executeGlobby(workingDir, pattern, ignore) { | ||
let supportedExtensions = new Set(['.hbs', '.handlebars']); | ||
|
||
// `--no-ignore-pattern` results in `ignorePattern === [false]` | ||
let options = | ||
ignore[0] === false ? { cwd: workingDir } : { cwd: workingDir, gitignore: true, ignore }; | ||
|
||
return globby | ||
.sync(pattern, options) | ||
.filter((filePath) => supportedExtensions.has(path.extname(filePath))); | ||
} | ||
|
||
function isFile(possibleFile) { | ||
try { | ||
let stat = fs.statSync(possibleFile); | ||
return stat.isFile(); | ||
} catch { | ||
return false; | ||
} | ||
} | ||
|
||
function expandFileGlobs(workingDir, filePatterns, ignorePattern, glob = executeGlobby) { | ||
let result = new Set(); | ||
|
||
for (const pattern of filePatterns) { | ||
let isLiteralPath = !isGlob(pattern) && isFile(path.resolve(workingDir, pattern)); | ||
|
||
if (isLiteralPath) { | ||
let isIgnored = micromatch.isMatch(pattern, ignorePattern); | ||
|
||
if (!isIgnored) { | ||
result.add(pattern); | ||
} | ||
|
||
continue; | ||
} | ||
|
||
const globResults = glob(workingDir, pattern, ignorePattern); | ||
if (!globResults || globResults.length === 0) { | ||
throw new Error(`No files matching the pattern were found: "${pattern}"`); | ||
} | ||
|
||
for (const filePath of globResults) { | ||
result.add(filePath); | ||
} | ||
} | ||
|
||
return result; | ||
} | ||
|
||
function getFilesToLint(workingDir, filePatterns, ignorePattern = []) { | ||
let files; | ||
|
||
if (filePatterns.length === 0 || filePatterns.includes('-') || filePatterns.includes(STDIN)) { | ||
files = new Set([STDIN]); | ||
} else { | ||
files = expandFileGlobs(workingDir, filePatterns, ignorePattern); | ||
} | ||
|
||
return files; | ||
} | ||
|
||
/** | ||
* @param {Object} specifiedOptions - options passed to yargs (option names should be in dasherized format) | ||
* @returns {String[]} a list of all possible CLI option names | ||
*/ | ||
function getPossibleOptionNames(specifiedOptions) { | ||
const optionAliases = Object.values(specifiedOptions) | ||
.map((option) => option.alias) | ||
.filter((option) => option !== undefined); | ||
const dasherizedOptionNames = [...Object.keys(specifiedOptions), ...optionAliases]; | ||
const camelizedOptionNames = dasherizedOptionNames.map((name) => camelize(name)); | ||
const negatedDasherizedOptionNames = dasherizedOptionNames.map((name) => | ||
name.startsWith('no-') ? name.slice(3) : `no-${name}` | ||
); | ||
const negatedCamelizedOptionNames = negatedDasherizedOptionNames.map((name) => camelize(name)); | ||
return [ | ||
...dasherizedOptionNames, | ||
...camelizedOptionNames, | ||
// Since yargs `boolean-negation` option is enabled (by default), assume any option can be passed with `no`/negated prefix. | ||
...negatedDasherizedOptionNames, | ||
...negatedCamelizedOptionNames, | ||
]; | ||
} | ||
|
||
function parseArgv(_argv) { | ||
const specifiedOptions = { | ||
'config-path': { | ||
describe: 'Define a custom config path', | ||
default: '.template-lintrc.js', | ||
type: 'string', | ||
}, | ||
config: { | ||
describe: | ||
'Define a custom configuration to be used - (e.g. \'{ "rules": { "no-implicit-this": "error" } }\') ', | ||
type: 'string', | ||
}, | ||
quiet: { | ||
describe: 'Ignore warnings and only show errors', | ||
boolean: true, | ||
}, | ||
rule: { | ||
describe: | ||
'Specify a rule and its severity to add that rule to loaded rules - (e.g. `no-implicit-this:error` or `rule:["error", { "allow": ["some-helper"] }]`)', | ||
type: 'string', | ||
}, | ||
filename: { | ||
describe: 'Used to indicate the filename to be assumed for contents from STDIN', | ||
type: 'string', | ||
}, | ||
fix: { | ||
describe: 'Fix any errors that are reported as fixable', | ||
boolean: true, | ||
default: false, | ||
}, | ||
format: { | ||
describe: 'Specify format to be used in printing output', | ||
type: 'string', | ||
default: 'pretty', | ||
}, | ||
'output-file': { | ||
describe: 'Specify file to write report to', | ||
type: 'string', | ||
implies: 'format', | ||
}, | ||
verbose: { | ||
describe: 'Output errors with source description', | ||
boolean: true, | ||
}, | ||
'working-directory': { | ||
alias: 'cwd', | ||
describe: 'Path to a directory that should be considered as the current working directory.', | ||
type: 'string', | ||
// defaulting to `.` here to refer to `process.cwd()`, setting the default to `process.cwd()` itself | ||
// would make our snapshots unstable (and make the help output unaligned since most directory paths | ||
// are fairly deep) | ||
default: '.', | ||
}, | ||
'no-config-path': { | ||
describe: 'Does not use the local template-lintrc, will use a blank template-lintrc instead', | ||
boolean: true, | ||
}, | ||
'update-todo': { | ||
describe: 'Update list of linting todos by transforming lint errors to todos', | ||
default: false, | ||
boolean: true, | ||
}, | ||
'include-todo': { | ||
describe: 'Include todos in the results', | ||
default: false, | ||
boolean: true, | ||
}, | ||
'clean-todo': { | ||
describe: 'Remove expired and invalid todo files', | ||
default: !ci.isCI, | ||
boolean: true, | ||
}, | ||
'compact-todo': { | ||
describe: 'Compacts the .lint-todo storage file, removing extraneous todos', | ||
boolean: true, | ||
}, | ||
'todo-days-to-warn': { | ||
describe: 'Number of days after its creation date that a todo transitions into a warning', | ||
type: 'number', | ||
}, | ||
'todo-days-to-error': { | ||
describe: 'Number of days after its creation date that a todo transitions into an error', | ||
type: 'number', | ||
}, | ||
'ignore-pattern': { | ||
describe: 'Specify custom ignore pattern (can be disabled with --no-ignore-pattern)', | ||
type: 'array', | ||
default: ['**/dist/**', '**/tmp/**', '**/node_modules/**'], | ||
}, | ||
'no-inline-config': { | ||
describe: 'Prevent inline configuration comments from changing config or rules', | ||
boolean: true, | ||
}, | ||
'print-config': { | ||
describe: 'Print the configuration for the given file', | ||
default: false, | ||
boolean: true, | ||
}, | ||
'max-warnings': { | ||
describe: 'Number of warnings to trigger nonzero exit code', | ||
type: 'number', | ||
}, | ||
}; | ||
|
||
let parser = require('yargs') | ||
.scriptName('ember-template-lint') | ||
.usage('$0 [options] [files..]') | ||
.options(specifiedOptions) | ||
.help() | ||
.version(); | ||
|
||
parser.parserConfiguration({ | ||
'greedy-arrays': false, | ||
}); | ||
|
||
if (_argv.length === 0) { | ||
parser.showHelp(); | ||
parser.exit(1); | ||
} else { | ||
let options = parser.parse(_argv); | ||
|
||
// TODO: Eventually use yargs strict() or strictOptions() to disallow unknown options (blocked by some inconsistencies in how we tell yargs about our options). | ||
const possibleOptionNames = getPossibleOptionNames(specifiedOptions); | ||
for (const optionName of Object.keys(options)) { | ||
if ( | ||
!['$0', '_'].includes(optionName) && // Built-in yargs options. | ||
!possibleOptionNames.includes(optionName) | ||
) { | ||
console.error(`Unknown option: --${optionName}`); | ||
parser.exit(1); | ||
return; | ||
} | ||
} | ||
|
||
if (options.workingDirectory === '.') { | ||
options.workingDirectory = process.cwd(); | ||
} | ||
|
||
return options; | ||
} | ||
} | ||
|
||
function getTodoConfigFromCommandLineOptions(options) { | ||
let todoConfig = {}; | ||
|
||
|
@@ -425,7 +194,7 @@ async function run() { | |
let fileResults; | ||
|
||
if (options.printConfig) { | ||
let fileConfig = linter.getConfigForFile(linterOptions); | ||
let fileConfig = await linter.getConfigForFile(linterOptions); | ||
|
||
_console.log(JSON.stringify(fileConfig, null, 2)); | ||
process.exitCode = 0; | ||
|
@@ -481,22 +250,11 @@ async function run() { | |
let hasTodos = options.includeTodo && results.todoCount; | ||
let hasUpdatedTodos = options.updateTodo; | ||
|
||
let Printer = require('../lib/formatters/default'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is now a static import at top of file. Could possibly be moved back to dynamic There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is fine since we're always loading a printer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, I did this in a few places since static imports don't require switching stuff to await, but probably wouldn't be difficult to move it back to dynamic (using |
||
let printer = new Printer({ | ||
...options, | ||
hasResultData: hasErrors || hasWarnings || hasTodos || hasUpdatedTodos, | ||
}); | ||
printer.print(results, todoInfo); | ||
} | ||
|
||
// exports are for easier unit testing | ||
module.exports = { | ||
_parseArgv: parseArgv, | ||
_expandFileGlobs: expandFileGlobs, | ||
_getFilesToLint: getFilesToLint, | ||
_getPossibleOptionNames: getPossibleOptionNames, | ||
}; | ||
|
||
if (require.main === module) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved the exported helper functions from this file to a new helper file |
||
run(); | ||
} | ||
run(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@babel/eslint-parser
added for parsing top-level await syntax now allowed in ESM.