diff --git a/.eslintrc.js b/.eslintrc.js index 45b3ed2..cc602bc 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -15,5 +15,7 @@ module.exports = { ], rules: { 'import/no-unresolved': 'off', + 'import/no-dynamic-require': 'off', + 'global-require': 'off', }, }; diff --git a/custom-eslint-plugin.js b/custom-eslint-plugin.js new file mode 100644 index 0000000..28883d1 --- /dev/null +++ b/custom-eslint-plugin.js @@ -0,0 +1,8 @@ +module.exports = { + parsers: { + 'eslint-esnext': require('babel-eslint'), + }, + // rules: { + // 'my-foo-plugin/some-my-rule': () => {}, + // }, +}; diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..4afb722 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,317 @@ +// const myConfig = require('./.eslintrc'); + +// const airbnbBaseConfig = require('eslint-config-airbnb-base'); +// const utils = require('./packages/eslint/src/utils'); + +// console.log(convertExtends(require('@tunnckocore/eslint-config').extends)); + +/* + +'eslint-config-airbnb-base/rules/best-practices', +'eslint-config-airbnb-base/rules/errors', +'eslint-config-airbnb-base/rules/node', +'eslint-config-airbnb-base/rules/style', +'eslint-config-airbnb-base/rules/variables', +'eslint-config-airbnb-base/rules/es6', +'eslint-config-airbnb-base/rules/imports', +'eslint-config-airbnb-base/rules/strict', + +'plugin:prettier/recommended', +'prettier', +'@tunnckocore/eslint-config/mdx', +'@tunnckocore/eslint-config/jest', +'@tunnckocore/eslint-config/node', +'@tunnckocore/eslint-config/promise', +'plugin:promise/recommended', +'@tunnckocore/eslint-config/unicorn', +'plugin:unicorn/recommended', + +*/ + +module.exports = [ + 'eslint:recommended', + require('eslint-config-airbnb-base/rules/best-practices'), + require('eslint-config-airbnb-base/rules/errors'), + require('eslint-config-airbnb-base/rules/node'), + require('eslint-config-airbnb-base/rules/style'), + require('eslint-config-airbnb-base/rules/variables'), + require('eslint-config-airbnb-base/rules/es6'), + require('eslint-config-airbnb-base/rules/imports'), + require('eslint-config-airbnb-base/rules/strict'), + { + name: 'enabled-prettier', + plugins: { + prettier: require('eslint-plugin-prettier'), + }, + rules: { + ...require('eslint-config-prettier').rules, + 'prettier/prettier': 'error', + }, + }, + { + name: 'tunnckocore-base-config', + plugins: { + 'no-use-extend-native': require('eslint-plugin-no-use-extend-native'), + }, + rules: require('@tunnckocore/eslint-config/base').rules, + }, + // require('@tunnckocore/eslint-config/mdx'), + // require('@tunnckocore/eslint-config/jest'), + { + name: 'node-plugin-and-rules', + plugins: { + node: require('eslint-plugin-node'), + }, + rules: { + 'node/no-deprecated-api': 'error', + 'node/no-exports-assign': 'error', + 'node/no-unpublished-bin': 'error', + + // Redundant with import/no-extraneous-dependencies + // 'node/no-extraneous-import': 'error', + // 'node/no-extraneous-require': 'error', + + // Redundant with import/no-unresolved + // 'node/no-missing-import': 'error', + // 'node/no-missing-require': 'error', + + 'node/no-unsupported-features/es-builtins': 'error', + 'node/no-unsupported-features/es-syntax': 'off', + 'node/no-unsupported-features/node-builtins': 'error', + 'no-process-exit': 'off', + 'node/process-exit-as-throw': 'error', + 'node/shebang': 'error', + + 'node/exports-style': 'off', + 'node/file-extension-in-import': [ + 'error', + 'never', + { + '.css': 'always', + '.scss': 'always', + '.sass': 'always', + '.less': 'always', + '.json': 'always', + }, + ], + 'node/prefer-global/buffer': 'error', + 'node/prefer-global/console': 'error', + 'node/prefer-global/process': 'error', + + // These below will be enabled in XO when it targets Node.js 10 + 'node/prefer-global/text-decoder': 'error', + 'node/prefer-global/text-encoder': 'error', + 'node/prefer-global/url-search-params': 'error', + 'node/prefer-global/url': 'error', + 'node/prefer-promises/dns': 'error', + 'node/prefer-promises/fs': 'error', + }, + }, + { + name: 'promise-plugin-and-rules', + plugins: { + promise: require('eslint-plugin-promise'), + }, + rules: { + ...require('eslint-plugin-promise').configs.recommended.rules, + + // These below are to ensure not changes + // inside upstream XO and the plugin:promise/recommended configs + 'promise/catch-or-return': 'off', + 'promise/always-return': 'off', + 'promise/no-native': 'off', + 'promise/no-nesting': 'off', + 'promise/no-promise-in-callback': 'off', + 'promise/no-callback-in-promise': 'off', + 'promise/avoid-new': 'off', + 'promise/prefer-await-to-then': 'error', + 'promise/prefer-await-to-callbacks': 'error', + + // These are the same as in XO CLI, but they are not in the eslint-config-xo + 'promise/no-return-wrap': ['error', { allowReject: true }], + 'promise/param-names': 'error', + 'promise/no-new-statics': 'error', + 'promise/no-return-in-finally': 'error', + 'promise/valid-params': 'error', + }, + }, + { + name: 'unicorn-plugin-and-rules', + plugins: { + unicorn: require('eslint-plugin-unicorn'), + }, + rules: { + ...require('eslint-plugin-unicorn').configs.recommended.rules, + + // It is too much annoyance for me. It's a good thing, but generally + // after so many years we already name things properly, + // so please don't mess with me and don't correct me. + 'unicorn/prevent-abbreviations': 'off', + + // These below are intentional & explicit overrides of XO and Unicorn + + // ! needed for `unicorn/no-unreadable-array-destructuring` + 'prefer-destructuring': ['error', { object: true, array: false }], + 'unicorn/no-unreadable-array-destructuring': 'error', // default in recommended + + 'unicorn/no-unused-properties': 'error', + // Disallow unsafe regular expressions. + // Don't allow potential catastrophic crashes, slow behaving and downtimes. + // You still can disable that and do whatever you want, + // but that will be explicit and visible. + // https://github.com/sindresorhus/eslint-plugin-unicorn/blob/master/docs/rules/no-unsafe-regex.md + 'unicorn/no-unsafe-regex': 'error', + + // Enforce importing index files with `.` instead of `./index`. (fixable) + // But we should be explicit. We know it is working without that, + // but at least it is good for newcomers. + // https://github.com/sindresorhus/eslint-plugin-unicorn/blob/master/docs/rules/import-index.md + 'unicorn/import-index': 'off', + + // Enforce proper Error subclassing. (fixable) + // https://github.com/sindresorhus/eslint-plugin-unicorn/blob/master/docs/rules/custom-error-definition.md + 'unicorn/custom-error-definition': 'error', + + // Pretty useful rule, but it depends. + // https://github.com/sindresorhus/eslint-plugin-unicorn/blob/master/docs/rules/filename-case.md + 'unicorn/filename-case': 'off', + + // It is pretty common to name it `err`, and there is almost no reason to be any other. + // https://github.com/sindresorhus/eslint-plugin-unicorn/blob/master/docs/rules/catch-error-name.md + 'unicorn/catch-error-name': ['error', { name: 'err' }], + + // Doesn't work well in node-land. We have `.on/.off` emitters in Nodejs. + 'unicorn/prefer-add-event-listener': 'off', + 'unicorn/no-process-exit': 'error', + }, + }, + { + parser: 'babel-eslint', + parserOptions: { + ecmaVersion: 2020, + sourceType: 'module', + }, + settings: require('@tunnckocore/eslint-config/settings'), + }, +].concat({ + name: 'lint-sources', + files: 'packages/*/src/**/*.js', + + rules: { + 'import/no-unresolved': 'off', + 'import/no-dynamic-require': 'off', + 'global-require': 'off', + // strict: ['error', 'always'], + }, +}); +// module.exports = utils.createItemsFromExtends(myConfig.extends).concat({ +// name: 'lint-sources', +// files: 'packages/*/src/**/*.js', +// ...myConfig, +// // rules: { +// // ...myConfig.rules, +// // 'import/no-unresolved': 'off', +// // 'import/no-dynamic-require': 'off', +// // 'global-require': 'off', +// // }, +// }); + +// module.exports = [ +// // { +// // files: 'packages/*/src/**/*.js', +// // plugins: { +// // unicorn: require('eslint-plugin-unicorn'), +// // 'custom-plugin': require('./custom-eslint-plugin'), +// // }, +// // languageOptions: { +// // // directly requiring the parser +// // // parser: require('babel-eslint'), + +// // // or: through the loaded plugin +// // parser: 'custom-plugin/eslint-esnext', +// // ecmaVersion: 2020, +// // sourceType: 'module', +// // }, +// // rules: { +// // ...require('eslint-config-airbnb').rules, +// // 'unicorn/no-process-exit': ['error'], +// // 'unicorn/consistent-function-scoping': 'off', +// // semi: 'error', +// // }, +// // }, +// // { +// // files: '**/__tests__/**/*.{js,jsx}', +// // plugins: { +// // unicorn: require('eslint-plugin-unicorn'), +// // 'custom-plugin': require('./custom-eslint-plugin'), +// // react: require('eslint-plugin-react'), +// // }, +// // languageOptions: { +// // // directly requiring the parser +// // // parser: require('babel-eslint'), + +// // // or: through the loaded plugin +// // parser: 'custom-plugin/eslint-esnext', +// // ecmaVersion: 2020, +// // sourceType: 'module', +// // }, +// // rules: { +// // ...require('eslint-config-airbnb').rules, +// // 'unicorn/no-process-exit': ['error'], +// // 'unicorn/consistent-function-scoping': 'off', +// // semi: 'error', +// // 'react/jsx-uses-react': 'error', +// // 'global-require': 'error', +// // }, +// // }, +// { +// name: 'loading-babel-eslint-parser-through-custom-plugin', +// plugins: { +// 'custom-plugin': require('./custom-eslint-plugin'), +// }, +// }, + +// // example using the custom loaded parser +// { +// name: 'use-parser-from-custom-plugin', +// plugins: { +// baw: require('eslint-plugin-import'), +// }, +// languageOptions: { +// globals: { +// qwqwqw: true, +// }, +// // directly requiring the parser +// // parser: require('babel-eslint'), + +// // or: through the loaded plugin +// parser: 'custom-plugin/eslint-esnext', +// }, +// }, +// { +// name: 'lint-sources', +// files: 'packages/*/src/**/*.js', +// ...myConfig, +// }, +// { +// name: 'lang-options', +// languageOptions: { +// ecmaVersion: 2020, +// sourceType: 'commonjs', +// }, +// }, +// { +// name: 'lint-tests', +// files: '**/__tests__/**/*.{js,jsx}', +// ...myConfig, +// // { +// // plugins: { +// // react: require('eslint-plugin-react'), +// // }, +// // rules: { +// // 'react/jsx-uses-react': 'error', +// // 'global-require': 'error', +// // }, +// // }, +// }, +// ]; diff --git a/origin-eslint.config.js b/origin-eslint.config.js new file mode 100644 index 0000000..aa32457 --- /dev/null +++ b/origin-eslint.config.js @@ -0,0 +1,99 @@ +'use strict'; + +// module.exports = { +// name: "name", +// files: ["*.js"], +// ignores: ["*.test.js"], +// settings: {}, +// languageOptions: { +// ecmaVersion: 2020, +// sourceType: "module", +// globals: {}, +// parser: object || "string", +// parserOptions: {}, +// linterOptions: { +// reportUnusedDisableDirectives: "string" +// } +// } +// processor: object || "string", +// plugins: {} +// rules: {} +// }; + +const { + DEFAULT_FILES, + DEFAULT_IGNORE, +} = require('./packages/eslint/src/constants'); + +module.exports = [ + /* + gets converted to + + { + plugins: { + 'eslint:recommended': require('somehow-load-eslint-internal-rules') + } + } + */ + // 'eslint:recommended', + { + name: 'loading-babel-eslint-parser-through-custom-plugin', + plugins: { + 'custom-plugin': require('./custom-eslint-plugin'), + }, + }, + + // consider linting src/index.jsx - both configs should apply for it + // so, what will the ConfigArray#getConfig(filename) return?? + { + files: '**/*.{js,jsx}', + plugins: { + unicorn: require('eslint-plugin-unicorn'), + }, + rules: { + 'unicorn/no-process-exit': ['error'], + 'unicorn/consistent-function-scoping': 'error', + semi: 'error', + }, + }, + { + files: '**/*.jsx', + plugins: { + react: require('eslint-plugin-react'), + }, + rules: { + 'react/jsx-uses-react': 'error', + 'global-require': 'error', + }, + }, + + { + name: 'some-tunnckocore-config', + files: DEFAULT_FILES, + ignores: DEFAULT_IGNORE, + languageOptions: { + globals: {}, + // directly requiring the parser + // parser: require('babel-eslint'), + + // or: through the loaded plugin + parser: 'custom-plugin/eslint-esnext', + + // or require-ing of the plugin directly + // parser: require('./custom-eslint-plugin').parsers['eslint-esnext'], + parserOptions: { + ecmaVersion: 2020, + sourceType: 'module', + ecmaFeatures: { + generators: false, + objectLiteralDuplicateProperties: false, + }, + }, + }, + plugins: {}, + rules: { + semi: ['error', 'always'], + 'global-require': ['error', 'always'], + }, + }, +]; diff --git a/package.json b/package.json index dd2d590..ffe417c 100644 --- a/package.json +++ b/package.json @@ -27,14 +27,18 @@ "dependencies": { "@tunnckocore/eslint-config": "^5.4.5", "@tunnckocore/prettier-config": "^1.3.4", + "@zeit/ncc": "^0.21.1", "acorn-globals": "ForbesLindesay/acorn-globals#greenkeeper/acorn-7.1.1", "babel-eslint": "^10.1.0", + "defaults-deep": "^0.2.4", "eslint": "^6.8.0", "husky": "^4.2.3", "jest": "^25.1.0", "lerna": "^3.20.2", "lint-staged": "^10.0.8", "lockfile-lint": "^4.0.0", + "merge-deep": "^3.0.2", + "mixin-deep": "^2.0.1", "prettier": "^1.19.1", "prettier-plugin-pkgjson": "^0.2.4" }, diff --git a/packages/eslint/__tests__/index.js b/packages/eslint/__tests__/index.js index 2d59f95..ca869d7 100644 --- a/packages/eslint/__tests__/index.js +++ b/packages/eslint/__tests__/index.js @@ -3,7 +3,7 @@ const mod = require('../src/index'); test('todo test for mod', () => { - // const foo = 123;x + const foo = 123; expect(typeof mod).toStrictEqual('object'); expect(typeof mod.helaCommand).toStrictEqual('function'); }); diff --git a/packages/eslint/example.js b/packages/eslint/example.js new file mode 100644 index 0000000..ab56311 --- /dev/null +++ b/packages/eslint/example.js @@ -0,0 +1,50 @@ +'use strict'; + +// require('v8-compile-cache'); + +// const path = require('path'); + +// const api = require('./src/api'); +const utils = require('./src/utils'); +const lingConfigItems = require('./src/lint-config'); + +(async () => { + const { config } = await utils.loadESLintConifg(); + // const oldCfg = require(path.join(process.cwd(), './.lint.config.js')); + + // const { config: cfg, linter } = utils.injectIntoLinter(oldCfg); + + await lingConfigItems(config, { + // config: oldCfg, + // linter, + fix: true, + mapper: (ctx) => { + const meta = ctx.cacheFile && ctx.cacheFile.metadata; + const rep = (meta && meta.report) || null; + if (rep) { + // TODO there are problems with double reporting (and from cache) + if (rep.errorCount > 0 || rep.warningCount > 0) { + console.log('zzz'); + console.error(utils.cleanFrame([rep])); + console.log('zzz'); + } + } + return ctx; + }, + }); + + // if (report.errorCount === 0 && report.warningCount === 0) { + // console.log('No problems found.'); + // return; + // } + + // const warnings = `and ${report.warningCount} warning(s) `; + + // console.log(''); + // console.log(`${report.errorCount} error(s) ${warnings}found.`); + + // if (report.errorCount > 0) { + // // eslint-disable-next-line unicorn/no-process-exit + // process.exit(1); + // } +})(); diff --git a/packages/eslint/foo.js b/packages/eslint/foo.js new file mode 100644 index 0000000..a7b21ae --- /dev/null +++ b/packages/eslint/foo.js @@ -0,0 +1,108 @@ +// async function pFlatten(arr, ...args) { +// const items = await (await arr).reduce(async (acc, e) => { +// const accum = await acc; + +// let item = await e; + +// if (typeof item === 'function') { +// item = await item(...args); +// } +// if (!item) { +// return accum; +// } +// if (Array.isArray(item)) { +// // if the element is an array, fall flatten on it again and then take the returned value and concat it. +// return accum.concat(await pFlatten(item)); +// } +// // otherwise just concat the value. +// return accum.concat(item); +// }, Promise.resolve([])); // initial value for the accumulator is [] + +// return Promise.all(items); +// } + +// pFlatten( +// Promise.resolve([ +// null, +// { foo: 1 }, +// () => null, +// (ctx) => [ +// null, +// ctx.arr, +// { z: 4 }, +// [null, { sasa: 33 }, async () => ({ as: 2 })], +// async () => ({ dada: 22 }), +// ], +// async (ctx) => ({ a: 1, ...ctx.asyncFn }), +// [ +// null, +// { qux: 2 }, +// [null, { zaz: 3 }, () => ({ f: 123 })], +// async () => [null, { qw: 534 }], +// ], +// ]), +// { +// arr: { ok: 123 }, +// asyncFn: { zazzz: 888 }, +// }, +// ).then(console.log); + +const defaultsDeep = require('defaults-deep'); +const mergeDeep = require('merge-deep'); +const mixinDeep = require('mixin-deep'); + +console.log( + mixinDeep( + { + settings: {}, + languageOptions: { + globals: { + sas: 'ok', + }, + parser: 'str', + parserOptions: {}, + linterOptions: { + reportUnusedDisableDirectives: 'string', + }, + }, + plugins: { + react: { + rules: { + jsx: () => {}, + }, + }, + }, + rules: { + 'react/jsx': 'error', + semi: 'error', + }, + }, + { + settings: { foo: 123 }, + processor: 'md', + languageOptions: { + globals: { + win: 1, + sas: 'yep', + }, + ecmaVersion: 2020, + sourceType: 'module', + parser: 'babel-eslint', + }, + plugins: { + unicorn: { + rules: { + 'foo-bar': () => {}, + 'qux-zaz': () => {}, + }, + }, + }, + rules: { + 'unicorn/foo-bar': 'off', + 'unicorn/qux-zaz': 'error', + semi: 'off', + foobie: 'error', + }, + }, + ), +); diff --git a/packages/eslint/package.json b/packages/eslint/package.json index 2f1e41b..9e05a47 100644 --- a/packages/eslint/package.json +++ b/packages/eslint/package.json @@ -21,8 +21,6 @@ "src" ], "bin": { - "eslint": "src/bin.js", - "eslint-cli": "src/bin.js", "hela-eslint": "src/bin.js" }, "engines": { @@ -37,9 +35,13 @@ "dependencies": { "@hela/core": "^3.2.2", "eslint": "^6.8.0", - "find-pkg": "^2.0.0", + "find-file-up": "^2.0.1", "glob-cache": "^1.0.1", - "memoize-fs": "^2.1.0" + "import-fresh": "^3.2.1", + "is-glob": "^4.0.1", + "jest-worker": "^25.1.0", + "memoize-fs": "^2.1.0", + "v8-compile-cache": "^2.1.0" }, "keywords": [ "tunnckocorehq", diff --git a/packages/eslint/src/api.js b/packages/eslint/src/api.js index c1bf810..e5f97a4 100644 --- a/packages/eslint/src/api.js +++ b/packages/eslint/src/api.js @@ -1,358 +1,286 @@ -/* eslint-disable global-require */ -/* eslint-disable import/no-dynamic-require */ -/* eslint-disable no-restricted-syntax */ /* eslint-disable max-statements */ +/* eslint-disable no-continue */ +/* eslint-disable no-restricted-syntax */ 'use strict'; -const DEFAULT_IGNORE = [ - '**/node_modules/**', - '**/bower_components/**', - 'flow-typed/**', - 'coverage/**', - '**/*fixture*/**', - '{tmp,temp}/**', - '**/*.min.js', - '**/bundle.js', - '**/vendor/**', - '**/dist/**', -]; - -const OUR_CONFIG_FILENAME = '.lint.config.js'; -const DEFAULT_EXTENSIONS = ['js', 'jsx', 'cjs', 'mjs', 'ts', 'tsx']; -const DEFAULT_INPUTS = [ - `**/src/**/*.{${DEFAULT_EXTENSIONS.join(',')}}`, - `**/*test*/**/*.{${DEFAULT_EXTENSIONS.join(',')}}`, -]; - -const fs = require('fs'); +// require('v8-compile-cache'); + +// const fs = require('fs'); +// const util = require('util'); const path = require('path'); +const isGlob = require('is-glob'); +const cacache = require('cacache'); +// const fastGlob = require('fast-glob'); + const memoizeFs = require('memoize-fs'); -const codeframe = require('eslint/lib/cli-engine/formatters/codeframe'); const globCache = require('glob-cache'); -const { CLIEngine, Linter } = require('eslint'); - -function resolveConfigSync(filePath, baseConfig, options) { - const { cwd, ...opt } = { /* cwd: process.cwd(), */ ...options }; - const doNotUseEslintRC = baseConfig && typeof baseConfig === 'object'; - - const settings = { - ...opt, - baseConfig, - cache: true, - useEslintrc: !doNotUseEslintRC, - cacheLocation: './.cache/custom-eslint-cache', - }; - settings.extensions = settings.extensions || DEFAULT_EXTENSIONS; +const { constants, ...utils } = require('./utils'); - const engine = new CLIEngine(settings); - - const config = engine.getConfigForFile(filePath); - - return config; -} - -async function resolveConfig(filePath, baseConfig, options) { - const opts = { ...options }; - const memoizer = memoizeFs({ - cachePath: path.join(process.cwd(), '.cache', 'eslint-resolve-config'), - }); +const memoizer = memoizeFs({ + cachePath: path.join(process.cwd(), '.cache', 'eslint-meta-cache'), +}); +const toIntegrityPromise = memoizer.fn(utils.toIntegrity); - const memoizedFn = await memoizer.fn( - opts.dirname - ? (_) => resolveConfigSync(filePath, baseConfig, options) - : resolveConfigSync, - ); - const cfg = await memoizedFn( - ...(opts.dirname ? [opts.dirname] : [filePath, baseConfig, options]), - ); - - return cfg; -} +module.exports = { + resolvePatternsStream, + resolvePatterns, + resolveItems, + lintFiles, + lintConfigItems, +}; -function formatCodeframe(rep, log) { - const res = codeframe(rep.results || rep); - return log ? console.log(res) : res; -} +async function* resolvePatternsStream(patterns, options) { + const opts = { ...constants.DEFAULT_OPTIONS, ...options }; + const iterable = await globCache(patterns, opts); -function injectIntoLinter(config, linter) { - if (!config) { - return linter; + for await (const ctx of iterable) { + if (opts.forceLoad === true) { + yield ctx; + continue; + } + if (ctx.changed) { + yield ctx; + } } +} - const linterInstance = linter || new Linter(); +// async function loadContexts(items, options) { +// const opts = { ...constants.DEFAULT_OPTIONS, ...options, forceLoad: true }; +// const argsJson = JSON.stringify({ items, options: { ...options } }); +// const argsHash = utils.hasha(argsJson, { algorithm: 'md5', digest: 'hex' }); +// const globalCache = path.join(opts.cwd, '.cache', 'eslint-global-cache'); +// const toIntegrity = await toIntegrityPromise; - [] - .concat(config.plugins) - .filter(Boolean) - .forEach((pluginName) => { - let plugin = null; +// const ctx = await utils.hasInCache( +// { path: argsHash, integrity: await toIntegrity(argsJson) }, +// { cacheLocation: globalCache }, +// ); - if (pluginName.startsWith('@')) { - plugin = require(pluginName); - } else { - plugin = require(`eslint-plugin-${pluginName}`); - } +// console.log('load', ctx); - // note: defineRules is buggy - Object.keys(plugin.rules).forEach((ruleName) => { - linterInstance.defineRule( - `${pluginName}/${ruleName}`, - plugin.rules[ruleName], - ); - }); +// if (ctx.changed === false && ctx.notFound === false) { +// return ctx.cacheFile && ctx.cacheFile.metadata.contexts; +// } - // note: otherwise this should work - // linterInstance.defineRules( - // Object.keys(plugin.rules).reduce((acc, ruleName) => { - // acc[`${pluginName}/${ruleName}`] = plugin.rules[ruleName]; - - // return acc; - // }, {}), - // ); - }); - - if (config.parser && config.parser.startsWith('/')) { - if (config.parser.includes('babel-eslint')) { - config.parser = 'babel-eslint'; - } else if (config.parser.includes('@typescript-eslint/parser')) { - config.parser = '@typescript-eslint/parser'; - } - // NOTE: more parsers - } +// const contexts = await resolveItems(items, opts); - // define only when we are passed with "raw" (not processed) config - if (config.parser && !config.parser.startsWith('/')) { - linterInstance.defineParser(config.parser, require(config.parser)); - } +// await cacache.put(globalCache, argsHash, argsJson, { +// metadata: { contexts }, +// }); - return linterInstance; -} +// return contexts; +// } -async function* lintFiles(patterns, options) { - const opts = { dirs: [], cwd: process.cwd(), ...options }; - opts.exclude = opts.exclude || DEFAULT_IGNORE; - opts.extensions = opts.extensions || DEFAULT_EXTENSIONS; - opts.cacheLocation = - typeof opts.cacheLocation === 'string' - ? opts.cacheLocation - : path.join(opts.cwd, '.cache', 'hela-eslint-cache'); +// async function lintItems(items, options) { +// const opts = { ...constants.DEFAULT_OPTIONS, ...options, forceLoad: true }; +// const contexts = await loadContexts(items, opts); - const iterable = await globCache(patterns, opts); - - const engine = new CLIEngine(); - let linter = opts.linter || new Linter(); - let eslintConfig = await tryLoadLintConfig(); +// // console.log(contexts); +// return lintFiles(contexts, opts); +// } - linter = injectIntoLinter(eslintConfig, linter); +async function lintFiles(items, options) { + const opts = { ...constants.DEFAULT_OPTIONS, ...options, forceLoad: true }; - // TODO use `cacache` for caching `options` and - // based on that force `ctx.changed` if it is `false` + const contexts = await resolveItems(items, opts); - for await (const ctx of iterable) { - const meta = ctx.cacheFile && ctx.cacheFile.metadata; + const results = []; - if (engine.isPathIgnored(ctx.file.path)) { - // eslint-disable-next-line no-continue - continue; - } + await Promise.all( + contexts.map(async (ctx) => { + const meta = ctx.cacheFile && ctx.cacheFile.metadata; - if (ctx.changed) { - const dirname = path.dirname(ctx.file.path); - if (opts.dirs.includes(dirname)) { - eslintConfig = await resolveConfig(ctx.file.path, 0, { - ...opts, - dirname, - }); + if (ctx.changed === false && ctx.notFound === false && meta) { + // console.log(ctx.file.path); + results.push(meta.report); + return; } - const contents = ctx.file.contents.toString(); - const { source, messages } = lint({ + const hrstart = process.hrtime(); + + const { source, messages } = utils.lint({ ...opts, - linter, filename: ctx.file.path, - contents, - config: eslintConfig || (meta && meta.eslintConfig), + contents: ctx.file.contents.toString(), }); - const res = createReportOrResult('messages', messages, { + const res = utils.createReportOrResult('messages', messages, { filePath: ctx.file.path, + source, }); - const diff = JSON.stringify(res) !== JSON.stringify(meta && meta.report); + const hrend = process.hrtime(hrstart); - // NOTE: `source` property seems deprecated but formatters need it so.. - yield { - ...ctx, - result: { ...res, source }, - eslintConfig: (meta && meta.eslintConfig) || eslintConfig, - }; + if (opts.verbose) { + console.log('#', ctx.file.path); + console.log('# Size:', ctx.file.size); + console.info('# Execution time:', hrend[0], hrend[1] / 1000000); - if (diff) { - // todo update cache with cacache.put - await ctx.cacache.put(ctx.cacheLocation, ctx.file.path, source, { - metadata: { report: { ...res, source }, eslintConfig }, - }); - } + const used = process.memoryUsage(); - // if (opts.report) { - // formatCodeframe([res]); - // } - } + Object.keys(used).forEach((key) => { + console.log( + `# ${key}:`, + Math.round((used[key] / 1024 / 1024) * 100) / 100, + ); + }); - if (ctx.changed === false && ctx.notFound === false) { - yield { - ...ctx, - result: meta.report, - eslintConfig: meta.eslintConfig || eslintConfig, - }; - // if (opts.report) { - // formatCodeframe([meta.report]); - // } - } - } -} + console.log('########'); + } -lintFiles.promise = async function lintFilesPromise(patterns, options) { - const opts = { ...options }; - const results = []; - const iterable = await lintFiles(patterns, opts); + // const cacheReport = (meta && meta.report) || {}; + // removing `source` from the meta cached, + // because the `res` report doesn't have it either - for await (const { result } of iterable) { - results.push(result); - } + // const { source: src, ...rep } = cacheReport; + // const { source: src, ...rep } = (meta && meta.report) || {}; + const rep = (meta && meta.report) || {}; + const reportChanged = JSON.stringify(res) !== JSON.stringify(rep); - return createReportOrResult('results', results); -}; + // TODO optionally! + if (res.errorCount > 0 || (res.warningCount > 0 && opts.warnings)) { + console.error(utils.cleanFrame([res])); + } -function lint(options) { - const opts = { ...options }; - const cfg = { ...opts.config, filename: opts.filename }; - const linter = opts.linter || new Linter(); - const filter = (x) => - opts.warnings ? true : !opts.warnings && x.severity === 2; + // const { config, ...setts } = opts; + // console.log(setts, reportChanged, ctx.file.path); + results.push(rep); - if (!opts.contents && !opts.text) { - opts.contents = fs.readFileSync(cfg.filename, 'utf8'); - } - if (opts.text) { - cfg.filename = opts.filename || ''; - } - if (opts.fix) { - const { output, messages } = linter.verifyAndFix(opts.contents, cfg); - if (!opts.text) { - fs.writeFileSync(cfg.filename, output); - } + if (reportChanged) { + await cacache.put(ctx.cacheLocation, ctx.file.path, source, { + metadata: { report: res }, + }); + } + }), + ); - return { source: output, messages: messages.filter(filter) }; - } + const report = utils.createReportOrResult('results', results.filter(Boolean)); + report.contexts = contexts; - const messages = linter.verify(opts.contents, cfg); - return { - source: opts.contents, - messages: messages.filter(filter), - }; + return report; } -async function lintText(contents, options) { - const opts = { ...options }; - let linter = opts.linter || new Linter(); - - const eslintConfig = opts.config || (await tryLoadLintConfig()); - - linter = injectIntoLinter(eslintConfig, linter); - const { source, messages } = lint({ - ...opts, - config: eslintConfig, - linter, - contents, - text: true, - }); +// filepath, glob patterns or File +async function resolveItems(items, options) { + const opts = { ...constants.DEFAULT_OPTIONS, ...options }; + + const globs = []; + const contexts = []; + const toIntegrity = await toIntegrityPromise; + + await Promise.all( + [] + .concat(items) + .filter(Boolean) + .map(async (x) => { + const item = await x; + + if (isGlob(item)) { + globs.push(item); + return; + } + if (utils.isContext(item)) { + contexts.push(item); + return; + } + + const file = await utils.toFile(item, { toIntegrity }); + const $ctx = await utils.hasInCache(file, opts); + + const ctx = { + file, + ...$ctx, + cacache, + cacheLocation: opts.cacheLocation, + }; + + if (opts.forceLoad === true || ctx.changed) { + contexts.push(ctx); + } + }), + ); - const result = createReportOrResult('messages', messages, { - filePath: opts.filename || '', - source, - }); - const report = createReportOrResult('results', [result]); + const results = globs.length > 0 ? await resolvePatterns(globs, opts) : []; - return { ...report, source }; + return results.concat(contexts).map(opts.mapper); } -function createReportOrResult(type, results, extra) { - const ret = { - ...extra, - errorCount: 0, - warningCount: 0, - fixableErrorCount: 0, - fixableWarningCount: 0, - }; - - ret[type] = []; +async function resolvePatterns(patterns, options) { + const opts = { ...constants.DEFAULT_OPTIONS, ...options }; - if (type === 'messages') { - ret.errorCount = calculateCount('error', results); - ret.warningCount = calculateCount('warning', results); - } - - return results.reduce((acc, res) => { - ret[type].push(res); + const iterable = await globCache(patterns, opts); + const results = []; - if (type === 'results') { - acc.errorCount += res.errorCount || 0; - acc.warningCount += res.warningCount || 0; - acc.fixableErrorCount += res.fixableErrorCount || 0; - acc.fixableWarningCount += res.fixableWarningCount || 0; + for await (const ctx of iterable) { + if (opts.forceLoad === true) { + results.push(ctx); + continue; + } + if (ctx.changed) { + results.push(ctx); } + } - return acc; - }, ret); + return results; } -async function tryLoadLintConfig() { - const rootDir = process.cwd(); - let cfg = null; +async function lintConfigItems(configArrayItems, options) { + const opts = { ...constants.DEFAULT_OPTIONS, ...options }; - try { - cfg = await require(path.join(rootDir, OUR_CONFIG_FILENAME)); - } catch (err) { - return null; - } + const itemsGroups = {}; - return cfg; -} + const configItems = ( + await utils.pFlatten( + configArrayItems, + utils.createFunctionConfigContext(), + opts, + ) + ).reduce((acc, item, idx) => { + const cfgItem = utils.nsToItem(item); -function calculateCount(type, items) { - return [] - .concat(items) - .filter(Boolean) - .filter((x) => (type === 'error' ? x.severity === 2 : x.severity === 1)) - .reduce((acc) => acc + 1, 0); -} + if (typeof cfgItem === 'string') { + throw new TypeError( + 'config item cannot be string other than starting with "eslint:"', + ); + } + cfgItem.name = cfgItem.name || `@position#${idx}`; -module.exports = { - injectIntoLinter, - tryLoadLintConfig, - resolveConfigSync, - resolveConfig, - formatCodeframe, - calculateCount, - createReportOrResult, - lintFiles, - lintText, - lint, - - DEFAULT_IGNORE, - DEFAULT_INPUT: DEFAULT_INPUTS, - DEFAULT_INPUTS, - DEFAULT_EXTENSIONS, - OUR_CONFIG_FILENAME, -}; + return acc.concat(cfgItem); + }, []); -// (async () => { -// // const patterns = 'packages/eslint/src/**/*.js'; -// // const report = await lintFiles(patterns, { fix: true }); -// const report = await lintText('var foo = 123', { fix: true }); + configItems.forEach((item) => { + itemsGroups[item.name] = itemsGroups[item.name] || []; + itemsGroups[item.name].push(item); + }); +} -// formatCodeframe(report.results); -// console.log(report.source); // fixed source code text -// })(); +// TODO +// async function lintText(contents, options) { +// const opts = { ...constants.DEFAULT_OPTIONS, ...options }; +// const source = contents; + +// return { +// source, +// errorCount: 0, +// warningCount: 0, +// fixableErrorCount: 0, +// fixableWarningCount: 0, +// }; +// } + +// lintItems([ +// // 'foobar.js', +// // { path: 'foobar.js' }, +// // { path: 'foobar.js', contents: Buffer.from('var foobar = 1;')}, +// // { file: { path: 'foobar.js' } }, +// // { file: { path: 'foobar.js', contents: Buffer.from('sasa') } }, + +// // and promises resolving to one of above +// 'modules/*/src/**/*.js', +// Promise.resolve(path.join(process.cwd(), 'packages/eslint/src/api.js')), +// ]).then((report) => { +// console.log(report.contexts); + +// // utils.formatCodeframe(report.results); +// }); diff --git a/packages/eslint/src/bin.js b/packages/eslint/src/bin.js index a548183..49aabed 100755 --- a/packages/eslint/src/bin.js +++ b/packages/eslint/src/bin.js @@ -2,8 +2,10 @@ 'use strict'; +// sasa + const { hela } = require('@hela/core'); -const { wrapper } = require('./index.js'); +const { wrapper } = require('./index'); const prog = wrapper(hela('eslint', { singleMode: true }).usage('[...files]')); diff --git a/packages/eslint/src/constants.js b/packages/eslint/src/constants.js new file mode 100644 index 0000000..bc08668 --- /dev/null +++ b/packages/eslint/src/constants.js @@ -0,0 +1,35 @@ +'use strict'; + +const DEFAULT_IGNORE = [ + '**/node_modules/**', + '**/bower_components/**', + 'flow-typed/**', + 'coverage/**', + '**/*fixture*/**', + '{tmp,temp}/**', + '**/*.min.js', + '**/bundle.js', + '**/vendor/**', + '**/dist/**', +]; + +const DEFAULT_EXTENSIONS = ['js', 'jsx', 'cjs', 'mjs', 'ts', 'tsx']; +const DEFAULT_FILES = [ + `**/src/**/*.{${DEFAULT_EXTENSIONS.join(',')}}`, + `**/*test*/**/*.{${DEFAULT_EXTENSIONS.join(',')}}`, +]; + +const DEFAULT_OPTIONS = { + cwd: process.cwd(), + forceLoad: false, + cacheLocation: '.cache/hela-eslint-cache', + exclude: DEFAULT_IGNORE, + mapper: (x) => x, +}; + +module.exports = { + DEFAULT_IGNORE, + DEFAULT_FILES, + DEFAULT_EXTENSIONS, + DEFAULT_OPTIONS, +}; diff --git a/packages/eslint/src/index.js b/packages/eslint/src/index.js index 3b7ae0e..82d496e 100644 --- a/packages/eslint/src/index.js +++ b/packages/eslint/src/index.js @@ -1,5 +1,4 @@ /* eslint-disable global-require */ -/* eslint-disable import/no-dynamic-require */ /* eslint-disable max-statements */ /* eslint-disable no-restricted-syntax */ @@ -8,6 +7,7 @@ const fs = require('fs'); const path = require('path'); const { hela } = require('@hela/core'); + const { DEFAULT_IGNORE, DEFAULT_INPUTS, @@ -26,88 +26,8 @@ function wrapper(prog) { .action(async (...args) => { const files = args.slice(0, -2); const argv = args[args.length - 2]; - // const opts = args[args.length - 1]; - - if (argv.init) { - const rootLintConfigFile = path.join( - process.cwd(), - OUR_CONFIG_FILENAME, - ); - const loadConfigContents = `\nmodule.exports = require('@hela/eslint').resolveConfig(__filename);`; - - // write our config loading resolution - fs.writeFileSync(rootLintConfigFile, loadConfigContents); - - // import/require/load the resolved config - const config = await require(rootLintConfigFile); - - // re-write the `.js` config file - fs.writeFileSync( - `${rootLintConfigFile}`, - `module.exports = ${JSON.stringify(config)}`, - ); - - console.log('Done.'); - return; - } - - const include = [] - .concat(files.length > 0 ? files : argv.include || DEFAULT_INPUTS) - .reduce((acc, x) => acc.concat(x), []) - .filter(Boolean); - - let exclude = [] - .concat(argv.exclude) - .reduce((acc, x) => acc.concat(x), []) - .filter(Boolean); - exclude = exclude.length > 0 ? exclude : null; - - const report = { - errorCount: 0, - warningCount: 0, - filesCount: 0, - }; - - const iterable = await api.lintFiles(include, { ...argv, exclude }); - - for await (const { result } of iterable) { - report.filesCount += 1; - if (result.errorCount || result.warningCount) { - const resultReport = api.createReportOrResult('results', [result]); - - report.errorCount += resultReport.errorCount || 0; - report.warningCount += resultReport.warningCount || 0; - - const output = api - .formatCodeframe(resultReport.results) - .trim() - .split('\n') - .slice(0, -2) - .join('\n'); - - console.log(output); - } else { - // console.log('File:', report.filesCount, file.path); - } - } - - if (report.errorCount === 0 && report.warningCount === 0) { - console.log('No problems found.'); - return; - } - - const warnings = argv.warnings - ? `and ${report.warningCount} warning(s) ` - : ''; - - console.log(''); - console.log(`${report.errorCount} error(s) ${warnings}found.`); - // formatCodeframe(report, true); - if (report.errorCount > 0) { - // eslint-disable-next-line unicorn/no-process-exit - process.exit(1); - } + console.log('files', files); }); } diff --git a/packages/eslint/src/lint-config.js b/packages/eslint/src/lint-config.js new file mode 100644 index 0000000..6886e8e --- /dev/null +++ b/packages/eslint/src/lint-config.js @@ -0,0 +1,78 @@ +'use strict'; + +// require('v8-compile-cache'); +// const path = require('path'); +// const JestWorker = require('jest-worker').default; +const utils = require('./utils'); +const lintFilesWrapper = require('./lint-files'); + +module.exports = async function lintConfigItems(configArrayItems, options) { + const opts = { ...utils.constants.DEFAULT_OPTIONS, ...options }; + + // const report = { + // results: [], + // errorCount: 0, + // warningCount: 0, + // fixableErrorCount: 0, + // fixableWarningCount: 0, + // }; + const configItems = ( + await utils.pFlatten( + configArrayItems, + utils.createFunctionConfigContext(), + opts, + ) + ).reduce((acc, item, idx) => { + const cfgItem = utils.nsToItem(item); + + if (typeof cfgItem === 'string') { + throw new TypeError( + 'config item cannot be string other than starting with "eslint:"', + ); + } + cfgItem.name = cfgItem.name || `@position#${idx}`; + + return acc.concat(cfgItem); + }, []); + + const cfg = configItems + // .filter((x) => (x && !x.files) || utils.isEslintNamespace(x)) + .filter((x) => x && !x.files) + .reduce(utils.normalizeAndMerge, {}); + + // NOTE: for easier migration, temporary: + // - those without `files` will be able to accept old format too + // - for those with `files`, accept only the new format + + return Promise.all( + configItems + .filter((x) => x && x.files) + .map(async (item) => { + const { files, ...configItem } = item; + const conf = utils.normalizeAndMerge(cfg, configItem); + + // rep.results + // rep.errorCount += res.errorCount || 0; + // rep.warningCount += res.warningCount || 0; + // rep.fixableErrorCount += res.fixableErrorCount || 0; + // rep.fixableWarningCount += res.fixableWarningCount || 0; + // const itemReport = await lintFiles(item.files, { ...opts, mapper }); + + await lintFilesWrapper(files, { ...opts, config: conf }); + + // report.errorCount += itemReport.errorCount; + // report.warningCount += itemReport.warningCount; + // report.fixableErrorCount += itemReport.fixableErrorCount; + // report.fixableWarningCount += itemReport.fixableWarningCount; + + // if (output.length > 0) { + // console.log(output); + // } + + // reports.push(reportForConfigItem); + }), + ); + + // const report = utils.createReportOrResult('results', reports); + // return report; +}; diff --git a/packages/eslint/src/lint-files.js b/packages/eslint/src/lint-files.js new file mode 100644 index 0000000..04e2bde --- /dev/null +++ b/packages/eslint/src/lint-files.js @@ -0,0 +1,12 @@ +'use strict'; + +// require('v8-compile-cache'); +const { constants } = require('./utils'); +const { lintFiles } = require('./api'); + +module.exports = async (files, options) => { + const opts = { ...constants.DEFAULT_OPTIONS, ...options }; + const mapper = (x) => opts.mapper(x, {}) || x; + + await lintFiles(files, { ...opts, mapper }); +}; diff --git a/packages/eslint/src/utils.js b/packages/eslint/src/utils.js new file mode 100644 index 0000000..bb95400 --- /dev/null +++ b/packages/eslint/src/utils.js @@ -0,0 +1,607 @@ +/* eslint-disable no-param-reassign */ + +'use strict'; + +// require('v8-compile-cache'); +// eslint:recommended eslint/conf/eslint-recommended.js +const fs = require('fs'); +const util = require('util'); +// const path = require('path');xx +const crypto = require('crypto'); + +const mixinDeep = require('mixin-deep'); +const cacache = require('cacache'); +const findFileUp = require('find-file-up'); +const importFresh = require('import-fresh'); +const { Linter } = require('eslint'); +const codeframe = require('eslint/lib/cli-engine/formatters/codeframe'); + +const eslintPackageJson = require('eslint/package.json'); +const constants = require('./constants'); + +const readFile = util.promisify(fs.readFile); +const writeFile = util.promisify(fs.writeFile); + +async function loadESLintConifg(options) { + const opts = { cwd: process.cwd(), ...options }; + const eslintConfigPath = await findFileUp('eslint.config.js', opts.cwd); + + if (!eslintConfigPath) { + throw new Error( + '@hela/eslint: No configuration found! Use `-c` or `--config-file` flags, or add `eslint.config.js` file', + ); + } + + return { + filepath: eslintConfigPath, + config: opts.fresh + ? importFresh(eslintConfigPath) + : require(eslintConfigPath), + }; +} + +function createFunctionConfigContext(/* cwd, fresh */) { + // TODO load and resolve full final config object (all configs from config array merged) + // const cfg = resolveAndMergeConfig(config); + // const folderOfLoadedConfig = loadESLintConifg(); + + return { + name: 'hela-eslint', + version: eslintPackageJson.version, + cwd: process.cwd(), + // NOTE: removed from the RFC + // hasRule(config, ruleId) { + // const cfg = config; + // const ruleParts = ruleId.split('/'); + // const pluginName = ruleParts.length > 1 ? ruleParts[0] : 'internals'; + // const ruleName = ruleParts.length > 1 ? ruleParts[1] : 'eslint-rule-name'; + // const plugin = cfg.plugins[pluginName]; + + // // TODO Do we need to return false or throw in this case? + // // if (!plugin) { + // // throw new Error(`@hela/eslint: Plugin with "${pluginName}" not found.`); + // // } + // if (!plugin) { + // return false; + // } + // if (!plugin.rules[ruleName]) { + // return false; + // } + // return true; + // }, + }; +} + +function processLint(options) { + const opts = { ...options }; + const lintConfig = { ...opts.config, filename: opts.filename }; + const linter = opts.linter || new Linter(); + const filter = (x) => + opts.warnings ? true : !opts.warnings && x.severity === 2; + + if (!opts.contents && !opts.text) { + opts.contents = fs.readFileSync(lintConfig.filename, 'utf8'); + } + if (opts.text) { + lintConfig.filename = opts.filename || ''; + } + if (opts.fix) { + const { output, messages } = linter.verifyAndFix(opts.contents, lintConfig); + if (!opts.text) { + fs.writeFileSync(lintConfig.filename, output); + } + + return { source: output, messages: messages.filter(filter) }; + } + + const messages = linter.verify(opts.contents, lintConfig); + return { + source: opts.contents, + messages: messages.filter(filter), + }; +} + +function isFile(x) { + return Boolean(x && x.path && x.contents) || false; +} + +function isCacheFile(x) { + return Boolean(x && x.path && x.key && x.integrity && x.time) || false; +} + +function isContext(item) { + return ( + (item && + isFile(item.file) && + (isCacheFile(item.cacheFile) || item.cacheFile === null)) || + false + ); +} + +async function toFile(file, options) { + const opts = { toIntegrity, ...options }; + + let x = file || {}; + + if (typeof x === 'string') { + x = { path: x }; + } + if (x && typeof x.path === 'string') { + x = { ...x, path: x.path }; + } + + x.contents = (x && x.contents) || (await readFile(x.path)); + x.size = x.contents.length; + x.integrity = x.integrity || (await opts.toIntegrity(x.contents)); + + return { ...x }; + // throw new Error('@hela/eslint: unknown type, pass filepath or File object'); +} + +function hasha(value, options) { + const opts = { algorithm: 'sha512', digest: 'base64', ...options }; + + return crypto + .createHash(opts.algorithm) + .update(value) + .digest(opts.digest); +} + +function defineRulesFrom(pluginName, rules, linter) { + Object.keys(rules || {}).forEach((ruleName) => { + linter.defineRule(`${pluginName}/${ruleName}`, rules[ruleName]); + }); +} + +function isObject(val) { + return Boolean(val && typeof val === 'object' && !Array.isArray(val)); +} + +// function load(val, loadType) { +// let [, type = loadType, value] = +// /(plugin|config|eslint|\/|@)?:?(.+)/i.exec(val) || []; + +// value = type === '/' || type === '@' ? type + value : value; +// // type = value.startsWith('@') ? '@' : type; + +// const parts = value.split('/'); + +// if (type === '@') { +// value = value.split('/').slice(1); +// } +// } + +// function loadConfig(val) { +// // e.g. `@tunnckocore/foo-config/mdx` or `promise/recommended` +// const parts = val.split('/'); + +// const name = parts.length === 2 ? parts[0] : `${parts[0]}/${parts[1]}`; +// const key = parts.length === 2 ? parts[1] : parts[2]; +// } + +function loadPresetString(name) { + if (name.includes('plugin:')) { + const { plugin, key } = loadPlugin(name.slice(7)); + return { preset: plugin.configs[key], name, key }; + } + const mod = name.startsWith('/') + ? require(name) + : eslintRequire('config', name); + + return { preset: mod, name, key: name }; +} + +function loadPlugin(val, plugins) { + const parts = val.split('/'); + + const name = parts.length === 2 ? parts[0] : `${parts[0]}/${parts[1]}`; + const key = parts.length === 2 ? parts[1] : parts[2]; + + const plugin = plugins ? plugins[name] : eslintRequire('plugin', name); + + return { + plugin, + name, + key, + parts, + }; +} + +function loadParser(parser, plugins) { + let parserMod = parser; + let parserName = ''; + + if (parser && typeof parser === 'string') { + // supports: + // - parser: '@my-scope/some-plugin/foo-parser` + // - parser: 'some-plugin/foo-parser` + if (parser.includes('/')) { + const { plugin, key } = loadPlugin(parser, plugins); + + if (!isObject(plugin.parsers)) { + throw new TypeError( + 'expect plugin "parsers" key to be an object like { "babel-eslint": require("babel-eslint") }', + ); + } + + parserMod = plugin.parsers[key]; + parserName = key; + } else { + // backward compat + // parser: 'babel-eslint` + parserMod = require(parser); + parserName = parser; + } + } + + if (!isObject(parserMod)) { + throw new TypeError('expect parser to be an object or a string'); + } + + return { + parser: parserMod, + name: parserName || parserMod.name || 'unknown-parser', + }; +} + +function eslintRequire(type, name, itIsParser) { + let mod = null; + if (name.startsWith('@')) { + mod = require(name.includes('/') ? name : `${name}/eslint-${type}`); + } else if (itIsParser) { + mod = require(name); + } else { + mod = require(`eslint-${type}-${name}`); + } + + return mod; +} + +function injectIntoLinter(config, linter, linterOptions) { + const linterInstance = linter || new Linter(linterOptions); + if (!config) { + return linterInstance; + } + + Object.keys(config.plugins || {}).forEach((name) => { + defineRulesFrom(name, config.plugins[name].rules, linterInstance); + }); + + if (config.languageOptions && config.languageOptions.parser) { + const { parser, name: parserName } = loadParser( + config.languageOptions.parser, + config.plugins, + ); + + linterInstance.defineParser(parserName, parser); + } + + // NOTE: delete `config.parser`, `config.plugins` and `config.extends` intentionally, + // because linter.verify/verifyAndFix may not understand the new definitions + const { + plugins: _, + parser: __, + extends: ___, + overrides: $$, + ...cleanedConfig + } = config; + // delete config.plugins; + // delete config.parser; + + // if (config.parser && config.parser.startsWith('/')) { + // if (config.parser.includes('babel-eslint')) { + // config.parser = 'babel-eslint'; + // } else if (config.parser.includes('@typescript-eslint/parser')) { + // config.parser = '@typescript-eslint/parser'; + // } + // // NOTE: more parsers + // } + + // define only when we are passed with "raw" (not processed) config + // if (config.parser && !config.parser.startsWith('/')) { + // linterInstance.defineParser(config.parser, require(config.parser)); + // } + + return { linter: linterInstance, config: cleanedConfig }; +} + +function toIntegrity(value) { + const hashId = hasha(value); + + return `sha512-${hashId}`; +} + +async function hasInCache(file, options) { + const opts = { ...options }; + const info = await cacache.get.info(opts.cacheLocation, file.path); + const hash = await cacache.get.hasContent(opts.cacheLocation, file.integrity); + + return { + changed: hash === false, + notFound: info === null, + cacheFile: info, + }; +} + +function createReportOrResult(type, results, extra) { + const ret = { + ...extra, + errorCount: 0, + warningCount: 0, + fixableErrorCount: 0, + fixableWarningCount: 0, + }; + + ret[type] = []; + + if (type === 'messages') { + ret.errorCount = calculateCount('error', results); + ret.warningCount = calculateCount('warning', results); + } + + return results.reduce((acc, res) => { + ret[type].push(res); + + if (type === 'results') { + acc.errorCount += res.errorCount || 0; + acc.warningCount += res.warningCount || 0; + acc.fixableErrorCount += res.fixableErrorCount || 0; + acc.fixableWarningCount += res.fixableWarningCount || 0; + } + + return acc; + }, ret); +} + +function calculateCount(type, items) { + return [] + .concat(items) + .filter(Boolean) + .filter((x) => (type === 'error' ? x.severity === 2 : x.severity === 1)) + .reduce((acc) => acc + 1, 0); +} + +function lint(options) { + const opts = { inject: true, ...options }; + const cfg = { ...opts.config, filename: opts.filename }; + const filter = (x) => + opts.warnings ? true : !opts.warnings && x.severity === 2; + + const linterOptions = + cfg.linterOptions || + (cfg.languageOptions && cfg.languageOptions.linterOptions) || + opts.linterOptions; + + const $linter = opts.linter || new Linter(linterOptions); + const { config, linter } = opts.inject + ? injectIntoLinter(cfg, $linter, linterOptions) + : { config: cfg, linter: $linter }; + + if (!opts.contents && !opts.text) { + opts.contents = fs.readFileSync(config.filename, 'utf8'); + } + if (opts.text) { + config.filename = opts.filename || ''; + } + if (opts.fix) { + const { output, messages } = linter.verifyAndFix(opts.contents, config); + if (!opts.text) { + fs.writeFileSync(config.filename, output); + } + + return { source: output, messages: messages.filter(filter) }; + } + + const messages = linter.verify(opts.contents, config); + return { + source: opts.contents, + messages: messages.filter(filter), + }; +} + +function formatCodeframe(rep, log = true) { + const res = codeframe(rep.results || rep); + return log ? console.log(res) : res; +} + +function cleanFrame(rep) { + return formatCodeframe(rep.results || rep, false) + .trim() + .split('\n') + .slice(0, -2) + .join('\n'); +} + +async function pFlatten(arr, ...args) { + const items = await ( + await Promise.all([].concat(arr).filter(Boolean)) + ).reduce(async (acc, e) => { + const accum = await acc; + + let item = await e; + + if (typeof item === 'function') { + item = await item(...args); + } + if (!item) { + return accum; + } + if (Array.isArray(item)) { + // if the element is an array, fall flatten on it again and then take the returned value and concat it. + return accum.concat(await pFlatten(item)); + } + // otherwise just concat the value. + return accum.concat(item); + }, Promise.resolve([])); // initial value for the accumulator is [] + + return (await Promise.all(items)).filter(Boolean); +} + +function normalizePlugins(plugins) { + if (!plugins) { + return {}; + } + if (typeof plugins !== 'object') { + throw new TypeError( + 'plugins property is expected to be an object like { react: require("eslint-plugin-react") } or an array of plugin name strings like ["react"]', + ); + } + if (Array.isArray(plugins)) { + return plugins.reduce((acc, pluginName) => { + if (typeof pluginName !== 'string') { + throw new TypeError( + 'when plugins is an array it can contain only strings', + ); + } + acc[pluginName] = eslintRequire('plugin', pluginName); + return acc; + }, {}); + } + + return plugins; +} + +function nsToItem(item) { + if (isEslintNamespace(item)) { + item = { name: item }; + item.rules = require(`eslint/conf/${item.name.replace(':', '-')}`).rules; + } + return item; +} + +// TODO for easier migration, temporary: +// - those without `files` will be able to accept old format too +// - for those with `files`, accept only the new format +function normalizeAndMerge(target, item, idx) { + item.plugins = normalizePlugins(item.plugins); + + // const res = { plugins: {} }; + const { plugins: _, ...x } = item; + // const { plugins: _, ...targ } = target; + const res = mixinDeep({ ...target }, x); + // const accum = mixinDeep({ ...target }, item); + + // const plgs = (whenFiles === true ? target.plugins : res.plugins) || {}; + + Object.keys(item.plugins).forEach((pluginName) => { + if (res.plugins && res.plugins[pluginName]) { + throw new Error( + `config item "${item.name || + idx}" trying to override "${pluginName}" plugin namespace`, + ); + } + + res.plugins = res.plugins || {}; + res.plugins[pluginName] = item.plugins[pluginName]; + }); + + // if (!accum.languageOptions) { + // accum.languageOptions = { + // parserOptions: { ...accum.parserOptions }, + // globals: { ...accum.globals }, + // parser: accum.parser, + // }; + // accum.languageOptions.sourceType = + // accum.languageOptions.parserOptions.sourceType; + // } + // const lang = { ...accum.languageOptions }; + + // if (lang.sourceType === 'commonjs') { + // lang.sourceType = 'script'; + // } + // if ( + // lang.sourceType === 'commonjs' || + // (lang.globals && + // (lang.globals.node === true || lang.globals.commonjs === true)) + // ) { + // lang.globals = { + // ...lang.globals, + // require: true, + // exports: true, + // module: true, + // }; + // lang.parserOptions = mixinDeep( + // { ...lang.parserOptions }, + // { ecmaFeatures: { globalReturn: true } }, + // ); + // } + + // accum.parserOptions = mixinDeep( + // accum.parserOptions, + // // { + // // ecmaVersion: lang.ecmaVersion, + // // sourceType: lang.sourceType, + // // }, + // lang.parserOptions, + // ); + // accum.globals = mixinDeep(accum.globals, lang.globals); + + // accum.languageOptions = lang || accum.parserOptions; + + return res; +} + +function isEslintNamespace(x) { + return typeof x === 'string' && x.startsWith('eslint:'); +} + +function createItemsFromExtends(presets) { + const $extends = [] + .concat(presets) + .filter(Boolean) + .filter((x) => !isEslintNamespace(x)) + .filter(Boolean); + + return $extends.reduce((acc, name) => { + let mod = null; + + const { preset } = loadPresetString(name); + mod = preset; + + if (mod.extends) { + return acc.concat(createItemsFromExtends(mod.extends)); + } + + return acc.concat({ + ...mod, + name, + plugins: normalizePlugins(mod.plugins), + }); + }, []); +} + +module.exports = { + isFile, + isCacheFile, + isContext, + isEslintNamespace, + nsToItem, + toFile, + injectIntoLinter, + hasha, + + pFlatten, + normalizeAndMerge, + normalizePlugins, + loadParser, + loadPlugin, + loadPresetString, + createItemsFromExtends, + defineRulesFrom, + eslintRequire, + isObject, + + hasInCache, + toIntegrity, + createReportOrResult, + calculateCount, + formatCodeframe, + cleanFrame, + lint, + readFile, + writeFile, + constants, + processLint, + loadESLintConifg, + createFunctionConfigContext, +}; diff --git a/yarn.lock b/yarn.lock index 08d7d47..fb10402 100644 --- a/yarn.lock +++ b/yarn.lock @@ -156,7 +156,7 @@ core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2": +"@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.7": version "7.8.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.7.tgz#8fefce9802db54881ba59f90bb28719b4996324d" integrity sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg== @@ -1698,6 +1698,11 @@ resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== +"@zeit/ncc@^0.21.1": + version "0.21.1" + resolved "https://registry.yarnpkg.com/@zeit/ncc/-/ncc-0.21.1.tgz#44fd4359c54ba34a018a5ccf7a315489db20a733" + integrity sha512-M9WzgquSOt2nsjRkYM9LRylBLmmlwNCwYbm3Up3PDEshfvdmIfqpFNSK8EJvR18NwZjGHE5z2avlDtYQx2JQnw== + "@zkochan/cmd-shim@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@zkochan/cmd-shim/-/cmd-shim-3.1.0.tgz#2ab8ed81f5bb5452a85f25758eb9b8681982fd2e" @@ -2520,6 +2525,17 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" +clone-deep@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.2.4.tgz#4e73dd09e9fb971cc38670c5dced9c1896481cc6" + integrity sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY= + dependencies: + for-own "^0.1.3" + is-plain-object "^2.0.1" + kind-of "^3.0.2" + lazy-cache "^1.0.3" + shallow-clone "^0.1.2" + clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" @@ -2988,6 +3004,15 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= +defaults-deep@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/defaults-deep/-/defaults-deep-0.2.4.tgz#a479cfeafce025810fb93aa8d2dde0ee2d677cc6" + integrity sha512-V6BtqzcMvn0EPOy7f+SfMhfmTawq+7UQdt9yZH0EBK89+IHo5f+Hse/qzTorAXOBrQpxpwb6cB/8OgtaMrT+Fg== + dependencies: + for-own "^0.1.3" + is-extendable "^0.1.1" + lazy-cache "^0.2.3" + defaults@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" @@ -3903,13 +3928,6 @@ find-file-up@^2.0.1: dependencies: resolve-dir "^1.0.1" -find-pkg@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/find-pkg/-/find-pkg-2.0.0.tgz#3a7c35c704e11a6e5722c56e45bd7e587507735e" - integrity sha512-WgZ+nKbELDa6N3i/9nrHeNznm+lY3z4YfhDDWgW+5P0pdmMj26bxaxU11ookgY3NyP9GC7HvZ9etp0jRFqGEeQ== - dependencies: - find-file-up "^2.0.1" - find-root@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4" @@ -3974,11 +3992,23 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" -for-in@^1.0.2: +for-in@^0.1.3: + version "0.1.8" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" + integrity sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE= + +for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= +for-own@^0.1.3: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= + dependencies: + for-in "^1.0.1" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -4544,7 +4574,7 @@ import-fresh@^2.0.0: caller-path "^2.0.0" resolve-from "^3.0.0" -import-fresh@^3.0.0, import-fresh@^3.1.0: +import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== @@ -4726,7 +4756,7 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -is-buffer@^1.1.5: +is-buffer@^1.0.2, is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== @@ -4915,7 +4945,7 @@ is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= -is-plain-object@^2.0.3, is-plain-object@^2.0.4: +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== @@ -5566,6 +5596,13 @@ jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.2.3: array-includes "^3.0.3" object.assign "^4.1.0" +kind-of@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" + integrity sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU= + dependencies: + is-buffer "^1.0.2" + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -5595,6 +5632,16 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +lazy-cache@^0.2.3: + version "0.2.7" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" + integrity sha1-f+3fLctu23fRHvHRF6tf/fCrG2U= + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= + lerna@^3.20.2: version "3.20.2" resolved "https://registry.yarnpkg.com/lerna/-/lerna-3.20.2.tgz#abf84e73055fe84ee21b46e64baf37b496c24864" @@ -6074,6 +6121,15 @@ meow@^5.0.0: trim-newlines "^2.0.0" yargs-parser "^10.0.0" +merge-deep@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/merge-deep/-/merge-deep-3.0.2.tgz#f39fa100a4f1bd34ff29f7d2bf4508fbb8d83ad2" + integrity sha512-T7qC8kg4Zoti1cFd8Cr0M+qaZfOwjlPDEdZIIPPB2JZctjaPM4fX+i7HOId69tAti2fvO6X5ldfYUONDODsrkA== + dependencies: + arr-union "^3.1.0" + clone-deep "^0.2.4" + kind-of "^3.0.2" + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" @@ -6235,6 +6291,19 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" +mixin-deep@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-2.0.1.tgz#9a6946bef4a368401b784970ae3caaaa6bab02fa" + integrity sha512-imbHQNRglyaplMmjBLL3V5R6Bfq5oM+ivds3SKgc6oRtzErEnBUUc5No11Z2pilkUvl42gJvi285xTNswcKCMA== + +mixin-object@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" + integrity sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4= + dependencies: + for-in "^0.1.3" + is-extendable "^0.1.1" + mkdirp-promise@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" @@ -7804,6 +7873,16 @@ set-value@^2.0.0, set-value@^2.0.1: is-plain-object "^2.0.3" split-string "^3.0.1" +shallow-clone@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-0.1.2.tgz#5909e874ba77106d73ac414cfec1ffca87d97060" + integrity sha1-WQnodLp3EG1zrEFM/sH/yofZcGA= + dependencies: + is-extendable "^0.1.1" + kind-of "^2.0.1" + lazy-cache "^0.2.3" + mixin-object "^2.0.1" + shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" @@ -8813,7 +8892,7 @@ uuid@^3.0.1, uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -v8-compile-cache@^2.0.3: +v8-compile-cache@^2.0.3, v8-compile-cache@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== @@ -9111,11 +9190,11 @@ yallist@^4.0.0: integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yaml@^1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.7.2.tgz#f26aabf738590ab61efaca502358e48dc9f348b2" - integrity sha512-qXROVp90sb83XtAoqE8bP9RwAkTTZbugRUTm5YeFCBfNRPEp2YzTeqWiz7m5OORHzEvrA/qcGS8hp/E+MMROYw== + version "1.8.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.8.0.tgz#169fbcfa2081302dc9441d02b0b6fe667e4f74c9" + integrity sha512-6qI/tTx7OVtA4qNqD0OyutbM6Z9EKu4rxWm/2Y3FDEBQ4/2X2XAnyuRXMzAE2+1BPyqzksJZtrIwblOHg0IEzA== dependencies: - "@babel/runtime" "^7.6.3" + "@babel/runtime" "^7.8.7" yargs-parser@^10.0.0: version "10.1.0"