Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: experimental ES Modules support #9772

Merged
merged 10 commits into from
Apr 16, 2020
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@

- `[jest-console]` Add code frame to `console.error` and `console.warn` ([#9741](https://github.com/facebook/jest/pull/9741))
- `[@jest/globals]` New package so Jest's globals can be explicitly imported ([#9801](https://github.com/facebook/jest/pull/9801))
- `[jest-runtime, jest-jasmine2, jest-circus]` Experimental, limited ECMAScript Modules support ([#9772](https://github.com/facebook/jest/pull/9772))

### Fixes

4 changes: 2 additions & 2 deletions e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ FAIL __tests__/index.js
12 | module.exports = () => 'test';
13 |
at createNoMappedModuleFoundError (../../packages/jest-resolve/build/index.js:540:17)
at createNoMappedModuleFoundError (../../packages/jest-resolve/build/index.js:545:17)
at Object.require (index.js:10:1)
`;

@@ -65,6 +65,6 @@ FAIL __tests__/index.js
12 | module.exports = () => 'test';
13 |
at createNoMappedModuleFoundError (../../packages/jest-resolve/build/index.js:540:17)
at createNoMappedModuleFoundError (../../packages/jest-resolve/build/index.js:545:17)
at Object.require (index.js:10:1)
`;
9 changes: 9 additions & 0 deletions e2e/__tests__/__snapshots__/nativeEsm.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`on node ^12.16.0 || >=13.0.0 runs test with native ESM 1`] = `
Test Suites: 1 passed, 1 total
Tests: 4 passed, 4 total
Snapshots: 0 total
Time: <<REPLACED>>
Ran all test suites.
`;
Original file line number Diff line number Diff line change
@@ -37,6 +37,6 @@ FAIL __tests__/test.js
| ^
9 |
at Resolver.resolveModule (../../packages/jest-resolve/build/index.js:296:11)
at Resolver.resolveModule (../../packages/jest-resolve/build/index.js:299:11)
at Object.require (index.js:8:18)
`;
36 changes: 36 additions & 0 deletions e2e/__tests__/nativeEsm.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import {resolve} from 'path';
import wrap from 'jest-snapshot-serializer-raw';
import {onNodeVersions} from '@jest/test-utils';
import runJest, {getConfig} from '../runJest';
import {extractSummary} from '../Utils';

const DIR = resolve(__dirname, '../native-esm');

test('test config is without transform', () => {
const {configs} = getConfig(DIR);

expect(configs).toHaveLength(1);
expect(configs[0].transform).toEqual([]);
});

// The versions vm.Module was introduced
onNodeVersions('^12.16.0 || >=13.0.0', () => {
test('runs test with native ESM', () => {
const {exitCode, stderr, stdout} = runJest(DIR, [], {
nodeOptions: '--experimental-vm-modules',
});

const {summary} = extractSummary(stderr);

expect(wrap(summary)).toMatchSnapshot();
expect(stdout).toBe('');
expect(exitCode).toBe(0);
});
});
47 changes: 47 additions & 0 deletions e2e/native-esm/__tests__/native-esm.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import {readFileSync} from 'fs';
import {dirname, resolve} from 'path';
import {fileURLToPath} from 'url';
import {double} from '../index';

test('should have correct import.meta', () => {
expect(typeof require).toBe('undefined');
expect(typeof jest).toBe('undefined');
expect(import.meta).toEqual({
jest: expect.anything(),
url: expect.any(String),
});
expect(
import.meta.url.endsWith('/e2e/native-esm/__tests__/native-esm.test.js')
).toBe(true);
});

test('should double stuff', () => {
expect(double(1)).toBe(2);
});

test('should support importing node core modules', () => {
const dir = dirname(fileURLToPath(import.meta.url));
const packageJsonPath = resolve(dir, '../package.json');

expect(JSON.parse(readFileSync(packageJsonPath, 'utf8'))).toEqual({
jest: {
testEnvironment: 'node',
transform: {},
},
type: 'module',
});
});

test('dynamic import should work', async () => {
const {double: importedDouble} = await import('../index');

expect(importedDouble).toBe(double);
expect(importedDouble(1)).toBe(2);
});
10 changes: 10 additions & 0 deletions e2e/native-esm/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

export function double(num) {
return num * 2;
}
7 changes: 7 additions & 0 deletions e2e/native-esm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "module",
"jest": {
"testEnvironment": "node",
"transform": {}
}
}
19 changes: 17 additions & 2 deletions packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts
Original file line number Diff line number Diff line change
@@ -76,9 +76,24 @@ const jestAdapter = async (
}
});

config.setupFilesAfterEnv.forEach(path => runtime.requireModule(path));
for (const path of config.setupFilesAfterEnv) {
const esm = runtime.unstable_shouldLoadAsEsm(path);

if (esm) {
await runtime.unstable_importModule(path);
} else {
runtime.requireModule(path);
}
}

const esm = runtime.unstable_shouldLoadAsEsm(testPath);

if (esm) {
await runtime.unstable_importModule(testPath);
} else {
runtime.requireModule(testPath);
}

runtime.requireModule(testPath);
const results = await runAndTransformResultsToJestFormat({
config,
globalConfig,
19 changes: 17 additions & 2 deletions packages/jest-jasmine2/src/index.ts
Original file line number Diff line number Diff line change
@@ -155,7 +155,15 @@ async function jasmine2(
testPath,
});

config.setupFilesAfterEnv.forEach(path => runtime.requireModule(path));
for (const path of config.setupFilesAfterEnv) {
const esm = runtime.unstable_shouldLoadAsEsm(path);

if (esm) {
await runtime.unstable_importModule(path);
} else {
runtime.requireModule(path);
}
}

if (globalConfig.enabledTestsMap) {
env.specFilter = (spec: Spec) => {
@@ -169,7 +177,14 @@ async function jasmine2(
env.specFilter = (spec: Spec) => testNameRegex.test(spec.getFullName());
}

runtime.requireModule(testPath);
const esm = runtime.unstable_shouldLoadAsEsm(testPath);

if (esm) {
await runtime.unstable_importModule(testPath);
} else {
runtime.requireModule(testPath);
}

await env.execute();

const results = await reporter.getResults();
1 change: 1 addition & 0 deletions packages/jest-resolve/package.json
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@
"browser-resolve": "^1.11.3",
"chalk": "^3.0.0",
"jest-pnp-resolver": "^1.2.1",
"read-pkg-up": "^7.0.1",
"realpath-native": "^2.0.0",
"resolve": "^1.15.1",
"slash": "^3.0.0"
5 changes: 5 additions & 0 deletions packages/jest-resolve/src/index.ts
Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@ import isBuiltinModule from './isBuiltinModule';
import defaultResolver, {clearDefaultResolverCache} from './defaultResolver';
import type {ResolverConfig} from './types';
import ModuleNotFoundError from './ModuleNotFoundError';
import shouldLoadAsEsm, {clearCachedLookups} from './shouldLoadAsEsm';

type FindNodeModuleConfig = {
basedir: Config.Path;
@@ -100,6 +101,7 @@ class Resolver {

static clearDefaultResolverCache(): void {
clearDefaultResolverCache();
clearCachedLookups();
}

static findNodeModule(
@@ -129,6 +131,9 @@ class Resolver {
return null;
}

// unstable as it should be replaced by https://github.com/nodejs/modules/issues/393, and we don't want people to use it
static unstable_shouldLoadAsEsm = shouldLoadAsEsm;

resolveModuleFromDirIfExists(
dirname: Config.Path,
moduleName: string,
65 changes: 65 additions & 0 deletions packages/jest-resolve/src/shouldLoadAsEsm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import {dirname, extname} from 'path';
// @ts-ignore: experimental, not added to the types
import {SourceTextModule} from 'vm';
import type {Config} from '@jest/types';
import readPkgUp = require('read-pkg-up');

const runtimeSupportsVmModules = typeof SourceTextModule === 'function';

const cachedLookups = new Map<string, boolean>();

export function clearCachedLookups(): void {
cachedLookups.clear();
}

export default function cachedShouldLoadAsEsm(path: Config.Path): boolean {
let cachedLookup = cachedLookups.get(path);

if (cachedLookup === undefined) {
cachedLookup = shouldLoadAsEsm(path);
cachedLookups.set(path, cachedLookup);
}

return cachedLookup;
}

// this is a bad version of what https://github.com/nodejs/modules/issues/393 would provide
function shouldLoadAsEsm(path: Config.Path): boolean {
if (!runtimeSupportsVmModules) {
return false;
}

const extension = extname(path);

if (extension === '.mjs') {
return true;
}

if (extension === '.cjs') {
return false;
}

// this isn't correct - we might wanna load any file as a module (using synthetic module)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the issue with falling back to pkg.packageJson.type === 'module'; for non-JS as well?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly because it's not how node would do it - it expects explicit loaders. This is wrong, but I think less wrong. This whole thing is gonna be implemented in resolve at some point regardless

// do we need an option to Jest so people can opt in to ESM for non-js?
if (extension !== '.js') {
return false;
}

const cwd = dirname(path);

// TODO: can we cache lookups somehow?
const pkg = readPkgUp.sync({cwd, normalize: false});

if (!pkg) {
return false;
}

return pkg.packageJson.type === 'module';
}
10 changes: 9 additions & 1 deletion packages/jest-runner/src/runTest.ts
Original file line number Diff line number Diff line change
@@ -156,7 +156,15 @@ async function runTestInternal(

const start = Date.now();

config.setupFiles.forEach(path => runtime.requireModule(path));
for (const path of config.setupFiles) {
const esm = runtime.unstable_shouldLoadAsEsm(path);

if (esm) {
await runtime.unstable_importModule(path);
} else {
runtime.requireModule(path);
}
}

const sourcemapOptions: sourcemapSupport.Options = {
environment: 'node',
10 changes: 9 additions & 1 deletion packages/jest-runtime/src/__mocks__/createRuntime.js
Original file line number Diff line number Diff line change
@@ -49,7 +49,15 @@ module.exports = async function createRuntime(filename, config) {
Runtime.createResolver(config, hasteMap.moduleMap),
);

config.setupFiles.forEach(path => runtime.requireModule(path));
for (const path of config.setupFiles) {
const esm = runtime.unstable_shouldLoadAsEsm(path);

if (esm) {
await runtime.unstable_importModule(path);
} else {
runtime.requireModule(path);
}
}

runtime.__mockRootPath = path.join(config.rootDir, 'root.js');
runtime.__mockSubdirPath = path.join(
17 changes: 15 additions & 2 deletions packages/jest-runtime/src/cli/index.ts
Original file line number Diff line number Diff line change
@@ -93,9 +93,22 @@ export async function run(

const runtime = new Runtime(config, environment, hasteMap.resolver);

config.setupFiles.forEach(path => runtime.requireModule(path));
for (const path of config.setupFiles) {
const esm = runtime.unstable_shouldLoadAsEsm(path);

runtime.requireModule(filePath);
if (esm) {
await runtime.unstable_importModule(path);
} else {
runtime.requireModule(path);
}
}
const esm = runtime.unstable_shouldLoadAsEsm(filePath);

if (esm) {
await runtime.unstable_importModule(filePath);
} else {
runtime.requireModule(filePath);
}
} catch (e) {
console.error(chalk.red(e.stack || e));
process.on('exit', () => (process.exitCode = 1));
166 changes: 146 additions & 20 deletions packages/jest-runtime/src/index.ts
Original file line number Diff line number Diff line change
@@ -5,9 +5,19 @@
* LICENSE file in the root directory of this source tree.
*/

import {URL, fileURLToPath} from 'url';
import {URL, fileURLToPath, pathToFileURL} from 'url';
import * as path from 'path';
import {Script, compileFunction} from 'vm';
import {
Script,
// @ts-ignore: experimental, not added to the types
SourceTextModule,
// @ts-ignore: experimental, not added to the types
SyntheticModule,
Context as VMContext,
// @ts-ignore: experimental, not added to the types
Module as VMModule,
compileFunction,
} from 'vm';
import * as nativeModule from 'module';
import type {Config, Global} from '@jest/types';
import type {
@@ -48,6 +58,10 @@ interface JestGlobalsValues extends Global.TestFrameworkGlobals {
expect: JestGlobals.expect;
}

interface JestImportMeta extends ImportMeta {
jest: Jest;
}

type HasteMapOptions = {
console?: Console;
maxWorkers: number;
@@ -106,6 +120,8 @@ const EVAL_RESULT_VARIABLE = 'Object.<anonymous>';

type RunScriptEvalResult = {[EVAL_RESULT_VARIABLE]: ModuleWrapper};

const runtimeSupportsVmModules = typeof SourceTextModule === 'function';

/* eslint-disable-next-line no-redeclare */
class Runtime {
private _cacheFS: CacheFS;
@@ -125,6 +141,7 @@ class Runtime {
private _moduleMocker: typeof jestMock;
private _isolatedModuleRegistry: ModuleRegistry | null;
private _moduleRegistry: ModuleRegistry;
private _esmoduleRegistry: Map<string, VMModule>;
private _needsCoverageMapped: Set<string>;
private _resolver: Resolver;
private _shouldAutoMock: boolean;
@@ -169,6 +186,7 @@ class Runtime {
this._isolatedModuleRegistry = null;
this._isolatedMockRegistry = null;
this._moduleRegistry = new Map();
this._esmoduleRegistry = new Map();
this._needsCoverageMapped = new Set();
this._resolver = resolver;
this._scriptTransformer = new ScriptTransformer(config);
@@ -302,6 +320,83 @@ class Runtime {
return cliOptions;
}

// unstable as it should be replaced by https://github.com/nodejs/modules/issues/393, and we don't want people to use it
unstable_shouldLoadAsEsm = Resolver.unstable_shouldLoadAsEsm;

private async loadEsmModule(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another limitation you did not mention seems to be that it does not use any of the logic we have for normal modules regarding mocks, isolation, etc., correct? If so, we could maybe leave a comment saying this needs to be rewritten completely to share code with requireModule

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mocks are not implemented no, but isolation is. THe runtime itself is per test, so isolation should be correct. will need to implement mocks, though

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry that was unclear, I meant isolateModules

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

aha, gotcha. isolateModules doesn't work with ESM regardless since it either has to be static or async. We probably need to do something else clever

modulePath: Config.Path,
query = '',
): Promise<VMModule> {
const cacheKey = modulePath + query;

if (!this._esmoduleRegistry.has(cacheKey)) {
const context = this._environment.getVmContext!();

invariant(context);

if (this._resolver.isCoreModule(modulePath)) {
const core = await this._importCoreModule(modulePath, context);
this._esmoduleRegistry.set(cacheKey, core);
return core;
}

const transformedFile = this.transformFile(modulePath, {
isInternalModule: false,
supportsDynamicImport: true,
supportsStaticESM: true,
});

const module = new SourceTextModule(transformedFile.code, {
context,
identifier: modulePath,
importModuleDynamically: (
specifier: string,
referencingModule: VMModule,
) =>
this.loadEsmModule(
this._resolveModule(referencingModule.identifier, specifier),
),
initializeImportMeta(meta: JestImportMeta) {
meta.url = pathToFileURL(modulePath).href;
// @ts-ignore TODO: fill this
meta.jest = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to provide Jest in this way at all, or do we want to rely on import from '@jest/globals' for ESM instead?

Copy link
Member Author

@SimenB SimenB Apr 13, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure. The @jest/globals thing is a thing I came of with this morning. I think I like importing more than adding to meta (it's easier to type (no messing with the global ImportMeta interface) which usually means it's easier to consume as well), so maybe just drop it?

},
});

this._esmoduleRegistry.set(cacheKey, module);
}

const module = this._esmoduleRegistry.get(cacheKey);

invariant(module);

return module;
}

async unstable_importModule(
from: Config.Path,
moduleName?: string,
): Promise<void> {
invariant(
runtimeSupportsVmModules,
'You need to run with a version of node that supports ES Modules in the VM API.',
);
invariant(
typeof this._environment.getVmContext === 'function',
'ES Modules are only supported if your test environment has the `getVmContext` function',
);

const modulePath = this._resolveModule(from, moduleName);

const module = await this.loadEsmModule(modulePath);
await module.link((specifier: string, referencingModule: VMModule) =>
this.loadEsmModule(
this._resolveModule(referencingModule.identifier, specifier),
),
);
await module.evaluate();
}

requireModule<T = unknown>(
from: Config.Path,
moduleName?: string,
@@ -795,23 +890,8 @@ class Runtime {
Object.defineProperty(localModule, 'require', {
value: this._createRequireImplementation(localModule, options),
});
const transformedFile = this._scriptTransformer.transform(
filename,
this._getFullTransformationOptions(options),
this._cacheFS[filename],
);

// we only care about non-internal modules
if (!options || !options.isInternalModule) {
this._fileTransforms.set(filename, transformedFile);
}

if (transformedFile.sourceMapPath) {
this._sourceMapRegistry[filename] = transformedFile.sourceMapPath;
if (transformedFile.mapCoverage) {
this._needsCoverageMapped.add(filename);
}
}
const transformedFile = this.transformFile(filename, options);

let compiledFunction: ModuleWrapper | null = null;

@@ -907,6 +987,27 @@ class Runtime {
this._currentlyExecutingModulePath = lastExecutingModulePath;
}

private transformFile(filename: string, options?: InternalModuleOptions) {
const transformedFile = this._scriptTransformer.transform(
filename,
this._getFullTransformationOptions(options),
this._cacheFS[filename],
);

// we only care about non-internal modules
if (!options || !options.isInternalModule) {
this._fileTransforms.set(filename, transformedFile);
}

if (transformedFile.sourceMapPath) {
this._sourceMapRegistry[filename] = transformedFile.sourceMapPath;
if (transformedFile.mapCoverage) {
this._needsCoverageMapped.add(filename);
}
}
return transformedFile;
}

private createScriptFromCode(scriptSource: string, filename: string) {
try {
return new Script(this.wrapCodeInModuleWrapper(scriptSource), {
@@ -932,6 +1033,24 @@ class Runtime {
return require(moduleName);
}

private _importCoreModule(moduleName: string, context: VMContext) {
const required = this._requireCoreModule(moduleName);

return new SyntheticModule(
['default', ...Object.keys(required)],
function () {
// @ts-ignore: TS doesn't know what `this` is
this.setExport('default', required);
Object.entries(required).forEach(([key, value]) => {
// @ts-ignore: TS doesn't know what `this` is
this.setExport(key, value);
});
},
// should identifier be `node://${moduleName}`?
{context, identifier: moduleName},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally stack traces will show e.g. fs.js as the identifier. Have you tried a fs.readFileSync('/does/not/exist') to see what shows up with this logic?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't seem to show up at all

$ node --experimental-vm-modules file.mjs
(node:93098) ExperimentalWarning: The ESM module loader is experimental.
Error: ENOENT: no such file or directory, open '/does/not/exist'
    at Object.openSync (fs.js:462:3)
    at Module.readFileSync (fs.js:364:35)
    at myTestingModule.js:3:6
    at SourceTextModule.evaluate (internal/vm/module.js:221:32)
    at file:///Users/simen/repos/jest/file.mjs:35:22 {
  errno: -2,
  syscall: 'open',
  code: 'ENOENT',
  path: '/does/not/exist'
}
(node:93098) ExperimentalWarning: VM Modules is an experimental feature. This feature could change at any time

Test:

import {SourceTextModule, SyntheticModule} from 'vm';
import {createRequire} from 'module';

const require = createRequire(import.meta.url);
const fs = require('fs');

const required = fs;

const module = new SourceTextModule(
  `
  import * as fs from 'fs';
  fs.readFileSync('/does/not/exist');
`,
  {identifier: 'myTestingModule.js'},
);

module
  .link(async () => {
    const m = new SyntheticModule(
      ['default', ...Object.keys(required)],
      function () {
        this.setExport('default', required);
        Object.entries(required).forEach(([key, value]) => {
          this.setExport(key, value);
        });
      },
      {identifier: 'fs halla'},
    );

    await m.link(() => {});
    await m.evaluate();

    return m;
  })
  .then(() => module.evaluate())
  .catch(error => {
    process.exitCode = 1;

    console.error(error);
  });

);
}

