fix: Use ESLint Linter API to more directly control parser and rules module loading (#4078)
* fix: Use ESLint Linter API to more directly control parser and rules module loading * chore: Add smoke test to verify conflicting eslint version do not break addons-linter
This commit is contained in:
Родитель
8690b32a57
Коммит
eeccf2547a
|
@ -123,6 +123,7 @@ jobs:
|
||||||
# This should run after we persist the workspace because this command
|
# This should run after we persist the workspace because this command
|
||||||
# invokes `npm install`.
|
# invokes `npm install`.
|
||||||
- run: npm run webext-test-functional
|
- run: npm run webext-test-functional
|
||||||
|
- run: npm run smoke-test-eslint-version-conflicts
|
||||||
- store_artifacts:
|
- store_artifacts:
|
||||||
path: coverage
|
path: coverage
|
||||||
|
|
||||||
|
@ -140,6 +141,7 @@ jobs:
|
||||||
# production-like environment
|
# production-like environment
|
||||||
- run: npm run test-integration:production
|
- run: npm run test-integration:production
|
||||||
- run: npm run webext-test-functional
|
- run: npm run webext-test-functional
|
||||||
|
- run: npm run smoke-test-eslint-version-conflicts
|
||||||
|
|
||||||
test-alternate:
|
test-alternate:
|
||||||
<<: *defaults-alternate
|
<<: *defaults-alternate
|
||||||
|
@ -155,6 +157,7 @@ jobs:
|
||||||
# production-like environment
|
# production-like environment
|
||||||
- run: npm run test-integration:production
|
- run: npm run test-integration:production
|
||||||
- run: npm run webext-test-functional
|
- run: npm run webext-test-functional
|
||||||
|
- run: npm run smoke-test-eslint-version-conflicts
|
||||||
|
|
||||||
release-tag:
|
release-tag:
|
||||||
<<: *defaults
|
<<: *defaults
|
||||||
|
|
|
@ -2,6 +2,7 @@ scripts/download-import-tag
|
||||||
scripts/list-firefox-tags
|
scripts/list-firefox-tags
|
||||||
scripts/run-l10n-extraction
|
scripts/run-l10n-extraction
|
||||||
scripts/webext-test-functional
|
scripts/webext-test-functional
|
||||||
|
scripts/smoke-test-eslint-version-conflicts
|
||||||
dist
|
dist
|
||||||
coverage
|
coverage
|
||||||
tests/fixtures/**
|
tests/fixtures/**
|
||||||
|
|
|
@ -8,9 +8,3 @@
|
||||||
!bin/addons-linter
|
!bin/addons-linter
|
||||||
!dist/addons-linter.*
|
!dist/addons-linter.*
|
||||||
!dist/locale/**
|
!dist/locale/**
|
||||||
!dist/rules/**
|
|
||||||
|
|
||||||
# Overridden .eslintignore file to prevent addons-linter
|
|
||||||
# from loading an arbirary .eslintignore from the cwd
|
|
||||||
# (See https://github.com/mozilla/addons-linter/issues/3390)
|
|
||||||
!addons-linter.eslintignore
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ scripts/download-import-tag
|
||||||
scripts/list-firefox-tags
|
scripts/list-firefox-tags
|
||||||
scripts/run-l10n-extraction
|
scripts/run-l10n-extraction
|
||||||
scripts/webext-test-functional
|
scripts/webext-test-functional
|
||||||
|
scripts/smoke-test-eslint-version-conflicts
|
||||||
package-lock.json
|
package-lock.json
|
||||||
LICENSE
|
LICENSE
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
# Overridden .eslintignore file to prevent addons-linter
|
|
||||||
# from loading an arbirary .eslintignore from the cwd
|
|
||||||
# (See https://github.com/mozilla/addons-linter/issues/3390)
|
|
|
@ -3,7 +3,6 @@
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
const absoluteAppRoot = path.resolve(path.join(__dirname, '..'));
|
const absoluteAppRoot = path.resolve(path.join(__dirname, '..'));
|
||||||
global.appRoot = path.relative(process.cwd(), absoluteAppRoot);
|
|
||||||
global.localesRoot = path.join(absoluteAppRoot, 'dist', 'locale');
|
global.localesRoot = path.join(absoluteAppRoot, 'dist', 'locale');
|
||||||
global.nodeRequire = require;
|
global.nodeRequire = require;
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
"build-rules": "scripts/build-rules && cp node_modules/github-markdown-css/github-markdown.css docs/github-markdown.css",
|
"build-rules": "scripts/build-rules && cp node_modules/github-markdown-css/github-markdown.css docs/github-markdown.css",
|
||||||
"gen-contributing-toc": "doctoc CONTRIBUTING.md",
|
"gen-contributing-toc": "doctoc CONTRIBUTING.md",
|
||||||
"webext-test-functional": "scripts/webext-test-functional",
|
"webext-test-functional": "scripts/webext-test-functional",
|
||||||
|
"smoke-test-eslint-version-conflicts": "scripts/smoke-test-eslint-version-conflicts",
|
||||||
"update-hashes": "scripts/dispensary > src/dispensary/hashes.txt"
|
"update-hashes": "scripts/dispensary > src/dispensary/hashes.txt"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
LINTER_PATH=$(pwd)
|
||||||
|
TARGET_TEST_PATH="$LINTER_PATH/tmp/test-project"
|
||||||
|
FIXTURE_TEST_PATH="$LINTER_PATH/tests/fixtures/eslint-versions-conflicts-package"
|
||||||
|
LINTER_VERSION=$(node -p 'require("./package.json").version')
|
||||||
|
|
||||||
|
# Fail if the target path already exists.
|
||||||
|
[[ -f "$TARGET_TEST_PATH" ]] && (
|
||||||
|
echo "ERROR: Temporary test dir '$TARGET_TEST_PATH' already exists."
|
||||||
|
exit 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo -e "INFO: Initializing test environment\n"
|
||||||
|
|
||||||
|
# Build addons-linter
|
||||||
|
npm ci
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Create target test path directory and cleanup on exit.
|
||||||
|
cp -rf "$FIXTURE_TEST_PATH" "$TARGET_TEST_PATH"
|
||||||
|
trap 'rm -rf "$TARGET_TEST_PATH"' EXIT
|
||||||
|
|
||||||
|
pushd "$TARGET_TEST_PATH"
|
||||||
|
npm pack "$LINTER_PATH"
|
||||||
|
npm install
|
||||||
|
npm install --save-dev "./addons-linter-$LINTER_VERSION.tgz"
|
||||||
|
|
||||||
|
echo -e "INFO: Run addons-linter on the test fixture project\n"
|
||||||
|
|
||||||
|
npx addons-linter ./src || (
|
||||||
|
echo "ERROR: addons-linter validation failed".
|
||||||
|
exit 2
|
||||||
|
)
|
|
@ -0,0 +1,25 @@
|
||||||
|
import content_scripts_file_absent from './content-scripts-file-absent';
|
||||||
|
import deprecated_entities from './deprecated-entities';
|
||||||
|
import event_listener_fourth from './event-listener-fourth';
|
||||||
|
import global_require_arg from './global-require-arg';
|
||||||
|
import opendialog_nonlit_uri from './opendialog-nonlit-uri';
|
||||||
|
import opendialog_remote_uri from './opendialog-remote-uri';
|
||||||
|
import webextension_api from './webextension-api';
|
||||||
|
import webextension_api_compat from './webextension-api-compat';
|
||||||
|
import webextension_api_compat_android from './webextension-api-compat-android';
|
||||||
|
import webextension_deprecated_api from './webextension-deprecated-api';
|
||||||
|
import webextension_unsupported_api from './webextension-unsupported-api';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
'content-scripts-file-absent': content_scripts_file_absent,
|
||||||
|
'deprecated-entities': deprecated_entities,
|
||||||
|
'event-listener-fourth': event_listener_fourth,
|
||||||
|
'global-require-arg': global_require_arg,
|
||||||
|
'opendialog-nonlit-uri': opendialog_nonlit_uri,
|
||||||
|
'opendialog-remote-uri': opendialog_remote_uri,
|
||||||
|
'webextension-api': webextension_api,
|
||||||
|
'webextension-api-compat': webextension_api_compat,
|
||||||
|
'webextension-api-compat-android': webextension_api_compat_android,
|
||||||
|
'webextension-deprecated-api': webextension_deprecated_api,
|
||||||
|
'webextension-unsupported-api': webextension_unsupported_api,
|
||||||
|
};
|
|
@ -1,7 +1,5 @@
|
||||||
/* global appRoot */
|
|
||||||
import path from 'path';
|
|
||||||
|
|
||||||
import ESLint from 'eslint';
|
import ESLint from 'eslint';
|
||||||
|
import noUnsanitized from 'eslint-plugin-no-unsanitized';
|
||||||
import { oneLine } from 'common-tags';
|
import { oneLine } from 'common-tags';
|
||||||
import * as espree from 'espree';
|
import * as espree from 'espree';
|
||||||
import * as vk from 'eslint-visitor-keys';
|
import * as vk from 'eslint-visitor-keys';
|
||||||
|
@ -11,7 +9,7 @@ import { ESLINT_RULE_MAPPING, ESLINT_TYPES } from 'const';
|
||||||
import * as messages from 'messages';
|
import * as messages from 'messages';
|
||||||
import { ensureFilenameExists } from 'utils';
|
import { ensureFilenameExists } from 'utils';
|
||||||
|
|
||||||
const IGNORE_FILE = 'addons-linter.eslintignore';
|
import customEslintRules from '../rules/javascript';
|
||||||
|
|
||||||
export default class JavaScriptScanner {
|
export default class JavaScriptScanner {
|
||||||
disabledRules = [];
|
disabledRules = [];
|
||||||
|
@ -45,10 +43,9 @@ export default class JavaScriptScanner {
|
||||||
_ESLint = ESLint,
|
_ESLint = ESLint,
|
||||||
_messages = messages,
|
_messages = messages,
|
||||||
_ruleMapping = ESLINT_RULE_MAPPING,
|
_ruleMapping = ESLINT_RULE_MAPPING,
|
||||||
// This tells ESLint where to expect the ESLint rules for addons-linter.
|
// This property is used to inject additional custom eslint rules
|
||||||
// Its default value is defined below. This property is mainly used for
|
// as part of tests.
|
||||||
// testing purposes.
|
_rules = undefined,
|
||||||
_rulePaths = undefined,
|
|
||||||
} = {}) {
|
} = {}) {
|
||||||
const detectedSourceType = this.detectSourceType(this.filename);
|
const detectedSourceType = this.detectSourceType(this.filename);
|
||||||
this.sourceType = detectedSourceType.sourceType;
|
this.sourceType = detectedSourceType.sourceType;
|
||||||
|
@ -61,34 +58,38 @@ export default class JavaScriptScanner {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const root =
|
const linter = new _ESLint.Linter();
|
||||||
typeof appRoot !== 'undefined' ? appRoot : path.join(__dirname, '..');
|
|
||||||
|
// Load additional rules injected by unit tests.
|
||||||
|
if (_rules) {
|
||||||
|
for (const ruleName of Object.keys(_rules)) {
|
||||||
|
linter.defineRule(ruleName, _rules[ruleName]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load custom eslint rules embedded into addons-linter bundle.
|
||||||
|
for (const key of Object.keys(customEslintRules)) {
|
||||||
|
linter.defineRule(key, customEslintRules[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load plugins rules.
|
||||||
|
const pluginRules = noUnsanitized.rules;
|
||||||
|
for (const key of Object.keys(pluginRules)) {
|
||||||
|
linter.defineRule(`no-unsanitized/${key}`, pluginRules[key]);
|
||||||
|
}
|
||||||
|
|
||||||
|
linter.defineParser('addons-linter-espree', espree);
|
||||||
|
|
||||||
const eslintConfig = {
|
const eslintConfig = {
|
||||||
resolvePluginsRelativeTo: path.resolve(root),
|
|
||||||
// The default value for `rulePaths` is configured so that it finds the
|
|
||||||
// files exported by webpack when this project is built.
|
|
||||||
rulePaths: _rulePaths || [path.join(root, 'dist', 'rules', 'javascript')],
|
|
||||||
allowInlineConfig: false,
|
|
||||||
|
|
||||||
// Avoid loading the addons-linter .eslintrc file
|
|
||||||
useEslintrc: false,
|
|
||||||
|
|
||||||
// Avoid loading the .eslintignore file from the cwd
|
|
||||||
// by explicitly configuring eslint with a custom ignore file
|
|
||||||
// packaged with the addons-linter npm package.
|
|
||||||
ignorePath: path.join(path.resolve(root), IGNORE_FILE),
|
|
||||||
|
|
||||||
baseConfig: {
|
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
es6: true,
|
es6: true,
|
||||||
webextensions: true,
|
webextensions: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
// It's the default but also shouldn't change since we're using
|
// Ensure we use the same parser and parserOptions used to detect
|
||||||
// espree to parse javascript files below manually to figure out
|
// the sourceType.
|
||||||
// if they're modules or not
|
parser: 'addons-linter-espree',
|
||||||
parser: 'espree',
|
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: ECMA_VERSION,
|
ecmaVersion: ECMA_VERSION,
|
||||||
sourceType: this.sourceType,
|
sourceType: this.sourceType,
|
||||||
|
@ -106,21 +107,18 @@ export default class JavaScriptScanner {
|
||||||
addonMetadata: this.options.addonMetadata,
|
addonMetadata: this.options.addonMetadata,
|
||||||
existingFiles: this.options.existingFiles,
|
existingFiles: this.options.existingFiles,
|
||||||
},
|
},
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const cli = new _ESLint.ESLint(eslintConfig);
|
const results = linter.verify(this.code, eslintConfig, {
|
||||||
const results = await cli.lintText(this.code, {
|
allowInlineConfig: false,
|
||||||
filePath: this.filename,
|
filename: this.filename,
|
||||||
warnIgnored: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// eslint prepends the filename with the current working directory,
|
// eslint prepends the filename with the current working directory,
|
||||||
// strip that out.
|
// strip that out.
|
||||||
this.scannedFiles.push(this.filename);
|
this.scannedFiles.push(this.filename);
|
||||||
|
|
||||||
results.forEach((result) => {
|
results.forEach((message) => {
|
||||||
result.messages.forEach((message) => {
|
|
||||||
let extraShortDescription = '';
|
let extraShortDescription = '';
|
||||||
|
|
||||||
// Fatal error messages (like SyntaxErrors) are a bit different, we
|
// Fatal error messages (like SyntaxErrors) are a bit different, we
|
||||||
|
@ -201,7 +199,6 @@ export default class JavaScriptScanner {
|
||||||
type: ESLINT_TYPES[message.severity],
|
type: ESLINT_TYPES[message.severity],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
linterMessages: this.linterMessages,
|
linterMessages: this.linterMessages,
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"name": "eslint-versions-conflicts-package",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "test issues with conflicting eslint versions under the same package",
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "MPL-v2",
|
||||||
|
"devDependencies": {
|
||||||
|
"eslint": "^7.32.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
console.log("background page loaded");
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"manifest_version": 2,
|
||||||
|
"name": "test-extension",
|
||||||
|
"version": "0.1",
|
||||||
|
"permissions": [],
|
||||||
|
"background": {
|
||||||
|
"scripts": [
|
||||||
|
"background.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,9 +25,7 @@ jest.mock('cli', () => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
global.appRoot = path.join(__dirname, '..');
|
if (!fs.existsSync(path.join(__dirname, '..', 'dist'))) {
|
||||||
|
|
||||||
if (!fs.existsSync(path.join(global.appRoot, 'dist'))) {
|
|
||||||
throw new Error('Please run `npm run build` before running the test suite.');
|
throw new Error('Please run `npm run build` before running the test suite.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -368,14 +368,17 @@ export const getJsRulePathForRule = (ruleName) => {
|
||||||
return path.join(FIXTURES_DIR, 'rules', 'javascript', ruleName);
|
return path.join(FIXTURES_DIR, 'rules', 'javascript', ruleName);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function runJsScanner(
|
export async function runJsScanner(
|
||||||
jsScanner,
|
jsScanner,
|
||||||
{ fixtureRules = [], scanOptions } = {}
|
{ fixtureRules = [], scanOptions } = {}
|
||||||
) {
|
) {
|
||||||
const ruleSource = path.join(global.appRoot, 'src/rules/javascript');
|
const _rules = {};
|
||||||
const fixturePaths = fixtureRules.map(getJsRulePathForRule);
|
for (const ruleName of fixtureRules) {
|
||||||
|
const mod = await import(getJsRulePathForRule(ruleName));
|
||||||
|
_rules[ruleName] = mod.default ? mod.default : mod;
|
||||||
|
}
|
||||||
return jsScanner.scan({
|
return jsScanner.scan({
|
||||||
...scanOptions,
|
...scanOptions,
|
||||||
_rulePaths: [ruleSource].concat(fixturePaths),
|
_rules,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,15 +4,30 @@ import { readdirSync } from 'fs';
|
||||||
import { ESLINT_RULE_MAPPING } from 'const';
|
import { ESLINT_RULE_MAPPING } from 'const';
|
||||||
|
|
||||||
describe('Eslint rules object', () => {
|
describe('Eslint rules object', () => {
|
||||||
it('should have files that match the keys', () => {
|
const files = readdirSync('src/rules/javascript').filter(
|
||||||
const files = readdirSync('src/rules/javascript');
|
(fileName) => fileName !== 'index.js'
|
||||||
files.forEach((fileName) => {
|
);
|
||||||
|
|
||||||
|
it.each(files)(
|
||||||
|
'%s rule module should have a matching rule mapping',
|
||||||
|
(fileName) => {
|
||||||
expect(
|
expect(
|
||||||
Object.prototype.hasOwnProperty.call(
|
Object.prototype.hasOwnProperty.call(
|
||||||
ESLINT_RULE_MAPPING,
|
ESLINT_RULE_MAPPING,
|
||||||
path.parse(fileName).name
|
path.parse(fileName).name
|
||||||
)
|
)
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
});
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
|
it.each(files)(
|
||||||
|
'%s rule module should be exported by the index.js module',
|
||||||
|
async (fileName) => {
|
||||||
|
const modName = path.parse(fileName).name;
|
||||||
|
const indexMod = (await import('rules/javascript')).default;
|
||||||
|
const ruleMod = (await import(`rules/javascript/${fileName}`)).default;
|
||||||
|
expect(typeof indexMod[modName]?.create).toBe('function');
|
||||||
|
expect(indexMod[modName]).toBe(ruleMod);
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -4,16 +4,17 @@ import * as messages from 'messages';
|
||||||
|
|
||||||
import { runJsScanner } from '../../helpers';
|
import { runJsScanner } from '../../helpers';
|
||||||
|
|
||||||
describe('unsupported manifest v2 APIs tested with mock', () => {
|
jest.mock('schema/browser-apis.js', () => {
|
||||||
beforeAll(() => jest.resetModules());
|
|
||||||
|
|
||||||
it('returns expected message for APIs unsupported in manifest_version >= 3', async () => {
|
|
||||||
jest.doMock('schema/browser-apis.js', () => {
|
|
||||||
return {
|
return {
|
||||||
hasBrowserApi: () => false,
|
hasBrowserApi: () => false,
|
||||||
isMV2RemovedApi: () => true,
|
isMV2RemovedApi: () => true,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('unsupported manifest v2 APIs tested with mock', () => {
|
||||||
|
beforeAll(() => jest.resetModules());
|
||||||
|
|
||||||
|
it('returns expected message for APIs unsupported in manifest_version >= 3', async () => {
|
||||||
const jsScanner = new JavaScriptScanner(
|
const jsScanner = new JavaScriptScanner(
|
||||||
'browser.pageAction.show();',
|
'browser.pageAction.show();',
|
||||||
'code.js',
|
'code.js',
|
||||||
|
|
|
@ -241,23 +241,18 @@ describe('JavaScript Scanner', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reject on missing message code', async () => {
|
it('should reject on missing message code', async () => {
|
||||||
class FakeESLintClass {
|
class FakeLinterClass {
|
||||||
async lintText() {
|
defineRule() {}
|
||||||
return Promise.resolve([
|
|
||||||
{
|
defineParser() {}
|
||||||
filePath: 'badcode.js',
|
|
||||||
messages: [
|
verify() {
|
||||||
{
|
return [{ fatal: false }];
|
||||||
fatal: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const FakeESLint = {
|
const FakeESLint = {
|
||||||
ESLint: FakeESLintClass,
|
Linter: FakeLinterClass,
|
||||||
};
|
};
|
||||||
|
|
||||||
const jsScanner = new JavaScriptScanner('whatever', 'badcode.js');
|
const jsScanner = new JavaScriptScanner('whatever', 'badcode.js');
|
||||||
|
@ -317,7 +312,6 @@ describe('JavaScript Scanner', () => {
|
||||||
const fakeMetadata = {
|
const fakeMetadata = {
|
||||||
addonMetadata: validMetadata({ guid: 'snowflake' }),
|
addonMetadata: validMetadata({ guid: 'snowflake' }),
|
||||||
};
|
};
|
||||||
const fakeESLintMapping = { 'metadata-not-passed': ESLINT_ERROR };
|
|
||||||
|
|
||||||
const jsScanner = new JavaScriptScanner(
|
const jsScanner = new JavaScriptScanner(
|
||||||
'var hello = "something";',
|
'var hello = "something";',
|
||||||
|
@ -328,7 +322,7 @@ describe('JavaScript Scanner', () => {
|
||||||
const { linterMessages } = await runJsScanner(jsScanner, {
|
const { linterMessages } = await runJsScanner(jsScanner, {
|
||||||
scanOptions: {
|
scanOptions: {
|
||||||
_messages: fakeMessages,
|
_messages: fakeMessages,
|
||||||
_ruleMapping: fakeESLintMapping,
|
_ruleMapping: { 'metadata-not-passed': ESLINT_ERROR },
|
||||||
},
|
},
|
||||||
fixtureRules: ['metadata-not-passed'],
|
fixtureRules: ['metadata-not-passed'],
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
/* eslint import/order: 0 */
|
/* eslint import/order: 0 */
|
||||||
const glob = require('glob');
|
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||||
|
@ -12,10 +11,6 @@ module.exports = {
|
||||||
// webpack3 bundling step.
|
// webpack3 bundling step.
|
||||||
mode: 'none',
|
mode: 'none',
|
||||||
entry: {
|
entry: {
|
||||||
...glob.sync('./src/rules/javascript/*.js').reduce((acc, file) => {
|
|
||||||
acc[file.replace(/^\.\/src\//, '').replace('.js', '')] = file;
|
|
||||||
return acc;
|
|
||||||
}, {}),
|
|
||||||
'addons-linter': './src/main.js',
|
'addons-linter': './src/main.js',
|
||||||
},
|
},
|
||||||
target: 'node',
|
target: 'node',
|
||||||
|
|
Загрузка…
Ссылка в новой задаче