1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-12-23 09:52:26 +01:00

Build: Lint files after removing their code fences (#12075)

* Add linting

* Type the eslintInstance variable

* Update documentation
This commit is contained in:
Erik Marks 2021-09-15 20:18:28 -07:00 committed by GitHub
parent 2b1e05f086
commit 2b104603d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 701 additions and 26 deletions

View File

@ -1,5 +1,5 @@
# Development
Several files which are needed for developing on(!) MetaMask.
Several files which are needed for developing on MetaMask.
Usually each files contains information about its scope / usage.
Usually each file or directory contains information about its scope / usage.

View File

@ -3,7 +3,7 @@
> _tl;dr_ `yarn dist` for prod, `yarn start` for local development
This directory contains the MetaMask build system, which is used to build the MetaMask Extension such that it can be used in a supported browser.
From the repository root, the build system entry file is located at `development/build/index.js`.
From the repository root, the build system entry file is located at [`./development/build/index.js`](https://github.com/MetaMask/metamask-extension/blob/develop/development/build/index.js).
Several package scripts invoke the build system.
For example, `yarn start` creates a watched development build, and `yarn dist` creates a production build.
@ -13,7 +13,17 @@ For local development, building without `lavamoat` is faster and therefore prefe
The build system is not a full-featured CLI, but rather a script that expects some command line arguments and environment variables.
For instructions regarding environment variables, see [the main repository readme](../../README.md#building-locally).
Here follows basic usage information for the build system.
Generally speaking, the build system consists of [`gulp`](https://npmjs.com/package/gulp) tasks that either manipulate static assets or bundle source files using [Browserify](https://browserify.org/).
Production-ready zip files are written to the `./builds` directory, while "unpacked" extension builds
are written to the `./dist` directory.
Our JavaScript source files are transformed using [Babel](https://babeljs.io/), specifically using
the [`babelify`](https://npmjs.com/package/babelify) Browserify transform.
Source file bundling tasks are implemented in the [`./development/build/scripts.js`](https://github.com/MetaMask/metamask-extension/blob/develop/development/build/scripts.js).
> Locally implemented Browserify transforms, _some of which affect how we write JavaScript_, are listed and documented [here](./transforms/README.md).
## Usage
```text
Usage: yarn build <entry-task> [options]
@ -30,15 +40,21 @@ Commands:
e2e tests.
Options:
--beta-version If the build type is "beta", the beta version number.
--beta-version If the build type is "beta", the beta version number.
[number] [default: 0]
--build-type The "type" of build to create. One of: "beta", "main"
--build-type The "type" of build to create. One of: "beta", "main"
[string] [default: "main"]
--omit-lockdown Whether to omit SES lockdown files from the extension
bundle. Useful when linking dependencies that are
incompatible with lockdown.
--lint-fence-files Whether files with code fences should be linted after
fences have been removed by the code fencing transform.
The build will fail if linting fails.
Defaults to `false` if the entry task is `dev` or
`testDev`, and `true` otherwise.
[boolean] [default: <varies>]
--omit-lockdown Whether to omit SES lockdown files from the extension
bundle. Useful when linking dependencies that are
incompatible with lockdown.
[boolean] [default: false]
--skip-stats Whether to refrain from logging build progress. Mostly used
internally.
--skip-stats Whether to refrain from logging build progress. Mostly
used internally.
[boolean] [default: false]
```

View File

@ -40,6 +40,7 @@ function defineAndRunBuildTasks() {
isBeta,
isLavaMoat,
shouldIncludeLockdown,
shouldLintFenceFiles,
skipStats,
} = parseArgv();
@ -74,6 +75,7 @@ function defineAndRunBuildTasks() {
buildType,
isLavaMoat,
livereload,
shouldLintFenceFiles,
});
const { clean, reload, zip } = createEtcTasks({
@ -146,16 +148,22 @@ function parseArgv() {
const NamedArgs = {
BetaVersion: 'beta-version',
BuildType: 'build-type',
LintFenceFiles: 'lint-fence-files',
OmitLockdown: 'omit-lockdown',
SkipStats: 'skip-stats',
};
const argv = minimist(process.argv.slice(2), {
boolean: [NamedArgs.OmitLockdown, NamedArgs.SkipStats],
boolean: [
NamedArgs.LintFenceFiles,
NamedArgs.OmitLockdown,
NamedArgs.SkipStats,
],
string: [NamedArgs.BuildType],
default: {
[NamedArgs.BetaVersion]: 0,
[NamedArgs.BuildType]: BuildTypes.main,
[NamedArgs.LintFenceFiles]: true,
[NamedArgs.OmitLockdown]: false,
[NamedArgs.SkipStats]: false,
},
@ -182,6 +190,13 @@ function parseArgv() {
throw new Error(`MetaMask build: Invalid build type: "${buildType}"`);
}
// Manually default this to `false` for dev builds only.
const shouldLintFenceFiles = process.argv.includes(
`--${NamedArgs.LintFenceFiles}`,
)
? argv[NamedArgs.LintFenceFiles]
: !/dev/iu.test(entryTask);
return {
betaVersion: String(betaVersion),
buildType,
@ -189,6 +204,7 @@ function parseArgv() {
isBeta: argv[NamedArgs.BuildType] === BuildTypes.beta,
isLavaMoat: process.argv[0].includes('lavamoat'),
shouldIncludeLockdown: argv[NamedArgs.OmitLockdown],
shouldLintFenceFiles,
skipStats: argv[NamedArgs.SkipStats],
};
}

View File

@ -55,6 +55,7 @@ function createScriptTasks({
buildType,
isLavaMoat,
livereload,
shouldLintFenceFiles,
}) {
// internal tasks
const core = {
@ -97,6 +98,7 @@ function createScriptTasks({
return `./app/scripts/${label}.js`;
}),
testing,
shouldLintFenceFiles,
}),
);
@ -151,6 +153,7 @@ function createScriptTasks({
runInChildProcess(subtask, {
buildType,
isLavaMoat,
shouldLintFenceFiles,
}),
);
// make a parent task that runs each task in a child thread
@ -166,6 +169,7 @@ function createScriptTasks({
devMode,
entryFilepath: `./app/scripts/${label}.js`,
label,
shouldLintFenceFiles,
});
}
@ -178,6 +182,7 @@ function createScriptTasks({
devMode,
entryFilepath: `./app/scripts/${label}.js`,
label,
shouldLintFenceFiles,
});
}
@ -190,6 +195,7 @@ function createScriptTasks({
devMode,
entryFilepath: `./app/scripts/${label}.js`,
label,
shouldLintFenceFiles,
});
}
@ -206,6 +212,7 @@ function createScriptTasks({
entryFilepath: `./app/scripts/${inpage}.js`,
label: inpage,
testing,
shouldLintFenceFiles,
}),
createNormalBundle({
buildType,
@ -215,6 +222,7 @@ function createScriptTasks({
entryFilepath: `./app/scripts/${contentscript}.js`,
label: contentscript,
testing,
shouldLintFenceFiles,
}),
);
}
@ -226,6 +234,7 @@ function createFactoredBuild({
devMode,
entryFiles,
testing,
shouldLintFenceFiles,
}) {
return async function () {
// create bundler setup and apply defaults
@ -244,6 +253,7 @@ function createFactoredBuild({
envVars,
minify,
reloadOnChange,
shouldLintFenceFiles,
});
// set bundle entries
@ -344,6 +354,7 @@ function createNormalBundle({
extraEntries = [],
label,
modulesToExpose,
shouldLintFenceFiles,
testing,
}) {
return async function () {
@ -363,6 +374,7 @@ function createNormalBundle({
envVars,
minify,
reloadOnChange,
shouldLintFenceFiles,
});
// set bundle entries
@ -409,7 +421,7 @@ function createBuildConfiguration() {
function setupBundlerDefaults(
buildConfiguration,
{ buildType, devMode, envVars, minify, reloadOnChange },
{ buildType, devMode, envVars, minify, reloadOnChange, shouldLintFenceFiles },
) {
const { bundlerOpts } = buildConfiguration;
@ -417,7 +429,7 @@ function setupBundlerDefaults(
// Source transforms
transform: [
// Remove code that should be excluded from builds of the current type
createRemoveFencedCodeTransform(buildType),
createRemoveFencedCodeTransform(buildType, shouldLintFenceFiles),
// Transpile top-level code
babelify,
// Inline `fs.readFileSync` files

View File

@ -48,7 +48,10 @@ function createTask(taskName, taskFn) {
return task;
}
function runInChildProcess(task, { buildType, isLavaMoat }) {
function runInChildProcess(
task,
{ buildType, isLavaMoat, shouldLintFenceFiles },
) {
const taskName = typeof task === 'string' ? task : task.taskName;
if (!taskName) {
throw new Error(
@ -63,7 +66,15 @@ function runInChildProcess(task, { buildType, isLavaMoat }) {
if (isLavaMoat) {
childProcess = spawn(
'yarn',
['build', taskName, '--build-type', buildType, '--skip-stats'],
[
'build',
taskName,
'--build-type',
buildType,
'--lint-fence-files',
shouldLintFenceFiles,
'--skip-stats',
],
{
env: process.env,
},
@ -71,7 +82,15 @@ function runInChildProcess(task, { buildType, isLavaMoat }) {
} else {
childProcess = spawn(
'yarn',
['build:dev', taskName, '--build-type', buildType, '--skip-stats'],
[
'build:dev',
taskName,
'--build-type',
buildType,
'--lint-fence-files',
shouldLintFenceFiles,
'--skip-stats',
],
{
env: process.env,
},

View File

@ -52,6 +52,13 @@ commands inside the parameter parentheses:
///: BEGIN:ONLY_INCLUDE_IN(beta,flask)
```
### Gotchas
By default, the transform will invoke ESLint on files that are modified by the transform.
This is our first line of defense against creating unsyntactic code using code fences, and the transform will error if linting fails.
(Live reloading will continue to work if enabled.)
To toggle this behavior via build system arguments, see [the build system readme](../README.md).
### Code Fencing Syntax
> In the specification, angle brackets, `< >`, indicate required tokens, while

View File

@ -1,6 +1,7 @@
const path = require('path');
const { PassThrough, Transform } = require('stream');
const { BuildTypes } = require('../utils');
const { lintTransformedFile } = require('./utils');
const hasOwnProperty = (obj, key) => Reflect.hasOwnProperty.call(obj, key);
@ -14,13 +15,18 @@ class RemoveFencedCodeTransform extends Transform {
* A transform stream that calls {@link removeFencedCode} on the complete
* string contents of the file read by Browserify.
*
* Optionally lints the file if it was modified.
*
* @param {string} filePath - The path to the file being transformed.
* @param {string} buildType - The type of the current build process.env.
* @param {string} buildType - The type of the current build process.
* @param {boolean} shouldLintTransformedFiles - Whether the file should be
* linted if modified by the transform.
*/
constructor(filePath, buildType) {
constructor(filePath, buildType, shouldLintTransformedFiles) {
super();
this.filePath = filePath;
this.buildType = buildType;
this.shouldLintTransformedFiles = shouldLintTransformedFiles;
this._fileBuffers = [];
}
@ -35,14 +41,24 @@ class RemoveFencedCodeTransform extends Transform {
// stream, immediately before the "end" event is emitted.
// It applies the transform to the concatenated file contents.
_flush(end) {
const [fileContent] = removeFencedCode(
const [fileContent, didModify] = removeFencedCode(
this.filePath,
this.buildType,
Buffer.concat(this._fileBuffers).toString('utf8'),
);
this.push(fileContent);
end();
const pushAndEnd = () => {
this.push(fileContent);
end();
};
if (this.shouldLintTransformedFiles && didModify) {
lintTransformedFile(fileContent, this.filePath)
.then(pushAndEnd)
.catch((error) => end(error));
} else {
pushAndEnd();
}
}
}
@ -57,10 +73,19 @@ class RemoveFencedCodeTransform extends Transform {
* For details on how the transform mutates source files, see
* {@link removeFencedCode} and the documentation.
*
* If specified (and by default), the transform will call ESLint on the text
* contents of any file that it modifies. The transform will error if such a
* file is ignored by ESLint, since linting is our first line of defense against
* making un-syntactic modifications to files using code fences.
*
* @param {string} buildType - The type of the current build.
* @param {boolean} shouldLintTransformedFiles - Whether to lint transformed files.
* @returns {(filePath: string) => Transform} The transform function.
*/
function createRemoveFencedCodeTransform(buildType) {
function createRemoveFencedCodeTransform(
buildType,
shouldLintTransformedFiles = true,
) {
if (!hasOwnProperty(BuildTypes, buildType)) {
throw new Error(
`Code fencing transform received unrecognized build type "${buildType}".`,
@ -81,7 +106,11 @@ function createRemoveFencedCodeTransform(buildType) {
return new PassThrough();
}
return new RemoveFencedCodeTransform(filePath, buildType);
return new RemoveFencedCodeTransform(
filePath,
buildType,
shouldLintTransformedFiles,
);
};
}
@ -156,7 +185,7 @@ const directiveParsingRegex = /^([A-Z]+):([A-Z_]+)(?:\(((?:\w+,)*\w+)\))?$/u;
* For details, please see the documentation.
*
* @param {string} filePath - The path to the file being transformed.
* @param {string} typeOfCurrentBuild - The type of the current build process.
* @param {string} typeOfCurrentBuild - The type of the current build.
* @param {string} fileContent - The contents of the file being transformed.
* @returns {[string, modified]} A tuple of the post-transform file contents and
* a boolean indicating whether they were modified.

View File

@ -4,6 +4,11 @@ const {
createRemoveFencedCodeTransform,
removeFencedCode,
} = require('./remove-fenced-code');
const transformUtils = require('./utils');
jest.mock('./utils', () => ({
lintTransformedFile: jest.fn(),
}));
// The test data is just strings. We get it from a function at the end of this
// file because it takes up a lot of lines and is very distracting.
@ -17,8 +22,13 @@ Conditionally_Included
describe('build/transforms/remove-fenced-code', () => {
describe('createRemoveFencedCodeTransform', () => {
const { lintTransformedFile: lintTransformedFileMock } = transformUtils;
const mockJsFileName = 'file.js';
beforeEach(() => {
lintTransformedFileMock.mockImplementation(() => Promise.resolve());
});
it('rejects invalid build types', () => {
expect(() => createRemoveFencedCodeTransform('foobar')).toThrow(
/received unrecognized build type "foobar".$/u,
@ -37,6 +47,7 @@ describe('build/transforms/remove-fenced-code', () => {
stream.on('end', () => {
expect(streamOutput).toStrictEqual(fileContent);
expect(lintTransformedFileMock).not.toHaveBeenCalled();
resolve();
});
@ -59,6 +70,11 @@ describe('build/transforms/remove-fenced-code', () => {
stream.on('end', () => {
expect(streamOutput).toStrictEqual(filePrefix);
expect(lintTransformedFileMock).toHaveBeenCalledTimes(1);
expect(lintTransformedFileMock).toHaveBeenCalledWith(
filePrefix,
mockJsFileName,
);
resolve();
});
@ -86,6 +102,11 @@ describe('build/transforms/remove-fenced-code', () => {
stream.on('end', () => {
expect(streamOutput).toStrictEqual(filePrefix);
expect(lintTransformedFileMock).toHaveBeenCalledTimes(1);
expect(lintTransformedFileMock).toHaveBeenCalledWith(
filePrefix,
mockJsFileName,
);
resolve();
});
@ -107,6 +128,57 @@ describe('build/transforms/remove-fenced-code', () => {
stream.on('end', () => {
expect(streamOutput).toStrictEqual(fileContent);
expect(lintTransformedFileMock).not.toHaveBeenCalled();
resolve();
});
stream.end(fileContent);
});
});
it('skips linting for transformed file if shouldLintTransformedFiles is false', async () => {
const filePrefix = '// A comment\n';
const fileContent = filePrefix.concat(getMinimalFencedCode());
const stream = createRemoveFencedCodeTransform(
'main',
false,
)(mockJsFileName);
let streamOutput = '';
await new Promise((resolve) => {
stream.on('data', (data) => {
streamOutput = streamOutput.concat(data.toString('utf8'));
});
stream.on('end', () => {
expect(streamOutput).toStrictEqual(filePrefix);
expect(lintTransformedFileMock).not.toHaveBeenCalled();
resolve();
});
stream.end(fileContent);
});
});
it('handles transformed file lint failure', async () => {
lintTransformedFileMock.mockImplementationOnce(() =>
Promise.reject(new Error('lint failure')),
);
const filePrefix = '// A comment\n';
const fileContent = filePrefix.concat(getMinimalFencedCode());
const stream = createRemoveFencedCodeTransform('main')(mockJsFileName);
await new Promise((resolve) => {
stream.on('error', (error) => {
expect(error).toStrictEqual(new Error('lint failure'));
expect(lintTransformedFileMock).toHaveBeenCalledTimes(1);
expect(lintTransformedFileMock).toHaveBeenCalledWith(
filePrefix,
mockJsFileName,
);
resolve();
});

View File

@ -0,0 +1,68 @@
const { ESLint } = require('eslint');
const eslintrc = require('../../../.eslintrc.js');
/**
* The singleton ESLint instance.
*
* @type {ESLint}
*/
let eslintInstance;
// We only need a single ESLint instance, and we only initialize it if necessary
const initializeESLint = () => {
if (!eslintInstance) {
eslintInstance = new ESLint({ baseConfig: eslintrc, useEslintrc: false });
}
};
// Four spaces
const TAB = ' ';
module.exports = {
lintTransformedFile,
};
/**
* Lints a transformed file by invoking ESLint programmatically on the string
* file contents. The path to the file must be specified so that the repository
* ESLint config can be applied properly.
*
* An error is thrown if linting produced any errors, or if the file is ignored
* by ESLint. Files linted by this function should never be ignored.
*
* @param {string} content - The file content.
* @param {string} filePath - The path to the file.
* @returns {Promise<void>} Returns `undefined` or throws an error if linting produced
* any errors, or if the linted file is ignored.
*/
async function lintTransformedFile(content, filePath) {
initializeESLint();
const lintResult = (
await eslintInstance.lintText(content, { filePath, warnIgnored: false })
)[0];
// This indicates that the file is ignored, which should never be the case for
// a transformed file.
if (lintResult === undefined) {
throw new Error(
`MetaMask build: Transformed file "${filePath}" appears to be ignored by ESLint.`,
);
}
// This is the success case
if (lintResult.errorCount === 0) {
return;
}
// Errors are stored in the messages array, and their "severity" is 2
const errorsString = lintResult.messages
.filter(({ severity }) => severity === 2)
.reduce((allErrors, { message, ruleId }) => {
return allErrors.concat(`${TAB}${ruleId}\n${TAB}${message}\n\n`);
}, '');
throw new Error(
`MetaMask build: Lint errors encountered for transformed file "${filePath}":\n\n${errorsString}`,
);
}

View File

@ -0,0 +1,71 @@
const { lintTransformedFile } = require('./utils');
let mockESLint;
jest.mock('eslint', () => ({
ESLint: class MockESLint {
constructor() {
if (mockESLint) {
throw new Error('Mock ESLint ref already assigned!');
}
// eslint-disable-next-line consistent-this
mockESLint = this;
// eslint-disable-next-line jest/prefer-spy-on
this.lintText = jest.fn();
}
},
}));
describe('transform utils', () => {
describe('lintTransformedFile', () => {
it('initializes the ESLint singleton', async () => {
expect(mockESLint).not.toBeDefined();
// This error is an artifact of how we're mocking the ESLint singleton,
// and won't actually occur in production.
await expect(() => lintTransformedFile()).rejects.toThrow(
`Cannot read property '0' of undefined`,
);
expect(mockESLint).toBeDefined();
});
it('returns if linting passes with no errors', async () => {
mockESLint.lintText.mockImplementationOnce(() =>
Promise.resolve([{ errorCount: 0 }]),
);
expect(
await lintTransformedFile('/* JavaScript */', 'file.js'),
).toBeUndefined();
});
it('throws if the file is ignored by ESLint', async () => {
mockESLint.lintText.mockImplementationOnce(() => Promise.resolve([]));
await expect(() =>
lintTransformedFile('/* JavaScript */', 'file.js'),
).rejects.toThrow(
/Transformed file "file\.js" appears to be ignored by ESLint\.$/u,
);
});
it('throws if linting produced any errors', async () => {
const ruleId = 'some-eslint-rule';
const message = 'You violated the rule!';
mockESLint.lintText.mockImplementationOnce(() =>
Promise.resolve([
{ errorCount: 1, messages: [{ message, ruleId, severity: 2 }] },
]),
);
await expect(() =>
lintTransformedFile('/* JavaScript */', 'file.js'),
).rejects.toThrow(
/Lint errors encountered for transformed file "file\.js":\n\n {4}some-eslint-rule\n {4}You violated the rule!\n\n$/u,
);
});
});
});

View File

@ -785,6 +785,42 @@
"console.log": true
}
},
"@eslint/eslintrc": {
"builtin": {
"assert": true,
"fs.existsSync": true,
"fs.readFileSync": true,
"module.createRequire": true,
"module.createRequireFromPath": true,
"os.homedir": true,
"path.basename": true,
"path.dirname": true,
"path.extname": true,
"path.isAbsolute": true,
"path.join": true,
"path.relative": true,
"path.resolve": true,
"path.sep": true,
"util.inspect": true
},
"globals": {
"__dirname": true,
"process.cwd": true,
"process.emitWarning": true,
"process.platform": true
},
"packages": {
"ajv": true,
"debug": true,
"espree": true,
"globals": true,
"ignore": true,
"import-fresh": true,
"js-yaml": true,
"minimatch": true,
"strip-json-comments": true
}
},
"@gulp-sourcemaps/identity-map": {
"packages": {
"acorn": true,
@ -897,6 +933,11 @@
"acorn": true
}
},
"acorn-jsx": {
"packages": {
"acorn": true
}
},
"acorn-node": {
"packages": {
"acorn": true,
@ -911,8 +952,14 @@
}
},
"ajv": {
"globals": {
"console": true
},
"packages": {
"fast-deep-equal": true
"fast-deep-equal": true,
"fast-json-stable-stringify": true,
"json-schema-traverse": true,
"uri-js": true
}
},
"amdefine": {
@ -970,6 +1017,16 @@
"buffer-equal": true
}
},
"are-we-there-yet": {
"builtin": {
"events.EventEmitter": true,
"util.inherits": true
},
"packages": {
"delegates": true,
"readable-stream": true
}
},
"arr-diff": {
"packages": {
"arr-flatten": true,
@ -1329,6 +1386,7 @@
"anymatch": true,
"async-each": true,
"braces": true,
"fsevents": true,
"glob-parent": true,
"inherits": true,
"is-binary-path": true,
@ -1580,6 +1638,16 @@
"through2": true
}
},
"detect-libc": {
"builtin": {
"child_process.spawnSync": true,
"fs.readdirSync": true,
"os.platform": true
},
"globals": {
"process.env": true
}
},
"detective": {
"packages": {
"acorn-node": true,
@ -1600,6 +1668,14 @@
"path-type": true
}
},
"doctrine": {
"builtin": {
"assert": true
},
"packages": {
"esutils": true
}
},
"dom-serializer": {
"packages": {
"domelementtype": true,
@ -1729,6 +1805,92 @@
"source-map": true
}
},
"eslint": {
"builtin": {
"assert": true,
"fs.existsSync": true,
"fs.lstatSync": true,
"fs.readFileSync": true,
"fs.readdirSync": true,
"fs.statSync": true,
"fs.unlinkSync": true,
"fs.writeFile": true,
"fs.writeFileSync": true,
"path.extname": true,
"path.isAbsolute": true,
"path.join": true,
"path.normalize": true,
"path.relative": true,
"path.resolve": true,
"path.sep": true,
"util.format": true,
"util.inspect": true,
"util.promisify": true
},
"globals": {
"__dirname": true,
"console.log": true,
"describe": true,
"it": true,
"process": true
},
"packages": {
"@eslint/eslintrc": true,
"ajv": true,
"debug": true,
"doctrine": true,
"eslint-scope": true,
"eslint-utils": true,
"eslint-visitor-keys": true,
"espree": true,
"esquery": true,
"esutils": true,
"file-entry-cache": true,
"functional-red-black-tree": true,
"glob-parent": true,
"globals": true,
"ignore": true,
"imurmurhash": true,
"is-glob": true,
"json-stable-stringify-without-jsonify": true,
"levn": true,
"lodash": true,
"minimatch": true,
"natural-compare": true,
"regexpp": true
}
},
"eslint-scope": {
"builtin": {
"assert": true
},
"packages": {
"esrecurse": true,
"estraverse": true
}
},
"eslint-utils": {
"packages": {
"eslint-visitor-keys": true
}
},
"espree": {
"packages": {
"acorn": true,
"acorn-jsx": true,
"eslint-visitor-keys": true
}
},
"esquery": {
"globals": {
"define": true
}
},
"esrecurse": {
"packages": {
"estraverse": true
}
},
"event-emitter": {
"packages": {
"d": true,
@ -1911,7 +2073,9 @@
"flat-cache": {
"builtin": {
"fs.existsSync": true,
"fs.mkdirSync": true,
"fs.readFileSync": true,
"fs.writeFileSync": true,
"path.basename": true,
"path.dirname": true,
"path.resolve": true
@ -2020,6 +2184,45 @@
"process.version": true
}
},
"fsevents": {
"builtin": {
"events.EventEmitter": true,
"fs.stat": true,
"path.join": true,
"util.inherits": true
},
"globals": {
"__dirname": true,
"process.nextTick": true,
"process.platform": true,
"setImmediate": true
},
"native": true,
"packages": {
"node-pre-gyp": true
}
},
"gauge": {
"builtin": {
"util.format": true
},
"globals": {
"clearInterval": true,
"process": true,
"setImmediate": true,
"setInterval": true
},
"packages": {
"aproba": true,
"console-control-strings": true,
"has-unicode": true,
"object-assign": true,
"signal-exit": true,
"string-width": true,
"strip-ansi": true,
"wide-align": true
}
},
"get-assigned-identifiers": {
"builtin": {
"assert.equal": true
@ -2380,6 +2583,16 @@
"process.argv": true
}
},
"has-unicode": {
"builtin": {
"os.type": true
},
"globals": {
"process.env.LANG": true,
"process.env.LC_ALL": true,
"process.env.LC_CTYPE": true
}
},
"has-value": {
"packages": {
"get-value": true,
@ -2533,6 +2746,11 @@
"is-plain-object": true
}
},
"is-fullwidth-code-point": {
"packages": {
"number-is-nan": true
}
},
"is-glob": {
"packages": {
"is-extglob": true
@ -2611,6 +2829,11 @@
"isarray": true
}
},
"js-yaml": {
"globals": {
"esprima": true
}
},
"jsesc": {
"globals": {
"Buffer.isBuffer": true
@ -2688,6 +2911,12 @@
"flush-write-stream": true
}
},
"levn": {
"packages": {
"prelude-ls": true,
"type-check": true
}
},
"lodash": {
"globals": {
"define": true
@ -2917,6 +3146,56 @@
"setTimeout": true
}
},
"node-pre-gyp": {
"builtin": {
"events.EventEmitter": true,
"fs.existsSync": true,
"fs.readFileSync": true,
"fs.renameSync": true,
"path.dirname": true,
"path.existsSync": true,
"path.join": true,
"path.resolve": true,
"url.parse": true,
"url.resolve": true,
"util.inherits": true
},
"globals": {
"__dirname": true,
"console.log": true,
"process.arch": true,
"process.cwd": true,
"process.env": true,
"process.platform": true,
"process.version.substr": true,
"process.versions": true
},
"packages": {
"detect-libc": true,
"nopt": true,
"npmlog": true,
"rimraf": true,
"semver": true
}
},
"nopt": {
"builtin": {
"path": true,
"stream.Stream": true,
"url": true
},
"globals": {
"console": true,
"process.argv": true,
"process.env.DEBUG_NOPT": true,
"process.env.NOPT_DEBUG": true,
"process.platform": true
},
"packages": {
"abbrev": true,
"osenv": true
}
},
"normalize-path": {
"packages": {
"remove-trailing-separator": true
@ -2932,6 +3211,22 @@
"once": true
}
},
"npmlog": {
"builtin": {
"events.EventEmitter": true,
"util": true
},
"globals": {
"process.nextTick": true,
"process.stderr": true
},
"packages": {
"are-we-there-yet": true,
"console-control-strings": true,
"gauge": true,
"set-blocking": true
}
},
"object-copy": {
"packages": {
"copy-descriptor": true,
@ -2998,6 +3293,54 @@
"readable-stream": true
}
},
"os-homedir": {
"builtin": {
"os.homedir": true
},
"globals": {
"process.env": true,
"process.getuid": true,
"process.platform": true
}
},
"os-tmpdir": {
"globals": {
"process.env.SystemRoot": true,
"process.env.TEMP": true,
"process.env.TMP": true,
"process.env.TMPDIR": true,
"process.env.windir": true,
"process.platform": true
}
},
"osenv": {
"builtin": {
"child_process.exec": true,
"path": true
},
"globals": {
"process.env.COMPUTERNAME": true,
"process.env.ComSpec": true,
"process.env.EDITOR": true,
"process.env.HOSTNAME": true,
"process.env.PATH": true,
"process.env.PROMPT": true,
"process.env.PS1": true,
"process.env.Path": true,
"process.env.SHELL": true,
"process.env.USER": true,
"process.env.USERDOMAIN": true,
"process.env.USERNAME": true,
"process.env.VISUAL": true,
"process.env.path": true,
"process.nextTick": true,
"process.platform": true
},
"packages": {
"os-homedir": true,
"os-tmpdir": true
}
},
"parent-module": {
"packages": {
"callsites": true
@ -3580,6 +3923,12 @@
"process": true
}
},
"set-blocking": {
"globals": {
"process.stderr": true,
"process.stdout": true
}
},
"set-value": {
"packages": {
"extend-shallow": true,
@ -3782,6 +4131,7 @@
},
"string-width": {
"packages": {
"code-point-at": true,
"emoji-regex": true,
"is-fullwidth-code-point": true,
"strip-ansi": true
@ -4040,6 +4390,11 @@
"through2": true
}
},
"type-check": {
"packages": {
"prelude-ls": true
}
},
"typedarray-to-buffer": {
"globals": {
"Buffer.from": true
@ -4145,6 +4500,11 @@
"path": true
}
},
"uri-js": {
"globals": {
"define": true
}
},
"urix": {
"builtin": {
"path.sep": true
@ -4348,6 +4708,11 @@
"isexe": true
}
},
"wide-align": {
"packages": {
"string-width": true
}
},
"write": {
"builtin": {
"fs.createWriteStream": true,