private _getMockedNativeModule(): typeof nativeModule.Module {
if (this._moduleImplementation) {
return this._moduleImplementation;
@@ -966,13 +1085,20 @@ class Runtime {
// should we implement the class ourselves?
class Module extends nativeModule.Module {}

Object.entries(nativeModule.Module).forEach(([key, value]) => {
// @ts-ignore
Module[key] = value;
});

Module.Module = Module;

if ('createRequire' in nativeModule) {
Module.createRequire = createRequire;
}
if ('createRequireFromPath' in nativeModule) {
Module.createRequireFromPath = (filename: string | URL) => {
Module.createRequireFromPath = function createRequireFromPath(
filename: string | URL,
) {
if (typeof filename !== 'string') {
const error = new TypeError(
`The argument 'filename' must be string. Received '${filename}'.${
@@ -989,7 +1115,7 @@ class Runtime {
};
}
if ('syncBuiltinESMExports' in nativeModule) {
Module.syncBuiltinESMExports = () => {};
Module.syncBuiltinESMExports = function syncBuiltinESMExports() {};
}

this._moduleImplementation = Module;
44 changes: 44 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
@@ -2435,6 +2435,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.1.tgz#49a2a83df9d26daacead30d0ccc8762b128d53c7"
integrity sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g==

"@types/normalize-package-data@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==

"@types/prettier@^1.19.0":
version "1.19.1"
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f"
@@ -9178,6 +9183,11 @@ levn@^0.3.0, levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"

lines-and-columns@^1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=

list-item@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/list-item/-/list-item-1.1.1.tgz#0c65d00e287cb663ccb3cb3849a77e89ec268a56"
@@ -11200,6 +11210,16 @@ parse-json@^4.0.0:
error-ex "^1.3.1"
json-parse-better-errors "^1.0.1"

parse-json@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.0.0.tgz#73e5114c986d143efa3712d4ea24db9a4266f60f"
integrity sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==
dependencies:
"@babel/code-frame" "^7.0.0"
error-ex "^1.3.1"
json-parse-better-errors "^1.0.1"
lines-and-columns "^1.1.6"

parse-node-version@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b"
@@ -12272,6 +12292,15 @@ read-pkg-up@^3.0.0:
find-up "^2.0.0"
read-pkg "^3.0.0"

read-pkg-up@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507"
integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==
dependencies:
find-up "^4.1.0"
read-pkg "^5.2.0"
type-fest "^0.8.1"

read-pkg@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
@@ -12299,6 +12328,16 @@ read-pkg@^3.0.0:
normalize-package-data "^2.3.2"
path-type "^3.0.0"

read-pkg@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc"
integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==
dependencies:
"@types/normalize-package-data" "^2.4.0"
normalize-package-data "^2.5.0"
parse-json "^5.0.0"
type-fest "^0.6.0"

read@1, read@~1.0.1:
version "1.0.7"
resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4"
@@ -14293,6 +14332,11 @@ type-fest@^0.3.0, type-fest@^0.3.1:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1"
integrity sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==

type-fest@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==

type-fest@^0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48"