From 6f61fa31a557ff4430ad5575299595d0f397cb11 Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Thu, 23 Apr 2020 11:11:26 -0500 Subject: [PATCH 01/21] fix: resolve symlinked packages during configureWebpack --- .gitignore | 5 +- .../lib/Utilities/getClientConfig.js | 13 +- .../lib/WebpackTools/MagentoResolver.js | 122 ++++++++++++-- .../__tests__/MagentoResolver.spec.js | 151 +++++++++++++----- .../resolverContext/localModule1/index.jsx | 1 + .../resolverContext/localModule1/index.wasm | 1 + .../resolverContext/localModule2.js | 2 + .../node_modules/depModule1/index.js | 1 + .../node_modules/depModule1/someFeature.ce.js | 1 + .../node_modules/depModule1/someFeature.ee.js | 1 + .../__fixtures__/resolverContext/package.json | 13 ++ .../__tests__/configureWebpack.spec.js | 24 +-- .../lib/WebpackTools/configureWebpack.js | 27 +++- packages/pwa-buildpack/package.json | 1 + yarn.lock | 2 +- 15 files changed, 286 insertions(+), 79 deletions(-) create mode 100644 packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/localModule1/index.jsx create mode 100644 packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/localModule1/index.wasm create mode 100644 packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/localModule2.js create mode 100644 packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/node_modules/depModule1/index.js create mode 100644 packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/node_modules/depModule1/someFeature.ce.js create mode 100644 packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/node_modules/depModule1/someFeature.ee.js create mode 100644 packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/package.json diff --git a/.gitignore b/.gitignore index b8147dbbf2..4eb3b139a3 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,7 @@ lerna-debug.log yarn-error.log # Packages that build partially transpiled ES modules put them here docker/certs -.history \ No newline at end of file +.history + +## exception: commit node_modules folders in test fixtures +!**/__fixtures__/*/node_modules diff --git a/packages/pwa-buildpack/lib/Utilities/getClientConfig.js b/packages/pwa-buildpack/lib/Utilities/getClientConfig.js index a7b0036b06..8cf5eab4af 100644 --- a/packages/pwa-buildpack/lib/Utilities/getClientConfig.js +++ b/packages/pwa-buildpack/lib/Utilities/getClientConfig.js @@ -7,7 +7,6 @@ const TerserPlugin = require('terser-webpack-plugin'); const PWADevServer = require('../WebpackTools/PWADevServer'); const RootComponentsPlugin = require('../WebpackTools/plugins/RootComponentsPlugin'); const UpwardIncludePlugin = require('../WebpackTools/plugins/UpwardIncludePlugin'); -const MagentoResolver = require('../WebpackTools/MagentoResolver'); function isDevServer() { return process.argv.find(v => v.includes('webpack-dev-server')); @@ -21,7 +20,8 @@ module.exports = async function({ hasFlag, vendor, projectConfig, - stats + stats, + resolve }) { let vendorTest = '[\\/]node_modules[\\/]'; @@ -29,8 +29,6 @@ module.exports = async function({ vendorTest += `(${vendor.join('|')})[\\\/]`; } - const isEE = projectConfig.env.MAGENTO_BACKEND_EDITION === 'EE'; - debug('Creating client config'); const config = { @@ -117,12 +115,7 @@ module.exports = async function({ } ] }, - resolve: await MagentoResolver.configure({ - paths: { - root: context - }, - isEE - }), + resolve, plugins: [ new RootComponentsPlugin({ rootComponentsDirs: [ diff --git a/packages/pwa-buildpack/lib/WebpackTools/MagentoResolver.js b/packages/pwa-buildpack/lib/WebpackTools/MagentoResolver.js index 5a5cf749a8..aea9c278bb 100644 --- a/packages/pwa-buildpack/lib/WebpackTools/MagentoResolver.js +++ b/packages/pwa-buildpack/lib/WebpackTools/MagentoResolver.js @@ -1,11 +1,70 @@ -const optionsValidator = require('../util/options-validator'); -const validateConfig = optionsValidator('MagentoResolver', { - 'paths.root': 'string' -}); -module.exports = { - validateConfig, - async configure(options) { - const { isEE, ...restOptions } = options; +/** + * Create a Webpack configuration object customized for your project. + * @module Buildpack/WebpackTools + */ + +const fs = require('fs'); +const { CachedInputFileSystem, ResolverFactory } = require('enhanced-resolve'); + +/** + * @typedef {Object} Buildpack/WebpackTools~MagentoResolverOptions + * @module MagentoResolver + * @property {boolean} isEE Resolve Magento Commerce (`*.ee.js`) modules instead of Magento Open Source `*.ce.js` modules + * @property {Object} paths Filesystem paths to resolve from + */ + +/** + * Wrapper for an + * [enhanced-resolver](https://github.com/webpack/enhanced-resolve/) which can + * resolve paths according to Webpack rules before the Webpack compiler has + * been constructed. + * + * @class Buildpack/WebpackTools~MagentoResolver + */ +class MagentoResolver { + /** + * Legacy method for returning Webpack `resolve` config options as before + * + * @deprecated Use `new MagentoResolver(options).config` instead + * @static + * @param {Buildpack/WebpackTools~MagentoResolverOptions} options + * @returns {webpack~WebpackResolveOptions} + */ + static async configure(options) { + const resolver = new MagentoResolver(options); + return resolver.config; + } + /** + * + * Lazy load an EnhancedResolver instance with a cached file system, + * configured from our constructor options. + * + * @ignore + * @private + * @readonly + */ + get myResolver() { + if (!this._resolver) { + this._resolver = ResolverFactory.createResolver({ + // Typical usage will consume the `fs` + `CachedInputFileSystem`, which wraps Node.js `fs` to add caching. + fileSystem: new CachedInputFileSystem(fs, 4000), + ...this.config + }); + } + return this._resolver; + } + /** + * A MagentoResolver can asynchronously resolve `require` and `import` + * strings the same way the built PWA will. + * @param {Buildpack/WebpackTools~MagentoResolverOptions} options + */ + constructor(options) { + const { isEE, paths, ...restOptions } = options; + if (!paths || typeof paths.root !== 'string') { + throw new Error( + 'new MagentoResolver(options) requires "options.paths.root" to be a string' + ); + } const extensions = [ '.wasm', '.mjs', @@ -15,13 +74,50 @@ module.exports = { '.json', '.graphql' ]; - validateConfig('.configure()', restOptions); - return { + /** @ignore */ + this._root = paths.root; + + /** @type {webpack~WebpackResolveOptions} */ + this.config = { alias: {}, - modules: [options.paths.root, 'node_modules'], + modules: [this._root, 'node_modules'], mainFiles: ['index'], mainFields: ['esnext', 'es2015', 'module', 'browser', 'main'], - extensions + extensions, + ...restOptions }; + /** @ignore */ + this._context = {}; + /** @ignore */ + this._requestContext = {}; + } + /** + * Asynchronously resolve a path the same way Webpack would given the + * current configuration. + * @async + * @param {string} request A module name or path, as in `require('')` or `import foo from ''`. + * @returns {Promise} Absolute filesystem location. + */ + async resolve(request) { + return new Promise((res, rej) => { + try { + this.myResolver.resolve( + this._context, + this._root, + request, + this._requestContext, + (err, filepath) => { + if (err) { + return rej(err); + } + res(filepath); + } + ); + } catch (e) { + rej(e); + } + }); } -}; +} + +module.exports = MagentoResolver; diff --git a/packages/pwa-buildpack/lib/WebpackTools/__tests__/MagentoResolver.spec.js b/packages/pwa-buildpack/lib/WebpackTools/__tests__/MagentoResolver.spec.js index fd81160937..c828a0b061 100644 --- a/packages/pwa-buildpack/lib/WebpackTools/__tests__/MagentoResolver.spec.js +++ b/packages/pwa-buildpack/lib/WebpackTools/__tests__/MagentoResolver.spec.js @@ -1,49 +1,120 @@ +const path = require('path'); const MagentoResolver = require('../MagentoResolver'); -test('static configure() produces a webpack resolver config with .ee.js extension if backend is enterprise edition', async () => { - const extensions = [ - '.wasm', - '.mjs', - '.ee.js', - '.js', - '.jsx', - '.json', - '.graphql' - ]; - await expect( - MagentoResolver.configure({ paths: { root: 'fakeRoot' }, isEE: true }) - ).resolves.toEqual({ - alias: {}, - modules: ['fakeRoot', 'node_modules'], - mainFiles: ['index'], - mainFields: ['esnext', 'es2015', 'module', 'browser', 'main'], - extensions +describe('legacy functionality', () => { + test('static configure() produces a webpack resolver config with .ee.js extension if backend is enterprise edition', async () => { + const extensions = [ + '.wasm', + '.mjs', + '.ee.js', + '.js', + '.jsx', + '.json', + '.graphql' + ]; + await expect( + MagentoResolver.configure({ + paths: { root: 'fakeRoot' }, + isEE: true + }) + ).resolves.toEqual({ + alias: {}, + modules: ['fakeRoot', 'node_modules'], + mainFiles: ['index'], + mainFields: ['esnext', 'es2015', 'module', 'browser', 'main'], + extensions + }); }); + + test('static configure() produces a webpack resolver config with .ce.js extension if backend is not enterprise edition', async () => { + const extensions = [ + '.wasm', + '.mjs', + '.ce.js', + '.js', + '.jsx', + '.json', + '.graphql' + ]; + await expect( + MagentoResolver.configure({ + paths: { root: 'fakeRoot' }, + isEE: false + }) + ).resolves.toEqual({ + alias: {}, + modules: ['fakeRoot', 'node_modules'], + mainFiles: ['index'], + mainFields: ['esnext', 'es2015', 'module', 'browser', 'main'], + extensions + }); + }); + + test('static configure() throws if required paths are missing', async () => { + await expect( + MagentoResolver.configure({ paths: { root: false } }) + ).rejects.toThrow('paths.root'); + }); +}); + +const context = path.resolve(__dirname, './__fixtures__/resolverContext'); +test('creates a promisifed resolver which respects provided config', async () => { + const resolver = new MagentoResolver({ + paths: { + root: context + } + }); + + await Promise.all( + [ + // root-relative + ['.', 'localModule2.js'], + // extension precedence + ['localModule1', './localModule1/index.wasm'], + // module folder + ['depModule1', 'node_modules/depModule1/index.js'], + // recursively resolve parent folder deps + ['jest', require.resolve('jest')] + ].map(([request, expected]) => + expect(resolver.resolve(request)).resolves.toBe( + path.resolve(context, expected) + ) + ) + ); + + await expect(resolver.resolve('./missing')).rejects.toThrowError(); }); -test('static configure() produces a webpack resolver config with .ce.js extension if backend is not enterprise edition', async () => { - const extensions = [ - '.wasm', - '.mjs', - '.ce.js', - '.js', - '.jsx', - '.json', - '.graphql' - ]; - await expect( - MagentoResolver.configure({ paths: { root: 'fakeRoot' }, isEE: false }) - ).resolves.toEqual({ - alias: {}, - modules: ['fakeRoot', 'node_modules'], - mainFiles: ['index'], - mainFields: ['esnext', 'es2015', 'module', 'browser', 'main'], - extensions +test('rejects if it has been configured weird', async () => { + const resolver = new MagentoResolver({ + paths: { + root: context + } }); + + resolver._resolver = {}; + + await expect(resolver.resolve('./bogus')).rejects.toThrowError(); }); -test('static configure() throws if required paths are missing', async () => { - await expect( - MagentoResolver.configure({ paths: { root: false } }) - ).rejects.toThrow('paths.root must be of type string'); +test('uses *.ee.js or *.ce.js depending on isEE boolean', async () => { + const ceResolver = new MagentoResolver({ + paths: { + root: context + }, + isEE: false + }); + await expect(ceResolver.resolve('depModule1/someFeature')).resolves.toBe( + path.resolve(context, 'node_modules/depModule1/someFeature.ce.js') + ); + + const eeResolver = new MagentoResolver({ + paths: { + root: context + }, + isEE: true + }); + await expect(eeResolver.resolve('depModule1/someFeature')).resolves.toBe( + path.resolve(context, 'node_modules/depModule1/someFeature.ee.js') + ); }); diff --git a/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/localModule1/index.jsx b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/localModule1/index.jsx new file mode 100644 index 0000000000..6d9723e638 --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/localModule1/index.jsx @@ -0,0 +1 @@ +export const Hi = () =>

hi

; diff --git a/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/localModule1/index.wasm b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/localModule1/index.wasm new file mode 100644 index 0000000000..d45775708e --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/localModule1/index.wasm @@ -0,0 +1 @@ +fake wasm fixture diff --git a/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/localModule2.js b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/localModule2.js new file mode 100644 index 0000000000..2c63c08510 --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/localModule2.js @@ -0,0 +1,2 @@ +{ +} diff --git a/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/node_modules/depModule1/index.js b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/node_modules/depModule1/index.js new file mode 100644 index 0000000000..49d504cc37 --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/node_modules/depModule1/index.js @@ -0,0 +1 @@ +module.exports = () => 'depModule1'; diff --git a/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/node_modules/depModule1/someFeature.ce.js b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/node_modules/depModule1/someFeature.ce.js new file mode 100644 index 0000000000..18183bae56 --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/node_modules/depModule1/someFeature.ce.js @@ -0,0 +1 @@ +module.exports = () => 'ce feature'; diff --git a/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/node_modules/depModule1/someFeature.ee.js b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/node_modules/depModule1/someFeature.ee.js new file mode 100644 index 0000000000..5c616942df --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/node_modules/depModule1/someFeature.ee.js @@ -0,0 +1 @@ +module.exports = () => 'ee feature'; diff --git a/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/package.json b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/package.json new file mode 100644 index 0000000000..405a8ef7be --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/resolverContext/package.json @@ -0,0 +1,13 @@ +{ + "name": "fake-resolver-context", + "version": "1.0.0", + "description": "", + "main": "localModule1/index.jsx", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "module": "localModule2.js" +} diff --git a/packages/pwa-buildpack/lib/WebpackTools/__tests__/configureWebpack.spec.js b/packages/pwa-buildpack/lib/WebpackTools/__tests__/configureWebpack.spec.js index 4067d1a7b7..c2faa3d834 100644 --- a/packages/pwa-buildpack/lib/WebpackTools/__tests__/configureWebpack.spec.js +++ b/packages/pwa-buildpack/lib/WebpackTools/__tests__/configureWebpack.spec.js @@ -1,4 +1,3 @@ -jest.mock('fs'); jest.mock('pertain'); jest.mock('pkg-dir'); jest.mock('webpack-assets-manifest'); @@ -8,7 +7,10 @@ jest.mock('../PWADevServer'); jest.mock('../../BuildBus/declare-base'); +const path = require('path'); const fs = require('fs'); +const stat = jest.spyOn(fs, 'stat'); + const { SyncHook } = require('tapable'); const declareBase = require('../../BuildBus/declare-base'); const pertain = require('pertain'); @@ -43,7 +45,7 @@ beforeEach(() => { }); const mockStat = (dir, file, err = null) => { - fs.stat.mockImplementationOnce((_, callback) => + stat.mockImplementationOnce((_, callback) => callback(err, { isDirectory: () => dir, isFile: () => file }) ); }; @@ -197,14 +199,14 @@ test('handles special flags', async () => { .productionEnvironment(); const special = { - jest: { + localModule1: { esModules: true, cssModules: true, graphqlQueries: true, rootComponents: true, upward: true }, - 'pkg-dir': { + depModule1: { esModules: true, cssModules: true, graphqlQueries: true, @@ -218,19 +220,19 @@ test('handles special flags', async () => { const specialFeaturesTap = jest.fn(x => x); specialFeaturesHook.tap('configureWebpack.spec.js', specialFeaturesTap); const { clientConfig } = await configureWebpack({ - context: './fake/different/context', + context: path.resolve(__dirname, '__fixtures__/resolverContext'), vendor: ['jest'], special }); - expect( - clientConfig.module.rules.find(({ use }) => - use.some(({ loader }) => /^graphql\-tag/.test(loader)) - ).include - ).toHaveLength(3); + + const gqlLoader = ({ use }) => + use.some(({ loader }) => /^graphql\-tag/.test(loader)); + expect(clientConfig.module.rules.find(gqlLoader).include).toHaveLength(3); + expect(RootComponentsPlugin).toHaveBeenCalled(); expect( RootComponentsPlugin.mock.calls[0][0].rootComponentsDirs.some(entry => - entry.includes('jest') + entry.includes('localModule1') ) ).toBeTruthy(); expect(declareBase).toHaveBeenCalledTimes(1); diff --git a/packages/pwa-buildpack/lib/WebpackTools/configureWebpack.js b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack.js index c9bd628340..5e0fb75304 100644 --- a/packages/pwa-buildpack/lib/WebpackTools/configureWebpack.js +++ b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack.js @@ -6,6 +6,7 @@ const pkgDir = require('pkg-dir'); const loadEnvironment = require('../Utilities/loadEnvironment'); const getClientConfig = require('../Utilities/getClientConfig'); const getServiceWorkerConfig = require('../Utilities/getServiceWorkerConfig'); +const MagentoResolver = require('../WebpackTools/MagentoResolver'); const BuildBus = require('../BuildBus'); const BuildBusPlugin = require('./plugins/BuildBusPlugin'); @@ -83,25 +84,44 @@ async function configureWebpack(options) { output: path.resolve(context, 'dist') }; + const isEE = projectConfig.env.MAGENTO_BACKEND_EDITION === 'EE'; + + const resolverOpts = { + paths: { + root: context, + isEE + } + }; + if (options.alias) { + resolverOpts.alias = { ...options.alias }; + } + + const magentoResolver = new MagentoResolver(resolverOpts); + const special = options.special || {}; bus.getTargetsOf('@magento/pwa-buildpack').specialFeatures.call(special); + // Resolve every module listed in the `special` object into an absolute + // filesystem path. Will be used as a test for the loader rules for each + // of these feature flags. const features = await Promise.all( Object.entries(special).map(async ([packageName, flags]) => [ - await pkgDir(path.dirname(require.resolve(packageName))), + packageName, + await pkgDir( + path.dirname(await magentoResolver.resolve(packageName)) + ), flags ]) ); const hasFlag = flag => features.reduce( - (hasIt, [packagePath, flags]) => + (hasIt, [, packagePath, flags]) => flags[flag] ? [...hasIt, packagePath] : hasIt, [] ); const mode = getMode(options.env, projectConfig); - const configOptions = { mode, context, @@ -109,6 +129,7 @@ async function configureWebpack(options) { paths, hasFlag, projectConfig, + resolve: magentoResolver.config, stats }; diff --git a/packages/pwa-buildpack/package.json b/packages/pwa-buildpack/package.json index dc757cba94..06057adcc3 100644 --- a/packages/pwa-buildpack/package.json +++ b/packages/pwa-buildpack/package.json @@ -36,6 +36,7 @@ "debug": "~4.1.1", "devcert": "~1.1.0", "dotenv": "~6.2.0", + "enhanced-resolve": "~4.1.1", "envalid": "~4.2.2", "errorhandler": "~1.5.1", "execa": "~1.0.0", diff --git a/yarn.lock b/yarn.lock index 689e92cf57..b8531f1591 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7295,7 +7295,7 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -enhanced-resolve@^4.1.0: +enhanced-resolve@^4.1.0, enhanced-resolve@~4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz#2937e2b8066cd0fe7ce0990a98f0d71a35189f66" integrity sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA== From 8a192260926ea0d7f1a7dafb0d6524ad2e975d7e Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Thu, 23 Apr 2020 12:03:39 -0500 Subject: [PATCH 02/21] chore(debugging): improve logging and debuggability of BuildBus --- .../pwa-buildpack/lib/BuildBus/BuildBus.js | 166 +++++++-- packages/pwa-buildpack/lib/BuildBus/Target.js | 181 ++++++++-- .../lib/BuildBus/TargetProvider.js | 128 ++++--- .../pwa-buildpack/lib/BuildBus/Trackable.js | 116 +++++- .../lib/BuildBus/__tests__/BuildBus.spec.js | 336 +++++++++++------- .../lib/BuildBus/__tests__/Target.spec.js | 21 +- .../BuildBus/__tests__/TargetProvider.spec.js | 39 +- .../lib/BuildBus/__tests__/Trackable.spec.js | 107 +++--- .../__snapshots__/BuildBus.spec.js.snap | 4 +- .../__snapshots__/Target.spec.js.snap | 6 +- .../__snapshots__/Trackable.spec.js.snap | 4 +- .../lib/BuildBus/mapHooksToTargets.js | 90 +++++ .../lib/Utilities/getEnvVarDefinitions.js | 1 + .../lib/WebpackTools/PWADevServer.js | 2 +- .../lib/WebpackTools/configureWebpack.js | 17 +- .../WebpackTools/plugins/BuildBusPlugin.js | 41 ++- .../plugins/__tests__/BuildBusPlugin.spec.js | 15 +- 17 files changed, 960 insertions(+), 314 deletions(-) create mode 100644 packages/pwa-buildpack/lib/BuildBus/mapHooksToTargets.js diff --git a/packages/pwa-buildpack/lib/BuildBus/BuildBus.js b/packages/pwa-buildpack/lib/BuildBus/BuildBus.js index 44709c936b..1ebbb88bbe 100644 --- a/packages/pwa-buildpack/lib/BuildBus/BuildBus.js +++ b/packages/pwa-buildpack/lib/BuildBus/BuildBus.js @@ -1,57 +1,137 @@ +/** + * @module Buildpack/BuildBus + */ + const path = require('path'); const pertain = require('pertain'); const TargetProvider = require('./TargetProvider'); const Trackable = require('./Trackable'); +/** + * @ignore + * A given project root (context) should always produce the same bus, so we can + * cache the heavy pertain operation. + */ const busCache = new Map(); -const FACTORY = Symbol('FORCE_BUILDBUS_CREATE_FACTORY'); +/** + * @ignore + * A way to strongly encourage users to use the BuildBus.for factory and not the + * BuildBus constructor. + */ +const INVOKE_FLAG = Symbol.for('FORCE_BUILDBUS_CREATE_FACTORY'); + +/** + * Manager of dependencies' participation in project builds and tasks. Broker + * for dependencies with Targets to interact with each other. + * + * @example Get or create the BuildBus for the package.json file in `./project-dir`, then bind targets, then call a target. + * const bus = BuildBus.for('./project-dir); + * bus.init(); + * bus.getTargetsOf('my-extension').myTarget.call(); + * + * @class BuildBus + * @extends {Trackable} + */ class BuildBus extends Trackable { + /** + * Remove the cached BuildBus for the given context. + * + * @static + * @hideconstructor + * @param {string} context + * @memberof BuildBus + */ static clear(context) { - busCache.delete(context); + const absContext = path.resolve(context); + busCache.delete(absContext); } + /** + * Remove all cached BuildBus objects. + * + * @static + * @memberof BuildBus + */ static clearAll() { busCache.clear(); } + /** + * Get or create the BuildBus for the given context. + * This factory is the supported way to construct BuildBuses. + * It caches BuildBuses and connects them to the logging infrastructure. + * + * @static + * @param {string} context + * @returns {BuildBus} + * @memberof BuildBus + */ static for(context) { const absContext = path.resolve(context); if (busCache.has(absContext)) { return busCache.get(absContext); } - const id = path.dirname(absContext); - const bus = new BuildBus(FACTORY, absContext, id); + const bus = new BuildBus(INVOKE_FLAG, absContext); busCache.set(absContext, bus); - bus.identify(id, console.log); - bus.runPhase('declare'); - bus.runPhase('intercept'); + bus.attach(context, console.log); //usually replaced w/ webpack logger return bus; } - constructor(invoker, context, id) { + /** + * @hideconstructor + */ + constructor(invoker, context) { super(); - if (invoker !== FACTORY) { + if (invoker !== INVOKE_FLAG) { throw new Error( `BuildBus must not be created with its constructor. Use the static factory method BuildBus.for(context) instead.` ); } + this._requestTargets = this._requestTargets.bind(this); + this._hasRun = {}; this.context = context; - this.id = id; this.targetProviders = new Map(); + this._getEnvOverrides(); } - getTargetsOf(depName) { - return this._getTargets(depName).own; + _getEnvOverrides() { + const envDepsAdditional = process.env.BUILDBUS_DEPS_ADDITIONAL; + this._depsAdditional = envDepsAdditional + ? envDepsAdditional.split(',') + : []; + } + _getPertaining(phase) { + return pertain(this.context, this._phaseToSubject(phase), foundDeps => + foundDeps.concat(this._depsAdditional) + ).map(dep => ({ + name: dep.name, + [phase]: require(dep.path) + })); } _getTargets(depName) { const targetProvider = this.targetProviders.get(depName); if (!targetProvider) { throw new Error( `${ - this.id + this._identifier }: Cannot getTargetsOf("${depName}"): ${depName} has not yet declared` ); } return targetProvider; } - _requestTargets(source, requested) { + _phaseToSubject(phase) { + return `pwa-studio.targets.${phase}`; + } + /** + * Method which connects TargetProviders to each other. BuildBus passes + * this method to TargetProvider as its `getExternalTargets` callback. + * + * @private + * @param {Object} requestor - Dependency requesting the targets. + * @param {string} requestor.name - Name of the dependency requesting targets. + * @param {string} requested - Name of the dependency whose targets are being requested. + * @returns {Object} - Object whose strings are target names and whose values are the Targets of the external dependency. + * @memberof BuildBus + */ + _requestTargets(requestor, requested) { + const source = requestor.name; this.track('requestTargets', { source, requested }); const targets = {}; @@ -63,19 +143,65 @@ class BuildBus extends Trackable { } return targets; } + /** + * Get {@link module:Buildpack/BuildBus/TargetProvider TargetProvider} for + * the given named dependency. + * Use this to retrieve and run targets in top-level code, when you have + * a reference to the BuildBus. + * Declare and intercept functions should not, and cannot, use this method. + * Instead, they retrieve external targets through their `targets.of()` + * methods. + * + * @param {string} depName + * @returns {module:Buildpack/BuildBus/TargetProvider} + * @memberof BuildBus + */ + getTargetsOf(depName) { + return this._getTargets(depName).own; + } + /** + * Run the two defined phases, `declare` and `intercept`, in order. + * This binds all targets which the BuildBus can find by analyzing + * dependencies in the project package file.. + * + * @memberof BuildBus + * @returns {BuildBus} Chainable. + */ + init() { + this.runPhase('declare'); + this.runPhase('intercept'); + return this; + } + /** + * Run the specified phase. The BuildBus finds all dependencies which say + * in their `package.json` that they need to run code in this phase. + * + * @example Find all dependencies whith have `pwa-studio: { targets: { declare: './path/to/js' }} defined, and run those functions. + * bus.runPhase('declare') + * + * @param {string} phase 'declare' or 'intercept' + * @memberof BuildBus + */ runPhase(phase) { - this.track('runPhase', phase); - pertain(this.context, `pwa-studio.targets.${phase}`).forEach(dep => { + if (this._hasRun[phase]) { + return; + } + this._hasRun[phase] = true; + this.track('runPhase', { phase }); + const pertaining = this._getPertaining(phase); + pertaining.forEach(dep => { let targetProvider = this.targetProviders.get(dep.name); if (!targetProvider) { - targetProvider = new TargetProvider(this, dep, extDep => - this._requestTargets(dep.name, extDep) + targetProvider = new TargetProvider( + this, + dep, + this._requestTargets ); this.targetProviders.set(dep.name, targetProvider); } targetProvider.phase = phase; - this.track('requireDep', phase, dep.name, dep.path); - require(dep.path)(targetProvider); + this.track('requireDep', { phase, dep }); + dep[phase](targetProvider); targetProvider.phase = null; }); } diff --git a/packages/pwa-buildpack/lib/BuildBus/Target.js b/packages/pwa-buildpack/lib/BuildBus/Target.js index a8eafea958..4b0a6908ea 100644 --- a/packages/pwa-buildpack/lib/BuildBus/Target.js +++ b/packages/pwa-buildpack/lib/BuildBus/Target.js @@ -1,52 +1,172 @@ +/** + * @module Buildpack/BuildBus + */ + +const Trackable = require('./Trackable'); + +const interceptionTypes = { + tap: 'sync', + tapAsync: 'async', + tapPromise: 'promise' +}; + /** * Represents an edge on the graph, or a "route" between stops, created between * two extensions when one of them references the target(s) of another. When * extension Foo requests targets of extension Bar, the BuildBus provides an * Target instead of the literal Tapable instance. This enables * better logging, error checking, and validation. + * + * @class Target + * @extends {Trackable} */ -const Trackable = require('./Trackable'); - class Target extends Trackable { + static get SOURCE_SEP() { + return '::'; + } constructor(owner, requestor, targetName, tapableType, tapable) { super(); this._owner = owner; this._tapable = tapable; - this._targetName = targetName; this._requestor = requestor; - this.identify(`${targetName}[${tapableType}]`, owner); - } - _invokeTap(method, customName, tap) { - let interceptor = tap; - let tapName = this._requestor; - if (interceptor) { - // a custom name was passed! - tapName = `${this._requestor}:${customName}`; + this.name = targetName; + this.type = tapableType; + this.attach(`${targetName}[${tapableType}]`, this._owner); + this._populateFlags(); + } + /** @ignore */ + _invokeTap(method, info, fn) { + const tap = { + name: this._requestor + }; + let customName; + if (typeof info === 'object') { + // a tapInfo object was passed! + customName = info.name; + Object.assign(tap, info); + } else if (fn) { + // a custom name and tap function were passed! + customName = info; + tap.fn = fn; } else { - interceptor = customName; + // a tap function was passed with no custom name + tap.fn = info; } - this.track(method, { - requestor: this._requestor, - interceptor: tapName + if (customName) { + tap.name += Target.SOURCE_SEP + customName; + } + this.track('intercept', { + source: this._requestor, + type: interceptionTypes[method] }); - return this._tapable[method](tapName, interceptor); + return this._tapable[method](tap); + } + /** @ignore */ + _populateFlags() { + /** + * Runs asynchronously. + * Can only be intercepted with `.tapAsync()` or `.tapPromise()`. + * Can only be run with `.callAsync()` or `.promise()`. + * @type {boolean} + */ + this.async = this.type.includes('Async'); + + /** + * When called, the first interceptor which returns a value will cancel + * the rest of the interceptors and return that value to the caller. + * @type {boolean} + */ + this.bail = this.type.includes('Bail'); + /** + * The first interceptor receives the arguments to the call method. + * Subsequent interceptors receive the return value of the previous + * interceptor to be run. Waterfall hooks allow interceptors to act as + * composed functions. + * @type {boolean} + */ + this.waterfall = this.type.includes('Waterfall'); + + /** + * Runs asynchronously and in parallel. Interceptors are called in + * subscription order, but concurrently without waiting for the previous + * interceptors to finish executing. + * @type {boolean} + */ + this.parallel = this.type.includes('Parallel'); + + /** + * Calls interceptors in subscription order and waits for each + * interceptor to return before calling the next. May run synchronously + * or asynchronously. + * @type {boolean} + */ + this.series = !this.async || this.type.includes('Series'); } + /** + * Run `.call(...args)` on the underlying Tapable Hook. + * Calls interceptors synchronously and in subscription order with the + * provided arguments. Returns the final value if it's a Waterfall target, + * or the value returned by the first interceptor that returns a value if + * it's a Bail target. + * @memberof Target + */ call(...args) { - this.track('call', ...args); - return this._tapable.call(...args); + this.track('beforeCall', { type: 'sync', args }); + const returned = this._tapable.call(...args); + this.track('afterCall', { type: 'sync', returned }); + return returned; } - callAsync(...args) { - this.track('callAsync', ...args); + /** + * Run `.callAsync(...args)` on the underlying Tapable Hook. Calls + * interceptors asynchronously with the provided arguments. Depending on + * the Target type, calls interceptors in parallel or in subscription + * order. Last argument must be a callback. It will be invoked when all + * interceptors have run, or when the first returning interceptor has run + * if it's a Bail target. + * @memberof Target + */ + callAsync(...incomingArgs) { + const callbackIndex = incomingArgs.length - 1; + const callback = incomingArgs[callbackIndex]; + const args = incomingArgs.slice(0, callbackIndex); + this.track('beforeCall', { type: 'async', args }); + args.push((...returned) => { + this.track('afterCall', { type: 'async', returned }); + callback(...returned); + }); return this._tapable.callAsync(...args); } + /** + * Run `.intercept(options)` on the underlying Tapable Hook. + * Can register meta-interceptors for other activity on this target. + * Use only for logging and debugging. + * @memberof Target + */ intercept(options) { - this.track('tapableIntercept', options); + this.track('intercept', { + type: 'intercept', + source: this._requestor, + options + }); return this._tapable.intercept(options); } + /** + * Run `.promise(...args)` on the underlying Tapable hook. Calls + * interceptors asynchronously with the provided arguments. Depending on + * the Target type, calls interceptors in parallel or in series. Returns a + * promise. It will be fulfilled when all interceptors have run, or when + * the first returning interceptor has run if it's a Bail target. + */ promise(...args) { - this.track('promise', ...args); - return this._tapable.promise(...args); + this.track('beforeCall', { type: 'promise', args }); + return this._tapable.promise(...args).then(returned => { + this.track('afterCall', { type: 'promise', returned }); + return returned; + }); } + /** + * + */ tap(name, interceptor) { return this._invokeTap('tap', name, interceptor); } @@ -56,14 +176,23 @@ class Target extends Trackable { tapPromise(name, interceptor) { return this._invokeTap('tapPromise', name, interceptor); } + toJSON() { + const json = super.toJSON(); + if (json) { + json.requestor = this._requestor; + } + return json; + } } Target.External = class ExternalTarget extends Target { _throwOnExternalInvoke(method) { throw new Error( - `${this._requestor} ran targets.of("${this._owner}").${ - this._targetName - }.${method}(). Only ${this.owner} can invoke its own targets. ${ + `${this._requestor} ran targets.of("${this._owner.name}").${ + this.name + }.${method}(). Only ${ + this._owner.name + } can invoke its own targets. ${ this._requestor } can only intercept them.` ); diff --git a/packages/pwa-buildpack/lib/BuildBus/TargetProvider.js b/packages/pwa-buildpack/lib/BuildBus/TargetProvider.js index 1134373a2e..3847ed83d5 100644 --- a/packages/pwa-buildpack/lib/BuildBus/TargetProvider.js +++ b/packages/pwa-buildpack/lib/BuildBus/TargetProvider.js @@ -1,55 +1,65 @@ +/** + * @module Buildpack/BuildBus + */ const Target = require('./Target'); -const Tapable = require('tapable'); const Trackable = require('./Trackable'); +const { + appearsToBeTapable, + getTapableType, + types +} = require('./mapHooksToTargets'); -const allowedTargetTypes = [ - 'Sync', - 'SyncBail', - 'SyncWaterfall', - 'SyncLoop', - 'AsyncParallel', - 'AsyncParallelBail', - 'AsyncSeries', - 'AsyncSeriesBail', - 'AsyncSeriesWaterfall' -]; - -const VALID_TYPES = new Map(); - -const types = {}; - -for (const type of allowedTargetTypes) { - const TargetClass = Tapable[type + 'Hook']; // We will call them targets. - types[type] = TargetClass; - VALID_TYPES.set(TargetClass, type); -} - -const hasAsyncHookInterface = thing => - typeof thing.tapAsync === 'function' && - typeof thing.tapPromise === 'function' && - typeof thing.callAsync === 'function' && - typeof thing.promise === 'function'; -const hasSyncHookInterface = thing => - typeof thing.tap === 'function' && typeof thing.call === 'function'; -const appearsToBeHook = thing => - thing && - typeof thing === 'object' && - typeof thing.intercept === 'function' && - (hasSyncHookInterface(thing) || hasAsyncHookInterface(thing)); - -const getType = thing => VALID_TYPES.get(thing.constructor) || ''; +/** + * Respond to a request from a {@link TargetProvider} to retrieve a different + * (external) TargetProvider. When using a TargetProvider disconnected from a + * {@link BuildBus}, this callback is necessary if anything requests external + * targets on the TargetProvider using {@link TargetProvider#of}. + * @callback getExternalTargets + * @param {TargetProvider} requestor - TargetProvider making the request. + * @param {string} requested - External targets being requested. + * @returns {TargetProvider} TargetProvider for the requested targets. + */ +/** + * Mediates between a BuildBus and an "extension" package participating in the + * BuildBus declare/intercept lifecycle. The `targets` object used by declare + * and intercept functions is a TargetProvider. Each extension receives its own + * TargetProvider, which provides methods for declaring its own targets, + * intercepting its own targets, and intercepting the targets of other + * extensions. + * + * @class TargetProvider + * @extends {Trackable} + */ class TargetProvider extends Trackable { + /** + * Creates an instance of TargetProvider. + * @param {BuildBus|function} bus - BuildBus using this TargetProvider. Alternately, can be a function which will be used for logging. + * @param {Object} dep - The package which owns this TargetProvider. + * @param {string} dep.name - Name of the package which owns this. + * @param {getExternalTargets} getExternalTargets - Function this TargetProvider will use to retrieve external packages when they are requested with `.of()`. Should usually be a delegate to {@link BuildBus#_requestTargets} + * @memberof TargetProvider + */ constructor(bus, dep, getExternalTargets) { super(); - this.identify(dep.name, bus); + this.attach(dep.name, bus); this._getExternalTargets = getExternalTargets; + /** @type string */ this.name = dep.name; this._tapables = {}; this._intercepted = {}; + /** + * The targets this package has declared in the `declare` phase. + * @type Object + */ this.own = {}; + /** + * The phase currently being executed. Either `declare` or `intercept`. + * @type string + */ this.phase = null; } + /** @ignore */ _linkTarget(requestorName, targetName, tapable) { const TargetClass = requestorName === this.name ? Target : Target.External; @@ -57,10 +67,19 @@ class TargetProvider extends Trackable { this, requestorName, targetName, - getType(tapable), + getTapableType(tapable), tapable ); } + /** + * Call in the declare phase to register targets that this package and + * other packages can intercept. + * + * @param {Object.} declarations - An object whose keys are + * the names of targets to declare, and whose properties are newly + * constructed Targets. + * @memberof TargetProvider + */ declare(declarations) { if (this.phase !== 'declare') { this.track( @@ -75,7 +94,7 @@ class TargetProvider extends Trackable { ); } for (const [targetName, hook] of Object.entries(declarations)) { - if (!appearsToBeHook(hook)) { + if (!appearsToBeTapable(hook)) { throw new Error( `Package "${ this.name @@ -85,7 +104,10 @@ class TargetProvider extends Trackable { ); } - this.track('declare', { targetName, tapableType: getType(hook) }); + this.track('declare', { + targetName, + tapableType: getTapableType(hook) + }); this._tapables[targetName] = hook; this.own[targetName] = this._linkTarget( this.name, @@ -94,6 +116,16 @@ class TargetProvider extends Trackable { ); } } + /** + * Call in the intercept phase to get the targets of other packages, which + * can then be intercepted by calling `.tap` methods on them. + * + * @param {string} depName - The package whose targets you want to retrieve. + * @returns {Object.} - An object whose keys are the names + * of the requested package's targets, and whose values are the target + * objects. + * @memberof TargetProvider + */ of(depName) { if (this.phase !== 'intercept') { this.track( @@ -111,10 +143,17 @@ class TargetProvider extends Trackable { return this.own; } if (!this._intercepted[depName]) { - this._intercepted[depName] = this._getExternalTargets(depName); + this._intercepted[depName] = this._getExternalTargets( + this, + depName + ); } return this._intercepted[depName]; } + /** + * @inner + * Serialize state for use in logging. + */ toJSON() { const json = super.toJSON(); if (json && this.phase) { @@ -124,6 +163,11 @@ class TargetProvider extends Trackable { } } +/** + * Constructors for the different Target classes. + * @memberof TargetProvider + * @type {Object.`; + } +}; + +/** + * Partial prototype for enabled Trackable instances. + * These are in a partial object instead of being class methods because they + * can be expensive when the Trackable tree is deep. So, unless tracking is + * turned on, they should be no-ops. + * @lends Trackable.prototype + */ const liveMethods = { + /** + * Serialize this Trackable and any parent Trackables. + * + * @returns {Object} JSON-serializable object + */ toJSON() { - if (!this.hasOwnProperty('_identifier')) { - throw new Error( - 'Trackable must be initialized with tracker.identify' - ); - } - const json = { - type: this.constructor.name, - id: this._identifier - }; + const json = Object.create(inspectable); + json.type = this.constructor.name; + json.id = this._ensureIdentifier(); if (this._parent) { json.parent = this._parent.toJSON(); } return json; }, - track(event, ...args) { + /** + * Push an event to the parent Trackable, or, if no parent, to the root + * output callback provided to {@link Trackable#attach}. All `.track` + * calls are tagged with the instance's identifier and then rolled up + * recursively until they call the root output callback. + * + * Throws an exception if {@link Trackable#attach} has never been called + * on this instance. + * + * @param {*} args - Any params the root logging function will understand + * @ + */ + track(...args) { if (!this._out) { throw new Error( - 'Trackable must be initialized with tracker.identify' + 'Trackable must be initialized with tracker.attach' ); } - return this._out({ - origin: this.toJSON(), - event, - args - }); + return this._out(this.toJSON(), ...args); } }; +/** + * Until tracking is turned on, these should be no-ops. + * @ignore + */ const deadMethods = { toJSON() {}, track() {} }; +/** + * Generic node in a tree of objects which can log their activity. Implemented + * for BuildBus, since it will eventually need sophisticated debugging and + * introspection for developers, but it has no BuildBus-specific functionality. + * + * @class Trackable + */ class Trackable { + /** + * Enable all active Trackable instances. **Do not run in production**. + * Carries a possibly significant performance cost. + */ static enableTracking() { Object.assign(Trackable.prototype, liveMethods); } + /** + * Disable all active Trackable instances. The parent logging callback will + * not be called. + */ static disableTracking() { Object.assign(Trackable.prototype, deadMethods); } - identify(identifier, owner) { + /** + * @private + */ + _ensureIdentifier() { + if (!this.hasOwnProperty('_identifier')) { + throw new Error( + 'Trackable must be initialized with tracker.attach' + ); + } + return this._identifier; + } + /** + * Attach this Trackable to a tree. Give it a name and an owner. If the + * owner is a Trackable, then this Trackable becomes a child node of the + * owner. If the owner is a function, then this Trackable becomes a root + * node, which will log all of its {@link Trackable#track} calls *and* its + * descendents' calls to the `owner` function. + * + * @see Trackable.spec.js + * + * @param {string} identifier - String identifier of this Trackable + * @param {(Trackable|function)} owner - Parent or root log callback + * @memberof Trackable + */ + attach(identifier, owner) { this._identifier = identifier; if (owner instanceof Trackable) { this._parent = owner; this._out = (...args) => this._parent._out(...args); - } else { + } else if (typeof owner === 'function') { this._out = owner; } } } +// Always start disabled. Trackable.disableTracking(); module.exports = Trackable; diff --git a/packages/pwa-buildpack/lib/BuildBus/__tests__/BuildBus.spec.js b/packages/pwa-buildpack/lib/BuildBus/__tests__/BuildBus.spec.js index 4933341262..916f169b9d 100644 --- a/packages/pwa-buildpack/lib/BuildBus/__tests__/BuildBus.spec.js +++ b/packages/pwa-buildpack/lib/BuildBus/__tests__/BuildBus.spec.js @@ -4,171 +4,259 @@ const pertain = require('pertain'); const BuildBus = require('../'); jest.spyOn(console, 'log'); -const mockTargets = { - declares3: new SyncWaterfallHook(['foo']), - declaresandintercepts2: new SyncWaterfallHook(['bar']) -}; -mockTargets.declaresandintercepts2.tap('append', x => `${x}-tail`); -mockTargets.declaresandintercepts2.tap('prepend', x => `head-${x}`); -mockTargets.declares3.tap('subtract', x => x - 1); -mockTargets.declares3.tap('sing', x => `${x} bottles of beer`); - beforeAll(() => BuildBus.enableTracking()); afterAll(() => BuildBus.disableTracking()); -const mockInterceptors = { - declaresandintercepts2: jest - .fn() - .mockName('mockInterceptors.declaresandintercepts2') - .mockImplementation(), - intercepts1: jest.fn().mockName('mockInterceptors.intercepts1') -}; -const mockHandlers = { - intercepts1: { - intercept: jest - .fn(targets => - mockInterceptors.intercepts1( - targets.of('declares3').declares3Target - ) - ) - .mockName('mockHandlers.intercepts1.intercept') - }, - declaresandintercepts2: { - declare: jest - .fn(targets => - targets.declare({ - declaresandintercepts2Target: - mockTargets.declaresandintercepts2 - }) - ) - .mockName('mockHandlers.declaresandintercepts2.declare'), - intercept: jest - .fn(targets => - mockInterceptors.declaresandintercepts2( - targets.of('declares3').declares3Target, - targets.own.declaresandintercepts2Target - ) - ) - .mockName('mockHandlers.declaresandintercepts2.intercept') +const hooks = {}; + +let song = ''; +const sing = jest.fn(lyric => { + song += lyric; +}); + +const oldDepsVar = process.env.BUILDBUS_DEPS_ADDITIONAL; +beforeEach(() => { + BuildBus.clearAll(); + sing.mockClear(); + pertain.mockClear(); + hooks.bottles = new SyncHook(['beersLeft']); + hooks.orders = new SyncWaterfallHook(['order']); + hooks.song = new SyncWaterfallHook(['lyrics']); + song = ''; + process.env.BUILDBUS_DEPS_ADDITIONAL = ''; +}); + +afterAll(() => { + if (oldDepsVar) process.env.BUILDBUS_DEPS_ADDITIONAL = oldDepsVar; +}); + +// mock workflow +const dependencies = new Map(); +dependencies.set('singer', { + declare(targets) { + targets.declare({ chorus: hooks.song }); }, - declares3: { - declare: jest - .fn(targets => - targets.declare({ declares3Target: mockTargets.declares3 }) - ) - .mockName('mockHandlers.declares3.declare') + intercept(targets) { + targets.of('bartender').beersLeft.tap(beersLeft => { + sing(targets.own.chorus.call(beersLeft)); + }); + targets.own.chorus.tap( + left => + `${left} bottles of beer on the wall\n${left} bottles of beeeeeer\n` + ); } -}; -const handlerList = [ - mockInterceptors.declaresandintercepts2, - mockInterceptors.intercepts1 -]; -const mockHandlerEntries = Object.entries(mockHandlers); -mockHandlerEntries.forEach(([depName, subjects]) => - Object.entries(subjects).forEach(([subject, fn]) => { - handlerList.push(fn); - jest.doMock(`/path/to/${depName}/${subject}`, () => fn, { - virtual: true +}); +dependencies.set('imbiber', { + intercept(targets) { + targets.of('bartender').takeOrders.tap(ordered => ordered + 1); + targets.of('singer').chorus.tap(lyrics => `${lyrics}take `); + } +}); +dependencies.set('bartender', { + declare(targets) { + targets.declare({ takeOrders: hooks.orders, beersLeft: hooks.bottles }); + }, + intercept(targets) { + targets.own.beersLeft.tap(bottles => { + const ordered = Math.max(targets.own.takeOrders.call(0), 0); + const bottlesLeft = Math.max(bottles - ordered); + sing( + `${ordered} down, pass em around\n${bottlesLeft} bottles of beer on the wall!\n` + ); + if (bottlesLeft) { + targets.own.beersLeft.call(bottlesLeft); + } }); - }) -); + } +}); + +const allDeps = [...dependencies.entries()]; -pertain.mockImplementation((_, subject) => { +// virtual module mocker +const phasePath = (depName, phase) => `/path/to/${depName}/${phase}`; +const doMockTargetDep = (depName, phases) => + Object.entries(phases).forEach(([phase, fn]) => + jest.doMock(phasePath(depName, phase), () => fn, { + virtual: true + }) + ); +const doMockAdditionalDep = (depName, phases) => { + const allPhases = Object.assign({ declare() {}, intercept() {} }, phases); + if (process.env.BUILDBUS_DEPS_ADDITIONAL) { + process.env.BUILDBUS_DEPS_ADDITIONAL += `,${depName}`; + } else { + process.env.BUILDBUS_DEPS_ADDITIONAL = depName; + } + doMockTargetDep(depName, allPhases); +}; +for (const [depName, phases] of allDeps) { + doMockTargetDep(depName, phases); +} +pertain.mockImplementation((context, subject, getDeps) => { const phase = subject.split('.').pop(); - const deps = mockHandlerEntries.filter(([, subjects]) => subjects[phase]); - return deps.map(([depName]) => ({ + const deps = getDeps( + allDeps + .filter(([, subjects]) => subjects[phase]) + .map(([depName]) => depName), + context, + subject + ); + return deps.map(depName => ({ name: depName, - path: `/path/to/${depName}/${phase}`, + path: phasePath(depName, phase), subject })); }); -beforeEach(() => { - BuildBus.clearAll(); - handlerList.forEach(handlerMock => handlerMock.mockClear()); -}); - test('will not let you construct it by itself', () => { expect(() => new BuildBus()).toThrowErrorMatchingSnapshot(); expect(() => BuildBus.for('./fake-context')).not.toThrow(); }); -test('caches buses for contexts', () => { - expect(BuildBus.for('./somewhere')).toBe(BuildBus.for('./somewhere')); - expect(mockHandlers.declares3.declare).toHaveBeenCalledTimes(1); +test('caches buses for contexts and does not init the same bus twice', () => { + const bus = BuildBus.for('./somewhere'); + const cachedBus = BuildBus.for('./somewhere'); + bus.init(); + cachedBus.init(); + expect(bus).toBe(cachedBus); + expect(pertain).toHaveBeenCalledTimes(2); +}); + +test('can clear cache by context name', () => { + const bus = BuildBus.for('./somewhere'); + expect(BuildBus.for('./somewhere')).toBe(bus); + BuildBus.clear('./somewhere'); + expect(BuildBus.for('./somewhere')).not.toBe(bus); }); test('calls declare and then intercept', () => { - BuildBus.for('./fake-context'); - expect(mockHandlers.declares3.declare).toHaveBeenCalledTimes(1); - expect(mockHandlers.declaresandintercepts2.declare).toHaveBeenCalledTimes( - 1 - ); - expect(mockHandlers.declaresandintercepts2.intercept).toHaveBeenCalledTimes( - 1 - ); - expect(mockHandlers.intercepts1.intercept).toHaveBeenCalledTimes(1); - const stop = mockHandlers.declaresandintercepts2.declare.mock.calls[0][0]; + hooks.bottles.call(100); + expect(sing).not.toHaveBeenCalled(); + BuildBus.for('./pub').init(); + hooks.bottles.call(3); + expect(sing).toHaveBeenCalledTimes(6); + expect(song).toBe(`3 bottles of beer on the wall +3 bottles of beeeeeer +take 1 down, pass em around +2 bottles of beer on the wall! +2 bottles of beer on the wall +2 bottles of beeeeeer +take 1 down, pass em around +1 bottles of beer on the wall! +1 bottles of beer on the wall +1 bottles of beeeeeer +take 1 down, pass em around +0 bottles of beer on the wall! +`); +}); - expect(stop).toBe( - mockHandlers.declaresandintercepts2.intercept.mock.calls[0][0] - ); +test('reads additional deps from env var', () => { + const orderMore = jest.fn(targets => { + targets.of('bartender').takeOrders.tap(orders => orders + 2); + }); + doMockAdditionalDep('keep-counting', { + intercept: orderMore + }); + + doMockAdditionalDep('speak-digits', { + intercept(targets) { + const namedDigits = [ + 'zero', + 'one', + 'two', + 'three', + 'four', + 'five', + 'six' + ]; + targets.of('singer').chorus.intercept({ + register(tapinfo) { + return { + ...tapinfo, + fn(lyrics) { + return tapinfo + .fn(lyrics) + .replace( + /\d+/g, + digits => namedDigits[parseInt(digits)] + ); + } + }; + } + }); + } + }); + BuildBus.for('./fake-context').init(); + + expect(orderMore).toHaveBeenCalled(); + + hooks.bottles.call(6); + expect(song).toBe(`six bottles of beer on the wall +six bottles of beeeeeer +take 3 down, pass em around +3 bottles of beer on the wall! +three bottles of beer on the wall +three bottles of beeeeeer +take 3 down, pass em around +0 bottles of beer on the wall! +`); }); -test('can intercept declared targets', () => { - BuildBus.for('./fake-context2'); - expect(mockInterceptors.declaresandintercepts2).toHaveBeenCalled(); - expect(mockInterceptors.intercepts1).toHaveBeenCalled(); - const singing = mockTargets.declares3; - const snake = mockTargets.declaresandintercepts2; - expect(snake.call('egg')).toBe('head-egg-tail'); - expect(singing.call(100)).toBe('99 bottles of beer'); +test('errors if declared target is not a hook', () => { + doMockAdditionalDep('bad-target', { + declare(targets) { + targets.declare({ bad: new Date() }); + } + }); + expect(() => + BuildBus.for('./fake-context').init() + ).toThrowErrorMatchingSnapshot(); }); test('errors if declared target is not a hook', () => { - mockHandlers.declares3.declare.mockImplementationOnce(targets => - targets.declare({ bad: new Date() }) - ); - expect(() => BuildBus.for('./fake-context')).toThrowErrorMatchingSnapshot(); - mockHandlers.declares3.declare.mockImplementationOnce(targets => - targets.declare({ worse: null }) - ); + doMockAdditionalDep('no-target', { + declare(targets) { + targets.declare({ bad: null }); + } + }); expect(() => - BuildBus.for('./another-fake-context') + BuildBus.for('./fake-context').init() ).toThrowErrorMatchingSnapshot(); }); test('logs but does not error if declaring not in declare phase', () => { - mockHandlers.intercepts1.intercept.mockImplementationOnce(targets => - targets.declare({ foo: new SyncHook() }) - ); - expect(() => BuildBus.for('./fake-context')).not.toThrow(); - expect(console.log).toHaveBeenCalled(); - expect(console.log.mock.calls[0][0]).toMatchObject({ - args: ['declare'], - event: 'runPhase', - origin: { - type: 'BuildBus' + doMockAdditionalDep('declare-in-intercept', { + intercept(targets) { + targets.declare({ foo: new SyncHook() }); } }); + expect(() => BuildBus.for('./fake-context').init()).not.toThrow(); + expect(console.log).toHaveBeenCalledWith( + expect.objectContaining({ + type: 'BuildBus' + }), + 'runPhase', + { phase: 'declare' } + ); }); test('logs but does not error if getting target not in intercept phase', () => { - mockHandlers.declaresandintercepts2.declare.mockImplementationOnce( - targets => targets.of('declaresandintercepts2') - ); - expect(() => BuildBus.for('./fake-context')).not.toThrow(); - expect(console.log).toHaveBeenCalled(); - expect(console.log.mock.calls[0][0]).toMatchObject({ - args: ['declare'], - event: 'runPhase', - origin: { - type: 'BuildBus' + doMockAdditionalDep('intercept-in-declare', { + declare(targets) { + targets.of('bartender').takeOrders.tap(x => x); } }); + expect(() => BuildBus.for('./fake-context').init()).not.toThrow(); + expect(console.log).toHaveBeenCalledWith( + expect.objectContaining({ + type: 'BuildBus' + }), + 'runPhase', + { phase: 'declare' } + ); }); test('errors if requested target source does not exist', () => { - const bus = BuildBus.for('./fake-context'); + const bus = BuildBus.for('./fake-context').init(); expect(() => bus.getTargetsOf('bar')).toThrow('has not yet declared'); }); diff --git a/packages/pwa-buildpack/lib/BuildBus/__tests__/Target.spec.js b/packages/pwa-buildpack/lib/BuildBus/__tests__/Target.spec.js index 7aa05b8e8b..c76fe6193a 100644 --- a/packages/pwa-buildpack/lib/BuildBus/__tests__/Target.spec.js +++ b/packages/pwa-buildpack/lib/BuildBus/__tests__/Target.spec.js @@ -29,7 +29,7 @@ test('runs sync interception methods on underlying tapable with default name arg expect(tapInfoSpy).toHaveBeenCalledWith( expect.objectContaining({ type: 'sync', - name: 'mockPackage:BailIfAsked' + name: `mockPackage${Target.SOURCE_SEP}BailIfAsked` }) ); @@ -148,3 +148,22 @@ test('throws when external consumer invokes a call method', async () => { }) ).resolves.toBe(7); }); + +test('serializes with its requestor', () => { + const syncHook = new SyncWaterfallHook(['x']); + const ownSync = new Target( + () => {}, + 'mockRequestor', + 'mockTargetName', + 'SyncBail', + syncHook + ); + expect(ownSync.toJSON()).toBeUndefined(); + Target.enableTracking(); + expect(ownSync.toJSON()).toMatchObject({ + type: 'Target', + id: 'mockTargetName[SyncBail]', + requestor: 'mockRequestor' + }); + Target.disableTracking(); +}); diff --git a/packages/pwa-buildpack/lib/BuildBus/__tests__/TargetProvider.spec.js b/packages/pwa-buildpack/lib/BuildBus/__tests__/TargetProvider.spec.js index da76a5d668..a26ac7717f 100644 --- a/packages/pwa-buildpack/lib/BuildBus/__tests__/TargetProvider.spec.js +++ b/packages/pwa-buildpack/lib/BuildBus/__tests__/TargetProvider.spec.js @@ -52,12 +52,18 @@ test('warns when declaring outside declare phase', () => { targets.declare({ someTarget: new targets.types.Sync([]) }); - expect(trackFn).toHaveBeenCalled(); - - expect(trackFn.mock.calls[0][0]).toMatchObject({ - event: 'warning', - args: [{ type: 'lifecycle' }] - }); + expect(trackFn).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ + type: 'TargetProvider', + id: 'myDep' + }), + 'warning', + expect.objectContaining({ + type: 'lifecycle', + message: expect.stringMatching('declare') + }) + ); TargetProvider.disableTracking(); }); @@ -66,17 +72,18 @@ test('warns when intercepting outside intercept phase', () => { const targets = new TargetProvider(trackFn, mockDep, getExternalTargets); targets.phase = 'declare'; targets.of('myDep'); - expect(trackFn).toHaveBeenCalled(); - - expect(trackFn.mock.calls[0][0]).toMatchObject({ - origin: { + expect(trackFn).toHaveBeenNthCalledWith( + 1, + expect.objectContaining({ type: 'TargetProvider', - id: 'myDep', - phase: 'declare' - }, - event: 'warning', - args: [{ type: 'lifecycle' }] - }); + id: 'myDep' + }), + 'warning', + expect.objectContaining({ + type: 'lifecycle', + message: expect.stringContaining('of(myDep) in the "declare" phase') + }) + ); TargetProvider.disableTracking(); }); diff --git a/packages/pwa-buildpack/lib/BuildBus/__tests__/Trackable.spec.js b/packages/pwa-buildpack/lib/BuildBus/__tests__/Trackable.spec.js index b213f65d89..7e5bc772d9 100644 --- a/packages/pwa-buildpack/lib/BuildBus/__tests__/Trackable.spec.js +++ b/packages/pwa-buildpack/lib/BuildBus/__tests__/Trackable.spec.js @@ -1,3 +1,4 @@ +const { inspect } = require('util'); const Trackable = require('../Trackable'); const logFn = jest.fn().mockName('console.log'); @@ -5,7 +6,7 @@ beforeEach(() => logFn.mockClear()); const confirmDead = () => { const trackable = new Trackable(); - trackable.identify('foo', logFn); + trackable.attach('foo', logFn); expect(() => trackable.toJSON()).not.toThrow(); expect(trackable.toJSON()).toBeUndefined(); expect(() => trackable.track('bar')).not.toThrow(); @@ -17,24 +18,22 @@ test('starts in dead mode', confirmDead); test('switches in and out of live mode', () => { const trackable = new Trackable(); Trackable.enableTracking(); - trackable.identify('foo', logFn); + trackable.attach('foo', logFn); expect(trackable.toJSON()).toMatchObject({ type: 'Trackable', id: 'foo' }); - expect(() => trackable.track('woo', ['yay'])).not.toThrow(); + expect(() => trackable.track('woo', 'yay')).not.toThrow(); expect(logFn).toHaveBeenCalledWith( expect.objectContaining({ - origin: { - type: 'Trackable', - id: 'foo' - }, - event: 'woo', - args: [['yay']] - }) + type: 'Trackable', + id: 'foo' + }), + 'woo', + 'yay' ); Trackable.disableTracking(); - expect(() => trackable.track('aah', ['no'])).not.toThrow(); + expect(() => trackable.track('aah', 'no')).not.toThrow(); expect(logFn).toHaveBeenCalledTimes(1); }); @@ -51,64 +50,82 @@ test('recursively builds origin object from parents', () => { const grandparent = new OldGuy(); const parent = new Patriarch(); const child = new Zookeeper(); - grandparent.identify('methuselah', logFn); - parent.identify('lamech', grandparent); - child.identify('noah', parent); + grandparent.attach('methuselah', logFn); + parent.attach('lamech', grandparent); + child.attach('noah', parent); Trackable.enableTracking(); grandparent.track('die', { age: 969 }); expect(logFn).toHaveBeenCalledWith( expect.objectContaining({ - origin: { + type: 'OldGuy', + id: 'methuselah' + }), + 'die', + { age: 969 } + ); + + parent.track('beget', 'sons', 'daughters'); + + expect(logFn).toHaveBeenCalledWith( + expect.objectContaining({ + type: 'Patriarch', + id: 'lamech', + parent: { type: 'OldGuy', id: 'methuselah' - }, - event: 'die', - args: [{ age: 969 }] - }) + } + }), + 'beget', + 'sons', + 'daughters' ); - parent.track('beget', 'sons', 'daughters'); + child.track('measure', { cubits: 300 }); expect(logFn).toHaveBeenCalledWith( expect.objectContaining({ - origin: { + type: 'Zookeeper', + id: 'noah', + norseName: 'Bergelmir', + parent: { type: 'Patriarch', id: 'lamech', parent: { type: 'OldGuy', id: 'methuselah' } - }, - event: 'beget', - args: ['sons', 'daughters'] - }) + } + }), + 'measure', + { cubits: 300 } ); - child.track('measure', { cubits: 300 }); + Trackable.disableTracking(); +}); - expect(logFn).toHaveBeenCalledWith( - expect.objectContaining({ - origin: { - type: 'Zookeeper', - id: 'noah', - norseName: 'Bergelmir', - parent: { - type: 'Patriarch', - id: 'lamech', - parent: { - type: 'OldGuy', - id: 'methuselah' - } - } - }, - event: 'measure', - args: [{ cubits: 300 }] - }) +test('limits visual recursion in util.inspect', () => { + class Wood extends Trackable {} + const tree = new Wood(); + const branch = new Wood(); + const twig = new Wood(); + const leaf = new Wood(); + tree.attach('tree', (origin, ...args) => + logFn(inspect(origin, { depth: 1 }), ...args) ); - + branch.attach('branch', tree); + twig.attach('twig', branch); + leaf.attach('leaf', twig); + Trackable.enableTracking(); + leaf.track('wind'); Trackable.disableTracking(); + expect(logFn).toHaveBeenCalledWith( + expect.stringContaining('Wood'), + 'wind' + ); + const printed = logFn.mock.calls[0][0]; + expect(printed).not.toMatch('tree'); }); test('errors if tracking enabled but this trackable was not identified', () => { diff --git a/packages/pwa-buildpack/lib/BuildBus/__tests__/__snapshots__/BuildBus.spec.js.snap b/packages/pwa-buildpack/lib/BuildBus/__tests__/__snapshots__/BuildBus.spec.js.snap index 8b55472bb8..9e0894ec49 100644 --- a/packages/pwa-buildpack/lib/BuildBus/__tests__/__snapshots__/BuildBus.spec.js.snap +++ b/packages/pwa-buildpack/lib/BuildBus/__tests__/__snapshots__/BuildBus.spec.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`errors if declared target is not a hook 1`] = `"Package \\"declares3\\" declared target \\"bad\\" with an invalid target type \\"[object Date]\\". Make sure you are not using a different version or instance of the Tapable library to declare your targets."`; +exports[`errors if declared target is not a hook 1`] = `"Package \\"bad-target\\" declared target \\"bad\\" with an invalid target type \\"[object Date]\\". Make sure you are not using a different version or instance of the Tapable library to declare your targets."`; -exports[`errors if declared target is not a hook 2`] = `"Package \\"declares3\\" declared target \\"worse\\" with an invalid target type \\"[object Null]\\". Make sure you are not using a different version or instance of the Tapable library to declare your targets."`; +exports[`errors if declared target is not a hook 2`] = `"Package \\"no-target\\" declared target \\"bad\\" with an invalid target type \\"[object Null]\\". Make sure you are not using a different version or instance of the Tapable library to declare your targets."`; exports[`will not let you construct it by itself 1`] = `"BuildBus must not be created with its constructor. Use the static factory method BuildBus.for(context) instead."`; diff --git a/packages/pwa-buildpack/lib/BuildBus/__tests__/__snapshots__/Target.spec.js.snap b/packages/pwa-buildpack/lib/BuildBus/__tests__/__snapshots__/Target.spec.js.snap index 7a83b203d9..faf39e2ddb 100644 --- a/packages/pwa-buildpack/lib/BuildBus/__tests__/__snapshots__/Target.spec.js.snap +++ b/packages/pwa-buildpack/lib/BuildBus/__tests__/__snapshots__/Target.spec.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`throws when external consumer invokes a call method 1`] = `"externalPackage ran targets.of(\\"mockPackage\\").mockTargetName.call(). Only undefined can invoke its own targets. externalPackage can only intercept them."`; +exports[`throws when external consumer invokes a call method 1`] = `"externalPackage ran targets.of(\\"undefined\\").mockTargetName.call(). Only undefined can invoke its own targets. externalPackage can only intercept them."`; -exports[`throws when external consumer invokes a call method 2`] = `"externalPackage ran targets.of(\\"mockPackage\\").mockTargetName.callAsync(). Only undefined can invoke its own targets. externalPackage can only intercept them."`; +exports[`throws when external consumer invokes a call method 2`] = `"externalPackage ran targets.of(\\"undefined\\").mockTargetName.callAsync(). Only undefined can invoke its own targets. externalPackage can only intercept them."`; -exports[`throws when external consumer invokes a call method 3`] = `"externalPackage ran targets.of(\\"mockPackage\\").mockTargetName.promise(). Only undefined can invoke its own targets. externalPackage can only intercept them."`; +exports[`throws when external consumer invokes a call method 3`] = `"externalPackage ran targets.of(\\"undefined\\").mockTargetName.promise(). Only undefined can invoke its own targets. externalPackage can only intercept them."`; diff --git a/packages/pwa-buildpack/lib/BuildBus/__tests__/__snapshots__/Trackable.spec.js.snap b/packages/pwa-buildpack/lib/BuildBus/__tests__/__snapshots__/Trackable.spec.js.snap index 5178a94326..bc9b963c32 100644 --- a/packages/pwa-buildpack/lib/BuildBus/__tests__/__snapshots__/Trackable.spec.js.snap +++ b/packages/pwa-buildpack/lib/BuildBus/__tests__/__snapshots__/Trackable.spec.js.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`errors if tracking enabled but this trackable was not identified 1`] = `"Trackable must be initialized with tracker.identify"`; +exports[`errors if tracking enabled but this trackable was not identified 1`] = `"Trackable must be initialized with tracker.attach"`; -exports[`errors if tracking enabled but this trackable was not identified 2`] = `"Trackable must be initialized with tracker.identify"`; +exports[`errors if tracking enabled but this trackable was not identified 2`] = `"Trackable must be initialized with tracker.attach"`; diff --git a/packages/pwa-buildpack/lib/BuildBus/mapHooksToTargets.js b/packages/pwa-buildpack/lib/BuildBus/mapHooksToTargets.js new file mode 100644 index 0000000000..c3c7122d56 --- /dev/null +++ b/packages/pwa-buildpack/lib/BuildBus/mapHooksToTargets.js @@ -0,0 +1,90 @@ +/** + * Mapping Tapable hooks to the Buildpack facade of Targets. + * @module Buildpack/BuildBus + */ +const Tapable = require('tapable'); + +/** + * The names of Tapable hooks, but without "Hook" on the end. + * We hide the "Hook" name a little bit because its name overlaps + * with React Hooks in a confusing way. + * @private + */ +const allowedTargetTypes = [ + 'Sync', + 'SyncBail', + 'SyncWaterfall', + 'SyncLoop', + 'AsyncParallel', + 'AsyncParallelBail', + 'AsyncSeries', + 'AsyncSeriesBail', + 'AsyncSeriesWaterfall' +]; + +/** + * Map of Tapable hook classes to the 'Hook'-free target names in + * `allowedTargetTypes` + * @private + */ +const VALID_TYPES = new Map(); + +/** + * Dictionary of Tapable Hook classes to expose under these new names. + * @public + * @type {Object.} + */ +const types = {}; + +// Populate the validator map and type dictionary +for (const type of allowedTargetTypes) { + const HookConstructor = Tapable[type + 'Hook']; + types[type] = HookConstructor; + VALID_TYPES.set(HookConstructor, type); +} + +/** + * Duck typing for async hooks + * @private + */ +const hasAsyncHookInterface = thing => + typeof thing.tapAsync === 'function' && + typeof thing.tapPromise === 'function' && + typeof thing.callAsync === 'function' && + typeof thing.promise === 'function'; + +/** + * Duck typing for sync hooks + * @private + */ +const hasSyncHookInterface = thing => + typeof thing.tap === 'function' && typeof thing.call === 'function'; + +/** + * Use duck typing to validate that the passed object seems like a Tapable hook. + * More robust than doing `instanceof` checks; allows hooks to be proxied and + * otherwise hacked by dependencies. + * @public + * @param {object} thing - Is it hook-ish? + * @returns {boolean} + */ +const appearsToBeTapable = thing => + thing && + typeof thing === 'object' && + typeof thing.intercept === 'function' && + (hasSyncHookInterface(thing) || hasAsyncHookInterface(thing)); + +/** + * Get the string type name of a provided object. If it is one of the base + * Tapable Hooks supported, returns the name of that Hook (without 'Hook' on + * the end). Otherwise, returns ''. + * @public + */ +const getTapableType = thing => + VALID_TYPES.get(thing.constructor) || ''; + +module.exports = { + appearsToBeTapable, + getTapableType, + types +}; diff --git a/packages/pwa-buildpack/lib/Utilities/getEnvVarDefinitions.js b/packages/pwa-buildpack/lib/Utilities/getEnvVarDefinitions.js index df5317e27d..d98c8ce725 100644 --- a/packages/pwa-buildpack/lib/Utilities/getEnvVarDefinitions.js +++ b/packages/pwa-buildpack/lib/Utilities/getEnvVarDefinitions.js @@ -7,6 +7,7 @@ function getEnvVarDefinitions(context) { BuildBus.enableTracking(); } const bus = BuildBus.for(context); + bus.init(); bus.getTargetsOf('@magento/pwa-buildpack').envVarDefinitions.call( definitions ); diff --git a/packages/pwa-buildpack/lib/WebpackTools/PWADevServer.js b/packages/pwa-buildpack/lib/WebpackTools/PWADevServer.js index a8eaa4543c..c4b455f77f 100644 --- a/packages/pwa-buildpack/lib/WebpackTools/PWADevServer.js +++ b/packages/pwa-buildpack/lib/WebpackTools/PWADevServer.js @@ -47,7 +47,7 @@ const PWADevServer = { host: devServer.host || '0.0.0.0', port: devServer.port || (await portscanner.findAPortNotInUse(10000)), - stats: 'normal', + stats: webpackConfig.stats || 'minimal', after(app, server) { server.middleware.waitUntilValid(() => { // We can try to set the hostname and port for the dev diff --git a/packages/pwa-buildpack/lib/WebpackTools/configureWebpack.js b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack.js index 5e0fb75304..20cc982884 100644 --- a/packages/pwa-buildpack/lib/WebpackTools/configureWebpack.js +++ b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack.js @@ -64,13 +64,26 @@ async function configureWebpack(options) { if (process.env.DEBUG && process.env.DEBUG.includes('BuildBus')) { BuildBus.enableTracking(); stats = { - logging: 'log' + logging: 'verbose', + loggingDebug: ['BuildBusPlugin'], + assets: false, + chunks: false, + chunkGroups: false, + chunkModules: false, + chunkOrigins: false, + entrypoints: false, + performance: false, + publicPath: false, + reasons: false, + timings: false, + version: false }; } const busTrackingQueue = []; const bus = BuildBus.for(context); - bus.identify('configureWebpack', (...args) => busTrackingQueue.push(args)); + bus.attach('configureWebpack', (...args) => busTrackingQueue.push(args)); + bus.init(); const babelConfigPresent = await checkForBabelConfig(context); diff --git a/packages/pwa-buildpack/lib/WebpackTools/plugins/BuildBusPlugin.js b/packages/pwa-buildpack/lib/WebpackTools/plugins/BuildBusPlugin.js index a9b7edc766..6efc7ab5c2 100644 --- a/packages/pwa-buildpack/lib/WebpackTools/plugins/BuildBusPlugin.js +++ b/packages/pwa-buildpack/lib/WebpackTools/plugins/BuildBusPlugin.js @@ -1,16 +1,43 @@ -const { inspect } = require('util'); class BuildBusPlugin { constructor(bus, trackingQueue) { this.bus = bus; this._trackingQueue = trackingQueue; + this._eventOriginTags = new WeakMap(); + } + _eventOriginTag(origin) { + let tag = this._eventOriginTags.get(origin); + if (!tag) { + let node = origin; + const segments = []; + while (node) { + segments.unshift(`${node.type}<${node.id}>`); + node = node.parent; + } + tag = segments.join(':'); + this._eventOriginTags.set(origin, tag); + } + return tag; } apply(compiler) { - compiler.hooks.thisCompilation.tap('BuildBusPlugin', compilation => { - const logger = compilation.getLogger('BuildBusPlugin'); - const logBusTracking = (...args) => logger.log(inspect(args)); - this._trackingQueue.forEach(line => logBusTracking(...line)); - this.bus.identify('BuildBusPlugin', logBusTracking); - }); + if (this._trackingQueue) { + compiler.hooks.thisCompilation.tap( + 'BuildBusPlugin', + compilation => { + const logger = compilation.getLogger('BuildBusPlugin'); + const logBusTracking = (origin, eventName, details) => { + logger.info( + eventName, + this._eventOriginTag(origin), + details + ); + }; + this._trackingQueue.forEach(line => + logBusTracking(...line) + ); + this.bus.attach('BuildBusPlugin', logBusTracking); + } + ); + } this.bus .getTargetsOf('@magento/pwa-buildpack') .webpackCompiler.call(compiler); diff --git a/packages/pwa-buildpack/lib/WebpackTools/plugins/__tests__/BuildBusPlugin.spec.js b/packages/pwa-buildpack/lib/WebpackTools/plugins/__tests__/BuildBusPlugin.spec.js index 59ccd63fd9..b6ff7aecce 100644 --- a/packages/pwa-buildpack/lib/WebpackTools/plugins/__tests__/BuildBusPlugin.spec.js +++ b/packages/pwa-buildpack/lib/WebpackTools/plugins/__tests__/BuildBusPlugin.spec.js @@ -1,4 +1,3 @@ -const { inspect } = require('util'); jest.mock('pertain', () => (_, subject) => { const phase = subject.split('.').pop(); return [ @@ -14,7 +13,7 @@ const BuildBusPlugin = require('../BuildBusPlugin'); test('binds and calls phases', () => { BuildBus.enableTracking(); - const bus = BuildBus.for('./'); + const bus = BuildBus.for('./').init(); const compilerTap = jest.fn(); bus.getTargetsOf('@magento/pwa-buildpack').webpackCompiler.tap(c => compilerTap(c) @@ -32,13 +31,19 @@ test('binds and calls phases', () => { } }; - const plugin = new BuildBusPlugin(bus, [['foo']]); + const plugin = new BuildBusPlugin(bus, [ + [{ type: 'night', id: 'ly', parent: { type: 'test', id: 'me' } }, 'foo'] + ]); plugin.apply(mockCompiler); compilationTap({ - getLogger: () => ({ log: mockLog }) + getLogger: () => ({ info: mockLog, log: mockLog }) }); - expect(mockLog).toHaveBeenCalledWith(inspect(['foo'])); + expect(mockLog).toHaveBeenCalledWith( + 'foo', + 'test:night', + undefined + ); expect(compilerTap).toHaveBeenCalledWith(mockCompiler); BuildBus.disableTracking(); From 7ab6808d277e8a2acce4811758394245832a823f Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Thu, 23 Apr 2020 12:09:03 -0500 Subject: [PATCH 03/21] chore: reorganize target impls into lib folders, not root --- packages/pagebuilder/{ => lib}/intercept.js | 2 +- packages/pagebuilder/package.json | 2 +- .../targets/__tests__/__fixtures__/project/entry.js | 3 +++ .../__fixtures__/project/fakePlainHtmlRenderer.js | 3 +++ .../{ => lib}/targets/rendererCollectionLoader.js | 10 ++++------ .../declare.js => lib/targets/venia-ui-declare.js} | 0 .../intercept.js => lib/targets/venia-ui-intercept.js} | 8 ++++---- packages/venia-ui/package.json | 4 ++-- 8 files changed, 18 insertions(+), 14 deletions(-) rename packages/pagebuilder/{ => lib}/intercept.js (96%) create mode 100644 packages/venia-ui/lib/targets/__tests__/__fixtures__/project/entry.js create mode 100644 packages/venia-ui/lib/targets/__tests__/__fixtures__/project/fakePlainHtmlRenderer.js rename packages/venia-ui/{ => lib}/targets/rendererCollectionLoader.js (67%) rename packages/venia-ui/{targets/declare.js => lib/targets/venia-ui-declare.js} (100%) rename packages/venia-ui/{targets/intercept.js => lib/targets/venia-ui-intercept.js} (92%) diff --git a/packages/pagebuilder/intercept.js b/packages/pagebuilder/lib/intercept.js similarity index 96% rename from packages/pagebuilder/intercept.js rename to packages/pagebuilder/lib/intercept.js index 5bdeb10d05..d3de861725 100644 --- a/packages/pagebuilder/intercept.js +++ b/packages/pagebuilder/lib/intercept.js @@ -27,7 +27,7 @@ module.exports = targets => { .richContentRenderers.tap(richContentRenderers => { richContentRenderers.add({ componentName: 'PageBuilder', - packageName: myName + importPath: myName }); }); }; diff --git a/packages/pagebuilder/package.json b/packages/pagebuilder/package.json index 49269e6a0f..fec709db7a 100644 --- a/packages/pagebuilder/package.json +++ b/packages/pagebuilder/package.json @@ -61,7 +61,7 @@ }, "pwa-studio": { "targets": { - "intercept": "./intercept" + "intercept": "./lib/intercept" } }, "sideEffects": false diff --git a/packages/venia-ui/lib/targets/__tests__/__fixtures__/project/entry.js b/packages/venia-ui/lib/targets/__tests__/__fixtures__/project/entry.js new file mode 100644 index 0000000000..061f02eb96 --- /dev/null +++ b/packages/venia-ui/lib/targets/__tests__/__fixtures__/project/entry.js @@ -0,0 +1,3 @@ +export { + default +} from '../../../../components/RichContent/richContentRenderers'; diff --git a/packages/venia-ui/lib/targets/__tests__/__fixtures__/project/fakePlainHtmlRenderer.js b/packages/venia-ui/lib/targets/__tests__/__fixtures__/project/fakePlainHtmlRenderer.js new file mode 100644 index 0000000000..228951acd1 --- /dev/null +++ b/packages/venia-ui/lib/targets/__tests__/__fixtures__/project/fakePlainHtmlRenderer.js @@ -0,0 +1,3 @@ +export const Component = ({ html }) => `I declare ${html}`; + +export const canRender = () => true; diff --git a/packages/venia-ui/targets/rendererCollectionLoader.js b/packages/venia-ui/lib/targets/rendererCollectionLoader.js similarity index 67% rename from packages/venia-ui/targets/rendererCollectionLoader.js rename to packages/venia-ui/lib/targets/rendererCollectionLoader.js index 3bbc25d3d1..08a909be47 100644 --- a/packages/venia-ui/targets/rendererCollectionLoader.js +++ b/packages/venia-ui/lib/targets/rendererCollectionLoader.js @@ -1,5 +1,3 @@ -const loaderUtils = require('loader-utils'); - const BLANK_LINE = '\n\n'; module.exports = function buildRichContentRendererArray(source) { @@ -8,12 +6,12 @@ module.exports = function buildRichContentRendererArray(source) { let importStatements = ''; const exportMembers = []; - const { renderers } = loaderUtils.getOptions(this); + const { renderers } = this.query; for (const r of renderers) { - importStatements += `import * as ${ - r.componentName - } from '${r.packageName || ''}${r.importPath || ''}';\n`; + importStatements += `import * as ${r.componentName} from '${ + r.importPath + }';\n`; exportMembers.push(r.componentName); } diff --git a/packages/venia-ui/targets/declare.js b/packages/venia-ui/lib/targets/venia-ui-declare.js similarity index 100% rename from packages/venia-ui/targets/declare.js rename to packages/venia-ui/lib/targets/venia-ui-declare.js diff --git a/packages/venia-ui/targets/intercept.js b/packages/venia-ui/lib/targets/venia-ui-intercept.js similarity index 92% rename from packages/venia-ui/targets/intercept.js rename to packages/venia-ui/lib/targets/venia-ui-intercept.js index 6567da0363..5289b6c376 100644 --- a/packages/venia-ui/targets/intercept.js +++ b/packages/venia-ui/lib/targets/venia-ui-intercept.js @@ -12,18 +12,18 @@ const isRCR = mod => mod.resource === path.resolve( __dirname, - '../lib/components/RichContent/richContentRenderers.js' + '../components/RichContent/richContentRenderers.js' ); // Webpack can process loaders best when each one has a unique identity. // We use thie filename, since there should only be one of these loaders // per compilation. -const loaderIdent = __filename; +const ident = __filename; const name = '@magento/venia-ui'; // Guard condition for whether the loader has already been installed. -const loaderIsInstalled = mod => mod.loaders.some(l => l.ident === loaderIdent); +const loaderIsInstalled = mod => mod.loaders.some(l => l.ident === ident); module.exports = targets => { const { richContentRenderers } = targets.own; @@ -48,7 +48,7 @@ module.exports = targets => { }; richContentRenderers.call(api); mod.loaders.push({ - ident: __filename, + ident: ident, loader, options: { renderers: renderers.reverse() diff --git a/packages/venia-ui/package.json b/packages/venia-ui/package.json index 47ed55ce29..2f405742df 100644 --- a/packages/venia-ui/package.json +++ b/packages/venia-ui/package.json @@ -90,8 +90,8 @@ }, "pwa-studio": { "targets": { - "declare": "./targets/declare", - "intercept": "./targets/intercept" + "declare": "./lib/targets/venia-ui-declare", + "intercept": "./lib/targets/venia-ui-intercept" } }, "sideEffects": false From c5114f0a947494f86ba0d5a57963819a23d3eff3 Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Thu, 23 Apr 2020 12:28:43 -0500 Subject: [PATCH 04/21] feat(extensibility): codemod targets and peregrine talon targets --- .../lib/targets/peregrine-declare.js | 48 +++++ .../lib/targets/peregrine-intercept.js | 68 ++++++ packages/peregrine/package.json | 6 + packages/pwa-buildpack/envVarDefinitions.json | 11 + .../pwa-buildpack/lib/BuildBus/BuildBus.js | 57 +++-- .../pwa-buildpack/lib/BuildBus/Trackable.js | 8 +- .../lib/BuildBus/declare-base.js | 193 ++++++++++++++++- .../lib/BuildBus/intercept-base.js | 5 + .../createDotEnvFile.spec.js.snap | 9 + .../lib/WebpackTools/ModuleTransformConfig.js | 119 ++++++++++ .../__tests__/configureWebpack.spec.js | 31 ++- .../lib/WebpackTools/configureWebpack.js | 161 -------------- .../configureWebpack/configureWebpack.js | 203 ++++++++++++++++++ .../configureWebpack}/getClientConfig.js | 116 +++------- .../configureWebpack/getModuleRules.js | 146 +++++++++++++ .../configureWebpack/getResolveLoader.js | 21 ++ .../getServiceWorkerConfig.js | 23 +- .../configureWebpack/getSpecialFlags.js | 60 ++++++ .../WebpackTools/configureWebpack/index.js | 1 + .../WebpackTools/loaders/wrap-esm-loader.js | 112 ++++++++++ packages/venia-concept/webpack.config.js | 11 - .../Routes/__tests__/routes.spec.js | 28 +++ .../venia-ui/lib/components/Routes/routes.js | 19 +- .../lib/targets/BabelRouteInjectionPlugin.js | 111 ++++++++++ .../lib/targets/rendererCollectionLoader.js | 16 +- .../venia-ui/lib/targets/venia-ui-declare.js | 92 +++++++- .../lib/targets/venia-ui-intercept.js | 89 ++++++-- packages/venia-ui/package.json | 1 + yarn.lock | 28 +++ 29 files changed, 1462 insertions(+), 331 deletions(-) create mode 100644 packages/peregrine/lib/targets/peregrine-declare.js create mode 100644 packages/peregrine/lib/targets/peregrine-intercept.js create mode 100644 packages/pwa-buildpack/lib/WebpackTools/ModuleTransformConfig.js delete mode 100644 packages/pwa-buildpack/lib/WebpackTools/configureWebpack.js create mode 100644 packages/pwa-buildpack/lib/WebpackTools/configureWebpack/configureWebpack.js rename packages/pwa-buildpack/lib/{Utilities => WebpackTools/configureWebpack}/getClientConfig.js (72%) create mode 100644 packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getModuleRules.js create mode 100644 packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getResolveLoader.js rename packages/pwa-buildpack/lib/{Utilities => WebpackTools/configureWebpack}/getServiceWorkerConfig.js (82%) create mode 100644 packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getSpecialFlags.js create mode 100644 packages/pwa-buildpack/lib/WebpackTools/configureWebpack/index.js create mode 100644 packages/pwa-buildpack/lib/WebpackTools/loaders/wrap-esm-loader.js create mode 100644 packages/venia-ui/lib/components/Routes/__tests__/routes.spec.js create mode 100644 packages/venia-ui/lib/targets/BabelRouteInjectionPlugin.js diff --git a/packages/peregrine/lib/targets/peregrine-declare.js b/packages/peregrine/lib/targets/peregrine-declare.js new file mode 100644 index 0000000000..8b92c9b654 --- /dev/null +++ b/packages/peregrine/lib/targets/peregrine-declare.js @@ -0,0 +1,48 @@ +/** + * These targets are available for interception to modules which depend on `@magento/peregrine`. + * + * Their implementations are found in `./peregrine-intercept.js`. + * + * @module Peregrine/Targets + */ +module.exports = targets => { + targets.declare({ + /** + * @callback talonIntercept + * @param {Peregrine/Targets.TalonWrapperConfig} talons - Registry of talon namespaces, talons, and Sets of interceptors + * @returns {undefined} - Interceptors of `talons` should add to the + * sets on passed TalonWrapperConfig instance. Any returned value will + * be ignored. + */ + + /** + * Collects requests to intercept and "wrap" individual Peregrine + * talons in decorator functions. Use it to add your own code to run + * when Peregrine talons are invoked, and/or to modify the behavior and + * output of those talons. + * + * This target is a convenience wrapper around the + * `@magento/pwa-buildpack` target `transformModules`. That target uses + * filenames, which are not guaranteed to be semantically versioned or + * to be readable APIs. + * This target publishes talons as functions to wrap, rather than as + * files to decorate. + * + * @type {tapable.SyncHook} + * @param {talonIntercept} + * + * @example Log whenever the `useApp()` hook runs. + * targets.of('@magento/pwa-buildpack').talons.tap(talons => { + * talons.App.useApp.wrapWith('./log-wrapper'); + * }) + * // log-wrapper.js: + * export default function wrapUseApp(original) { + * return function useApp(...args) { + * console.log('calling useApp with', ...args); + * return original(...args); + * } + * } + */ + talons: new targets.types.Sync(['talons']) + }); +}; diff --git a/packages/peregrine/lib/targets/peregrine-intercept.js b/packages/peregrine/lib/targets/peregrine-intercept.js new file mode 100644 index 0000000000..d81a1f4b28 --- /dev/null +++ b/packages/peregrine/lib/targets/peregrine-intercept.js @@ -0,0 +1,68 @@ +/** + * @module Peregrine/Targets + */ +const path = require('path'); + +/** + * + * @class TalonWrapperConfig + * @hideconstructor + */ +function TalonWrapperConfig(addTransforms) { + const wrappable = (talonFile, exportName) => ({ + wrapWith: wrapperModule => + addTransforms({ + type: 'source', + fileToTransform: path.join('./lib/talons/', talonFile), + transformModule: + '@magento/pwa-buildpack/lib/WebpackTools/loaders/wrap-esm-loader', + options: { + wrapperModule, + exportName + } + }) + }); + return { + /** + * @memberof TalonWrapperConfig + * + */ + + ProductFullDetail: { + useProductFullDetail: wrappable( + 'ProductFullDetail/useProductFullDetail', + 'useProductFullDetail' + ) + }, + App: { + useApp: wrappable('App/useApp', 'useApp') + } + }; +} + +module.exports = targets => { + const builtins = targets.of('@magento/pwa-buildpack'); + + builtins.specialFeatures.tap(featuresByModule => { + featuresByModule['@magento/peregrine'] = { + cssModules: true, + esModules: true, + graphQlQueries: true + }; + }); + + /** + * Tap the low-level Buildpack target for wrapping _any_ frontend module. + * Wrap the config object in a TalonWrapperConfig, which presents + * higher-level targets for named and namespaced talons, instead of the + * file paths directly. + * Pass that higher-level config through `talons` interceptors, so they can + * add wrappers for the talon modules without tapping the `transformModules` + * config themselves. + */ + builtins.transformModules.tap(addTransform => { + const talonWrapperConfig = new TalonWrapperConfig(addTransform); + + targets.own.talons.call(talonWrapperConfig); + }); +}; diff --git a/packages/peregrine/package.json b/packages/peregrine/package.json index 144103441b..bb5c192e16 100644 --- a/packages/peregrine/package.json +++ b/packages/peregrine/package.json @@ -44,6 +44,12 @@ "node": ">=10.x", "yarn": ">=1.12.0" }, + "pwa-studio": { + "targets": { + "declare": "./lib/targets/peregrine-declare", + "intercept": "./lib/targets/peregrine-intercept" + } + }, "module": "lib/index.js", "jsnext:main": "lib/index.js", "es2015": "lib/index.js", diff --git a/packages/pwa-buildpack/envVarDefinitions.json b/packages/pwa-buildpack/envVarDefinitions.json index a2ca1f700a..1413a963bf 100644 --- a/packages/pwa-buildpack/envVarDefinitions.json +++ b/packages/pwa-buildpack/envVarDefinitions.json @@ -171,6 +171,17 @@ "example": "sandbox_8yrzsvtm_s2bg8fs563crhqzk" } ] + }, + { + "name": "BuildBus and targets", + "variables": [ + { + "name": "BUILDBUS_DEPS_ADDITIONAL", + "type": "str", + "desc": "A list of resolvable NPM modules that BuildBus will scan for targets, in addition to those declared in project `dependencies` and `devDependencies`.", + "default": "" + } + ] } ], "changes": [ diff --git a/packages/pwa-buildpack/lib/BuildBus/BuildBus.js b/packages/pwa-buildpack/lib/BuildBus/BuildBus.js index 1ebbb88bbe..71351f4bf2 100644 --- a/packages/pwa-buildpack/lib/BuildBus/BuildBus.js +++ b/packages/pwa-buildpack/lib/BuildBus/BuildBus.js @@ -1,5 +1,5 @@ /** - * @module Buildpack/BuildBus + * @module @magento/pwa-buildpack */ const path = require('path'); @@ -25,22 +25,14 @@ const INVOKE_FLAG = Symbol.for('FORCE_BUILDBUS_CREATE_FACTORY'); * Manager of dependencies' participation in project builds and tasks. Broker * for dependencies with Targets to interact with each other. * - * @example Get or create the BuildBus for the package.json file in `./project-dir`, then bind targets, then call a target. - * const bus = BuildBus.for('./project-dir); - * bus.init(); - * bus.getTargetsOf('my-extension').myTarget.call(); - * - * @class BuildBus - * @extends {Trackable} */ class BuildBus extends Trackable { /** * Remove the cached BuildBus for the given context. * * @static - * @hideconstructor - * @param {string} context - * @memberof BuildBus + * @param {string} context - Root directory whose BuildBus to delete. + * @returns {undefined} */ static clear(context) { const absContext = path.resolve(context); @@ -50,7 +42,7 @@ class BuildBus extends Trackable { * Remove all cached BuildBus objects. * * @static - * @memberof BuildBus + * @returns {undefined} */ static clearAll() { busCache.clear(); @@ -60,10 +52,15 @@ class BuildBus extends Trackable { * This factory is the supported way to construct BuildBuses. * It caches BuildBuses and connects them to the logging infrastructure. * - * @static - * @param {string} context + * @example Get or create the BuildBus for the package.json file in `./project-dir`, then bind targets, then call a target. + * ```js + * const bus = BuildBus.for('./project-dir); + * bus.init(); + * bus.getTargetsOf('my-extension').myTarget.call(); + * ``` + * + * @param {string} context - Root directory whose BuildBus to get or create. * @returns {BuildBus} - * @memberof BuildBus */ static for(context) { const absContext = path.resolve(context); @@ -75,9 +72,6 @@ class BuildBus extends Trackable { bus.attach(context, console.log); //usually replaced w/ webpack logger return bus; } - /** - * @hideconstructor - */ constructor(invoker, context) { super(); if (invoker !== INVOKE_FLAG) { @@ -91,12 +85,14 @@ class BuildBus extends Trackable { this.targetProviders = new Map(); this._getEnvOverrides(); } + /** @private */ _getEnvOverrides() { const envDepsAdditional = process.env.BUILDBUS_DEPS_ADDITIONAL; this._depsAdditional = envDepsAdditional ? envDepsAdditional.split(',') : []; } + /** @private */ _getPertaining(phase) { return pertain(this.context, this._phaseToSubject(phase), foundDeps => foundDeps.concat(this._depsAdditional) @@ -105,6 +101,7 @@ class BuildBus extends Trackable { [phase]: require(dep.path) })); } + /** @private */ _getTargets(depName) { const targetProvider = this.targetProviders.get(depName); if (!targetProvider) { @@ -116,6 +113,7 @@ class BuildBus extends Trackable { } return targetProvider; } + /** @private */ _phaseToSubject(phase) { return `pwa-studio.targets.${phase}`; } @@ -128,7 +126,6 @@ class BuildBus extends Trackable { * @param {string} requestor.name - Name of the dependency requesting targets. * @param {string} requested - Name of the dependency whose targets are being requested. * @returns {Object} - Object whose strings are target names and whose values are the Targets of the external dependency. - * @memberof BuildBus */ _requestTargets(requestor, requested) { const source = requestor.name; @@ -144,17 +141,14 @@ class BuildBus extends Trackable { return targets; } /** - * Get {@link module:Buildpack/BuildBus/TargetProvider TargetProvider} for - * the given named dependency. - * Use this to retrieve and run targets in top-level code, when you have - * a reference to the BuildBus. - * Declare and intercept functions should not, and cannot, use this method. - * Instead, they retrieve external targets through their `targets.of()` - * methods. + * Get {@link TargetProvider} for the given named dependency. Use this to + * retrieve and run targets in top-level code, when you have a reference to + * the BuildBus. Declare and intercept functions should not, and cannot, + * use this method. Instead, they retrieve external targets through their + * `targets.of()` methods. * - * @param {string} depName - * @returns {module:Buildpack/BuildBus/TargetProvider} - * @memberof BuildBus + * @param {string} depName - Dependency whose targets to retrieve. + * @returns {Object.<(string, Target)>} TargetProvider for the dependency. */ getTargetsOf(depName) { return this._getTargets(depName).own; @@ -164,8 +158,8 @@ class BuildBus extends Trackable { * This binds all targets which the BuildBus can find by analyzing * dependencies in the project package file.. * - * @memberof BuildBus - * @returns {BuildBus} Chainable. + * @chainable + * @returns Returns the instance (chainable). */ init() { this.runPhase('declare'); @@ -180,7 +174,6 @@ class BuildBus extends Trackable { * bus.runPhase('declare') * * @param {string} phase 'declare' or 'intercept' - * @memberof BuildBus */ runPhase(phase) { if (this._hasRun[phase]) { diff --git a/packages/pwa-buildpack/lib/BuildBus/Trackable.js b/packages/pwa-buildpack/lib/BuildBus/Trackable.js index 41b1fc60c4..4573d28e9b 100644 --- a/packages/pwa-buildpack/lib/BuildBus/Trackable.js +++ b/packages/pwa-buildpack/lib/BuildBus/Trackable.js @@ -1,5 +1,5 @@ /** - * @private + * @module @magento/pwa-buildpack */ const { inspect } = require('util'); @@ -31,7 +31,7 @@ const liveMethods = { /** * Serialize this Trackable and any parent Trackables. * - * @returns {Object} JSON-serializable object + * @returns {Object} JSON-clean object that recurses up the parent tree. */ toJSON() { const json = Object.create(inspectable); @@ -78,7 +78,6 @@ const deadMethods = { * for BuildBus, since it will eventually need sophisticated debugging and * introspection for developers, but it has no BuildBus-specific functionality. * - * @class Trackable */ class Trackable { /** @@ -116,8 +115,7 @@ class Trackable { * @see Trackable.spec.js * * @param {string} identifier - String identifier of this Trackable - * @param {(Trackable|function)} owner - Parent or root log callback - * @memberof Trackable + * @param {(Trackable | Function)} owner - Parent or root log callback */ attach(identifier, owner) { this._identifier = identifier; diff --git a/packages/pwa-buildpack/lib/BuildBus/declare-base.js b/packages/pwa-buildpack/lib/BuildBus/declare-base.js index 310d272cc8..9fdae0d51f 100644 --- a/packages/pwa-buildpack/lib/BuildBus/declare-base.js +++ b/packages/pwa-buildpack/lib/BuildBus/declare-base.js @@ -1,7 +1,198 @@ +/** + * Target definitions for Buildpack. + * + * Since Buildpack implements the BuildBus itself, Buildpack's own targets can + * be considered the "base targets". Most other targets will be called in + * interceptors to other targets. However, Buildpack code has access to the + * BuildBus instance directly, so it can and does call the below targets + * directly in its module code using `bus.getTargetsOf`. + * + * @namespace {Object} BuiltinTargets + * + * @memberof @magento/pwa-buildpack + * + */ +const { SOURCE_SEP } = require('./Target'); module.exports = targets => { - targets.declare({ + /** + * @exports BuiltinTargets + */ + const builtins = { + /** + * @callback envVarIntercept + * @param {Object} defs - The envVarDefinitions.json structure. + * @returns {undefined} - Interceptors of `envVarDefinitions` may mutate + * the definitions object. Any returned value will be ignored. + */ + + /** + * Collects definitions and documentation for project-wide configuration + * values. The environment variable schema in `envVarDefinitions.json` + * is extensible. Extensions are often configurable, and they can + * integrate their configuration with the project-wide environment + * variables system by tapping `envVarDefinitions`. + * + * @type {tapable.SyncHook} + * @param {envVarIntercept} callback + * @memberof BuiltinTargets + * @example Add config fields for your extension + * targets.of('@magento/pwa-buildpack').envVarDefinitions.tap(defs => { + * defs.sections.push({ + * name: 'My Extension Settings', + * variables: [ + * { + * name: 'MY_EXTENSION_API_KEY', + * type: 'str', + * desc: 'API key for remote service access.' + * } + * ] + * }) + * }); + * + */ envVarDefinitions: new targets.types.Sync(['definitions']), + + /** + * @callback addTransform + * @param {Buildpack/WebpackTools~TransformRequest} transformRequest - + * Request to apply a transform to a file provided by this dependency. + */ + + /** + * @callback transformModuleIntercept + * @param {addTransform} addTransform - Callback to add a transform. + * @returns {undefined} - Interceptors of `transformModules` should call + * the `addTransform()` callback. Any returned value will be ignored. + */ + + /** + * Collects requests to intercept and modify individual files from this + * dependency. Only files from the currently requesting dependency may + * be transformed. + * **This is a very low-level extension point; it should be used as a + * building block for higher-level extensions that expose functional + * areas rather than files on disk.** + * + * @type {tapable.SyncHook} + * @param {transformModuleIntercept} + * @memberof BuiltinTargets + * + * @example Strip unnecessary Lodash code from a specific JS module. + * targets.of('@magento/pwa-buildpack').transformModules.tap(addTransform => addTransform({ + * type: 'babel', + * fileToTransform: './lib/uses-pipeline-syntax.js', + * transformModule: 'babel-plugin-lodash', + * options: { id: ["async", "lodash-bound" ]} + * })); + */ + transformModules: new targets.types.Sync(['requestTransform']), + + /** + * @callback webpackCompilerIntercept + * @param {webpack.Compiler} compiler - The Webpack compiler instance + * @returns {undefined} - Interceptors of `webpackCompiler` should tap + * hooks on the provided `compiler` object. Any returned value will be + * ignored. + */ + + /** + * + * Calls interceptors whenever a Webpack Compiler object is created. + * This almost always happens once per build, even in dev mode. + * + * @type {tapable.SyncHook} + * @param {webpackCompilerIntercept} callback + * @memberof BuiltinTargets + * + * @example Tap the compiler's `watchRun` hook. + * targets.of('@magento/pwa-buildpack').webpackCompiler.tap(compiler => { + * compiler.hooks.watchRun.tapPromise(async () => { + * compiler.getInfrastructureLogger('my-extension') + * .info('I do something special in the dev server!'); + * }); + * }); + */ webpackCompiler: new targets.types.Sync(['compiler']), + + /** + * @callback specialFeaturesIntercept + * @param {Object.} + * @returns {undefined} - Interceptors do no need to return. + */ + + /** + * Collects flags for special build features that dependency packages + * want to use. If your extension uses ES Modules instead of CommonJS in + * its frontend code (as most should), Webpack will not parse and build + * the modules by default; it will expect extension code to be CommonJS + * style and will not process the ES Modules. + * + * @example Declare that your extension contains CSS modules. + * targets.of('@magento/pwa-buildpack').specialFeatures.tap(special => { + * specialFeatures['my-module'] = { cssModules: true }; + * }) + * + * + * @see {configureWebpack} + * @type {tapable.SyncHook} + * @param {Object<(string, boolean)>} featureFlags + * @memberof BuiltinTargets + */ specialFeatures: new targets.types.Sync(['special']) + }; + + /** + * Special behavior for the `transformModules` target: interceptors should + * only be able to transform _their own code_, for proper encapsulation and + * compatibility. So their transform requests will provide relative paths + * to the files to be transformed, and the requestTransforms callback will + * enforce the module root. + * + * Basically, when we call `transformModules`, we need to know what + * dependency each transform request is coming from, so we don't have to + * trust third-party code to mind its own business. + * + * In order to do that, we have to have slightly different private state + * for each invocation of an interceptor. Tapable doesn't do that by + * default, so we use a meta-interceptor to bind every subsequently + * registered interceptor to its own callback, bound to its own module root + * as an extra argument. Then, we can make sure all requested files for + * transform belong to the module doing the requesting. + */ + builtins.transformModules.intercept({ + /** + * With the "register" meta-interceptor, we can mess with the arguments + * that subsequent interceptors receive. + */ + register: tapInfo => { + /** + * Get the requestor module out of the module name set by Target. + * If something has cheated and directly tapped the underlying + * tapable, it'll just be the name they passed and might not + * resolve to a real module. + */ + const requestor = tapInfo.name.split(SOURCE_SEP)[0]; + + // Reference to the original callback that the interceptor passed. + const callback = tapInfo.fn; + return { + /** + * Make a new tap object with a wrapped callback function. It + * now calls each interceptor with a slightly different arg. We + * know that transformModules receives a function as its first + * argument. So we expect it and bind it. + */ + ...tapInfo, + fn: (requestTransform, ...args) => { + // Ensure the request always has the right `requestor`. + const requestOwnModuleTransform = request => + requestTransform({ ...request, requestor }); + // yoink! + return callback(requestOwnModuleTransform, ...args); + } + }; + } }); + + targets.declare(builtins); }; diff --git a/packages/pwa-buildpack/lib/BuildBus/intercept-base.js b/packages/pwa-buildpack/lib/BuildBus/intercept-base.js index 9af5a97f18..a6d2e35303 100644 --- a/packages/pwa-buildpack/lib/BuildBus/intercept-base.js +++ b/packages/pwa-buildpack/lib/BuildBus/intercept-base.js @@ -1,4 +1,9 @@ +/** @ignore */ module.exports = targets => { + /** + * Intercept our own `envVarDefinitions` target to provide the base + * definitions for core functionality. + */ targets.own.envVarDefinitions.tap(defs => { const { sections, changes } = require('../../envVarDefinitions.json'); defs.sections.push(...sections); diff --git a/packages/pwa-buildpack/lib/Utilities/__tests__/__snapshots__/createDotEnvFile.spec.js.snap b/packages/pwa-buildpack/lib/Utilities/__tests__/__snapshots__/createDotEnvFile.spec.js.snap index 6dd1333e2b..8928055450 100644 --- a/packages/pwa-buildpack/lib/Utilities/__tests__/__snapshots__/createDotEnvFile.spec.js.snap +++ b/packages/pwa-buildpack/lib/Utilities/__tests__/__snapshots__/createDotEnvFile.spec.js.snap @@ -185,5 +185,14 @@ MAGENTO_BACKEND_URL=https://local.magento/ #CHECKOUT_BRAINTREE_TOKEN= # ################################################################################ + +#### BuildBus and targets ###################################################### +# +# A list of resolvable NPM modules that BuildBus will scan for targets, in +# addition to those declared in project \`dependencies\` and \`devDependencies\`. +# - Default when not set: +#BUILDBUS_DEPS_ADDITIONAL= +# +################################################################################ " `; diff --git a/packages/pwa-buildpack/lib/WebpackTools/ModuleTransformConfig.js b/packages/pwa-buildpack/lib/WebpackTools/ModuleTransformConfig.js new file mode 100644 index 0000000000..a4603a5c5e --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/ModuleTransformConfig.js @@ -0,0 +1,119 @@ +/** + * @module Buildpack/WebpackTools + */ +const path = require('path'); + +/** + * @typedef {Object} TransformRequest + * Instruction to the Webpack transform loader to pass a given file through a + * transform function implemented in a given Node module, with an optional set + * of configuration values that will be passed to the transform function. + * @prop {string} type - Type of transform. `'babel'` expects a Babel plugin as the `transformModule`. `"source"` expects a Webpack loader. + * @prop {string} requestor - Name of the file doing the requesting. + * @prop {string} fileToTransform - Relative path to the file in this module + * to be transformed when it is loaded by the compilation. + * @prop {string} transformModule - The Node module that exports the transform + * function to use. Node will resolve this relative to project root. + * @prop {object} [options] - Config values to send to the transform function. + * _Note: Options should be serializable to JSON as Webpack loader options._ + */ + +/** + * @typedef {Object.} WrapLoaderConfigSerialized + * A map of filenames of modules to be wrapped, to FileExportWrappers declaring + * which exports of those modules will be wrapped with what. + */ + +/** + * Configuration builder for module transforms. Accepts TransformRequests + * and emits loader config objects for Buildpack's custom transform loaders. + */ +class ModuleTransformConfig { + constructor(resolver) { + this._resolver = resolver; + this._reqs = []; + } + _traceableError(msg) { + const capturedError = new Error(`ModuleTransformConfig: ${msg}`); + Error.captureStackTrace(capturedError, ModuleTransformConfig); + return new Error(capturedError.stack); + } + /** + * + * Add a request to transform a file in the build. + * @param {TransformRequest} req - Request object + * @memberof ModuleTransformConfig + */ + add({ requestor, fileToTransform, transformModule, type, options }) { + if (typeof requestor !== 'string') { + throw this._traceableError( + `Invalid transform request for "${fileToTransform}": requestor module is a required property of a transform request..` + ); + } + if (path.isAbsolute(fileToTransform)) { + throw this._traceableError( + `Invalid fileToTransform path "${fileToTransform}": Absolute fileToTransform paths are not allowed! This transform request from "${requestor}" must provide a relative path to one of its own files.` + ); + } + let absTransformModule; + try { + absTransformModule = require.resolve(transformModule); + } catch (e) { + absTransformModule = require.resolve( + path.join(requestor, transformModule) + ); + } + // make module-absolute if relative + const toResolve = fileToTransform.startsWith(requestor) + ? fileToTransform + : path.join(requestor, fileToTransform); + // Capturing in the sync phase so that a resolve failure is traceable. + const resolveError = this._traceableError( + `ModuleTransformConfig could not resolve ${toResolve} in order to transform it with ${transformModule}.` + ); + // push the promise, so we don't run a bunch of resolves all at once + this._reqs.push( + this._resolver + .resolve(toResolve) + .then(absToTransform => ({ + requestor, + type, + fileToTransform: absToTransform, + transformModule: absTransformModule, + options + })) + .catch(() => { + throw resolveError; + }) + ); + } + /** + * Resolve paths and emit as JSON. + */ + async toLoaderOptions() { + const byType = { + babel: {}, + source: {} + }; + // Some reqs may still be outstanding! + (await Promise.all(this._reqs)).map(req => { + // Split them up by the transform module to use. + // Several requests will share one transform instance. + const { type, transformModule, fileToTransform } = req; + const xformsForType = byType[type]; + if (!xformsForType) { + throw new Error(`Unknown transform type "${type}"`); + } + const filesForXform = + xformsForType[transformModule] || + (xformsForType[transformModule] = {}); + const requestsForFile = + filesForXform[fileToTransform] || + (filesForXform[fileToTransform] = []); + requestsForFile.push(req); + }); + return JSON.parse(JSON.stringify(byType)); + } +} + +module.exports = ModuleTransformConfig; diff --git a/packages/pwa-buildpack/lib/WebpackTools/__tests__/configureWebpack.spec.js b/packages/pwa-buildpack/lib/WebpackTools/__tests__/configureWebpack.spec.js index c2faa3d834..2ce9e733d6 100644 --- a/packages/pwa-buildpack/lib/WebpackTools/__tests__/configureWebpack.spec.js +++ b/packages/pwa-buildpack/lib/WebpackTools/__tests__/configureWebpack.spec.js @@ -31,10 +31,13 @@ pkgDir.mockImplementation(x => x); const specialFeaturesHook = new SyncHook(['special']); const envVarDefsHook = new SyncHook(['envVarDefs']); +const transformModulesHook = new SyncHook(['addTransform']); declareBase.mockImplementation(targets => { targets.declare({ envVarDefinitions: envVarDefsHook, - specialFeatures: specialFeaturesHook + specialFeatures: specialFeaturesHook, + webpackCompiler: new SyncHook(['compiler']), + transformModules: transformModulesHook }); }); @@ -238,3 +241,29 @@ test('handles special flags', async () => { expect(declareBase).toHaveBeenCalledTimes(1); expect(specialFeaturesTap).toHaveBeenCalledWith(special); }); + +test('accepts aliases', async () => { + simulate + .statsAsDirectory() + .statsAsFile() + .productionEnvironment(); + + await expect( + configureWebpack({ + context: path.resolve(__dirname, '__fixtures__/resolverContext'), + alias: { + garner: 'bristow', + cooper: 'tippin' + } + }) + ).resolves.toMatchObject({ + clientConfig: { + resolve: { + alias: { + garner: 'bristow', + cooper: 'tippin' + } + } + } + }); +}); diff --git a/packages/pwa-buildpack/lib/WebpackTools/configureWebpack.js b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack.js deleted file mode 100644 index 20cc982884..0000000000 --- a/packages/pwa-buildpack/lib/WebpackTools/configureWebpack.js +++ /dev/null @@ -1,161 +0,0 @@ -const { promisify } = require('util'); -const stat = promisify(require('fs').stat); -const path = require('path'); -const pkgDir = require('pkg-dir'); - -const loadEnvironment = require('../Utilities/loadEnvironment'); -const getClientConfig = require('../Utilities/getClientConfig'); -const getServiceWorkerConfig = require('../Utilities/getServiceWorkerConfig'); -const MagentoResolver = require('../WebpackTools/MagentoResolver'); -const BuildBus = require('../BuildBus'); -const BuildBusPlugin = require('./plugins/BuildBusPlugin'); - -/** - * We need a root directory for the app in order to build all paths relative to - * that app root. It's not safe to use process.cwd() here because that depends - * on what directory Node is run in. The project root should be the dir of the - * webpack.config.js which called this function. - * - * There is no safe way to get the path of this function's caller, so instead we - * expect that the webpack.config.js will do: - * - * configureWebpack(__dirname); - */ -async function validateRoot(appRoot) { - if (!appRoot) { - throw new Error( - 'Must provide the root directory of the PWA as the "context" property of configureWebpack() options. `configureWebpack()`. In webpack.config.js, the recommended code is `configureWebpack({ context: __dirname })` to set the context to the exact location of webpack.config.js.' - ); - } - // If root doesn't exist, an ENOENT will throw here and log to stderr. - const dirStat = await stat(appRoot); - if (!dirStat.isDirectory()) { - throw new Error( - `Provided application root "${appRoot}" is not a directory.` - ); - } -} - -async function checkForBabelConfig(appRoot) { - try { - await stat(path.resolve(appRoot, 'babel.config.js')); - return true; - } catch (e) { - return false; - } -} - -function getMode(cliEnv = {}, projectConfig) { - if (cliEnv.mode) { - return cliEnv.mode; - } - if (projectConfig.isProd) { - return 'production'; - } - return 'development'; -} - -async function configureWebpack(options) { - const { context } = options; - - await validateRoot(context); - - let stats; - if (process.env.DEBUG && process.env.DEBUG.includes('BuildBus')) { - BuildBus.enableTracking(); - stats = { - logging: 'verbose', - loggingDebug: ['BuildBusPlugin'], - assets: false, - chunks: false, - chunkGroups: false, - chunkModules: false, - chunkOrigins: false, - entrypoints: false, - performance: false, - publicPath: false, - reasons: false, - timings: false, - version: false - }; - } - - const busTrackingQueue = []; - const bus = BuildBus.for(context); - bus.attach('configureWebpack', (...args) => busTrackingQueue.push(args)); - bus.init(); - - const babelConfigPresent = await checkForBabelConfig(context); - - const projectConfig = loadEnvironment(context); - if (projectConfig.error) { - throw projectConfig.error; - } - - const paths = { - src: path.resolve(context, 'src'), - output: path.resolve(context, 'dist') - }; - - const isEE = projectConfig.env.MAGENTO_BACKEND_EDITION === 'EE'; - - const resolverOpts = { - paths: { - root: context, - isEE - } - }; - if (options.alias) { - resolverOpts.alias = { ...options.alias }; - } - - const magentoResolver = new MagentoResolver(resolverOpts); - - const special = options.special || {}; - bus.getTargetsOf('@magento/pwa-buildpack').specialFeatures.call(special); - - // Resolve every module listed in the `special` object into an absolute - // filesystem path. Will be used as a test for the loader rules for each - // of these feature flags. - const features = await Promise.all( - Object.entries(special).map(async ([packageName, flags]) => [ - packageName, - await pkgDir( - path.dirname(await magentoResolver.resolve(packageName)) - ), - flags - ]) - ); - - const hasFlag = flag => - features.reduce( - (hasIt, [, packagePath, flags]) => - flags[flag] ? [...hasIt, packagePath] : hasIt, - [] - ); - - const mode = getMode(options.env, projectConfig); - const configOptions = { - mode, - context, - babelConfigPresent, - paths, - hasFlag, - projectConfig, - resolve: magentoResolver.config, - stats - }; - - const serviceWorkerConfig = getServiceWorkerConfig(configOptions); - - const clientConfig = await getClientConfig({ - ...configOptions, - vendor: options.vendor || [] - }); - - clientConfig.plugins.unshift(new BuildBusPlugin(bus, busTrackingQueue)); - - return { clientConfig, serviceWorkerConfig }; -} - -module.exports = configureWebpack; diff --git a/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/configureWebpack.js b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/configureWebpack.js new file mode 100644 index 0000000000..41e6025396 --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/configureWebpack.js @@ -0,0 +1,203 @@ +/** + * Create a Webpack configuration object customized for your project. + * @module Buildpack/WebpackTools + */ +const { promisify } = require('util'); +const stat = promisify(require('fs').stat); +const path = require('path'); + +const loadEnvironment = require('../../Utilities/loadEnvironment'); +const getClientConfig = require('./getClientConfig'); +const getModuleRules = require('./getModuleRules'); +const getResolveLoader = require('./getResolveLoader'); +const getServiceWorkerConfig = require('./getServiceWorkerConfig'); +const getSpecialFlags = require('./getSpecialFlags'); +const MagentoResolver = require('../MagentoResolver'); +const BuildBus = require('../../BuildBus'); +const BuildBusPlugin = require('../plugins/BuildBusPlugin'); +const ModuleTransformConfig = require('../ModuleTransformConfig'); + +/** + * Helps convert PWA Studio Buildpack settings and project properties into + * Webpack configuration. + * @typedef {Object} WebpackConfigHelper + * @property {boolean} mode - Webpack mode derived from env: + * 'development'|'production'|'none' + * @property {string} context - Root directory of project + * @property {boolean} babelRootMode - Babel config search mode: + * 'root'|'upward'|'upward-optional + * @property {Object} paths - Relevant Webpack paths + * @property {string} paths.root - Project root, equivalent to Webpack `context` + * @property {string} paths.src - Source code folder, should be a subdirectory + * of root + * @property {Buildpack/WebpackTools~hasSpecialFlags} hasFlag - Returns a list + * of real paths to modules which have set the provided flag. Use for module + * rules. + * @property {Buildpack/Utilities~ProjectConfiguration} projectConfig - + * Configuration object for retrieving any environment variable setting for + * the project being built. + * @property {MagentoResolver} resolver - Module resolver function for this + * build + * @property {Object} transformRequests - Map of requests to apply loader + * transforms to individual modules. + * @property {Object} transformRequests.babel - Map of individual ES modules to + * requests to transform them via Babel. + * @property {Buildpack/BuildBus.BuildBus} bus - {@link BuildBus} for the currently running task. + * Will be used to call build-specific targets. + */ + +/** + * @ignore + * We need a root directory for the app in order to build all paths relative to + * that app root. It's not safe to use process.cwd() here because that depends + * on what directory Node is run in. The project root should be the dir of the + * webpack.config.js which called this function. + * + * There is no safe way to get the path of this function's caller, so instead we + * expect that the webpack.config.js will do: + * + * configureWebpack(__dirname); + */ +async function validateRoot(appRoot) { + if (!appRoot) { + throw new Error( + 'Must provide the root directory of the PWA as the "context" property of configureWebpack() options. `configureWebpack()`. In webpack.config.js, the recommended code is `configureWebpack({ context: __dirname })` to set the context to the exact location of webpack.config.js.' + ); + } + // If root doesn't exist, an ENOENT will throw here and log to stderr. + const dirStat = await stat(appRoot); + if (!dirStat.isDirectory()) { + throw new Error( + `Provided application root "${appRoot}" is not a directory.` + ); + } +} + +async function getBabelRootMode(appRoot) { + try { + await stat(path.resolve(appRoot, 'babel.config.js')); + return 'root'; + } catch (e) { + return 'upward'; + } +} + +function getMode(cliEnv = {}, projectConfig) { + if (cliEnv.mode) { + return cliEnv.mode; + } + if (projectConfig.isProd) { + return 'production'; + } + return 'development'; +} + +/** + * Create a configuration object to pass to Webpack for build. Use this in + * the `webpack.config.js` file in your project root. + * + * @static + * @returns An array of two Webpack configurations for simultaneously building the client bundles and the ServiceWorker bundle. + */ +async function configureWebpack(options) { + const { context } = options; + + await validateRoot(context); + + let stats; + /* istanbul ignore next: this is an internal debug mechanism for now */ + if (process.env.DEBUG && process.env.DEBUG.includes('BuildBus')) { + BuildBus.enableTracking(); + stats = { + logging: 'verbose', + loggingDebug: ['BuildBusPlugin'], + assets: false, + chunks: false, + chunkGroups: false, + chunkModules: false, + chunkOrigins: false, + entrypoints: false, + performance: false, + publicPath: false, + reasons: false, + timings: false, + version: false + }; + } + + const busTrackingQueue = []; + const bus = BuildBus.for(context); + bus.attach('configureWebpack', (...args) => busTrackingQueue.push(args)); + bus.init(); + + const babelRootMode = await getBabelRootMode(context); + + const projectConfig = loadEnvironment(context); + if (projectConfig.error) { + throw projectConfig.error; + } + + const paths = { + src: path.resolve(context, 'src'), + output: path.resolve(context, 'dist') + }; + + const isEE = projectConfig.env.MAGENTO_BACKEND_EDITION === 'EE'; + + const resolverOpts = { + paths: { + root: context, + isEE + } + }; + if (options.alias) { + resolverOpts.alias = { ...options.alias }; + } + + const resolver = new MagentoResolver(resolverOpts); + + const hasFlag = await getSpecialFlags(options.special, bus, resolver); + + const mode = getMode(options.env, projectConfig); + + const transforms = new ModuleTransformConfig(resolver); + + /** @typedef {import('../../BuildBus/declare-base)} BuiltinTargets */ + + bus.getTargetsOf('@magento/pwa-buildpack').transformModules.call(x => + transforms.add(x) + ); + const transformRequests = await transforms.toLoaderOptions(); + + const configHelper = { + mode, + context, + babelRootMode, + paths, + hasFlag, + projectConfig, + resolver, + stats, + transformRequests, + bus + }; + + const serviceWorkerConfig = getServiceWorkerConfig(configHelper); + + const clientConfig = await getClientConfig({ + ...configHelper, + vendor: options.vendor || [] + }); + + clientConfig.plugins.unshift(new BuildBusPlugin(bus, busTrackingQueue)); + + return { clientConfig, serviceWorkerConfig }; +} + +configureWebpack.getClientConfig = getClientConfig; +configureWebpack.getModuleRules = getModuleRules; +configureWebpack.getResolveLoader = getResolveLoader; +configureWebpack.getServiceWorkerConfig = getServiceWorkerConfig; +configureWebpack.getSpecialFlags = getSpecialFlags; + +module.exports = configureWebpack; diff --git a/packages/pwa-buildpack/lib/Utilities/getClientConfig.js b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getClientConfig.js similarity index 72% rename from packages/pwa-buildpack/lib/Utilities/getClientConfig.js rename to packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getClientConfig.js index 8cf5eab4af..9297c77efe 100644 --- a/packages/pwa-buildpack/lib/Utilities/getClientConfig.js +++ b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getClientConfig.js @@ -1,28 +1,40 @@ +/** + * @module Buildpack/WebpackTools + */ const debug = require('debug')('pwa-buildpack:createClientConfig'); const path = require('path'); const webpack = require('webpack'); const WebpackAssetsManifest = require('webpack-assets-manifest'); const TerserPlugin = require('terser-webpack-plugin'); -const PWADevServer = require('../WebpackTools/PWADevServer'); -const RootComponentsPlugin = require('../WebpackTools/plugins/RootComponentsPlugin'); -const UpwardIncludePlugin = require('../WebpackTools/plugins/UpwardIncludePlugin'); +const getModuleRules = require('./getModuleRules'); +const getResolveLoader = require('./getResolveLoader'); + +const RootComponentsPlugin = require('../plugins/RootComponentsPlugin'); +const UpwardIncludePlugin = require('../plugins/UpwardIncludePlugin'); function isDevServer() { return process.argv.find(v => v.includes('webpack-dev-server')); } -module.exports = async function({ - mode, - context, - paths, - babelConfigPresent, - hasFlag, - vendor, - projectConfig, - stats, - resolve -}) { +/** + * Create a Webpack configuration object for the browser bundle. + * + * @param {Buildpack/WebpackTools~WebpackConfigHelper} opts + * @returns {Object} A Webpack configuration for the main app. + */ +async function getClientConfig(opts) { + const { + mode, + context, + paths, + hasFlag, + vendor, + projectConfig, + stats, + resolver + } = opts; + let vendorTest = '[\\/]node_modules[\\/]'; if (vendor.length > 0) { @@ -47,75 +59,10 @@ module.exports = async function({ chunkFilename: '[name].[chunkhash].js' }, module: { - rules: [ - { - test: /\.graphql$/, - include: [paths.src, ...hasFlag('graphqlQueries')], - use: [ - { - loader: 'graphql-tag/loader' - } - ] - }, - { - test: /\.(mjs|js|jsx)$/, - include: [paths.src, ...hasFlag('esModules')], - sideEffects: false, - use: [ - { - loader: 'babel-loader', - options: { - envName: mode, - rootMode: babelConfigPresent ? 'root' : 'upward' - } - } - ] - }, - { - test: /\.css$/, - oneOf: [ - { - test: [paths.src, ...hasFlag('cssModules')], - use: [ - 'style-loader', - { - loader: 'css-loader', - options: { - localIdentName: - '[name]-[local]-[hash:base64:3]', - modules: true - } - } - ] - }, - { - include: /node_modules/, - use: [ - 'style-loader', - { - loader: 'css-loader', - options: { - modules: false - } - } - ] - } - ] - }, - { - test: /\.(jpg|svg|png)$/, - use: [ - { - loader: 'file-loader', - options: { - name: '[name]-[hash:base58:3].[ext]' - } - } - ] - } - ] + rules: await getModuleRules(opts) }, - resolve, + resolve: resolver.config, + resolveLoader: getResolveLoader(), plugins: [ new RootComponentsPlugin({ rootComponentsDirs: [ @@ -198,6 +145,7 @@ module.exports = async function({ // See https://webpack.js.org/configuration/devtool/ config.devtool = 'eval-source-map'; debug('Configuring Dev Server'); + const PWADevServer = require('../PWADevServer'); await PWADevServer.configure( { graphqlPlayground: true, @@ -296,4 +244,6 @@ module.exports = async function({ } debug('Client config created'); return config; -}; +} + +module.exports = getClientConfig; diff --git a/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getModuleRules.js b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getModuleRules.js new file mode 100644 index 0000000000..5f8f6471ff --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getModuleRules.js @@ -0,0 +1,146 @@ +/** + * @module Buildpack/WebpackTools + */ + +/** + * Create a Webpack + * [module rules object](https://webpack.js.org/configuration/module/#rule) for + * processing all the filetypes that the project will contain. + * + * @param {Buildpack/WebpackTools~WebpackConfigHelper} helper + * @returns {Object[]} Array of Webpack rules. + */ +async function getModuleRules(helper) { + return Promise.all([ + getModuleRules.graphql(helper), + getModuleRules.js(helper), + getModuleRules.css(helper), + getModuleRules.files(helper) + ]); +} + +/** + * @param {Buildpack/WebpackTools~WebpackConfigHelper} helper + * @returns Rule object for Webpack `module` configuration which parses + * `.graphql` files + */ +getModuleRules.graphql = async ({ paths, hasFlag }) => ({ + test: /\.graphql$/, + include: [paths.src, ...hasFlag('graphqlQueries')], + use: [ + { + loader: 'graphql-tag/loader' + } + ] +}); + +/** + * @param {Buildpack/WebpackTools~WebpackConfigHelper} helper + * @returns Rule object for Webpack `module` configuration which parses + * JavaScript files + */ +getModuleRules.js = async ({ + mode, + paths, + hasFlag, + babelRootMode, + transformRequests +}) => { + const overrides = Object.entries(transformRequests.babel).map( + ([plugin, requestsByFile]) => ({ + test: Object.keys(requestsByFile), + plugins: [[plugin, { requestsByFile }]] + }) + ); + + const astLoaders = [ + { + loader: 'babel-loader', + options: { + sourceMaps: mode === 'development' && 'inline', + envName: mode, + root: paths.root, + rootMode: babelRootMode, + overrides + } + } + ]; + + const sourceLoaders = Object.entries(transformRequests.source).map( + ([loader, requestsByFile]) => { + return { + test: Object.keys(requestsByFile), + use: [ + info => ({ + loader, + options: requestsByFile[info.realResource].map( + req => req.options + ) + }) + ] + }; + } + ); + + return { + test: /\.(mjs|js|jsx)$/, + include: [paths.src, ...hasFlag('esModules')], + sideEffects: false, + rules: [...astLoaders, ...sourceLoaders] + }; +}; + +/** + * @param {Buildpack/WebpackTools~WebpackConfigHelper} helper + * @returns Rule object for Webpack `module` configuration which parses + * CSS files + */ +getModuleRules.css = async ({ paths, hasFlag }) => ({ + test: /\.css$/, + oneOf: [ + { + test: [paths.src, ...hasFlag('cssModules')], + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + localIdentName: '[name]-[local]-[hash:base64:3]', + modules: true + } + } + ] + }, + { + include: /node_modules/, + use: [ + 'style-loader', + { + loader: 'css-loader', + options: { + modules: false + } + } + ] + } + ] +}); + +/** + * @param {Buildpack/WebpackTools~WebpackConfigHelper} helper + * @returns Rule object for Webpack `module` configuration which parses + * and inlines binary files below a certain size + */ +getModuleRules.files = async () => ({ + test: /\.(jpg|svg|png)$/, + use: [ + { + loader: 'file-loader', + options: { + name: '[name]-[hash:base58:3].[ext]' + } + } + ] +}); + +module.exports = getModuleRules; diff --git a/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getResolveLoader.js b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getResolveLoader.js new file mode 100644 index 0000000000..2e0dcc8677 --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getResolveLoader.js @@ -0,0 +1,21 @@ +/** + * @module Buildpack/WebpackTools + */ +const path = require('path'); + +/** + * Configuration object that helps Webpack find Buildpack's builtin loaders, + * along with any other loaders installed as Node modules. + * Assign the return value to the `resolveLoaders` property of Webpack config. + * + * @returns {Object} Loader resolve configuration. + */ +function getResolveLoader() { + return { + modules: [path.resolve(__dirname, '../loaders'), 'node_modules'], + extensions: ['.js'], + mainFields: ['loader', 'main'] + }; +} + +module.exports = getResolveLoader; diff --git a/packages/pwa-buildpack/lib/Utilities/getServiceWorkerConfig.js b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getServiceWorkerConfig.js similarity index 82% rename from packages/pwa-buildpack/lib/Utilities/getServiceWorkerConfig.js rename to packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getServiceWorkerConfig.js index 73ad3b5581..1125fe52e5 100644 --- a/packages/pwa-buildpack/lib/Utilities/getServiceWorkerConfig.js +++ b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getServiceWorkerConfig.js @@ -1,16 +1,26 @@ +/** + * @module Buildpack/WebpackTools + */ const debug = require('debug')('pwa-buildpack:createServiceWorkerConfig'); const path = require('path'); const TerserPlugin = require('terser-webpack-plugin'); -const ServiceWorkerPlugin = require('../WebpackTools/plugins/ServiceWorkerPlugin'); +const ServiceWorkerPlugin = require('../plugins/ServiceWorkerPlugin'); -module.exports = function({ +/** + * Create a Webpack configuration object for the browser bundle. + * + * @param {Buildpack/WebpackTools~WebpackConfigHelper} helper + * @returns A Webpack configuration for the ServiceWorker. + */ +function getServiceWorkerConfig({ mode, context, paths, - babelConfigPresent, + babelRootMode, hasFlag, projectConfig, + resolver, stats }) { debug('Creating service worker config'); @@ -39,13 +49,14 @@ module.exports = function({ loader: 'babel-loader', options: { envName: mode, - rootMode: babelConfigPresent ? 'root' : 'upward' + rootMode: babelRootMode } } ] } ] }, + resolve: resolver.config, plugins: [ new ServiceWorkerPlugin({ mode, @@ -86,4 +97,6 @@ module.exports = function({ }; debug('Done creating service worker config.'); return config; -}; +} + +module.exports = getServiceWorkerConfig; diff --git a/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getSpecialFlags.js b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getSpecialFlags.js new file mode 100644 index 0000000000..8c61ff2898 --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/getSpecialFlags.js @@ -0,0 +1,60 @@ +/** + * Create a Webpack configuration object customized for your project. + * @module Buildpack/WebpackTools + */ +const path = require('path'); +const pkgDir = require('pkg-dir'); + +/** + * Boolean flags indicating that a dependent package has special capabilities + * requiring Webpack to process its modules differently. + * @typedef {Object} Buildpack/WebpackTools~SpecialBuildFlags + * @property {boolean} [cssModules] Parse `.css` files as CSS Modules + * @property {boolean} [esModules] Parse JS files as ECMAScript Modules + * @property {boolean} [graphqlQueries] Transpile and inline .graphql files + * @property {boolean} [rootComponents] Look for RootComponents to handle + * Magento page types + * @property {boolean} [upward] Look for `upward.yml` files to be merged into + * the final UPWARD config + */ + +/** + * For any flag in {@link SpecialBuildFlags}, return a list of real paths to + * modules which declared that flag to be `true` before or during build. + * @callback Buildpack/WebpackTools~hasSpecialFlags + * @param {string} flag - Special flag the returned dependencies must have + * @returns {string[]} realpaths - Real paths to the root directories of the modules which have set that flag. + */ + +/** + * + * @param {Object} [special={}] - Map of module names + * to special flags manually declared by build configuration. + * @param {BuildBus} bus - {@link BuildBus} for the currently running task. + * Will be used to call the `specialFeatures` target. + * @param {Buildpack/WebpackTools~MagentoResolver} resolver - Enhanced resolver + * @returns {hasSpecialFlags} + */ +async function getSpecialFlags(special = {}, bus, resolver) { + bus.getTargetsOf('@magento/pwa-buildpack').specialFeatures.call(special); + + // Resolve every module listed in the `special` object into an absolute + // filesystem path. Will be used as a test for the loader rules for each + // of these feature flags. + const features = await Promise.all( + Object.entries(special).map(async ([packageName, flags]) => [ + packageName, + await pkgDir(path.dirname(await resolver.resolve(packageName))), + flags + ]) + ); + + return flag => + features.reduce( + (hasIt, [, packagePath, flags]) => + flags[flag] ? [...hasIt, packagePath] : hasIt, + [] + ); +} + +module.exports = getSpecialFlags; diff --git a/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/index.js b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/index.js new file mode 100644 index 0000000000..b17c7745e0 --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/index.js @@ -0,0 +1 @@ +module.exports = require('./configureWebpack'); diff --git a/packages/pwa-buildpack/lib/WebpackTools/loaders/wrap-esm-loader.js b/packages/pwa-buildpack/lib/WebpackTools/loaders/wrap-esm-loader.js new file mode 100644 index 0000000000..5bbb40997a --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/loaders/wrap-esm-loader.js @@ -0,0 +1,112 @@ +const Template = require('webpack/lib/Template'); + +function wrapEsmLoader(content) { + let ids = 0; + + const uniqueJsId = name => + `${Template.toIdentifier( + name.includes('/') ? name.slice(name.lastIndexOf('/')) : name + )}_${++ids}`; + + const defaultExportRE = /^\s*export\s+default/m; + // todo make this actually spec with a parser + const exportRE = name => + new RegExp(`^\\s*export\\s+((?:const|let|var|function) )${name}`, 'm'); + const hasExport = name => exportRE(name).test(content); + const hasDefaultExport = () => defaultExportRE.test(content); + + const importsMap = new Map(); + const addImport = modulePath => { + let identifier = importsMap.get(modulePath); + if (!identifier) { + identifier = uniqueJsId(modulePath); + importsMap.set(modulePath, identifier); + this.addDependency(modulePath); + } + return identifier; + }; + + const defaultExportWrappers = new Set(); + const wrapDefaultExport = identifier => { + defaultExportWrappers.add(identifier); + }; + + const exportWrappers = new Map(); + const wrapExport = (exportName, wrapperIdentifier) => { + let wrappers = exportWrappers.get(exportName); + if (!wrappers) { + wrappers = new Set(); + exportWrappers.set(exportName, wrappers); + } + wrappers.add(wrapperIdentifier); + }; + + for (const { wrapperModule, exportName, defaultExport } of this.query) { + if (defaultExport) { + if (!hasDefaultExport()) { + this.emitWarning( + `wrap-js-loader: Cannot wrap default export of "${ + this.resourcePath + }" with module "${wrapperModule}" because it does not have a default export.` + ); + } else { + const wrapperIdentifier = addImport(wrapperModule); + wrapDefaultExport(wrapperIdentifier); + } + } else if (!hasExport(exportName)) { + this.emitWarning( + `wrap-js-loader: Cannot wrap export "${exportName}" of "${ + this.resourcePath + }" with module "${wrapperModule}" because it does not have an export named "${exportName}".` + ); + } else { + const wrapperIdentifier = addImport(wrapperModule); + wrapExport(exportName, wrapperIdentifier); + } + } + + if (importsMap.size === 0) { + this.callback(null, content); + } + + let imports = `// BUILDPACK: wrap-esm-loader added ${ + importsMap.size + } imports +`; + for (const [modulePath, identifier] of importsMap.entries()) { + imports += `import ${identifier} from '${modulePath}';\n`; + } + + let wrappedExports = content; + + if (defaultExportWrappers.size > 0) { + const defaultExportIntermediateVar = uniqueJsId('default'); + let finalDefaultExport = defaultExportIntermediateVar; + for (const defaultExportWrapper of defaultExportWrappers) { + finalDefaultExport = `${defaultExportWrapper}(${finalDefaultExport})`; + } + wrappedExports = + wrappedExports.replace( + defaultExportRE, + `const ${defaultExportIntermediateVar} = ` + ) + `;\nexport default ${finalDefaultExport};\n`; + } + + if (exportWrappers.size > 0) { + for (const [exportName, wrappers] of exportWrappers.entries()) { + const exportIntermediateVar = uniqueJsId(exportName); + let finalExport = exportIntermediateVar; + for (const exportWrapper of wrappers) { + finalExport = `${exportWrapper}(${finalExport})`; + } + wrappedExports = + wrappedExports.replace( + exportRE(exportName), + `\n$1${exportIntermediateVar}` + ) + `;\nexport const ${exportName} = ${finalExport};\n`; + } + } + this.callback(null, imports + '\n' + wrappedExports); //, finalSourceMap, ast); +} + +module.exports = wrapEsmLoader; diff --git a/packages/venia-concept/webpack.config.js b/packages/venia-concept/webpack.config.js index 8a8cd46991..15f592ccac 100644 --- a/packages/venia-concept/webpack.config.js +++ b/packages/venia-concept/webpack.config.js @@ -34,17 +34,6 @@ module.exports = async env => { special: { 'react-feather': { esModules: true - }, - '@magento/peregrine': { - esModules: true, - cssModules: true - }, - '@magento/venia-ui': { - cssModules: true, - esModules: true, - graphqlQueries: true, - rootComponents: true, - upward: true } }, env diff --git a/packages/venia-ui/lib/components/Routes/__tests__/routes.spec.js b/packages/venia-ui/lib/components/Routes/__tests__/routes.spec.js new file mode 100644 index 0000000000..3e96b55c0e --- /dev/null +++ b/packages/venia-ui/lib/components/Routes/__tests__/routes.spec.js @@ -0,0 +1,28 @@ +import React from 'react'; +import { StaticRouter } from 'react-router-dom'; +import { createTestInstance } from '@magento/peregrine'; +import Routes from '../routes'; +import MagentoRoute from '../../MagentoRoute'; +import { useScrollTopOnChange } from '@magento/peregrine/lib/hooks/useScrollTopOnChange'; + +jest.mock('@magento/peregrine/lib/hooks/useScrollTopOnChange'); +jest.mock( + '../../MagentoRoute', + () => + function MagentoRoute() { + return null; + } +); + +test('renders routes component', () => { + const component = createTestInstance( + + + + ); + + expect(component.root.findByType(MagentoRoute)).toBeTruthy(); + expect(useScrollTopOnChange).toHaveBeenCalledWith( + 'https://localhost/fake-location' + ); +}); diff --git a/packages/venia-ui/lib/components/Routes/routes.js b/packages/venia-ui/lib/components/Routes/routes.js index 5d9a1404fc..a1e70081de 100644 --- a/packages/venia-ui/lib/components/Routes/routes.js +++ b/packages/venia-ui/lib/components/Routes/routes.js @@ -1,15 +1,10 @@ -import React, { Suspense, lazy } from 'react'; +import React, { Suspense } from 'react'; import { Route, Switch, useLocation } from 'react-router-dom'; import { fullPageLoadingIndicator } from '../LoadingIndicator'; import MagentoRoute from '../MagentoRoute'; import { useScrollTopOnChange } from '@magento/peregrine/lib/hooks/useScrollTopOnChange'; -const CartPage = lazy(() => import('../CartPage')); -const CheckoutPage = lazy(() => import('../CheckoutPage')); -const CreateAccountPage = lazy(() => import('../CreateAccountPage')); -const Search = lazy(() => import('../../RootComponents/Search')); - const Routes = () => { const { pathname } = useLocation(); useScrollTopOnChange(pathname); @@ -17,18 +12,6 @@ const Routes = () => { return ( - - - - - - - - - - - - diff --git a/packages/venia-ui/lib/targets/BabelRouteInjectionPlugin.js b/packages/venia-ui/lib/targets/BabelRouteInjectionPlugin.js new file mode 100644 index 0000000000..74051ccfde --- /dev/null +++ b/packages/venia-ui/lib/targets/BabelRouteInjectionPlugin.js @@ -0,0 +1,111 @@ +const babelTemplate = require('@babel/template'); + +function BabelRouteInjectionPlugin(babel) { + const { types: t } = babel; + + const lazyRouteImport = babelTemplate.statement( + `const %%id%% = React.lazy(() => import(%%path%%));`, + { + plugins: ['dynamicImport'] + } + ); + + return { + visitor: { + Program: { + enter(path, state) { + state.routes = []; + const seenPatterns = new Map(); + const seenModules = new Map(); + const requests = this.opts.requestsByFile[this.filename]; + for (const request of requests) { + const { requestor } = request; + for (const route of request.options.routes) { + const routeWithRequestor = { ...route, requestor }; + const seenPattern = seenPatterns.get(route.pattern); + if (!seenPattern) + seenPatterns.set( + route.pattern, + routeWithRequestor + ); + + const seenExactPattern = + seenPattern && + seenPattern.exact === route.exact; + + const seenModule = seenModules.get(route.path); + if (!seenModule) + seenModules.set(route.path, routeWithRequestor); + + if (!seenModule && !seenExactPattern) { + // a nice, fresh route! + state.routes.push(route); + } else if (seenExactPattern) { + throw new Error( + `@magento/venia-ui: Conflict in "routes" target. "${ + request.requestor + }" is trying to add a route ${JSON.stringify( + route + )}, but "${ + seenExactPattern.requestor + }" has already declared that route pattern: ${JSON.stringify( + seenExactPattern + )}` + ); + } else if (seenModule) { + throw new Error( + `@magento/venia-ui: Conflict in "routes" target. "${ + request.requestor + }" is trying to add a route ${JSON.stringify( + route + )}, but "${ + seenModule.requestor + }" has already declared another pattern for that same module: ${JSON.stringify( + seenModule + )}` + ); + } + // if we got here, both have been seen, so this is an exact duplicate; + // fine to do nothing. + } + } + const dynamicImports = []; + state.routeElements = []; + for (const route of state.routes) { + const id = path.scope.generateUidIdentifier(route.name); + // must begin with uppercase! + id.name = 'InjectedRoute_' + id.name; + dynamicImports.push( + lazyRouteImport({ + id, + path: t.stringLiteral(route.path) + }) + ); + state.routeElements.push( + babelTemplate.expression.ast( + `<${id.name}/>`, + { plugins: ['jsx'] } + ) + ); + } + path.unshiftContainer('body', dynamicImports); + } + }, + JSXElement: { + enter(path, state) { + if (state.routesInserted) return; + const { openingElement } = path.node; + const elmId = openingElement && openingElement.name; + if (elmId && elmId.name === 'Route') { + path.insertBefore(state.routeElements); + state.routesInserted = true; + } + } + } + } + }; +} + +module.exports = BabelRouteInjectionPlugin; diff --git a/packages/venia-ui/lib/targets/rendererCollectionLoader.js b/packages/venia-ui/lib/targets/rendererCollectionLoader.js index 08a909be47..21b1a33255 100644 --- a/packages/venia-ui/lib/targets/rendererCollectionLoader.js +++ b/packages/venia-ui/lib/targets/rendererCollectionLoader.js @@ -1,7 +1,21 @@ +/** + * This "loader" codegens a module from scratch that just imports and + * re-exports an array of Rich Content Renderers that extensions have + * registered via the `richContentRenderers` target. + * It's only meant to be used for the one specific + * `./components/RichContent/richContentRenderers.js` module file. + * {@see ./venia-ui-intercept} + * + * TODO: This is a cheap hardcode, written as a first pass before we had + * general-purpose transformModules target. Refactor to use that! + * + * @module VeniaUI/Targets + */ const BLANK_LINE = '\n\n'; module.exports = function buildRichContentRendererArray(source) { - // remove placeholder export, which only exists so linters don't complain + // The actual source is almost empty, save an explanatory comment. + // Remove placeholder export, which only exists so linters don't complain const docString = source.slice(0, source.indexOf(BLANK_LINE)); let importStatements = ''; const exportMembers = []; diff --git a/packages/venia-ui/lib/targets/venia-ui-declare.js b/packages/venia-ui/lib/targets/venia-ui-declare.js index 8c58c23519..113161387a 100644 --- a/packages/venia-ui/lib/targets/venia-ui-declare.js +++ b/packages/venia-ui/lib/targets/venia-ui-declare.js @@ -1,5 +1,95 @@ +/** + * These targets are available for interception to modules which depend on `@magento/venia-ui`. + * + * Their implementations are found in `./venia-ui-intercept.js`. + * + * @module VeniaUI/Targets + */ module.exports = targets => { targets.declare({ - richContentRenderers: new targets.types.Sync(['renderers']) + /** + * A file that implements the RichContentRenderer interface. + * + * @typedef {Object} RichContentRenderer + * @property {string} componentName - Friendly name for the React + * component, for debugging purposes. + * @property {string} importPath - Path to the implementation file. + * Can be anything the resolves with an `import` statement. + */ + + /** + * Registers RichContentRenderers by putting them in an array. + * + * @typedef {Object} RichContentRendererCollection + * @method add - Register a RichContentRenderer. + * @param {RichContentRenderer} renderer + */ + + /** + * @callback rendererIntercept + * @param {RichContentRendererCollection} renderers + * @returns {undefined} - Interceptors of `richContentRenderers` can + * call `renderers.add()` and not return anything. + */ + + /** + * Collects RichContentRenderers contributed by third party extensions. + * Builds an array of these renderers which Venia's RichContent + * component uses to try and render a block of "rich" content, most + * likely HTML. + * + * @type {tapable.SyncHook} + * @param {rendererIntercept} callback + * + * @example Add a renderer from this package. + * targets.of('@magento/venia-ui').richContentRenderers.tap( + * renderers => renderers.add({ + * componentName: 'AdobeXM', + * importPath: '@adobe/xm-components/xm-renderer' + * }) + * ) + */ + richContentRenderers: new targets.types.Sync(['renderers']), + + /** + * A description of a route in the Venia app structure. + * + * @typedef {Object} VeniaRoute + * @property {string} name - Friendly name for the React component. + * @property {string} pattern - Route pattern. Will be used as the + * `` component's `path` prop. + * @property {boolean} [exact] - Exactly match the route? + * @property {string} path - Resolvable path to the component which the + * Route will render. + */ + + /** + * @callback routesIntercept + * @param {VeniaRoute[]} routes - Array of registered routes. + * @returns {VeniaRoute[]} - You must return the array, or a new + * array you have constructed. + */ + + /** + * Registers custom client-side routes for third-party extensions. + * Venia uses the Peregrine MagentoRoute for most of the site; + * catalog and CMS page URLs are controlled by admins, and dispatched + * via the UrlResolver query. + * + * Venia also has some "static" routes for built-in pages, like cart + * and checkout. With the `routes` target, you can inject additional + * routes into Venia's main router. + * + * @example Add a custom route for a blog module. + * targets.of('@magento/venia-ui').routes.tap(routes => { + * routes.push({ + * name: 'Blog', + * pattern: '/blog/:slug/:id', + * path: '@partner/pwa-studio-blog' + * }); + * return routes; + * }) + */ + routes: new targets.types.SyncWaterfall(['routes']) }); }; diff --git a/packages/venia-ui/lib/targets/venia-ui-intercept.js b/packages/venia-ui/lib/targets/venia-ui-intercept.js index 5289b6c376..2814161cb1 100644 --- a/packages/venia-ui/lib/targets/venia-ui-intercept.js +++ b/packages/venia-ui/lib/targets/venia-ui-intercept.js @@ -1,3 +1,6 @@ +/** + * @module VeniaUI/Targets + */ const path = require('path'); const loader = require.resolve('./rendererCollectionLoader'); @@ -15,9 +18,11 @@ const isRCR = mod => '../components/RichContent/richContentRenderers.js' ); -// Webpack can process loaders best when each one has a unique identity. -// We use thie filename, since there should only be one of these loaders -// per compilation. +/** + * Webpack can process loaders best when each one has a unique identity. We + * use the filename, since there should only be one of these loaders per + * compilation. + */ const ident = __filename; const name = '@magento/venia-ui'; @@ -26,15 +31,19 @@ const name = '@magento/venia-ui'; const loaderIsInstalled = mod => mod.loaders.some(l => l.ident === ident); module.exports = targets => { - const { richContentRenderers } = targets.own; - richContentRenderers.tap(api => - api.add({ - componentName: 'PlainHtmlRenderer', - importPath: './plainHtmlRenderer' - }) - ); + const builtins = targets.of('@magento/pwa-buildpack'); - targets.of('@magento/pwa-buildpack').webpackCompiler.tap(compiler => + builtins.specialFeatures.tap(featuresByModule => { + featuresByModule['@magento/venia-ui'] = { + cssModules: true, + esModules: true, + graphqlQueries: true, + rootComponents: true, + upward: true + }; + }); + + builtins.webpackCompiler.tap(compiler => compiler.hooks.compilation.tap(name, compilation => compilation.hooks.normalModuleLoader.tap( name, @@ -46,7 +55,7 @@ module.exports = targets => { renderers.push(renderer); } }; - richContentRenderers.call(api); + targets.own.richContentRenderers.call(api); mod.loaders.push({ ident: ident, loader, @@ -59,4 +68,60 @@ module.exports = targets => { ) ) ); + + // Dogfood our own richContentRenderer hook to insert the fallback renderer. + targets.own.richContentRenderers.tap(api => + api.add({ + componentName: 'PlainHtmlRenderer', + importPath: './plainHtmlRenderer' + }) + ); + + /** + * Implementation of our `routes` target. When Buildpack runs + * `transformModules`, this interceptor will provide a nice API to + * consumers of `routes`: instead of specifying the transform file + * and the path to the routes component, you can just push route + * requests into a neat little array. + */ + builtins.transformModules.tap(addTransform => { + addTransform({ + type: 'babel', + fileToTransform: + '@magento/venia-ui/lib/components/Routes/routes.js', + transformModule: + '@magento/venia-ui/lib/targets/BabelRouteInjectionPlugin', + options: { + routes: targets.own.routes.call([]) + } + }); + }); + + targets.own.routes.tap(routes => [ + ...routes, + { + name: 'Cart', + pattern: '/cart', + exact: true, + path: '../CartPage' + }, + { + name: 'Search', + pattern: '/search.html', + exact: true, + path: '../../RootComponents/Search' + }, + { + name: 'CreateAccountPage', + pattern: '/create-account', + exact: true, + path: '../CreateAccountPage' + }, + { + name: 'CheckoutPage', + pattern: '/checkout', + exact: true, + path: '../CheckoutPage' + } + ]); }; diff --git a/packages/venia-ui/package.json b/packages/venia-ui/package.json index 2f405742df..46710ea78c 100644 --- a/packages/venia-ui/package.json +++ b/packages/venia-ui/package.json @@ -34,6 +34,7 @@ "homepage": "https://github.com/magento/pwa-studio/tree/master/packages/venia-ui#readme", "dependencies": { "@apollo/react-hooks": "~3.1.2", + "@babel/template": "~7.8.6", "informed": "~2.11.17", "jarallax": "~1.11.1", "load-google-maps-api": "~2.0.1", diff --git a/yarn.lock b/yarn.lock index b8531f1591..c294eeb55e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -350,6 +350,11 @@ dependencies: "@babel/types" "^7.8.3" +"@babel/helper-validator-identifier@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80" + integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g== + "@babel/helper-wrap-function@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.8.3.tgz#9dbdb2bb55ef14aaa01fe8c99b629bd5352d8610" @@ -383,6 +388,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.3.tgz#790874091d2001c9be6ec426c2eed47bc7679081" integrity sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ== +"@babel/parser@^7.8.6": + version "7.9.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8" + integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA== + "@babel/plugin-proposal-async-generator-functions@^7.2.0", "@babel/plugin-proposal-async-generator-functions@^7.7.4", "@babel/plugin-proposal-async-generator-functions@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f" @@ -1253,6 +1263,15 @@ "@babel/parser" "^7.8.3" "@babel/types" "^7.8.3" +"@babel/template@~7.8.6": + version "7.8.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" + integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/parser" "^7.8.6" + "@babel/types" "^7.8.6" + "@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.1.6", "@babel/traverse@^7.3.4", "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.4", "@babel/traverse@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.3.tgz#a826215b011c9b4f73f3a893afbc05151358bf9a" @@ -1286,6 +1305,15 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@babel/types@^7.8.6": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.5.tgz#89231f82915a8a566a703b3b20133f73da6b9444" + integrity sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg== + dependencies: + "@babel/helper-validator-identifier" "^7.9.5" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + "@braintree/asset-loader@0.2.1": version "0.2.1" resolved "https://registry.yarnpkg.com/@braintree/asset-loader/-/asset-loader-0.2.1.tgz#1f744aada54321ff591e846d707a7b7d58d7ca9f" From 9486131ce87d61a448f5c3c228a3af77da63080e Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Thu, 23 Apr 2020 12:38:42 -0500 Subject: [PATCH 05/21] chore(ci): upgrade jest to v25 and adjust tests for it --- package.json | 4 +- .../Block/__tests__/block.spec.js | 4 +- .../ButtonItem/__tests__/buttonItem.spec.js | 16 +- packages/pwa-buildpack/__mocks__/devcert.js | 7 + packages/pwa-buildpack/__mocks__/word-wrap.js | 8 + .../createDotEnvFile.spec.js.snap | 85 +- .../loadEnvironment.spec.js.snap | 30 +- .../Utilities/__tests__/configureHost.spec.js | 1 - .../__tests__/createDotEnvFile.spec.js | 2 +- .../__tests__/loadEnvironment.spec.js | 10 +- .../lib/Utilities/loadEnvironment.js | 35 +- .../cli-create-custom-origin.spec.js | 1 - packages/pwa-buildpack/package.json | 1 + .../lib/components/App/__tests__/app.spec.js | 21 +- yarn.lock | 1921 ++++++++++++----- 15 files changed, 1447 insertions(+), 699 deletions(-) create mode 100644 packages/pwa-buildpack/__mocks__/devcert.js create mode 100644 packages/pwa-buildpack/__mocks__/word-wrap.js diff --git a/package.json b/package.json index 1ab120a67f..21d97852bb 100755 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "storybook:venia": "yarn workspace @magento/venia-ui run storybook", "test": "jest", "test:ci": "jest --no-cache --max-workers=3 --json --outputFile=test-results.json", - "test:debug": "node --inspect-brk node_modules/.bin/jest --no-cache --no-coverage --runInBand", + "test:debug": "node --inspect-brk node_modules/.bin/jest --no-cache --no-coverage --runInBand --testTimeout 86400", "test:dev": "jest --watch", "validate-queries": "yarn venia run validate-queries", "venia": "yarn workspace @magento/venia-concept", @@ -67,7 +67,7 @@ "first-run": "~2.0.0", "graphql-tag": "~2.10.1", "identity-obj-proxy": "~3.0.0", - "jest": "~24.3.1", + "jest": "~25.3.0", "jest-fetch-mock": "~2.1.1", "jest-junit": "~6.3.0", "jest-transform-graphql": "~2.1.0", diff --git a/packages/pagebuilder/lib/ContentTypes/Block/__tests__/block.spec.js b/packages/pagebuilder/lib/ContentTypes/Block/__tests__/block.spec.js index ec58568094..80dd282837 100644 --- a/packages/pagebuilder/lib/ContentTypes/Block/__tests__/block.spec.js +++ b/packages/pagebuilder/lib/ContentTypes/Block/__tests__/block.spec.js @@ -58,9 +58,7 @@ test('renders a Block component with all props configured and Page Builder rich }; const component = createTestInstance(); - expect( - component.root.find(child => child.type.name === 'Row') - ).toBeTruthy(); + expect(component.root.find(child => child.type === MockRow)).toBeTruthy(); }); test('renders a Block component with HTML content', () => { diff --git a/packages/pagebuilder/lib/ContentTypes/ButtonItem/__tests__/buttonItem.spec.js b/packages/pagebuilder/lib/ContentTypes/ButtonItem/__tests__/buttonItem.spec.js index 77b9930ad6..7fd2d26141 100644 --- a/packages/pagebuilder/lib/ContentTypes/ButtonItem/__tests__/buttonItem.spec.js +++ b/packages/pagebuilder/lib/ContentTypes/ButtonItem/__tests__/buttonItem.spec.js @@ -14,6 +14,21 @@ jest.mock('@magento/venia-drivers', () => ({ })); useHistory.mockImplementation(() => history); +const mockWindowLocation = { + assign: jest.fn() +}; + +let oldWindowLocation; +beforeEach(() => { + oldWindowLocation = window.location; + delete window.location; + window.location = mockWindowLocation; + mockWindowLocation.assign.mockClear(); +}); +afterEach(() => { + window.location = oldWindowLocation; +}); + test('renders a ButtonItem component', () => { const component = createTestInstance(); @@ -73,7 +88,6 @@ test('clicking button with external link goes to correct destination', () => { }; const component = createTestInstance(); const button = component.root.findByType(Button); - window.location.assign = jest.fn(); button.props.onClick(); expect(window.location.assign).toBeCalledWith('https://www.adobe.com'); }); diff --git a/packages/pwa-buildpack/__mocks__/devcert.js b/packages/pwa-buildpack/__mocks__/devcert.js new file mode 100644 index 0000000000..832f90affa --- /dev/null +++ b/packages/pwa-buildpack/__mocks__/devcert.js @@ -0,0 +1,7 @@ +module.exports = { + configuredDomains: jest.fn().mockReturnValue([]), + certificateFor: jest.fn().mockReturnValue({ + key: 'fakekey', + cert: 'fakecert' + }) +}; diff --git a/packages/pwa-buildpack/__mocks__/word-wrap.js b/packages/pwa-buildpack/__mocks__/word-wrap.js new file mode 100644 index 0000000000..654efce6c3 --- /dev/null +++ b/packages/pwa-buildpack/__mocks__/word-wrap.js @@ -0,0 +1,8 @@ +// don't actually word wrap, it messes with tests since the autodetect is +// system-dependent +const wordWrap = jest.requireActual('word-wrap'); +module.exports = (str, opts) => + wordWrap(str, { + ...opts, + width: 1000 + }); diff --git a/packages/pwa-buildpack/lib/Utilities/__tests__/__snapshots__/createDotEnvFile.spec.js.snap b/packages/pwa-buildpack/lib/Utilities/__tests__/__snapshots__/createDotEnvFile.spec.js.snap index 8928055450..1d60828db6 100644 --- a/packages/pwa-buildpack/lib/Utilities/__tests__/__snapshots__/createDotEnvFile.spec.js.snap +++ b/packages/pwa-buildpack/lib/Utilities/__tests__/__snapshots__/createDotEnvFile.spec.js.snap @@ -3,16 +3,11 @@ exports[`passing an env object works, but warns deprecation and assumes cwd is context 1`] = ` "######## PWA Studio Environment Variables ###################################### # -# This file contains environment variables for a Magento PWA Studio project. -# PWA Studio uses environment variables for all variable cross-project -# values, so that a developer or a build system can override any variable -# with standard tools. -# -# This file belongs at the root of the PWA, and must be named \`.env\`. -# Uncomment and modify variable declarations in this file and they will take -# effect throughout the Buildpack tool chain. +# This file contains environment variables for a Magento PWA Studio project. PWA Studio uses environment variables for all variable cross-project values, so that a developer or a build system can override any variable with standard tools. # +# This file belongs at the root of the PWA, and must be named \`.env\`. Uncomment and modify variable declarations in this file and they will take effect throughout the Buildpack tool chain. # +# # ################################################################################ #### Nothing special ########################################################### @@ -31,16 +26,11 @@ TEST_ENV_VAR_SOMETHING=true exports[`returns valid dotenv file if env is valid 1`] = ` "######## PWA Studio Environment Variables ###################################### # -# This file contains environment variables for a Magento PWA Studio project. -# PWA Studio uses environment variables for all variable cross-project -# values, so that a developer or a build system can override any variable -# with standard tools. -# -# This file belongs at the root of the PWA, and must be named \`.env\`. -# Uncomment and modify variable declarations in this file and they will take -# effect throughout the Buildpack tool chain. +# This file contains environment variables for a Magento PWA Studio project. PWA Studio uses environment variables for all variable cross-project values, so that a developer or a build system can override any variable with standard tools. # +# This file belongs at the root of the PWA, and must be named \`.env\`. Uncomment and modify variable declarations in this file and they will take effect throughout the Buildpack tool chain. # +# # ################################################################################ #### Connecting to a Magento store ############################################# @@ -52,8 +42,7 @@ MAGENTO_BACKEND_URL=https://local.magento/ #### Magento Store Edition ##################################################### # -# Specify the edition of the magento store (Enterprise Edition or Community -# Edition). Can be one of CE or EE. +# Specify the edition of the magento store (Enterprise Edition or Community Edition). Can be one of CE or EE. # - Example: CE # - Default when not set: CE #MAGENTO_BACKEND_EDITION=CE @@ -62,12 +51,7 @@ MAGENTO_BACKEND_URL=https://local.magento/ #### Image Optimizing Origin ################################################### # -# Specify the origin to use for images in the UI. Set to \`backend\` when -# Fastly or another CDN is optimizing images, and the frontend will load -# images directly from the Magento instance at MAGENTO_BACKEND_URL. To force -# usage of the onboard image optimizer, set to \`onboard\`. Set to \`auto\` to -# autodetect whether the backend is using FastlyIO and optimize onboard only -# if it needs to. +# Specify the origin to use for images in the UI. Set to \`backend\` when Fastly or another CDN is optimizing images, and the frontend will load images directly from the Magento instance at MAGENTO_BACKEND_URL. To force usage of the onboard image optimizer, set to \`onboard\`. Set to \`auto\` to autodetect whether the backend is using FastlyIO and optimize onboard only if it needs to. # - Default when not set: auto #IMAGE_OPTIMIZING_ORIGIN=auto # @@ -75,19 +59,15 @@ MAGENTO_BACKEND_URL=https://local.magento/ #### Custom local origin ####################################################### # -# Get or create a secure, unique hostname/port combination and a trusted SSL -# certificate for local development. +# Get or create a secure, unique hostname/port combination and a trusted SSL certificate for local development. # - Default when not set: true #CUSTOM_ORIGIN_ENABLED=true # -# Add a unique hash string to the custom origin, based on filesystem -# location. This naturally separates domains when running multiple project -# folders on one developer machine. +# Add a unique hash string to the custom origin, based on filesystem location. This naturally separates domains when running multiple project folders on one developer machine. # - Default when not set: true #CUSTOM_ORIGIN_ADD_UNIQUE_HASH=true # -# Specify the subdomain prefix of the custom origin manually, instead of -# using the package name. +# Specify the subdomain prefix of the custom origin manually, instead of using the package name. # - Default when not set: #CUSTOM_ORIGIN_SUBDOMAIN= # @@ -99,26 +79,19 @@ MAGENTO_BACKEND_URL=https://local.magento/ #### Development server ######################################################## # -# Specify the hostname the dev server should bind to. If this is set, it -# overrides the host chosen by custom origin settings. +# Specify the hostname the dev server should bind to. If this is set, it overrides the host chosen by custom origin settings. # - Default when not set: #DEV_SERVER_HOST= # -# Specify the port the dev server should bind to. If this is set, it -# overrides the port chosen by custom origin settings. +# Specify the port the dev server should bind to. If this is set, it overrides the port chosen by custom origin settings. # - Default when not set: 0 #DEV_SERVER_PORT= # -# Use a service worker in developer mode (PWADevServer), which are disabled -# in dev mode normally to simplify cache. Good for debugging. +# Use a service worker in developer mode (PWADevServer), which are disabled in dev mode normally to simplify cache. Good for debugging. # - Default when not set: false #DEV_SERVER_SERVICE_WORKER_ENABLED= # -# Set to a number greater than 0 to denote a polling interval in -# milliseconds. If this is greater than 0, the dev server will use filesystem -# polling instead of native filesystem events to watch for changes. Can -# increase CPU usage, but sometimes is the best option for exotic filesystems -# (e.g. NFS). +# Set to a number greater than 0 to denote a polling interval in milliseconds. If this is greater than 0, the dev server will use filesystem polling instead of native filesystem events to watch for changes. Can increase CPU usage, but sometimes is the best option for exotic filesystems (e.g. NFS). # - Default when not set: 0 #DEV_SERVER_WATCH_OPTIONS_USE_POLLING= # @@ -126,18 +99,15 @@ MAGENTO_BACKEND_URL=https://local.magento/ #### Staging server ############################################################ # -# Specify the hostname the staging server should bind to. If this is set, it -# overrides the host chosen by custom origin settings. +# Specify the hostname the staging server should bind to. If this is set, it overrides the host chosen by custom origin settings. # - Default when not set: #STAGING_SERVER_HOST= # -# Specify the port the staging server should bind to. If this is set, it -# overrides the port chosen by custom origin settings. +# Specify the port the staging server should bind to. If this is set, it overrides the port chosen by custom origin settings. # - Default when not set: 0 #STAGING_SERVER_PORT= # -# Specify the id which Buildpack will put in a comment above all generated -# bundle files and the index.html file +# Specify the id which Buildpack will put in a comment above all generated bundle files and the index.html file # - Default when not set: #STAGING_BUILD_ID= # @@ -145,8 +115,7 @@ MAGENTO_BACKEND_URL=https://local.magento/ #### Onboard image optimization service ######################################## # -# Lifetime of images in the local cache of resized images. Format is -# \\"[length] [unit]\\", as in \\"10 minutes\\" or \\"1 day\\". +# Lifetime of images in the local cache of resized images. Format is \\"[length] [unit]\\", as in \\"10 minutes\\" or \\"1 day\\". # - Example: 5 minutes # - Default when not set: 1 hour #IMAGE_SERVICE_CACHE_EXPIRES=1 hour @@ -159,18 +128,15 @@ MAGENTO_BACKEND_URL=https://local.magento/ #### UPWARD server settings #################################################### # -# UPWARD configuration file to use for the PWADevServer and the \\"stage:venia\\" -# sample server. +# UPWARD configuration file to use for the PWADevServer and the \\"stage:venia\\" sample server. # - Default when not set: upward.yml #UPWARD_JS_UPWARD_PATH=upward.yml # -# Upon launching the staging server, automatically attach to a local port and -# listen. +# Upon launching the staging server, automatically attach to a local port and listen. # - Default when not set: true #UPWARD_JS_BIND_LOCAL=true # -# Log the bound URL to stdout once the sever is listening. Useful in testing -# and discovery scenarios, but may be disabled in production. +# Log the bound URL to stdout once the sever is listening. Useful in testing and discovery scenarios, but may be disabled in production. # - Default when not set: true #UPWARD_JS_LOG_URL=true # @@ -178,9 +144,7 @@ MAGENTO_BACKEND_URL=https://local.magento/ #### Checkout and payment settings ############################################# # -# Specify a Braintree API token to direct the Venia storefront to communicate -# with your Braintree instance. You can find this value in Braintree's -# Control Panel under Settings > API Keys > Tokenization Keys. +# Specify a Braintree API token to direct the Venia storefront to communicate with your Braintree instance. You can find this value in Braintree's Control Panel under Settings > API Keys > Tokenization Keys. # - Example: sandbox_8yrzsvtm_s2bg8fs563crhqzk #CHECKOUT_BRAINTREE_TOKEN= # @@ -188,8 +152,7 @@ MAGENTO_BACKEND_URL=https://local.magento/ #### BuildBus and targets ###################################################### # -# A list of resolvable NPM modules that BuildBus will scan for targets, in -# addition to those declared in project \`dependencies\` and \`devDependencies\`. +# A list of resolvable NPM modules that BuildBus will scan for targets, in addition to those declared in project \`dependencies\` and \`devDependencies\`. # - Default when not set: #BUILDBUS_DEPS_ADDITIONAL= # diff --git a/packages/pwa-buildpack/lib/Utilities/__tests__/__snapshots__/loadEnvironment.spec.js.snap b/packages/pwa-buildpack/lib/Utilities/__tests__/__snapshots__/loadEnvironment.spec.js.snap index 905ab56693..937c1358ec 100644 --- a/packages/pwa-buildpack/lib/Utilities/__tests__/__snapshots__/loadEnvironment.spec.js.snap +++ b/packages/pwa-buildpack/lib/Utilities/__tests__/__snapshots__/loadEnvironment.spec.js.snap @@ -3,31 +3,31 @@ exports[`logs all types of change 1`] = ` Array [ Array [ - "⚠ Environment variable HAKEEM_OLAJUWON has been renamed in PWA Studio Buildpack v123.45-test.1. Its new name is AKEEM_OLAJUWON.", + " ⚠ Environment variable HAKEEM_OLAJUWON has been renamed in Buildpack vX.X.X-snapshot-testing. Its new name is AKEEM_OLAJUWON.", ], Array [ - "⚠ Environment variable HAS_BEEN_REMOVED has been removed in PWA Studio Buildpack v123.45-test.1, because creative destruction. -Current value is motivation, but it will be ignored.", + " ⚠ Environment variable HAS_BEEN_REMOVED has been removed in Buildpack vX.X.X-snapshot-testing, because creative destruction. + Current value is motivation, but it will be ignored.", ], Array [ - "⚠ Default value for HAS_DEFAULT_CHANGE has changed in PWA Studio Buildpack v123.45-test.1, due to whimsy. -Old value: old default -New value: new default -This project is using the old default value for HAS_DEFAULT_CHANGE. Check to make sure the change does not cause regressions.", + " ⚠ Default value for HAS_DEFAULT_CHANGE has changed in Buildpack vX.X.X-snapshot-testing, due to whimsy. + Old value: old default + New value: new default + This project is using the old default value for HAS_DEFAULT_CHANGE. Check to make sure the change does not cause regressions.", ], Array [ - "⚠ Example value for HAS_EXAMPLE_CHANGE has changed in PWA Studio Buildpack v123.45-test.1, due to capriciousness. -Old value: old example -New value: new example -This project is using the old example value; check to make sure this is intentional.", + " ⚠ Example value for HAS_EXAMPLE_CHANGE has changed in Buildpack vX.X.X-snapshot-testing, due to capriciousness. + Old value: old example + New value: new example + This project is using the old example value; check to make sure this is intentional.", ], Array [ - "⚠ Environment variable LEW_ALCINDOR has been renamed in PWA Studio Buildpack v123.45-test.1. Its new name is KAREEM_ABDUL_JABBAR. -The old variable is no longer functional. Please migrate to the new \${change.update} variable as soon as possible.", + " ⚠ Environment variable LEW_ALCINDOR has been renamed in Buildpack vX.X.X-snapshot-testing. Its new name is KAREEM_ABDUL_JABBAR. + The old variable is no longer functional. Please migrate to the new \${change.update} variable as soon as possible.", ], Array [ - "⚠ Environment variable RON_ARTEST has been renamed in PWA Studio Buildpack v123.45-test.1. Its new name is METTA_WORLD_PEACE. -The old variable will continue to work for the next several versions, but it will eventually be removed. Please migrate it as soon as possible.", + " ⚠ Environment variable RON_ARTEST has been renamed in Buildpack vX.X.X-snapshot-testing. Its new name is METTA_WORLD_PEACE. + The old variable will continue to work for the next several versions, but it will eventually be removed. Please migrate it as soon as possible.", ], ] `; diff --git a/packages/pwa-buildpack/lib/Utilities/__tests__/configureHost.spec.js b/packages/pwa-buildpack/lib/Utilities/__tests__/configureHost.spec.js index 3c173c1dc5..d3abbb61ad 100644 --- a/packages/pwa-buildpack/lib/Utilities/__tests__/configureHost.spec.js +++ b/packages/pwa-buildpack/lib/Utilities/__tests__/configureHost.spec.js @@ -1,4 +1,3 @@ -jest.mock('devcert'); jest.mock('pkg-dir'); const FAKE_CWD = '/path/to/fake/cwd'; diff --git a/packages/pwa-buildpack/lib/Utilities/__tests__/createDotEnvFile.spec.js b/packages/pwa-buildpack/lib/Utilities/__tests__/createDotEnvFile.spec.js index b775e72ed7..ba69991dbc 100644 --- a/packages/pwa-buildpack/lib/Utilities/__tests__/createDotEnvFile.spec.js +++ b/packages/pwa-buildpack/lib/Utilities/__tests__/createDotEnvFile.spec.js @@ -9,7 +9,7 @@ const createDotEnvFile = require('../createDotEnvFile'); const prettyLogger = require('../../util/pretty-logger'); const snapshotEnvFile = contents => - contents.replace(/.+?Generated by @magento[\S\s]+?Z\.\n/gim, ''); + contents.replace(/Generated by @magento.+?on.+?\n/gim, ''); jest.spyOn(dotenv, 'config'); const envVarDefs = require('../../../envVarDefinitions.json'); diff --git a/packages/pwa-buildpack/lib/Utilities/__tests__/loadEnvironment.spec.js b/packages/pwa-buildpack/lib/Utilities/__tests__/loadEnvironment.spec.js index 147c3868e6..a48565e4f7 100644 --- a/packages/pwa-buildpack/lib/Utilities/__tests__/loadEnvironment.spec.js +++ b/packages/pwa-buildpack/lib/Utilities/__tests__/loadEnvironment.spec.js @@ -1,5 +1,4 @@ jest.doMock('dotenv'); -jest.doMock('word-wrap', () => x => x); const debug = jest.fn().mockName('debug'); jest.doMock('debug', () => () => debug); @@ -51,6 +50,15 @@ afterEach(jest.clearAllMocks); const stripAnsi = require('strip-ansi'); const loadEnvironment = require('../loadEnvironment'); +let oldReleaseName; +beforeAll(() => { + oldReleaseName = loadEnvironment.RELEASE_NAME; + loadEnvironment.RELEASE_NAME = 'Buildpack vX.X.X-snapshot-testing'; +}); +afterAll(() => { + loadEnvironment.RELEASE_NAME = oldReleaseName; +}); + test('throws on load if variable defs are invalid', () => { getEnvVarDefinitions.mockReturnValueOnce({ sections: [ diff --git a/packages/pwa-buildpack/lib/Utilities/loadEnvironment.js b/packages/pwa-buildpack/lib/Utilities/loadEnvironment.js index 59ba541e7e..8de5083e12 100644 --- a/packages/pwa-buildpack/lib/Utilities/loadEnvironment.js +++ b/packages/pwa-buildpack/lib/Utilities/loadEnvironment.js @@ -7,9 +7,6 @@ const camelspace = require('camelspace'); const prettyLogger = require('../util/pretty-logger'); const getEnvVarDefinitions = require('./getEnvVarDefinitions'); -const buildpackVersion = require('../../package.json').version; -const buildpackReleaseName = `PWA Studio Buildpack v${buildpackVersion}`; - /** * Replaces the envalid default reporter, which crashes the process, with an * error-returning reporter that a consumer can handle. @@ -244,11 +241,9 @@ function applyBackwardsCompatChanges(definitions, env, varsByName, log) { if (env[change.name] === change.original) { const updatedValue = varsByName[change.name].default; log.warn( - `Default value for ${ - change.name - } has changed in ${buildpackReleaseName}, due to ${ - change.reason - }.\nOld value: ${ + `Default value for ${change.name} has changed in ${ + loadEnvironment.RELEASE_NAME + }, due to ${change.reason}.\nOld value: ${ change.original }\nNew value: ${updatedValue}\nThis project is using the old default value for ${ change.name @@ -261,11 +256,9 @@ function applyBackwardsCompatChanges(definitions, env, varsByName, log) { if (env[change.name] === change.original) { const updatedValue = varsByName[change.name].example; log.warn( - `Example value for ${ - change.name - } has changed in ${buildpackReleaseName}, due to ${ - change.reason - }.\nOld value: ${ + `Example value for ${change.name} has changed in ${ + loadEnvironment.RELEASE_NAME + }, due to ${change.reason}.\nOld value: ${ change.original }\nNew value: ${updatedValue}\nThis project is using the old example value; check to make sure this is intentional.` ); @@ -276,9 +269,9 @@ function applyBackwardsCompatChanges(definitions, env, varsByName, log) { log.warn( `Environment variable ${ change.name - } has been removed in ${buildpackReleaseName}, because ${ - change.reason - }.\nCurrent value is ${ + } has been removed in ${ + loadEnvironment.RELEASE_NAME + }, because ${change.reason}.\nCurrent value is ${ env[change.name] }, but it will be ignored.` ); @@ -288,9 +281,9 @@ function applyBackwardsCompatChanges(definitions, env, varsByName, log) { if (isSet) { let logMsg = `Environment variable ${ change.name - } has been renamed in ${buildpackReleaseName}. Its new name is ${ - change.update - }.`; + } has been renamed in ${ + loadEnvironment.RELEASE_NAME + }. Its new name is ${change.update}.`; if (change.supportLegacy) { if (!env.hasOwnProperty(change.update)) { logMsg += @@ -319,4 +312,8 @@ function applyBackwardsCompatChanges(definitions, env, varsByName, log) { }; } +const buildpackVersion = require('../../package.json').version; +// Expose release name for testing, so snapshots don't change every version. +loadEnvironment.RELEASE_NAME = `PWA Studio Buildpack v${buildpackVersion}`; + module.exports = loadEnvironment; diff --git a/packages/pwa-buildpack/lib/__tests__/cli-create-custom-origin.spec.js b/packages/pwa-buildpack/lib/__tests__/cli-create-custom-origin.spec.js index 3c004e4c0a..2319d51cb8 100644 --- a/packages/pwa-buildpack/lib/__tests__/cli-create-custom-origin.spec.js +++ b/packages/pwa-buildpack/lib/__tests__/cli-create-custom-origin.spec.js @@ -1,6 +1,5 @@ jest.mock('../Utilities/configureHost'); jest.mock('../Utilities/loadEnvironment'); -jest.mock('word-wrap', () => x => x); const createCustomOriginBuilder = require('../cli/create-custom-origin'); const configureHost = require('../Utilities/configureHost'); const loadEnvironment = require('../Utilities/loadEnvironment'); diff --git a/packages/pwa-buildpack/package.json b/packages/pwa-buildpack/package.json index 06057adcc3..1652a7a0b4 100644 --- a/packages/pwa-buildpack/package.json +++ b/packages/pwa-buildpack/package.json @@ -46,6 +46,7 @@ "graphql-playground-middleware-express": "~1.7.12", "hastily": "~0.4.2", "js-yaml": "~3.13.1", + "jsdom": "~16.2.2", "klaw": "~3.0.0", "lodash": "~4.17.11", "micromatch": "~4.0.2", diff --git a/packages/venia-ui/lib/components/App/__tests__/app.spec.js b/packages/venia-ui/lib/components/App/__tests__/app.spec.js index 0160d9411d..61ea31570f 100644 --- a/packages/venia-ui/lib/components/App/__tests__/app.spec.js +++ b/packages/venia-ui/lib/components/App/__tests__/app.spec.js @@ -18,10 +18,6 @@ jest.mock('../../Navigation', () => 'Navigation'); jest.mock('../../Routes', () => 'Routes'); jest.mock('../../ToastContainer', () => 'ToastContainer'); -Object.defineProperty(window.location, 'reload', { - configurable: true -}); - const mockAddToast = jest.fn(); jest.mock('@magento/peregrine', () => { const useToasts = jest.fn(() => [ @@ -98,8 +94,6 @@ jest.mock('@apollo/react-hooks', () => ({ ]) })); -window.location.reload = jest.fn(); - // require app after mock is complete const App = require('../app').default; @@ -113,7 +107,20 @@ beforeAll(() => { global.STORE_NAME = 'Venia'; }); -afterAll(() => window.location.reload.mockRestore()); +const mockWindowLocation = { + reload: jest.fn() +}; + +let oldWindowLocation; +beforeEach(() => { + oldWindowLocation = window.location; + delete window.location; + window.location = mockWindowLocation; + mockWindowLocation.reload.mockClear(); +}); +afterEach(() => { + window.location = oldWindowLocation; +}); test('renders a full page with onlineIndicator and routes', () => { const [appState, appApi] = useAppContext(); diff --git a/yarn.lock b/yarn.lock index c294eeb55e..92c265cfbc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -120,6 +120,28 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/core@^7.7.5": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e" + integrity sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.9.0" + "@babel/helper-module-transforms" "^7.9.0" + "@babel/helpers" "^7.9.0" + "@babel/parser" "^7.9.0" + "@babel/template" "^7.8.6" + "@babel/traverse" "^7.9.0" + "@babel/types" "^7.9.0" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.2" + lodash "^4.17.13" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + "@babel/core@~7.3.4": version "7.3.4" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.3.4.tgz#921a5a13746c21e32445bf0798680e9d11a6530b" @@ -161,6 +183,26 @@ lodash "^4.17.13" source-map "^0.5.0" +"@babel/generator@^7.8.6": + version "7.8.8" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.8.tgz#cdcd58caab730834cee9eeadb729e833b625da3e" + integrity sha512-HKyUVu69cZoclptr8t8U5b6sx6zoWjh8jiUhnuj3MpZuKT2dJ8zPTuiy31luq32swhI0SpwItCIlU8XW7BZeJg== + dependencies: + "@babel/types" "^7.8.7" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" + +"@babel/generator@^7.9.0", "@babel/generator@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.5.tgz#27f0917741acc41e6eaaced6d68f96c3fa9afaf9" + integrity sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ== + dependencies: + "@babel/types" "^7.9.5" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" + "@babel/helper-annotate-as-pure@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee" @@ -250,6 +292,15 @@ "@babel/template" "^7.8.3" "@babel/types" "^7.8.3" +"@babel/helper-function-name@^7.9.5": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c" + integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw== + dependencies: + "@babel/helper-get-function-arity" "^7.8.3" + "@babel/template" "^7.8.3" + "@babel/types" "^7.9.5" + "@babel/helper-get-function-arity@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz#b894b947bd004381ce63ea1db9f08547e920abd5" @@ -290,6 +341,19 @@ "@babel/types" "^7.8.3" lodash "^4.17.13" +"@babel/helper-module-transforms@^7.9.0": + version "7.9.0" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5" + integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA== + dependencies: + "@babel/helper-module-imports" "^7.8.3" + "@babel/helper-replace-supers" "^7.8.6" + "@babel/helper-simple-access" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/template" "^7.8.6" + "@babel/types" "^7.9.0" + lodash "^4.17.13" + "@babel/helper-optimise-call-expression@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9" @@ -335,6 +399,16 @@ "@babel/traverse" "^7.8.3" "@babel/types" "^7.8.3" +"@babel/helper-replace-supers@^7.8.6": + version "7.8.6" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz#5ada744fd5ad73203bf1d67459a27dcba67effc8" + integrity sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.8.3" + "@babel/helper-optimise-call-expression" "^7.8.3" + "@babel/traverse" "^7.8.6" + "@babel/types" "^7.8.6" + "@babel/helper-simple-access@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae" @@ -374,6 +448,15 @@ "@babel/traverse" "^7.8.3" "@babel/types" "^7.8.3" +"@babel/helpers@^7.9.0": + version "7.9.2" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.2.tgz#b42a81a811f1e7313b88cba8adc66b3d9ae6c09f" + integrity sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA== + dependencies: + "@babel/template" "^7.8.3" + "@babel/traverse" "^7.9.0" + "@babel/types" "^7.9.0" + "@babel/highlight@^7.0.0", "@babel/highlight@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797" @@ -388,11 +471,16 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.3.tgz#790874091d2001c9be6ec426c2eed47bc7679081" integrity sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ== -"@babel/parser@^7.8.6": +"@babel/parser@^7.7.5", "@babel/parser@^7.9.0": version "7.9.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8" integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA== +"@babel/parser@^7.8.6": + version "7.8.8" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.8.tgz#4c3b7ce36db37e0629be1f0d50a571d2f86f6cd4" + integrity sha512-mO5GWzBPsPf6865iIbzNE0AvkKF3NE+2S3eRUpE+FE07BOAkXh6G+GW/Pj01hhXjve1WScbaIO4UlY1JKeqCcA== + "@babel/plugin-proposal-async-generator-functions@^7.2.0", "@babel/plugin-proposal-async-generator-functions@^7.7.4", "@babel/plugin-proposal-async-generator-functions@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f" @@ -531,13 +619,27 @@ "@babel/helper-create-regexp-features-plugin" "^7.8.3" "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-async-generators@^7.2.0", "@babel/plugin-syntax-async-generators@^7.7.4", "@babel/plugin-syntax-async-generators@^7.8.0": +"@babel/plugin-syntax-async-generators@^7.2.0", "@babel/plugin-syntax-async-generators@^7.7.4", "@babel/plugin-syntax-async-generators@^7.8.0", "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== dependencies: "@babel/helper-plugin-utils" "^7.8.0" +"@babel/plugin-syntax-bigint@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" + integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.0" + +"@babel/plugin-syntax-class-properties@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.8.3.tgz#6cb933a8872c8d359bfde69bbeaae5162fd1e8f7" + integrity sha512-UcAyQWg2bAN647Q+O811tG9MrJ38Z10jjhQdKNAL8fsyPzE3cCN/uT+f55cFVY4aGO4jqJAvmqsuY3GQDwAoXg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + "@babel/plugin-syntax-decorators@^7.7.4": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.8.3.tgz#8d2c15a9f1af624b0025f961682a9d53d3001bda" @@ -573,7 +675,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-json-strings@^7.2.0", "@babel/plugin-syntax-json-strings@^7.7.4", "@babel/plugin-syntax-json-strings@^7.8.0": +"@babel/plugin-syntax-json-strings@^7.2.0", "@babel/plugin-syntax-json-strings@^7.7.4", "@babel/plugin-syntax-json-strings@^7.8.0", "@babel/plugin-syntax-json-strings@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== @@ -601,14 +703,21 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-nullish-coalescing-operator@^7.7.4", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0": +"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": + version "7.8.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.8.3.tgz#3995d7d7ffff432f6ddc742b47e730c054599897" + integrity sha512-Zpg2Sgc++37kuFl6ppq2Q7Awc6E6AIW671x5PY8E/f7MCIyPPGK/EoeZXvvY3P42exZ3Q4/t3YOzP/HiN79jDg== + dependencies: + "@babel/helper-plugin-utils" "^7.8.3" + +"@babel/plugin-syntax-nullish-coalescing-operator@^7.7.4", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-numeric-separator@^7.7.4": +"@babel/plugin-syntax-numeric-separator@^7.7.4", "@babel/plugin-syntax-numeric-separator@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz#0e3fb63e09bea1b11e96467271c8308007e7c41f" integrity sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw== @@ -622,21 +731,21 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.2.0", "@babel/plugin-syntax-object-rest-spread@^7.7.4", "@babel/plugin-syntax-object-rest-spread@^7.8.0": +"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.2.0", "@babel/plugin-syntax-object-rest-spread@^7.7.4", "@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-optional-catch-binding@^7.2.0", "@babel/plugin-syntax-optional-catch-binding@^7.7.4", "@babel/plugin-syntax-optional-catch-binding@^7.8.0": +"@babel/plugin-syntax-optional-catch-binding@^7.2.0", "@babel/plugin-syntax-optional-catch-binding@^7.7.4", "@babel/plugin-syntax-optional-catch-binding@^7.8.0", "@babel/plugin-syntax-optional-catch-binding@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== dependencies: "@babel/helper-plugin-utils" "^7.8.0" -"@babel/plugin-syntax-optional-chaining@^7.7.4", "@babel/plugin-syntax-optional-chaining@^7.8.0": +"@babel/plugin-syntax-optional-chaining@^7.7.4", "@babel/plugin-syntax-optional-chaining@^7.8.0", "@babel/plugin-syntax-optional-chaining@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== @@ -1263,7 +1372,7 @@ "@babel/parser" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/template@~7.8.6": +"@babel/template@^7.8.6", "@babel/template@~7.8.6": version "7.8.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== @@ -1287,6 +1396,36 @@ globals "^11.1.0" lodash "^4.17.13" +"@babel/traverse@^7.8.6": + version "7.8.6" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.6.tgz#acfe0c64e1cd991b3e32eae813a6eb564954b5ff" + integrity sha512-2B8l0db/DPi8iinITKuo7cbPznLCEk0kCxDoB9/N6gGNg/gxOXiR/IcymAFPiBwk5w6TtQ27w4wpElgp9btR9A== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.8.6" + "@babel/helper-function-name" "^7.8.3" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/parser" "^7.8.6" + "@babel/types" "^7.8.6" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.13" + +"@babel/traverse@^7.9.0": + version "7.9.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.5.tgz#6e7c56b44e2ac7011a948c21e283ddd9d9db97a2" + integrity sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.9.5" + "@babel/helper-function-name" "^7.9.5" + "@babel/helper-split-export-declaration" "^7.8.3" + "@babel/parser" "^7.9.0" + "@babel/types" "^7.9.5" + debug "^4.1.0" + globals "^11.1.0" + lodash "^4.17.13" + "@babel/types@7.0.0-beta.38": version "7.0.0-beta.38" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.38.tgz#2ce2443f7dc6ad535a67db4940cbd34e64035a6f" @@ -1305,7 +1444,16 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@babel/types@^7.8.6": +"@babel/types@^7.8.6", "@babel/types@^7.8.7": + version "7.8.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.7.tgz#1fc9729e1acbb2337d5b6977a63979b4819f5d1d" + integrity sha512-k2TreEHxFA4CjGkL+GYjRyx35W0Mr7DP5+9q6WMkyKXB+904bYmG40syjMFV0oLlhhFCwWl0vA0DyzTDkwAiJw== + dependencies: + esutils "^2.0.2" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + +"@babel/types@^7.9.0", "@babel/types@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.5.tgz#89231f82915a8a566a703b3b20133f73da6b9444" integrity sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg== @@ -1314,6 +1462,11 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@bcoe/v8-coverage@^0.2.3": + version "0.2.3" + resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" + integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== + "@braintree/asset-loader@0.2.1": version "0.2.1" resolved "https://registry.yarnpkg.com/@braintree/asset-loader/-/asset-loader-0.2.1.tgz#1f744aada54321ff591e846d707a7b7d58d7ca9f" @@ -1493,144 +1646,166 @@ dependencies: "@hapi/hoek" "^8.3.0" -"@jest/console@^24.7.1", "@jest/console@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.9.0.tgz#79b1bc06fb74a8cfb01cbdedf945584b1b9707f0" - integrity sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ== - dependencies: - "@jest/source-map" "^24.9.0" - chalk "^2.0.1" - slash "^2.0.0" - -"@jest/core@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.9.0.tgz#2ceccd0b93181f9c4850e74f2a9ad43d351369c4" - integrity sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A== +"@istanbuljs/load-nyc-config@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz#10602de5570baea82f8afbfa2630b24e7a8cfe5b" + integrity sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg== dependencies: - "@jest/console" "^24.7.1" - "@jest/reporters" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - ansi-escapes "^3.0.0" - chalk "^2.0.1" - exit "^0.1.2" - graceful-fs "^4.1.15" - jest-changed-files "^24.9.0" - jest-config "^24.9.0" - jest-haste-map "^24.9.0" - jest-message-util "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-resolve-dependencies "^24.9.0" - jest-runner "^24.9.0" - jest-runtime "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - jest-watcher "^24.9.0" - micromatch "^3.1.10" - p-each-series "^1.0.0" - realpath-native "^1.1.0" - rimraf "^2.5.4" - slash "^2.0.0" - strip-ansi "^5.0.0" + camelcase "^5.3.1" + find-up "^4.1.0" + js-yaml "^3.13.1" + resolve-from "^5.0.0" -"@jest/environment@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.9.0.tgz#21e3afa2d65c0586cbd6cbefe208bafade44ab18" - integrity sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ== - dependencies: - "@jest/fake-timers" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" +"@istanbuljs/schema@^0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" + integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== -"@jest/fake-timers@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.9.0.tgz#ba3e6bf0eecd09a636049896434d306636540c93" - integrity sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A== +"@jest/console@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-25.3.0.tgz#33b56b81238427bf3ebe3f7b3378d2f79cdbd409" + integrity sha512-LvSDNqpmZIZyweFaEQ6wKY7CbexPitlsLHGJtcooNECo0An/w49rFhjCJzu6efeb6+a3ee946xss1Jcd9r03UQ== dependencies: - "@jest/types" "^24.9.0" - jest-message-util "^24.9.0" - jest-mock "^24.9.0" + "@jest/source-map" "^25.2.6" + chalk "^3.0.0" + jest-util "^25.3.0" + slash "^3.0.0" + +"@jest/core@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-25.3.0.tgz#80f97a7a8b59dde741a24f30871cc26d0197d426" + integrity sha512-+D5a/tFf6pA/Gqft2DLBp/yeSRgXhlJ+Wpst0X/ZkfTRP54qDR3C61VfHwaex+GzZBiTcE9vQeoZ2v5T10+Mqw== + dependencies: + "@jest/console" "^25.3.0" + "@jest/reporters" "^25.3.0" + "@jest/test-result" "^25.3.0" + "@jest/transform" "^25.3.0" + "@jest/types" "^25.3.0" + ansi-escapes "^4.2.1" + chalk "^3.0.0" + exit "^0.1.2" + graceful-fs "^4.2.3" + jest-changed-files "^25.3.0" + jest-config "^25.3.0" + jest-haste-map "^25.3.0" + jest-message-util "^25.3.0" + jest-regex-util "^25.2.6" + jest-resolve "^25.3.0" + jest-resolve-dependencies "^25.3.0" + jest-runner "^25.3.0" + jest-runtime "^25.3.0" + jest-snapshot "^25.3.0" + jest-util "^25.3.0" + jest-validate "^25.3.0" + jest-watcher "^25.3.0" + micromatch "^4.0.2" + p-each-series "^2.1.0" + realpath-native "^2.0.0" + rimraf "^3.0.0" + slash "^3.0.0" + strip-ansi "^6.0.0" -"@jest/reporters@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.9.0.tgz#86660eff8e2b9661d042a8e98a028b8d631a5b43" - integrity sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw== - dependencies: - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" +"@jest/environment@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-25.3.0.tgz#587f28ddb4b0dfe97404d3d4a4c9dbfa0245fb2e" + integrity sha512-vgooqwJTHLLak4fE+TaCGeYP7Tz1Y3CKOsNxR1sE0V3nx3KRUHn3NUnt+wbcfd5yQWKZQKAfW6wqbuwQLrXo3g== + dependencies: + "@jest/fake-timers" "^25.3.0" + "@jest/types" "^25.3.0" + jest-mock "^25.3.0" + +"@jest/fake-timers@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-25.3.0.tgz#995aad36d5c8984165ca5db12e740ab8dbf7042a" + integrity sha512-NHAj7WbsyR3qBJPpBwSwqaq2WluIvUQsyzpJTN7XDVk7VnlC/y1BAnaYZL3vbPIP8Nhm0Ae5DJe0KExr/SdMJQ== + dependencies: + "@jest/types" "^25.3.0" + jest-message-util "^25.3.0" + jest-mock "^25.3.0" + jest-util "^25.3.0" + lolex "^5.0.0" + +"@jest/reporters@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-25.3.0.tgz#7f39f0e6911561cc5112a1b54656de18faee269b" + integrity sha512-1u0ZBygs0C9DhdYgLCrRfZfNKQa+9+J7Uo+Z9z0RWLHzgsxhoG32lrmMOtUw48yR6bLNELdvzormwUqSk4H4Vg== + dependencies: + "@bcoe/v8-coverage" "^0.2.3" + "@jest/console" "^25.3.0" + "@jest/test-result" "^25.3.0" + "@jest/transform" "^25.3.0" + "@jest/types" "^25.3.0" + chalk "^3.0.0" + collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.2" - istanbul-lib-coverage "^2.0.2" - istanbul-lib-instrument "^3.0.1" - istanbul-lib-report "^2.0.4" - istanbul-lib-source-maps "^3.0.1" - istanbul-reports "^2.2.6" - jest-haste-map "^24.9.0" - jest-resolve "^24.9.0" - jest-runtime "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.6.0" - node-notifier "^5.4.2" - slash "^2.0.0" + istanbul-lib-coverage "^3.0.0" + istanbul-lib-instrument "^4.0.0" + istanbul-lib-report "^3.0.0" + istanbul-lib-source-maps "^4.0.0" + istanbul-reports "^3.0.2" + jest-haste-map "^25.3.0" + jest-resolve "^25.3.0" + jest-util "^25.3.0" + jest-worker "^25.2.6" + slash "^3.0.0" source-map "^0.6.0" - string-length "^2.0.0" + string-length "^3.1.0" + terminal-link "^2.0.0" + v8-to-istanbul "^4.0.1" + optionalDependencies: + node-notifier "^6.0.0" -"@jest/source-map@^24.3.0", "@jest/source-map@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-24.9.0.tgz#0e263a94430be4b41da683ccc1e6bffe2a191714" - integrity sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg== +"@jest/source-map@^25.2.6": + version "25.2.6" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-25.2.6.tgz#0ef2209514c6d445ebccea1438c55647f22abb4c" + integrity sha512-VuIRZF8M2zxYFGTEhkNSvQkUKafQro4y+mwUxy5ewRqs5N/ynSFUODYp3fy1zCnbCMy1pz3k+u57uCqx8QRSQQ== dependencies: callsites "^3.0.0" - graceful-fs "^4.1.15" + graceful-fs "^4.2.3" source-map "^0.6.0" -"@jest/test-result@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" - integrity sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA== +"@jest/test-result@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-25.3.0.tgz#137fab5e5c6fed36e5d40735d1eb029325e3bf06" + integrity sha512-mqrGuiiPXl1ap09Mydg4O782F3ouDQfsKqtQzIjitpwv3t1cHDwCto21jThw6WRRE+dKcWQvLG70GpyLJICfGw== dependencies: - "@jest/console" "^24.9.0" - "@jest/types" "^24.9.0" + "@jest/console" "^25.3.0" + "@jest/types" "^25.3.0" "@types/istanbul-lib-coverage" "^2.0.0" + collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz#f8f334f35b625a4f2f355f2fe7e6036dad2e6b31" - integrity sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A== +"@jest/test-sequencer@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-25.3.0.tgz#271ad5f2b8f8137d092ccedc87e16a50f8676209" + integrity sha512-Xvns3xbji7JCvVcDGvqJ/pf4IpmohPODumoPEZJ0/VgC5gI4XaNVIBET2Dq5Czu6Gk3xFcmhtthh/MBOTljdNg== dependencies: - "@jest/test-result" "^24.9.0" - jest-haste-map "^24.9.0" - jest-runner "^24.9.0" - jest-runtime "^24.9.0" + "@jest/test-result" "^25.3.0" + jest-haste-map "^25.3.0" + jest-runner "^25.3.0" + jest-runtime "^25.3.0" -"@jest/transform@^24.9.0": - version "24.9.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.9.0.tgz#4ae2768b296553fadab09e9ec119543c90b16c56" - integrity sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ== +"@jest/transform@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-25.3.0.tgz#083c5447d5307d9b9494d6968115b647460e71f1" + integrity sha512-W01p8kTDvvEX6kd0tJc7Y5VdYyFaKwNWy1HQz6Jqlhu48z/8Gxp+yFCDVj+H8Rc7ezl3Mg0hDaGuFVkmHOqirg== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^24.9.0" - babel-plugin-istanbul "^5.1.0" - chalk "^2.0.1" + "@jest/types" "^25.3.0" + babel-plugin-istanbul "^6.0.0" + chalk "^3.0.0" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.1.15" - jest-haste-map "^24.9.0" - jest-regex-util "^24.9.0" - jest-util "^24.9.0" - micromatch "^3.1.10" + graceful-fs "^4.2.3" + jest-haste-map "^25.3.0" + jest-regex-util "^25.2.6" + jest-util "^25.3.0" + micromatch "^4.0.2" pirates "^4.0.1" - realpath-native "^1.1.0" - slash "^2.0.0" + realpath-native "^2.0.0" + slash "^3.0.0" source-map "^0.6.1" - write-file-atomic "2.4.1" + write-file-atomic "^3.0.0" "@jest/types@^24.9.0": version "24.9.0" @@ -1641,6 +1816,16 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^13.0.0" +"@jest/types@^25.3.0": + version "25.3.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.3.0.tgz#88f94b277a1d028fd7117bc1f74451e0fc2131e7" + integrity sha512-UkaDNewdqXAmCDbN2GlUM6amDKS78eCqiw/UmF5nE0mmLTd6moJkiZJML/X52Ke3LH7Swhw883IRXq8o9nWjVw== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^1.1.1" + "@types/yargs" "^15.0.0" + chalk "^3.0.0" + "@kbrandwijk/swagger-to-graphql@2.4.3": version "2.4.3" resolved "https://registry.yarnpkg.com/@kbrandwijk/swagger-to-graphql/-/swagger-to-graphql-2.4.3.tgz#7c0fb2410eb0b6b9cc81fad28cc20f9386153cf1" @@ -2476,6 +2661,13 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== +"@sinonjs/commons@^1.7.0": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.2.tgz#505f55c74e0272b43f6c52d81946bed7058fc0e2" + integrity sha512-+DUO6pnp3udV/v2VfUWgaY5BIE1IfT7lLfeDzPVeMT1XKkaAp9LgSI9x5RtrFQoZ9Oi0PgXQQHPaoKu7dCjVxw== + dependencies: + type-detect "4.0.8" + "@storybook/addons@5.2.8": version "5.2.8" resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-5.2.8.tgz#f8bf8bd555b7a69fb1e9a52ab8cdb96384d931ff" @@ -2896,10 +3088,10 @@ resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.1.tgz#336badc1beecb9dacc38bea2cf32adf627a8421a" integrity sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA== -"@types/babel__core@^7.1.0": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.3.tgz#e441ea7df63cd080dfcd02ab199e6d16a735fc30" - integrity sha512-8fBo0UR2CcwWxeX7WIIgJ7lXjasFxoYgRnFHUj+hRvKkpiBJbxhdAPTCY6/ZKM0uxANFVzt4yObSLuTiTnazDA== +"@types/babel__core@^7.1.7": + version "7.1.7" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.7.tgz#1dacad8840364a57c98d0dd4855c6dd3752c6b89" + integrity sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" @@ -3045,7 +3237,7 @@ resolved "https://registry.yarnpkg.com/@types/is-function/-/is-function-1.0.0.tgz#1b0b819b1636c7baf0d6785d030d12edf70c3e83" integrity sha512-iTs9HReBu7evG77Q4EC8hZnqRt57irBDkK9nvmHroiOIVwYMQc4IvYvdRgwKfYepunIY7Oh/dBuuld+Gj9uo6w== -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff" integrity sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg== @@ -3139,6 +3331,11 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/prettier@^1.19.0": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" + integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== + "@types/prop-types@*": version "15.7.3" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" @@ -3277,6 +3474,13 @@ dependencies: "@types/yargs-parser" "*" +"@types/yargs@^15.0.0": + version "15.0.4" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.4.tgz#7e5d0f8ca25e9d5849f2ea443cf7c402decd8299" + integrity sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg== + dependencies: + "@types/yargs-parser" "*" + "@types/zen-observable@0.8.0", "@types/zen-observable@^0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d" @@ -3461,7 +3665,7 @@ JSONStream@^1.0.4, JSONStream@^1.3.4: jsonparse "^1.2.0" through ">=2.2.7 <3" -abab@^2.0.0: +abab@^2.0.0, abab@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== @@ -3479,7 +3683,7 @@ accepts@^1.3.0, accepts@^1.3.5, accepts@^1.3.7, accepts@~1.3.4, accepts@~1.3.5, mime-types "~2.1.24" negotiator "0.6.2" -acorn-globals@^4.1.0: +acorn-globals@^4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== @@ -3487,6 +3691,14 @@ acorn-globals@^4.1.0: acorn "^6.0.1" acorn-walk "^6.0.1" +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + acorn-jsx@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" @@ -3497,16 +3709,26 @@ acorn-walk@^6.0.1, acorn-walk@^6.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== -acorn@^5.5.3: - version "5.7.4" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" - integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== +acorn-walk@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e" + integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== -acorn@^6.0.1, acorn@^6.0.7, acorn@^6.2.0, acorn@^6.2.1: +acorn@^6.0.1, acorn@^6.0.7, acorn@^6.2.1: version "6.4.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.0.tgz#b659d2ffbafa24baf5db1cdbb2c94a983ecd2784" integrity sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw== +acorn@^6.2.0: + version "6.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" + integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== + +acorn@^7.1.0, acorn@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" + integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== + address@1.1.2, address@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/address/-/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6" @@ -3670,7 +3892,7 @@ ansi-styles@^3.1.0, ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-styles@^4.1.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== @@ -3693,6 +3915,14 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +anymatch@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + apicache@~1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/apicache/-/apicache-1.4.0.tgz#3835fbe18717caca3a44cb6272d49b52cac30d3a" @@ -4411,18 +4641,18 @@ babel-helper-to-multiple-sequence-expressions@^0.5.0: resolved "https://registry.yarnpkg.com/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.5.0.tgz#a3f924e3561882d42fcf48907aa98f7979a4588d" integrity sha512-m2CvfDW4+1qfDdsrtf4dwOslQC3yhbgyBFptncp4wvtdrDHqueW7slsYv4gArie056phvQFhT2nRcGS4bnm6mA== -babel-jest@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.9.0.tgz#3fc327cb8467b89d14d7bc70e315104a783ccd54" - integrity sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw== +babel-jest@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-25.3.0.tgz#999d0c19e8427f66b796bf9ea233eedf087b957c" + integrity sha512-qiXeX1Cmw4JZ5yQ4H57WpkO0MZ61Qj+YnsVUwAMnDV5ls+yHon11XjarDdgP7H8lTmiEi6biiZA8y3Tmvx6pCg== dependencies: - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/babel__core" "^7.1.0" - babel-plugin-istanbul "^5.1.0" - babel-preset-jest "^24.9.0" - chalk "^2.4.2" - slash "^2.0.0" + "@jest/transform" "^25.3.0" + "@jest/types" "^25.3.0" + "@types/babel__core" "^7.1.7" + babel-plugin-istanbul "^6.0.0" + babel-preset-jest "^25.3.0" + chalk "^3.0.0" + slash "^3.0.0" babel-jest@~24.1.0: version "24.1.0" @@ -4522,6 +4752,17 @@ babel-plugin-istanbul@^5.1.0: istanbul-lib-instrument "^3.3.0" test-exclude "^5.2.3" +babel-plugin-istanbul@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765" + integrity sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@istanbuljs/load-nyc-config" "^1.0.0" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-instrument "^4.0.0" + test-exclude "^6.0.0" + babel-plugin-jest-hoist@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz#4f837091eb407e01447c8843cbec546d0002d756" @@ -4529,6 +4770,13 @@ babel-plugin-jest-hoist@^24.9.0: dependencies: "@types/babel__traverse" "^7.0.6" +babel-plugin-jest-hoist@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.2.6.tgz#2af07632b8ac7aad7d414c1e58425d5fc8e84909" + integrity sha512-qE2xjMathybYxjiGFJg0mLFrz0qNp83aNZycWDY/SuHiZNq+vQfRQtuINqyXyue1ELd8Rd+1OhFSLjms8msMbw== + dependencies: + "@types/babel__traverse" "^7.0.6" + babel-plugin-macros@2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/babel-plugin-macros/-/babel-plugin-macros-2.7.1.tgz#ee294383c1a38f9d6535be3d89734824cb3ed415" @@ -4730,7 +4978,23 @@ babel-plugin-transform-undefined-to-void@^6.9.4: resolved "https://registry.yarnpkg.com/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.9.4.tgz#be241ca81404030678b748717322b89d0c8fe280" integrity sha1-viQcqBQEAwZ4t0hxcyK4nQyP4oA= -babel-preset-jest@^24.1.0, babel-preset-jest@^24.9.0: +babel-preset-current-node-syntax@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz#fb4a4c51fe38ca60fede1dc74ab35eb843cb41d6" + integrity sha512-u/8cS+dEiK1SFILbOC8/rUI3ml9lboKuuMvZ/4aQnQmhecQAgPw5ew066C1ObnEAUmlx7dv/s2z52psWEtLNiw== + dependencies: + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-bigint" "^7.8.3" + "@babel/plugin-syntax-class-properties" "^7.8.3" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.8.3" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + +babel-preset-jest@^24.1.0: version "24.9.0" resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz#192b521e2217fb1d1f67cf73f70c336650ad3cdc" integrity sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg== @@ -4738,6 +5002,14 @@ babel-preset-jest@^24.1.0, babel-preset-jest@^24.9.0: "@babel/plugin-syntax-object-rest-spread" "^7.0.0" babel-plugin-jest-hoist "^24.9.0" +babel-preset-jest@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-25.3.0.tgz#9ab40aee52a19bdc52b8b1ec2403d5914ac3d86b" + integrity sha512-tjdvLKNMwDI9r+QWz9sZUQGTq1dpoxjUqFUpEasAc7MOtHg9XuLT2fx0udFG+k1nvMV0WvHHVAN7VmCZ+1Zxbw== + dependencies: + babel-plugin-jest-hoist "^25.2.6" + babel-preset-current-node-syntax "^0.1.2" + "babel-preset-minify@^0.5.0 || 0.6.0-alpha.5": version "0.5.1" resolved "https://registry.yarnpkg.com/babel-preset-minify/-/babel-preset-minify-0.5.1.tgz#25f5d0bce36ec818be80338d0e594106e21eaa9f" @@ -5099,6 +5371,11 @@ browser-process-hrtime@^0.1.2: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4" integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw== +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + browser-resolve@^1.11.3: version "1.11.3" resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" @@ -5759,6 +6036,15 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" +cliui@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" + integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== + dependencies: + string-width "^4.2.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" @@ -5814,6 +6100,11 @@ collapse-white-space@^1.0.0, collapse-white-space@^1.0.2: resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.5.tgz#c2495b699ab1ed380d29a1091e01063e75dbbe3a" integrity sha512-703bOOmytCYAX9cXYqoikYIx6twmFCXsnzRQheBcTG3nzKYBR4P/+wkYeH+Mvj7qUz8zZDtdyzbxfnEi/kYzRQ== +collect-v8-coverage@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" + integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== + collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -6163,7 +6454,7 @@ conventional-recommended-bump@^4.0.4: meow "^4.0.0" q "^1.5.1" -convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.7.0: +convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== @@ -6428,6 +6719,15 @@ cross-spawn@^5.0.1: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.2.tgz#d0d7dcfa74e89115c7619f4f721a94e1fdb716d6" + integrity sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -6540,17 +6840,22 @@ csso@^4.0.2: dependencies: css-tree "1.0.0-alpha.37" -cssom@0.3.x, "cssom@>= 0.3.2 < 0.4.0": +cssom@^0.4.1, cssom@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" + integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== + +cssom@~0.3.6: version "0.3.8" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== -cssstyle@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-1.4.0.tgz#9d31328229d3c565c61e586b02041a28fccdccf1" - integrity sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA== +cssstyle@^2.0.0, cssstyle@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.2.0.tgz#e4c44debccd6b7911ed617a4395e5754bba59992" + integrity sha512-sEb3XFPx3jNnCAMtqrXPDeSgQr+jojtCeNf8cvMNMh1cG970+lljssvQDzPq6lmmJu2Vhqood/gtEomBiHOGnA== dependencies: - cssom "0.3.x" + cssom "~0.3.6" csstype@^2.2.0, csstype@^2.5.7: version "2.6.8" @@ -6645,7 +6950,7 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -data-urls@^1.0.0: +data-urls@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== @@ -6654,6 +6959,15 @@ data-urls@^1.0.0: whatwg-mimetype "^2.2.0" whatwg-url "^7.0.0" +data-urls@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" + integrity sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ== + dependencies: + abab "^2.0.3" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + dateformat@^3.0.0: version "3.0.3" resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" @@ -6712,6 +7026,11 @@ decamelize@^2.0.0: dependencies: xregexp "4.0.0" +decimal.js@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.0.tgz#39466113a9e036111d02f82489b5fd6b0b5ed231" + integrity sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw== + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -6768,6 +7087,11 @@ deep-object-diff@^1.1.0: resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.0.tgz#d6fabf476c2ed1751fc94d5ca693d2ed8c18bc5a" integrity sha512-b+QLs5vHgS+IoSNcUE4n9HP2NwcHj7aqnJWsjPtuG75Rh5TOaGt0OjAYInh77d5T16V5cRDC+Pw/6ZZZiETBGw== +deepmerge@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" + integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== + default-gateway@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" @@ -6899,10 +7223,10 @@ detect-libc@^1.0.3: resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= -detect-newline@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" - integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= +detect-newline@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" + integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== detect-node@^2.0.4: version "2.0.4" @@ -6974,6 +7298,11 @@ diff-sequences@^24.9.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== +diff-sequences@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" + integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== + diff@^1.3.2: version "1.4.0" resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" @@ -7118,6 +7447,13 @@ domexception@^1.0.1: dependencies: webidl-conversions "^4.0.2" +domexception@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" + integrity sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg== + dependencies: + webidl-conversions "^5.0.0" + domhandler@^2.3.0: version "2.4.2" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" @@ -7522,12 +7858,12 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escodegen@^1.9.1: - version "1.12.1" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.12.1.tgz#08770602a74ac34c7a90ca9229e7d51e379abc76" - integrity sha512-Q8t2YZ+0e0pc7NRVj3B4tSQ9rim1oi4Fh46k2xhJ2qOiEwhQfdjyEQddWdj7ZFaKmU+5104vn1qrcjEPWq+bgQ== +escodegen@^1.11.1, escodegen@^1.14.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" + integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ== dependencies: - esprima "^3.1.3" + esprima "^4.0.1" estraverse "^4.2.0" esutils "^2.0.2" optionator "^0.8.1" @@ -7736,12 +8072,7 @@ espree@^5.0.1: acorn-jsx "^5.0.0" eslint-visitor-keys "^1.0.0" -esprima@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" - integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= - -esprima@^4.0.0, esprima@~4.0.0: +esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -7841,6 +8172,22 @@ execa@^1.0.0, execa@~1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^3.2.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-3.4.0.tgz#c08ed4550ef65d858fac269ffc8572446f37eb89" + integrity sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g== + dependencies: + cross-spawn "^7.0.0" + get-stream "^5.0.0" + human-signals "^1.1.1" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.0" + onetime "^5.1.0" + p-finally "^2.0.0" + signal-exit "^3.0.2" + strip-final-newline "^2.0.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -7876,17 +8223,17 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -expect@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" - integrity sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q== +expect@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-25.3.0.tgz#5fd36e51befd05afb7184bc954f8a4792d184c71" + integrity sha512-buboTXML2h/L0Kh44Ys2Cx49mX20ISc5KDirkxIs3Q9AJv0kazweUAbukegr+nHDOvFRKmxdojjIHCjqAceYfg== dependencies: - "@jest/types" "^24.9.0" - ansi-styles "^3.2.0" - jest-get-type "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-regex-util "^24.9.0" + "@jest/types" "^25.3.0" + ansi-styles "^4.0.0" + jest-get-type "^25.2.6" + jest-matcher-utils "^25.3.0" + jest-message-util "^25.3.0" + jest-regex-util "^25.2.6" express-request-proxy@^2.2.2: version "2.2.2" @@ -8282,7 +8629,7 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" -find-up@^4.0.0: +find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -8575,6 +8922,11 @@ fsevents@^1.2.7: bindings "^1.5.0" nan "^2.12.1" +fsevents@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" + integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== + fstream@^1.0.0, fstream@^1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" @@ -8691,7 +9043,7 @@ get-stream@^4.0.0, get-stream@^4.1.0: dependencies: pump "^3.0.0" -get-stream@^5.1.0: +get-stream@^5.0.0, get-stream@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.1.0.tgz#01203cdc92597f9b909067c3e656cc1f4d3c4dc9" integrity sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw== @@ -8968,7 +9320,7 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== @@ -9221,7 +9573,7 @@ har-schema@^2.0.0: resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= -har-validator@~5.1.0: +har-validator@~5.1.0, har-validator@~5.1.3: version "5.1.3" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== @@ -9500,6 +9852,13 @@ html-encoding-sniffer@^1.0.2: dependencies: whatwg-encoding "^1.0.1" +html-encoding-sniffer@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" + integrity sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ== + dependencies: + whatwg-encoding "^1.0.5" + html-entities@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" @@ -9679,6 +10038,11 @@ https-proxy-agent@^2.2.1, https-proxy-agent@^2.2.3: agent-base "^4.3.0" debug "^3.1.0" +human-signals@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" + integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -9835,6 +10199,14 @@ import-local@^2.0.0: pkg-dir "^3.0.0" resolve-cwd "^2.0.0" +import-local@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" + integrity sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -10393,6 +10765,11 @@ is-plain-object@^3.0.0: dependencies: isobject "^4.0.0" +is-potential-custom-element-name@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397" + integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= + is-promise@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" @@ -10447,6 +10824,11 @@ is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= +is-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" + integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== + is-string@^1.0.4, is-string@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" @@ -10471,7 +10853,7 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" -is-typedarray@~1.0.0: +is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= @@ -10522,6 +10904,11 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= +is-wsl@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.1.1.tgz#4a1c152d429df3d441669498e2486d3596ebaf1d" + integrity sha512-umZHcSrwlDHo2TGMXv0DZ8dIUGunZ2Iv68YZnrmCiBPkZ4aaOhtv7pXJKeki9k3qJ3RJr0cDyitcl5wEH3AYog== + is-yarn-global@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" @@ -10577,12 +10964,17 @@ isstream@~0.1.2: resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istanbul-lib-coverage@^2.0.2, istanbul-lib-coverage@^2.0.5: +istanbul-lib-coverage@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== -istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0: +istanbul-lib-coverage@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" + integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== + +istanbul-lib-instrument@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz#a5f63d91f0bbc0c3e479ef4c5de027335ec6d630" integrity sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA== @@ -10595,32 +10987,44 @@ istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.3.0: istanbul-lib-coverage "^2.0.5" semver "^6.0.0" -istanbul-lib-report@^2.0.4: - version "2.0.8" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz#5a8113cd746d43c4889eba36ab10e7d50c9b4f33" - integrity sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ== +istanbul-lib-instrument@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.1.tgz#61f13ac2c96cfefb076fe7131156cc05907874e6" + integrity sha512-imIchxnodll7pvQBYOqUu88EufLCU56LMeFPZZM/fJZ1irYcYdqroaV+ACK1Ila8ls09iEYArp+nqyC6lW1Vfg== dependencies: - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - supports-color "^6.1.0" + "@babel/core" "^7.7.5" + "@babel/parser" "^7.7.5" + "@babel/template" "^7.7.4" + "@babel/traverse" "^7.7.4" + "@istanbuljs/schema" "^0.1.2" + istanbul-lib-coverage "^3.0.0" + semver "^6.3.0" -istanbul-lib-source-maps@^3.0.1: - version "3.0.6" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" - integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== +istanbul-lib-report@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" + integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== + dependencies: + istanbul-lib-coverage "^3.0.0" + make-dir "^3.0.0" + supports-color "^7.1.0" + +istanbul-lib-source-maps@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9" + integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg== dependencies: debug "^4.1.1" - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - rimraf "^2.6.3" + istanbul-lib-coverage "^3.0.0" source-map "^0.6.1" -istanbul-reports@^2.2.6: - version "2.2.7" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.2.7.tgz#5d939f6237d7b48393cc0959eab40cd4fd056931" - integrity sha512-uu1F/L1o5Y6LzPVSVZXNOoD/KXpJue9aeLRd0sM9uMXfZvzomB0WxVamWb5ue8kA2vVWEmW7EG+A5n3f1kqHKg== +istanbul-reports@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" + integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== dependencies: html-escaper "^2.0.0" + istanbul-lib-report "^3.0.0" iterall@^1.1.3, iterall@^1.2.1, iterall@^1.2.2: version "1.3.0" @@ -10644,58 +11048,59 @@ jarallax@~1.11.1: rafl "^1.2.2" video-worker "^1.1.6" -jest-changed-files@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.9.0.tgz#08d8c15eb79a7fa3fc98269bc14b451ee82f8039" - integrity sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg== +jest-changed-files@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-25.3.0.tgz#85d8de6f4bd13dafda9d7f1e3f2565fc0e183c78" + integrity sha512-eqd5hyLbUjIVvLlJ3vQ/MoPxsxfESVXG9gvU19XXjKzxr+dXmZIqCXiY0OiYaibwlHZBJl2Vebkc0ADEMzCXew== dependencies: - "@jest/types" "^24.9.0" - execa "^1.0.0" - throat "^4.0.0" + "@jest/types" "^25.3.0" + execa "^3.2.0" + throat "^5.0.0" -jest-cli@^24.3.1: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.9.0.tgz#ad2de62d07472d419c6abc301fc432b98b10d2af" - integrity sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg== +jest-cli@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-25.3.0.tgz#d9e11f5700cc5946583cf0d01a9bdebceed448d2" + integrity sha512-XpNQPlW1tzpP7RGG8dxpkRegYDuLjzSiENu92+CYM87nEbmEPb3b4+yo8xcsHOnj0AG7DUt9b3uG8LuHI3MDzw== dependencies: - "@jest/core" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" + "@jest/core" "^25.3.0" + "@jest/test-result" "^25.3.0" + "@jest/types" "^25.3.0" + chalk "^3.0.0" exit "^0.1.2" - import-local "^2.0.0" + import-local "^3.0.2" is-ci "^2.0.0" - jest-config "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" + jest-config "^25.3.0" + jest-util "^25.3.0" + jest-validate "^25.3.0" prompts "^2.0.1" - realpath-native "^1.1.0" - yargs "^13.3.0" + realpath-native "^2.0.0" + yargs "^15.3.1" -jest-config@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.9.0.tgz#fb1bbc60c73a46af03590719efa4825e6e4dd1b5" - integrity sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ== +jest-config@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-25.3.0.tgz#112b5e2f2e57dec4501dd2fe979044c06fb1317e" + integrity sha512-CmF1JnNWFmoCSPC4tnU52wnVBpuxHjilA40qH/03IHxIevkjUInSMwaDeE6ACfxMPTLidBGBCO3EbxvzPbo8wA== dependencies: "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^24.9.0" - "@jest/types" "^24.9.0" - babel-jest "^24.9.0" - chalk "^2.0.1" + "@jest/test-sequencer" "^25.3.0" + "@jest/types" "^25.3.0" + babel-jest "^25.3.0" + chalk "^3.0.0" + deepmerge "^4.2.2" glob "^7.1.1" - jest-environment-jsdom "^24.9.0" - jest-environment-node "^24.9.0" - jest-get-type "^24.9.0" - jest-jasmine2 "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - micromatch "^3.1.10" - pretty-format "^24.9.0" - realpath-native "^1.1.0" - -jest-diff@^24.3.0, jest-diff@^24.9.0: + jest-environment-jsdom "^25.3.0" + jest-environment-node "^25.3.0" + jest-get-type "^25.2.6" + jest-jasmine2 "^25.3.0" + jest-regex-util "^25.2.6" + jest-resolve "^25.3.0" + jest-util "^25.3.0" + jest-validate "^25.3.0" + micromatch "^4.0.2" + pretty-format "^25.3.0" + realpath-native "^2.0.0" + +jest-diff@^24.3.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== @@ -10705,46 +11110,57 @@ jest-diff@^24.3.0, jest-diff@^24.9.0: jest-get-type "^24.9.0" pretty-format "^24.9.0" -jest-docblock@^24.3.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.9.0.tgz#7970201802ba560e1c4092cc25cbedf5af5a8ce2" - integrity sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA== - dependencies: - detect-newline "^2.1.0" - -jest-each@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.9.0.tgz#eb2da602e2a610898dbc5f1f6df3ba86b55f8b05" - integrity sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog== +jest-diff@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.3.0.tgz#0d7d6f5d6171e5dacde9e05be47b3615e147c26f" + integrity sha512-vyvs6RPoVdiwARwY4kqFWd4PirPLm2dmmkNzKqo38uZOzJvLee87yzDjIZLmY1SjM3XR5DwsUH+cdQ12vgqi1w== dependencies: - "@jest/types" "^24.9.0" - chalk "^2.0.1" - jest-get-type "^24.9.0" - jest-util "^24.9.0" - pretty-format "^24.9.0" + chalk "^3.0.0" + diff-sequences "^25.2.6" + jest-get-type "^25.2.6" + pretty-format "^25.3.0" -jest-environment-jsdom@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz#4b0806c7fc94f95edb369a69cc2778eec2b7375b" - integrity sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA== +jest-docblock@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-25.3.0.tgz#8b777a27e3477cd77a168c05290c471a575623ef" + integrity sha512-aktF0kCar8+zxRHxQZwxMy70stc9R1mOmrLsT5VO3pIT0uzGRSDAXxSlz4NqQWpuLjPpuMhPRl7H+5FRsvIQAg== dependencies: - "@jest/environment" "^24.9.0" - "@jest/fake-timers" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" - jest-util "^24.9.0" - jsdom "^11.5.1" + detect-newline "^3.0.0" -jest-environment-node@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.9.0.tgz#333d2d2796f9687f2aeebf0742b519f33c1cbfd3" - integrity sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA== +jest-each@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-25.3.0.tgz#a319eecf1f6076164ab86f99ca166a55b96c0bd4" + integrity sha512-aBfS4VOf/Qs95yUlX6d6WBv0szvOcTkTTyCIaLuQGj4bSHsT+Wd9dDngVHrCe5uytxpN8VM+NAloI6nbPjXfXw== dependencies: - "@jest/environment" "^24.9.0" - "@jest/fake-timers" "^24.9.0" - "@jest/types" "^24.9.0" - jest-mock "^24.9.0" - jest-util "^24.9.0" + "@jest/types" "^25.3.0" + chalk "^3.0.0" + jest-get-type "^25.2.6" + jest-util "^25.3.0" + pretty-format "^25.3.0" + +jest-environment-jsdom@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-25.3.0.tgz#c493ab8c41f28001520c70ef67dd88b88be6af05" + integrity sha512-jdE4bQN+k2QEZ9sWOxsqDJvMzbdFSCN/4tw8X0TQaCqyzKz58PyEf41oIr4WO7ERdp7WaJGBSUKF7imR3UW1lg== + dependencies: + "@jest/environment" "^25.3.0" + "@jest/fake-timers" "^25.3.0" + "@jest/types" "^25.3.0" + jest-mock "^25.3.0" + jest-util "^25.3.0" + jsdom "^15.2.1" + +jest-environment-node@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-25.3.0.tgz#9845f0e63991e8498448cb0ae804935689533db9" + integrity sha512-XO09S29Nx1NU7TiMPHMoDIkxoGBuKSTbE+sHp0gXbeLDXhIdhysUI25kOqFFSD9AuDgvPvxWCXrvNqiFsOH33g== + dependencies: + "@jest/environment" "^25.3.0" + "@jest/fake-timers" "^25.3.0" + "@jest/types" "^25.3.0" + jest-mock "^25.3.0" + jest-util "^25.3.0" + semver "^6.3.0" jest-fetch-mock@~2.1.1: version "2.1.2" @@ -10759,46 +11175,52 @@ jest-get-type@^24.9.0: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e" integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q== -jest-haste-map@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d" - integrity sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ== +jest-get-type@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" + integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig== + +jest-haste-map@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-25.3.0.tgz#b7683031c9c9ddc0521d311564108b244b11e4c6" + integrity sha512-LjXaRa+F8wwtSxo9G+hHD/Cp63PPQzvaBL9XCVoJD2rrcJO0Zr2+YYzAFWWYJ5GlPUkoaJFJtOuk0sL6MJY80A== dependencies: - "@jest/types" "^24.9.0" - anymatch "^2.0.0" + "@jest/types" "^25.3.0" + anymatch "^3.0.3" fb-watchman "^2.0.0" - graceful-fs "^4.1.15" - invariant "^2.2.4" - jest-serializer "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.9.0" - micromatch "^3.1.10" + graceful-fs "^4.2.3" + jest-serializer "^25.2.6" + jest-util "^25.3.0" + jest-worker "^25.2.6" + micromatch "^4.0.2" sane "^4.0.3" walker "^1.0.7" + which "^2.0.2" optionalDependencies: - fsevents "^1.2.7" + fsevents "^2.1.2" -jest-jasmine2@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz#1f7b1bd3242c1774e62acabb3646d96afc3be6a0" - integrity sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw== +jest-jasmine2@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-25.3.0.tgz#16ae4f68adef65fb45001b26c864bcbcbf972830" + integrity sha512-NCYOGE6+HNzYFSui52SefgpsnIzvxjn6KAgqw66BdRp37xpMD/4kujDHLNW5bS5i53os5TcMn6jYrzQRO8VPrQ== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" + "@jest/environment" "^25.3.0" + "@jest/source-map" "^25.2.6" + "@jest/test-result" "^25.3.0" + "@jest/types" "^25.3.0" + chalk "^3.0.0" co "^4.6.0" - expect "^24.9.0" + expect "^25.3.0" is-generator-fn "^2.0.0" - jest-each "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-runtime "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - pretty-format "^24.9.0" - throat "^4.0.0" + jest-each "^25.3.0" + jest-matcher-utils "^25.3.0" + jest-message-util "^25.3.0" + jest-runtime "^25.3.0" + jest-snapshot "^25.3.0" + jest-util "^25.3.0" + pretty-format "^25.3.0" + throat "^5.0.0" jest-junit@~6.3.0: version "6.3.0" @@ -10810,177 +11232,172 @@ jest-junit@~6.3.0: strip-ansi "^4.0.0" xml "^1.0.1" -jest-leak-detector@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz#b665dea7c77100c5c4f7dfcb153b65cf07dcf96a" - integrity sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA== +jest-leak-detector@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-25.3.0.tgz#5b6bf04903b35be56038915a55f47291771f769f" + integrity sha512-jk7k24dMIfk8LUSQQGN8PyOy9+J0NAfHZWiDmUDYVMctY8FLJQ1eQ8+PjMoN8PgwhLIggUqgYJnyRFvUz3jLRw== dependencies: - jest-get-type "^24.9.0" - pretty-format "^24.9.0" + jest-get-type "^25.2.6" + pretty-format "^25.3.0" -jest-matcher-utils@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz#f5b3661d5e628dffe6dd65251dfdae0e87c3a073" - integrity sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA== +jest-matcher-utils@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.3.0.tgz#76765788a26edaa8bc5f0100aea52ae383559648" + integrity sha512-ZBUJ2fchNIZt+fyzkuCFBb8SKaU//Rln45augfUtbHaGyVxCO++ANARdBK9oPGXU3hEDgyy7UHnOP/qNOJXFUg== dependencies: - chalk "^2.0.1" - jest-diff "^24.9.0" - jest-get-type "^24.9.0" - pretty-format "^24.9.0" + chalk "^3.0.0" + jest-diff "^25.3.0" + jest-get-type "^25.2.6" + pretty-format "^25.3.0" -jest-message-util@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.9.0.tgz#527f54a1e380f5e202a8d1149b0ec872f43119e3" - integrity sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw== +jest-message-util@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.3.0.tgz#e3836826fe5ca538a337b87d9bd2648190867f85" + integrity sha512-5QNy9Id4WxJbRITEbA1T1kem9bk7y2fD0updZMSTNHtbEDnYOGLDPAuFBhFgVmOZpv0n6OMdVkK+WhyXEPCcOw== dependencies: "@babel/code-frame" "^7.0.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" + "@jest/types" "^25.3.0" "@types/stack-utils" "^1.0.1" - chalk "^2.0.1" - micromatch "^3.1.10" - slash "^2.0.0" + chalk "^3.0.0" + micromatch "^4.0.2" + slash "^3.0.0" stack-utils "^1.0.1" -jest-mock@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.9.0.tgz#c22835541ee379b908673ad51087a2185c13f1c6" - integrity sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w== +jest-mock@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-25.3.0.tgz#d72644509e40987a732a9a2534a1054f4649402c" + integrity sha512-yRn6GbuqB4j3aYu+Z1ezwRiZfp0o9om5uOcBovVtkcRLeBCNP5mT0ysdenUsxAHnQUgGwPOE1wwhtQYe6NKirQ== dependencies: - "@jest/types" "^24.9.0" + "@jest/types" "^25.3.0" jest-pnp-resolver@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== -jest-regex-util@^24.3.0, jest-regex-util@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.9.0.tgz#c13fb3380bde22bf6575432c493ea8fe37965636" - integrity sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA== +jest-regex-util@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-25.2.6.tgz#d847d38ba15d2118d3b06390056028d0f2fd3964" + integrity sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw== -jest-resolve-dependencies@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz#ad055198959c4cfba8a4f066c673a3f0786507ab" - integrity sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g== +jest-resolve-dependencies@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-25.3.0.tgz#b0e4ae053dd44ddacc18c6ee12b5b7c28e445a90" + integrity sha512-bDUlLYmHW+f7J7KgcY2lkq8EMRqKonRl0XoD4Wp5SJkgAxKJnsaIOlrrVNTfXYf+YOu3VCjm/Ac2hPF2nfsCIA== dependencies: - "@jest/types" "^24.9.0" - jest-regex-util "^24.3.0" - jest-snapshot "^24.9.0" + "@jest/types" "^25.3.0" + jest-regex-util "^25.2.6" + jest-snapshot "^25.3.0" -jest-resolve@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.9.0.tgz#dff04c7687af34c4dd7e524892d9cf77e5d17321" - integrity sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ== +jest-resolve@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-25.3.0.tgz#cb90a5bbea54a02eccdbbf4126a819595dcf91d6" + integrity sha512-IHoQAAybulsJ+ZgWis+ekYKDAoFkVH5Nx/znpb41zRtpxj4fr2WNV9iDqavdSm8GIpMlsfZxbC/fV9DhW0q9VQ== dependencies: - "@jest/types" "^24.9.0" + "@jest/types" "^25.3.0" browser-resolve "^1.11.3" - chalk "^2.0.1" + chalk "^3.0.0" jest-pnp-resolver "^1.2.1" - realpath-native "^1.1.0" - -jest-runner@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.9.0.tgz#574fafdbd54455c2b34b4bdf4365a23857fcdf42" - integrity sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg== - dependencies: - "@jest/console" "^24.7.1" - "@jest/environment" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - chalk "^2.4.2" + realpath-native "^2.0.0" + resolve "^1.15.1" + +jest-runner@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-25.3.0.tgz#673ef2ac79d2810eb6b2c1a3f82398375a3d1174" + integrity sha512-csDqSC9qGHYWDrzrElzEgFbteztFeZJmKhSgY5jlCIcN0+PhActzRNku0DA1Xa1HxGOb0/AfbP1EGJlP4fGPtA== + dependencies: + "@jest/console" "^25.3.0" + "@jest/environment" "^25.3.0" + "@jest/test-result" "^25.3.0" + "@jest/types" "^25.3.0" + chalk "^3.0.0" exit "^0.1.2" - graceful-fs "^4.1.15" - jest-config "^24.9.0" - jest-docblock "^24.3.0" - jest-haste-map "^24.9.0" - jest-jasmine2 "^24.9.0" - jest-leak-detector "^24.9.0" - jest-message-util "^24.9.0" - jest-resolve "^24.9.0" - jest-runtime "^24.9.0" - jest-util "^24.9.0" - jest-worker "^24.6.0" + graceful-fs "^4.2.3" + jest-config "^25.3.0" + jest-docblock "^25.3.0" + jest-haste-map "^25.3.0" + jest-jasmine2 "^25.3.0" + jest-leak-detector "^25.3.0" + jest-message-util "^25.3.0" + jest-resolve "^25.3.0" + jest-runtime "^25.3.0" + jest-util "^25.3.0" + jest-worker "^25.2.6" source-map-support "^0.5.6" - throat "^4.0.0" - -jest-runtime@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.9.0.tgz#9f14583af6a4f7314a6a9d9f0226e1a781c8e4ac" - integrity sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw== - dependencies: - "@jest/console" "^24.7.1" - "@jest/environment" "^24.9.0" - "@jest/source-map" "^24.3.0" - "@jest/transform" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/yargs" "^13.0.0" - chalk "^2.0.1" + throat "^5.0.0" + +jest-runtime@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-25.3.0.tgz#af4d40dbcc590fa5de9910cb6a120a13d131050b" + integrity sha512-gn5KYB1wxXRM3nfw8fVpthFu60vxQUCr+ShGq41+ZBFF3DRHZRKj3HDWVAVB4iTNBj2y04QeAo5cZ/boYaPg0w== + dependencies: + "@jest/console" "^25.3.0" + "@jest/environment" "^25.3.0" + "@jest/source-map" "^25.2.6" + "@jest/test-result" "^25.3.0" + "@jest/transform" "^25.3.0" + "@jest/types" "^25.3.0" + "@types/yargs" "^15.0.0" + chalk "^3.0.0" + collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.3" - graceful-fs "^4.1.15" - jest-config "^24.9.0" - jest-haste-map "^24.9.0" - jest-message-util "^24.9.0" - jest-mock "^24.9.0" - jest-regex-util "^24.3.0" - jest-resolve "^24.9.0" - jest-snapshot "^24.9.0" - jest-util "^24.9.0" - jest-validate "^24.9.0" - realpath-native "^1.1.0" - slash "^2.0.0" - strip-bom "^3.0.0" - yargs "^13.3.0" - -jest-serializer@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.9.0.tgz#e6d7d7ef96d31e8b9079a714754c5d5c58288e73" - integrity sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ== - -jest-snapshot@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.9.0.tgz#ec8e9ca4f2ec0c5c87ae8f925cf97497b0e951ba" - integrity sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew== + graceful-fs "^4.2.3" + jest-config "^25.3.0" + jest-haste-map "^25.3.0" + jest-message-util "^25.3.0" + jest-mock "^25.3.0" + jest-regex-util "^25.2.6" + jest-resolve "^25.3.0" + jest-snapshot "^25.3.0" + jest-util "^25.3.0" + jest-validate "^25.3.0" + realpath-native "^2.0.0" + slash "^3.0.0" + strip-bom "^4.0.0" + yargs "^15.3.1" + +jest-serializer@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-25.2.6.tgz#3bb4cc14fe0d8358489dbbefbb8a4e708ce039b7" + integrity sha512-RMVCfZsezQS2Ww4kB5HJTMaMJ0asmC0BHlnobQC6yEtxiFKIxohFA4QSXSabKwSggaNkqxn6Z2VwdFCjhUWuiQ== + +jest-snapshot@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-25.3.0.tgz#d4feb457494f4aaedcc83fbbf1ca21808fc3df71" + integrity sha512-GGpR6Oro2htJPKh5RX4PR1xwo5jCEjtvSPLW1IS7N85y+2bWKbiknHpJJRKSdGXghElb5hWaeQASJI4IiRayGg== dependencies: "@babel/types" "^7.0.0" - "@jest/types" "^24.9.0" - chalk "^2.0.1" - expect "^24.9.0" - jest-diff "^24.9.0" - jest-get-type "^24.9.0" - jest-matcher-utils "^24.9.0" - jest-message-util "^24.9.0" - jest-resolve "^24.9.0" - mkdirp "^0.5.1" + "@jest/types" "^25.3.0" + "@types/prettier" "^1.19.0" + chalk "^3.0.0" + expect "^25.3.0" + jest-diff "^25.3.0" + jest-get-type "^25.2.6" + jest-matcher-utils "^25.3.0" + jest-message-util "^25.3.0" + jest-resolve "^25.3.0" + make-dir "^3.0.0" natural-compare "^1.4.0" - pretty-format "^24.9.0" - semver "^6.2.0" + pretty-format "^25.3.0" + semver "^6.3.0" jest-transform-graphql@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/jest-transform-graphql/-/jest-transform-graphql-2.1.0.tgz#903cb66bb27bc2772fd3e5dd4f7e9b57230f5829" integrity sha1-kDy2a7J7wncv0+XdT36bVyMPWCk= -jest-util@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.9.0.tgz#7396814e48536d2e85a37de3e4c431d7cb140162" - integrity sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg== +jest-util@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-25.3.0.tgz#e3b0064165818f10d78514696fd25efba82cf049" + integrity sha512-dc625P/KS/CpWTJJJxKc4bA3A6c+PJGBAqS8JTJqx4HqPoKNqXg/Ec8biL2Z1TabwK7E7Ilf0/ukSEXM1VwzNA== dependencies: - "@jest/console" "^24.9.0" - "@jest/fake-timers" "^24.9.0" - "@jest/source-map" "^24.9.0" - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - callsites "^3.0.0" - chalk "^2.0.1" - graceful-fs "^4.1.15" + "@jest/types" "^25.3.0" + chalk "^3.0.0" is-ci "^2.0.0" - mkdirp "^0.5.1" - slash "^2.0.0" - source-map "^0.6.0" + make-dir "^3.0.0" -jest-validate@^24.0.0, jest-validate@^24.9.0: +jest-validate@^24.0.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.9.0.tgz#0775c55360d173cd854e40180756d4ff52def8ab" integrity sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ== @@ -10992,34 +11409,46 @@ jest-validate@^24.0.0, jest-validate@^24.9.0: leven "^3.1.0" pretty-format "^24.9.0" -jest-watcher@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.9.0.tgz#4b56e5d1ceff005f5b88e528dc9afc8dd4ed2b3b" - integrity sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw== +jest-validate@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-25.3.0.tgz#eb95fdee0039647bcd5d4be641b21e4a142a880c" + integrity sha512-3WuXgIZ4HXUvW6gk9twFFkT9j6zUorKnF2oEY8VEsHb7x5LGvVlN3WUsbqazVKuyXwvikO2zFJ/YTySMsMje2w== dependencies: - "@jest/test-result" "^24.9.0" - "@jest/types" "^24.9.0" - "@types/yargs" "^13.0.0" - ansi-escapes "^3.0.0" - chalk "^2.0.1" - jest-util "^24.9.0" - string-length "^2.0.0" + "@jest/types" "^25.3.0" + camelcase "^5.3.1" + chalk "^3.0.0" + jest-get-type "^25.2.6" + leven "^3.1.0" + pretty-format "^25.3.0" -jest-worker@^24.6.0, jest-worker@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.9.0.tgz#5dbfdb5b2d322e98567898238a9697bcce67b3e5" - integrity sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw== +jest-watcher@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-25.3.0.tgz#fd03fd5ca52f02bd3161ab177466bf1bfdd34e5c" + integrity sha512-dtFkfidFCS9Ucv8azOg2hkiY3sgJEHeTLtGFHS+jfBEE7eRtrO6+2r1BokyDkaG2FOD7485r/SgpC1MFAENfeA== + dependencies: + "@jest/test-result" "^25.3.0" + "@jest/types" "^25.3.0" + ansi-escapes "^4.2.1" + chalk "^3.0.0" + jest-util "^25.3.0" + string-length "^3.1.0" + +jest-worker@^25.2.6: + version "25.2.6" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.2.6.tgz#d1292625326794ce187c38f51109faced3846c58" + integrity sha512-FJn9XDUSxcOR4cwDzRfL1z56rUofNTFs539FGASpd50RHdb6EVkhxQqktodW2mI49l+W3H+tFJDotCHUQF6dmA== dependencies: merge-stream "^2.0.0" - supports-color "^6.1.0" + supports-color "^7.0.0" -jest@~24.3.1: - version "24.3.1" - resolved "https://registry.yarnpkg.com/jest/-/jest-24.3.1.tgz#81959de0d57b2df923510f4fafe266712d37dcca" - integrity sha512-SqZguEbYNcZ3r0KUUBN+IkKfyPS1VBbIUiK4Wrc0AiGUR52gJa0fmlWSOCL3x25908QrfoQwkVDu5jCsfXb2ig== +jest@~25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-25.3.0.tgz#7a5e59741d94b8662664c77a9f346246d6bf228b" + integrity sha512-iKd5ShQSHzFT5IL/6h5RZJhApgqXSoPxhp5HEi94v6OAw9QkF8T7X+liEU2eEHJ1eMFYTHmeWLrpBWulsDpaUg== dependencies: - import-local "^2.0.0" - jest-cli "^24.3.1" + "@jest/core" "^25.3.0" + import-local "^3.0.2" + jest-cli "^25.3.0" js-base64@^2.3.2: version "2.5.1" @@ -11054,36 +11483,68 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsdom@^11.5.1: - version "11.12.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8" - integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw== +jsdom@^15.2.1: + version "15.2.1" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-15.2.1.tgz#d2feb1aef7183f86be521b8c6833ff5296d07ec5" + integrity sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g== dependencies: abab "^2.0.0" - acorn "^5.5.3" - acorn-globals "^4.1.0" + acorn "^7.1.0" + acorn-globals "^4.3.2" array-equal "^1.0.0" - cssom ">= 0.3.2 < 0.4.0" - cssstyle "^1.0.0" - data-urls "^1.0.0" + cssom "^0.4.1" + cssstyle "^2.0.0" + data-urls "^1.1.0" domexception "^1.0.1" - escodegen "^1.9.1" + escodegen "^1.11.1" html-encoding-sniffer "^1.0.2" - left-pad "^1.3.0" - nwsapi "^2.0.7" - parse5 "4.0.0" + nwsapi "^2.2.0" + parse5 "5.1.0" pn "^1.1.0" - request "^2.87.0" - request-promise-native "^1.0.5" - sax "^1.2.4" + request "^2.88.0" + request-promise-native "^1.0.7" + saxes "^3.1.9" symbol-tree "^3.2.2" - tough-cookie "^2.3.4" + tough-cookie "^3.0.1" w3c-hr-time "^1.0.1" + w3c-xmlserializer "^1.1.2" webidl-conversions "^4.0.2" - whatwg-encoding "^1.0.3" - whatwg-mimetype "^2.1.0" - whatwg-url "^6.4.1" - ws "^5.2.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^7.0.0" + ws "^7.0.0" + xml-name-validator "^3.0.0" + +jsdom@~16.2.2: + version "16.2.2" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.2.2.tgz#76f2f7541646beb46a938f5dc476b88705bedf2b" + integrity sha512-pDFQbcYtKBHxRaP55zGXCJWgFHkDAYbKcsXEK/3Icu9nKYZkutUXfLBwbD+09XDutkYSHcgfQLZ0qvpAAm9mvg== + dependencies: + abab "^2.0.3" + acorn "^7.1.1" + acorn-globals "^6.0.0" + cssom "^0.4.4" + cssstyle "^2.2.0" + data-urls "^2.0.0" + decimal.js "^10.2.0" + domexception "^2.0.1" + escodegen "^1.14.1" + html-encoding-sniffer "^2.0.1" + is-potential-custom-element-name "^1.0.0" + nwsapi "^2.2.0" + parse5 "5.1.1" + request "^2.88.2" + request-promise-native "^1.0.8" + saxes "^5.0.0" + symbol-tree "^3.2.4" + tough-cookie "^3.0.1" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^2.0.0" + webidl-conversions "^6.0.0" + whatwg-encoding "^1.0.5" + whatwg-mimetype "^2.3.0" + whatwg-url "^8.0.0" + ws "^7.2.3" xml-name-validator "^3.0.0" jsesc@^2.5.1: @@ -11195,6 +11656,13 @@ json5@^2.1.0: dependencies: minimist "^1.2.0" +json5@^2.1.2: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" + jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" @@ -11403,11 +11871,6 @@ lcov-parse@^1.0.0: resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-1.0.0.tgz#eb0d46b54111ebc561acb4c408ef9363bdc8f7e0" integrity sha1-6w1GtUER68VhrLTECO+TY73I9+A= -left-pad@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" - integrity sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA== - lerna@~3.13.0: version "3.13.4" resolved "https://registry.yarnpkg.com/lerna/-/lerna-3.13.4.tgz#03026c11c5643f341fda42e4fb1882e2df35e6cb" @@ -11753,6 +12216,13 @@ loglevel@^1.4.1: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.6.tgz#0ee6300cc058db6b3551fa1c4bf73b83bb771312" integrity sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ== +lolex@^5.0.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367" + integrity sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A== + dependencies: + "@sinonjs/commons" "^1.7.0" + long@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" @@ -11830,7 +12300,7 @@ make-dir@^1.0.0: dependencies: pify "^3.0.0" -make-dir@^2.0.0, make-dir@^2.1.0: +make-dir@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== @@ -12146,7 +12616,7 @@ micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8: snapdragon "^0.8.1" to-regex "^3.0.2" -micromatch@~4.0.2: +micromatch@^4.0.2, micromatch@~4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== @@ -12282,6 +12752,11 @@ minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0, minimist@~1.2.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + minimist@~0.0.1: version "0.0.10" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" @@ -12662,16 +13137,16 @@ node-modules-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= -node-notifier@^5.4.2: - version "5.4.3" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.3.tgz#cb72daf94c93904098e28b9c590fd866e464bd50" - integrity sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q== +node-notifier@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-6.0.0.tgz#cea319e06baa16deec8ce5cd7f133c4a46b68e12" + integrity sha512-SVfQ/wMw+DesunOm5cKqr6yDcvUTDl/yc97ybGHMrteNEY6oekXpNpS3lZwgLlwz0FLgHoiW28ZpmBHUDg37cw== dependencies: growly "^1.3.0" - is-wsl "^1.1.0" - semver "^5.5.0" + is-wsl "^2.1.1" + semver "^6.3.0" shellwords "^0.1.1" - which "^1.3.0" + which "^1.3.1" node-releases@^1.1.29, node-releases@^1.1.44: version "1.1.45" @@ -12860,6 +13335,13 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" +npm-run-path@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + npm-run@4.1.2, npm-run@~5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/npm-run/-/npm-run-5.0.1.tgz#1baea93389b50ae25a32382c8ca322398e50cd16" @@ -12906,7 +13388,7 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= -nwsapi@^2.0.7: +nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ== @@ -13224,18 +13706,21 @@ p-defer@^1.0.0: resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww= -p-each-series@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71" - integrity sha1-kw89Et0fUOdDRFeiLNbwSsatf3E= - dependencies: - p-reduce "^1.0.0" +p-each-series@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48" + integrity sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ== p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= +p-finally@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" + integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== + p-is-promise@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" @@ -13524,10 +14009,15 @@ parse-url@^5.0.0: parse-path "^4.0.0" protocols "^1.4.0" -parse5@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" - integrity sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA== +parse5@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" + integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== + +parse5@5.1.1, parse5@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" + integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== parse5@^3.0.1: version "3.0.3" @@ -13536,11 +14026,6 @@ parse5@^3.0.1: dependencies: "@types/node" "*" -parse5@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" - integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== - parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -13616,6 +14101,11 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" @@ -13685,15 +14175,20 @@ performance-now@^2.1.0: integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= pertain@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/pertain/-/pertain-0.1.3.tgz#16818a329079fb57ef724b2117005caf5c3f1d6b" - integrity sha512-V3tWIudp1ufQfIRTvJ0/KmCPvcLqamuAphXK23kWY+uVgoDpIsLUZUnYZdoK4qYf3xeHBnvQzUjbMdX6s3mRnA== + version "0.1.11" + resolved "https://registry.yarnpkg.com/pertain/-/pertain-0.1.11.tgz#8fa15a7bf7f04138ec383a2286da71b4e76173c4" + integrity sha512-PmnDz2qhkrntsrSD7r78gGSo6KFjs1O10mEowQo0L3UskjoEMRvKE+T5auM4xmbu4a18a/bBE3tbWv7tzEezkA== dependencies: debug "~4.1.1" dot-prop "^5.2.0" - pkg-dir "^4.1.0" + pkg-dir "^4.2.0" resolve-pkg "^2.0.0" +picomatch@^2.0.4: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + picomatch@^2.0.5: version "2.2.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" @@ -13752,7 +14247,7 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" -pkg-dir@^4.1.0: +pkg-dir@^4.1.0, pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== @@ -14012,6 +14507,16 @@ pretty-format@^24.9.0: ansi-styles "^3.2.0" react-is "^16.8.4" +pretty-format@^25.3.0: + version "25.3.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.3.0.tgz#d0a4f988ff4a6cd350342fdabbb809aeb4d49ad5" + integrity sha512-wToHwF8bkQknIcFkBqNfKu4+UZqnrLn/Vr+wwKQwwvPzkBfDDKp/qIabFqdgtoi5PEnM8LFByVsOrHoa3SpTVA== + dependencies: + "@jest/types" "^25.3.0" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^16.12.0" + pretty-hrtime@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" @@ -14604,6 +15109,11 @@ react-hotkeys@2.0.0-pre4: dependencies: prop-types "^15.6.1" +react-is@^16.12.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: version "16.12.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c" @@ -14744,16 +15254,7 @@ react-textarea-autosize@^7.1.0: "@babel/runtime" "^7.1.2" prop-types "^15.6.0" -react@^16.8.3: - version "16.12.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83" - integrity sha512-fglqy3k5E+81pA8s+7K0/T3DBCF0ZDOher1elBFzF7O6arXJgzyu/FW+COxFvAWXJoJN9KIZbT2LXlukwphYTA== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" - -react@~16.9.0: +react@^16.8.3, react@~16.9.0: version "16.9.0" resolved "https://registry.yarnpkg.com/react/-/react-16.9.0.tgz#40ba2f9af13bc1a38d75dbf2f4359a5185c4f7aa" integrity sha512-+7LQnFBwkiw+BobzOF6N//BdoNw0ouwmSJTEm9cglOOmsg/TMiFHZLe2sEoN5M7LgJTj9oHH0gxklfnQe66S1w== @@ -14920,12 +15421,10 @@ readline-sync@^1.4.9: resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b" integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw== -realpath-native@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" - integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA== - dependencies: - util.promisify "^1.0.0" +realpath-native@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-2.0.0.tgz#7377ac429b6e1fd599dc38d08ed942d0d7beb866" + integrity sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q== recast@^0.14.7: version "0.14.7" @@ -15226,7 +15725,7 @@ request-promise-core@1.1.3: dependencies: lodash "^4.17.15" -request-promise-native@^1.0.5: +request-promise-native@^1.0.7, request-promise-native@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== @@ -15271,6 +15770,32 @@ request@^2.75.0, request@^2.87.0, request@^2.88.0: tunnel-agent "^0.6.0" uuid "^3.3.2" +request@^2.88.2: + version "2.88.2" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" + integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.3" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.5.0" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -15318,6 +15843,13 @@ resolve-cwd@^2.0.0: dependencies: resolve-from "^3.0.0" +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + resolve-dir@^1.0.0, resolve-dir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" @@ -15370,6 +15902,13 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.3. dependencies: path-parse "^1.0.6" +resolve@^1.15.1: + version "1.15.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" + integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== + dependencies: + path-parse "^1.0.6" + resolve@~1.10.1: version "1.10.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.1.tgz#664842ac960795bbe758221cdccda61fb64b5f18" @@ -15448,6 +15987,13 @@ rimraf@2.6.3, rimraf@~2.6.3: dependencies: glob "^7.1.3" +rimraf@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -15532,11 +16078,25 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sax@^1.2.4, sax@~1.2.4: +sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== +saxes@^3.1.9: + version "3.1.11" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" + integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== + dependencies: + xmlchars "^2.1.1" + +saxes@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.0.tgz#b7d30284d7583a5ca6ad0248b56d8889da53788b" + integrity sha512-LXTZygxhf8lfwKaTP/8N9CsVdjTlea3teze4lL6u37ivbgGbV0GGMuNtS/I9rnD/HC2/txUM7Df4S2LVl1qhiA== + dependencies: + xmlchars "^2.2.0" + scheduler@^0.15.0: version "0.15.0" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.15.0.tgz#6bfcf80ff850b280fed4aeecc6513bc0b4f17f8e" @@ -15811,11 +16371,23 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + shell-quote@1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" @@ -15938,6 +16510,11 @@ slash@^2.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + slice-ansi@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" @@ -16312,13 +16889,13 @@ string-convert@^0.2.0: resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97" integrity sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c= -string-length@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" - integrity sha1-1A27aGo6zpYMHP/KVivyxF+DY+0= +string-length@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-3.1.0.tgz#107ef8c23456e187a8abd4a61162ff4ac6e25837" + integrity sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA== dependencies: astral-regex "^1.0.0" - strip-ansi "^4.0.0" + strip-ansi "^5.2.0" string-width@^1.0.1: version "1.0.2" @@ -16346,7 +16923,7 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.1.0: +string-width@^4.1.0, string-width@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== @@ -16487,6 +17064,11 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= +strip-bom@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" + integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== + strip-comments@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/strip-comments/-/strip-comments-1.0.2.tgz#82b9c45e7f05873bee53f37168af930aa368679d" @@ -16500,6 +17082,11 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + strip-indent@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" @@ -16614,7 +17201,7 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" -supports-color@^7.1.0: +supports-color@^7.0.0, supports-color@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== @@ -16629,6 +17216,14 @@ supports-hyperlinks@^1.0.1: has-flag "^2.0.0" supports-color "^5.0.0" +supports-hyperlinks@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz#f663df252af5f37c5d49bbd7eeefa9e0b9e59e47" + integrity sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA== + dependencies: + has-flag "^4.0.0" + supports-color "^7.0.0" + svg-parser@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.2.tgz#d134cc396fa2681dc64f518330784e98bd801ec8" @@ -16666,7 +17261,7 @@ symbol-observable@^1.0.2, symbol-observable@^1.0.4, symbol-observable@^1.2.0: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== -symbol-tree@^3.2.2: +symbol-tree@^3.2.2, symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== @@ -16876,6 +17471,14 @@ term-size@^1.2.0: dependencies: execa "^0.7.0" +terminal-link@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" + integrity sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ== + dependencies: + ansi-escapes "^4.2.1" + supports-hyperlinks "^2.0.0" + terser-webpack-plugin@^1.1.0, terser-webpack-plugin@^1.2.4, terser-webpack-plugin@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz#5ecaf2dbdc5fb99745fd06791f46fc9ddb1c9a7c" @@ -16934,6 +17537,15 @@ test-exclude@^5.2.3: read-pkg-up "^4.0.0" require-main-filename "^2.0.0" +test-exclude@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" + integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== + dependencies: + "@istanbuljs/schema" "^0.1.2" + glob "^7.1.4" + minimatch "^3.0.4" + text-extensions@^1.0.0: version "1.9.0" resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" @@ -16944,10 +17556,10 @@ text-table@0.2.0, text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= -throat@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" - integrity sha1-iQN8vJLFarGJJua6TLsgDhVnKmo= +throat@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" + integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== throttle-debounce@^2.1.0: version "2.1.0" @@ -17127,7 +17739,7 @@ toposort@^1.0.0: resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029" integrity sha1-LmhELZ9k7HILjMieZEOsbKqVACk= -tough-cookie@^2.3.3, tough-cookie@^2.3.4: +tough-cookie@^2.3.3, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== @@ -17135,6 +17747,15 @@ tough-cookie@^2.3.3, tough-cookie@^2.3.4: psl "^1.1.28" punycode "^2.1.1" +tough-cookie@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2" + integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg== + dependencies: + ip-regex "^2.1.0" + psl "^1.1.28" + punycode "^2.1.1" + tough-cookie@~2.4.3: version "2.4.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" @@ -17150,6 +17771,13 @@ tr46@^1.0.1: dependencies: punycode "^2.1.0" +tr46@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479" + integrity sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg== + dependencies: + punycode "^2.1.1" + traverse-chain@~0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" @@ -17241,6 +17869,11 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" +type-detect@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + type-fest@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.3.1.tgz#63d00d204e059474fe5e1b7c011112bbd1dc29e1" @@ -17269,6 +17902,13 @@ typed-styles@^0.0.7: resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9" integrity sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q== +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -17675,6 +18315,15 @@ v8-compile-cache@^2.0.2: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== +v8-to-istanbul@^4.0.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.1.3.tgz#22fe35709a64955f49a08a7c7c959f6520ad6f20" + integrity sha512-sAjOC+Kki6aJVbUOXJbcR0MnbfjvBzwKZazEJymA2IX49uoOdEdk+4fBq5cXgYgiyKtAyrrJNtBZdOeDIF+Fng== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.1" + convert-source-map "^1.6.0" + source-map "^0.7.3" + validate-npm-package-license@^3.0.1, validate-npm-package-license@^3.0.3: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -17757,6 +18406,29 @@ w3c-hr-time@^1.0.1: dependencies: browser-process-hrtime "^0.1.2" +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" + integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== + dependencies: + domexception "^1.0.1" + webidl-conversions "^4.0.2" + xml-name-validator "^3.0.0" + +w3c-xmlserializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" + integrity sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA== + dependencies: + xml-name-validator "^3.0.0" + wait-for-expect@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.2.0.tgz#fdab6a26e87d2039101db88bff3d8158e5c3e13f" @@ -17823,6 +18495,16 @@ webidl-conversions@^4.0.2: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== +webidl-conversions@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" + integrity sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA== + +webidl-conversions@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.0.0.tgz#ff41d921371f43e772dba311b146ab6c0ef0ead4" + integrity sha512-jTZAeJnc6D+yAOjygbJOs33kVQIk5H6fj9SFDOhIKjsf9HiAzL/c+tAJsc8ASWafvhNkH+wJZms47pmajkhatA== + webpack-assets-manifest@~3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/webpack-assets-manifest/-/webpack-assets-manifest-3.1.1.tgz#39bbc3bf2ee57fcd8ba07cda51c9ba4a3c6ae1de" @@ -17954,7 +18636,36 @@ webpack-sources@^1.0.0, webpack-sources@^1.1.0, webpack-sources@^1.3.0, webpack- source-list-map "^2.0.0" source-map "~0.6.1" -webpack@^4.29.5, webpack@^4.33.0, webpack@^4.38.0: +webpack@^4.29.5: + version "4.42.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.42.0.tgz#b901635dd6179391d90740a63c93f76f39883eb8" + integrity sha512-EzJRHvwQyBiYrYqhyjW9AqM90dE4+s1/XtCfn7uWg6cS72zH+2VPFAlsnW0+W0cDi0XRjNKUMoJtpSi50+Ph6w== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/wasm-edit" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + acorn "^6.2.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" + enhanced-resolve "^4.1.0" + eslint-scope "^4.0.3" + json-parse-better-errors "^1.0.2" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.1" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" + schema-utils "^1.0.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.3" + watchpack "^1.6.0" + webpack-sources "^1.4.1" + +webpack@^4.33.0, webpack@^4.38.0: version "4.41.5" resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.5.tgz#3210f1886bce5310e62bb97204d18c263341b77c" integrity sha512-wp0Co4vpyumnp3KlkmpM5LWuzvZYayDwM2n17EHFr4qxBBbRokC7DJawPJC7TfSFZ9HZ6GsdH40EBj4UV0nmpw== @@ -18026,7 +18737,7 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== -whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: +whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== @@ -18043,20 +18754,11 @@ whatwg-fetch@>=0.10.0: resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== -whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0: +whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-url@^6.4.1: - version "6.5.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" - integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - whatwg-url@^7.0.0: version "7.1.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" @@ -18066,6 +18768,15 @@ whatwg-url@^7.0.0: tr46 "^1.0.1" webidl-conversions "^4.0.2" +whatwg-url@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.0.0.tgz#37f256cb746398e19b107bd6ef820b4ae2d15871" + integrity sha512-41ou2Dugpij8/LPO5Pq64K5q++MnRCBpEHvQr26/mArEKTkCV5aoXIqyhuYtE0pkqScXwhf2JP57rkRTYM29lQ== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^2.0.0" + webidl-conversions "^5.0.0" + which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" @@ -18076,13 +18787,20 @@ which-pm-runs@^1.0.0: resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= -which@1, which@^1.2.10, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: +which@1, which@^1.2.10, which@^1.2.14, which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" +which@^2.0.1, which@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" @@ -18280,20 +18998,20 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" +wrap-ansi@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write-file-atomic@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.1.tgz#d0b05463c188ae804396fd5ab2a370062af87529" - integrity sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg== - dependencies: - graceful-fs "^4.1.11" - imurmurhash "^0.1.4" - signal-exit "^3.0.2" - write-file-atomic@^2.0.0, write-file-atomic@^2.3.0: version "2.4.3" resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.3.tgz#1fd2e9ae1df3e75b8d8c367443c692d4ca81f481" @@ -18303,6 +19021,16 @@ write-file-atomic@^2.0.0, write-file-atomic@^2.3.0: imurmurhash "^0.1.4" signal-exit "^3.0.2" +write-file-atomic@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" + integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== + dependencies: + imurmurhash "^0.1.4" + is-typedarray "^1.0.0" + signal-exit "^3.0.2" + typedarray-to-buffer "^3.1.5" + write-file-webpack-plugin@~4.5.0: version "4.5.1" resolved "https://registry.yarnpkg.com/write-file-webpack-plugin/-/write-file-webpack-plugin-4.5.1.tgz#aeeb68889194da5ec8a864667d46da9e00ee92d5" @@ -18357,6 +19085,11 @@ ws@^6.0.0: dependencies: async-limiter "~1.0.0" +ws@^7.0.0, ws@^7.2.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.3.tgz#a5411e1fb04d5ed0efee76d26d5c46d830c39b46" + integrity sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ== + xdg-basedir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" @@ -18379,6 +19112,11 @@ xmlbuilder@~4.2.0: dependencies: lodash "^4.0.0" +xmlchars@^2.1.1, xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + xregexp@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020" @@ -18441,7 +19179,7 @@ yargs-parser@^11.1.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^13.1.0, yargs-parser@^13.1.1: +yargs-parser@^13.1.0: version "13.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0" integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== @@ -18449,6 +19187,14 @@ yargs-parser@^13.1.0, yargs-parser@^13.1.1: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@^18.1.1: + version "18.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.2.tgz#2f482bea2136dbde0861683abea7756d30b504f1" + integrity sha512-hlIPNR3IzC1YuL1c2UwwDKpXlNFBqD1Fswwh1khz5+d8Cq/8yc/Mn0i+rQXduu8hcrFKvO7Eryk+09NecTQAAQ== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" @@ -18542,21 +19288,22 @@ yargs@^11.0.0: y18n "^3.2.1" yargs-parser "^9.0.2" -yargs@^13.3.0: - version "13.3.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83" - integrity sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== +yargs@^15.3.1: + version "15.3.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.1.tgz#9505b472763963e54afe60148ad27a330818e98b" + integrity sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== dependencies: - cliui "^5.0.0" - find-up "^3.0.0" + cliui "^6.0.0" + decamelize "^1.2.0" + find-up "^4.1.0" get-caller-file "^2.0.1" require-directory "^2.1.1" require-main-filename "^2.0.0" set-blocking "^2.0.0" - string-width "^3.0.0" + string-width "^4.2.0" which-module "^2.0.0" y18n "^4.0.0" - yargs-parser "^13.1.1" + yargs-parser "^18.1.1" yargs@^8.0.2: version "8.0.2" From d34cc2b68fa4c7c898f52314f46b56534fe7e5c8 Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Thu, 23 Apr 2020 12:52:06 -0500 Subject: [PATCH 06/21] feat(extensibility): buildpack test helpers for extension devs --- jest.config.js | 9 +- .../__tests__/peregrine-targets.spec.js | 82 +++++++++ .../lib/TestHelpers/evaluateScripts.js | 105 ++++++++++++ .../pwa-buildpack/lib/TestHelpers/index.js | 7 + .../lib/TestHelpers/testFullBuild.js | 159 ++++++++++++++++++ .../TestHelpers/testTargets/MockedBuildBus.js | 111 ++++++++++++ .../lib/TestHelpers/testTargets/index.js | 6 + .../TestHelpers/testTargets/testTargets.js | 73 ++++++++ .../lib/TestHelpers/testWebpackCompiler.js | 112 ++++++++++++ .../MockedWebpackLoaderContext.js | 52 ++++++ .../TestHelpers/testWebpackLoader/index.js | 6 + .../testWebpackLoader/testWebpackLoader.js | 35 ++++ .../lib/Utilities/loadEnvironment.js | 37 +++- .../__tests__/__fixtures__/add-badly.es6.js | 2 + .../__tests__/__fixtures__/math.js | 19 +++ .../__fixtures__/print-as-binary.es6.js | 3 + .../__fixtures__/speak-binary.es6.js | 13 ++ .../__fixtures__/square-to-cube.es6.js | 3 + .../__tests__/wrap-esm-loader.spec.js | 136 +++++++++++++++ .../loaders/identity-obj-proxy-loader.js | 14 ++ packages/pwa-buildpack/lib/index.js | 6 + .../pwa-buildpack/lib/util/deep-defaults.js | 24 +++ packages/pwa-buildpack/package.json | 6 +- packages/venia-concept/webpack.config.js | 7 + .../venia-ui-targets.spec.js.snap | 11 ++ .../__tests__/venia-ui-targets.spec.js | 94 +++++++++++ yarn.lock | 59 ++++++- 27 files changed, 1177 insertions(+), 14 deletions(-) create mode 100644 packages/peregrine/lib/targets/__tests__/peregrine-targets.spec.js create mode 100644 packages/pwa-buildpack/lib/TestHelpers/evaluateScripts.js create mode 100644 packages/pwa-buildpack/lib/TestHelpers/index.js create mode 100644 packages/pwa-buildpack/lib/TestHelpers/testFullBuild.js create mode 100644 packages/pwa-buildpack/lib/TestHelpers/testTargets/MockedBuildBus.js create mode 100644 packages/pwa-buildpack/lib/TestHelpers/testTargets/index.js create mode 100644 packages/pwa-buildpack/lib/TestHelpers/testTargets/testTargets.js create mode 100644 packages/pwa-buildpack/lib/TestHelpers/testWebpackCompiler.js create mode 100644 packages/pwa-buildpack/lib/TestHelpers/testWebpackLoader/MockedWebpackLoaderContext.js create mode 100644 packages/pwa-buildpack/lib/TestHelpers/testWebpackLoader/index.js create mode 100644 packages/pwa-buildpack/lib/TestHelpers/testWebpackLoader/testWebpackLoader.js create mode 100644 packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/add-badly.es6.js create mode 100644 packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/math.js create mode 100644 packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/print-as-binary.es6.js create mode 100644 packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/speak-binary.es6.js create mode 100644 packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/square-to-cube.es6.js create mode 100644 packages/pwa-buildpack/lib/WebpackTools/__tests__/wrap-esm-loader.spec.js create mode 100644 packages/pwa-buildpack/lib/WebpackTools/loaders/identity-obj-proxy-loader.js create mode 100644 packages/pwa-buildpack/lib/util/deep-defaults.js create mode 100644 packages/venia-ui/lib/targets/__tests__/__snapshots__/venia-ui-targets.spec.js.snap create mode 100644 packages/venia-ui/lib/targets/__tests__/venia-ui-targets.spec.js diff --git a/jest.config.js b/jest.config.js index cec6c87f38..ec5b392884 100644 --- a/jest.config.js +++ b/jest.config.js @@ -42,7 +42,6 @@ const testGlob = '/**/{src,lib,_buildpack}/**/__tests__/*.(test|spec).js'; // Reusable test configuration for Venia UI and storefront packages. const testReactComponents = inPackage => ({ // Expose jsdom to tests. - browser: true, moduleNameMapper: { // Mock binary files to avoid excess RAM usage. '\\.(jpg|jpeg|png)$': @@ -52,7 +51,9 @@ const testReactComponents = inPackage => ({ // This mapping forces CSS Modules to return literal identies, // so e.g. `classes.root` is always `"root"`. '\\.css$': 'identity-obj-proxy', - '\\.svg$': 'identity-obj-proxy' + '\\.svg$': 'identity-obj-proxy', + '@magento/venia-drivers': + '/packages/venia-ui/lib/drivers/index.js' }, moduleFileExtensions: ['ee.js', 'ce.js', 'js', 'json', 'jsx', 'node'], // Reproduce the Webpack resolution config that lets Venia import @@ -73,7 +74,7 @@ const testReactComponents = inPackage => ({ // import `.graphql` files into JS. '\\.(gql|graphql)$': 'jest-transform-graphql', // Use the default babel-jest for everything else. - '\\.(js|css)$': 'babel-jest' + '\\.(jsx?|css)$': 'babel-jest' }, // Normally babel-jest ignores node_modules and only transpiles the current // package's source. The below setting forces babel-jest to transpile @@ -256,7 +257,6 @@ const jestConfig = { configureProject('pagebuilder', 'Pagebuilder', testReactComponents), configureProject('peregrine', 'Peregrine', inPackage => ({ // Expose jsdom to tests. - browser: true, setupFiles: [ // Shim DOM properties not supported by jsdom inPackage('scripts/shim.js'), @@ -318,6 +318,7 @@ const jestConfig = { // Not node_modules '!**/node_modules/**', // Not __tests__, __helpers__, or __any_double_underscore_folders__ + '!**/TestHelpers/**', '!**/__[[:alpha:]]*__/**', '!**/.*/__[[:alpha:]]*__/**', // Not this file itself diff --git a/packages/peregrine/lib/targets/__tests__/peregrine-targets.spec.js b/packages/peregrine/lib/targets/__tests__/peregrine-targets.spec.js new file mode 100644 index 0000000000..bf21747928 --- /dev/null +++ b/packages/peregrine/lib/targets/__tests__/peregrine-targets.spec.js @@ -0,0 +1,82 @@ +const { + buildModuleWith, + mockTargetProvider +} = require('@magento/pwa-buildpack'); +const declare = require('../peregrine-declare'); +const intercept = require('../peregrine-intercept'); + +test('declares a sync target talons and intercepts transformModules', () => { + const targets = mockTargetProvider( + '@magento/peregrine', + (_, dep) => + ({ + '@magento/pwa-buildpack': { + specialFeatures: { + tap: jest.fn() + }, + transformModules: { + tap: jest.fn() + } + } + }[dep]) + ); + declare(targets); + expect(targets.own.talons.tap).toBeDefined(); + const hook = jest.fn(); + // no implementation testing in declare phase + targets.own.talons.tap('test', hook); + targets.own.talons.call('woah'); + expect(hook).toHaveBeenCalledWith('woah'); + + intercept(targets); + const buildpackTargets = targets.of('@magento/pwa-buildpack'); + expect(buildpackTargets.transformModules.tap).toHaveBeenCalled(); +}); + +test('enables third parties to wrap talons', async () => { + jest.setTimeout(10000); // sorry, buildModuleWith is slow + const talonIntegratingDep = { + name: 'goose-app', + declare() {}, + intercept(targets) { + targets.of('@magento/peregrine').talons.tap(talons => { + talons.ProductFullDetail.useProductFullDetail.wrapWith( + 'src/usePFDIntercept' + ); + talons.App.useApp.wrapWith('src/useAppIntercept'); + talons.App.useApp.wrapWith('src/swedish'); + }); + } + }; + const built = await buildModuleWith('src/index.js', { + context: __dirname, + dependencies: [ + { + name: '@magento/peregrine', + declare, + intercept + }, + talonIntegratingDep + ], + mockFiles: { + 'src/index.js': ` + import { useApp } from '@magento/peregrine/lib/talons/App/useApp'; + import { useProductFullDetail } from '@magento/peregrine/lib/talons/ProductFullDetail/useProductFullDetail'; + export default useApp() + useProductFullDetail()`, + 'src/usePFDIntercept': `export default function usePFDIntercept(original) { return function usePFD() { return 'BEEP >o'; } };`, + 'src/useAppIntercept': `export default function useAppIntercept(original) { + return function useApp() { + return 'o< HONK'; + }; + } + `, + 'src/swedish': `export default function swedish(impl) { + return function() { + return impl().replace("O", "Ö") + } +}` + } + }); + + expect(built.run()).toBe('o< HÖNKBEEP >o'); +}); diff --git a/packages/pwa-buildpack/lib/TestHelpers/evaluateScripts.js b/packages/pwa-buildpack/lib/TestHelpers/evaluateScripts.js new file mode 100644 index 0000000000..09b2ba5e5c --- /dev/null +++ b/packages/pwa-buildpack/lib/TestHelpers/evaluateScripts.js @@ -0,0 +1,105 @@ +/** + * Helper functions for running generated code in tests. + * @module Buildpack/TestHelpers + */ +const path = require('path'); +const babel = require('@babel/core'); +const vm = require('vm'); +const { JSDOM } = require('jsdom'); + +/** + * Consolidate all types of export in a sandbox into the returned export object. + * @private + */ +const getExports = sandbox => { + if (sandbox.exports.default) { + const defaultType = typeof sandbox.exports.default; + if (defaultType === 'function' || defaultType === 'object') { + return Object.assign(sandbox.exports.default, sandbox.exports); + } else { + return sandbox.exports.default; + } + } else { + return sandbox.exports; + } +}; + +/** + * Evaluate JavaScript source code in a test environment. CommonJS exports will + * be returned. Imperative code in the module will run in the context of an + * isolated sandbox, using the Node {@link https://nodejs.org/docs/latest-v10.x/api/vm.html#vm_vm_executing_javascript vm module}. + * + * `evalScript` can only execute JS that would run natively in Node. + * Use {@linkcode evalEsModule} to run ESNext modules, and {@linkcode evalInDom} + * to run code in a browser-like environment. + * @param {string} source - Code to be evaluated. + * @param {function} require - The `require()` function the code will use to request other modules. + * @returns The `exports` from the source code sandbox. + */ +function evalScript(source, requireFn) { + const sandbox = { require: requireFn, exports: {} }; + vm.runInNewContext(source, sandbox); + return getExports(sandbox); +} + +/** + * Evaluate ECMAScript modules in a test environment. Exported values will be + * returned. Imperative code (boo!) in the module will run in the context of an + * isolated sandbox. + * + * Code is transpiled on the fly using Babel. By default, Babel will use the + * configuration file accessible from the working directory. Dynamic imports + * are not supported. + * + * @see evalScript + * @param {string} source - Module code to be evaluated. + * @param {Function} require - The `require()` function the transpiled code + * will use to `import` other modules. + * @param {Object} [babelOptions={}] Additional options to be passed to Babel. + * @returns Exported module. + */ +function evalEsModule(source, require, babelOptions = {}) { + if (!babelOptions.filename) { + babelOptions.filename = path.resolve( + __dirname, + 'evaluated-es6-module.js' // For debugging + ); + } + const out = evalScript( + babel.transformSync(source, babelOptions).code, + require + ); + return out.exports || out; +} + +/** + * Evaluate JavaScript source code in the context of a JSDOM simulated browser + * environment. Imperative code in the module will run with access to the + * simulated `window` and `document` objects. + * + * The code does not run in a headless browser, but in Node natively. + * + * @see evalScript + * @param {string} source - Module code to be evaluated. + * @param {Function} require - The `require()` function the transpiled code + * will use to `import` other modules. + * @param {Object} [jsDomOptions] Additional options to be passed to `JSDOM`. + * @returns The `exports` from the DOM sandbox. + */ +function evalInDom(content, require, jsDomOptions) { + const options = Object.assign( + { + url: 'https://localhost', + runScripts: 'outside-only' + }, + jsDomOptions + ); + const dom = new JSDOM('', options); + const sandbox = dom.getInternalVMContext(); + sandbox.require = require; + sandbox.exports = {}; + vm.runInContext(content, sandbox); + return getExports(sandbox); +} + +module.exports = { evalEsModule, evalInDom, evalScript }; diff --git a/packages/pwa-buildpack/lib/TestHelpers/index.js b/packages/pwa-buildpack/lib/TestHelpers/index.js new file mode 100644 index 0000000000..1f8cb8d648 --- /dev/null +++ b/packages/pwa-buildpack/lib/TestHelpers/index.js @@ -0,0 +1,7 @@ +module.exports = { + ...require('./testWebpackCompiler'), + ...require('./evaluateScripts'), + ...require('./testWebpackLoader'), + ...require('./testFullBuild'), + ...require('./testTargets/testTargets') +}; diff --git a/packages/pwa-buildpack/lib/TestHelpers/testFullBuild.js b/packages/pwa-buildpack/lib/TestHelpers/testFullBuild.js new file mode 100644 index 0000000000..7341d751c9 --- /dev/null +++ b/packages/pwa-buildpack/lib/TestHelpers/testFullBuild.js @@ -0,0 +1,159 @@ +/** + * Helper functions for running a full PWA build in integration tests to combine + * extension targets, build configurations, and frontend code. + * @module Buildpack/TestHelpers + */ +const path = require('path'); +const nodeExternals = require('webpack-node-externals'); +const { mockBuildBus } = require('./testTargets/testTargets'); +const { makeCompiler, compileToPromise } = require('./testWebpackCompiler'); +const MagentoResolver = require('../WebpackTools/MagentoResolver'); +const ModuleTransformConfig = require('../WebpackTools/ModuleTransformConfig'); +const { evalInDom } = require('./evaluateScripts'); +const { + getModuleRules, + getSpecialFlags, + getResolveLoader +} = require('../WebpackTools/configureWebpack'); +const BuildBusPlugin = require('../WebpackTools/plugins/BuildBusPlugin'); + +/** + * A helper function for compiling source code to test PWA Studio extension + * targets. Uses a full Webpack build and executes all extension intercepts. + * + * The BuildBus will run only with the list of dependencies you specify in your + * test. + * + * @example Test your target which intercepts a Peregrine target. + * const { useApp } = buildModuleWith('../lib/talons/App/useApp', { + * context: __dirname, // where to resolve modules from + * dependencies: [ + * '@magento/peregrine', + * { + * name: 'my-extension', + * declare: require('../declare'), + * intercept: require('../intercept'), + * } + * }) + * expect(useApp()).toHaveProperty('my-additions'); + * + * + * + * @param {string} moduleUnderTest - Path to the module to be evaluated. + * @param {Object} options - Dependencies and Webpack options to use. + * @param {string} options.context - Project root of the simulated build, from + * which on-disk dependencies will be resolved. + * @param {Array.(string|MockDependency) options.dependencies + * @returns + */ +async function buildModuleWith( + moduleUnderTest, + { context, dependencies, mockFiles = {}, alias, ...otherWebpackSettings } +) { + const bus = mockBuildBus({ + context, + dependencies + }); + bus.init(); + + const paths = { + root: context, + src: path.resolve(context, 'src') + }; + + const resolver = new MagentoResolver({ paths, alias }); + + const transforms = new ModuleTransformConfig(resolver); + bus.getTargetsOf('@magento/pwa-buildpack').transformModules.call(x => + transforms.add(x) + ); + const transformRequests = await transforms.toLoaderOptions(); + + let entry; + // Most of the time, the entry module is a real file. + // But it could also be supplied by mockFiles, in which case the resolver + // would not be able to resolve it. + if (mockFiles[moduleUnderTest]) { + entry = moduleUnderTest; + } else { + entry = await resolver.resolve(moduleUnderTest); + } + + const hasFlag = await getSpecialFlags( + otherWebpackSettings.special || {}, + bus, + resolver + ); + + const helper = { + bus, + hasFlag, + babelRootMode: 'upward', + mode: 'test', + paths, + resolver, + transformRequests + }; + + // Leave most modules external, so Webpack builds faster. + const externals = [ + // Exclude any require() of a node_module. + nodeExternals({ + // Except... + whitelist: bus + // the dependencies under test, which Webpack must compile for + // the targets to work! + .getMockDependencyNames() + // A string would require an exact match: + // '@magento/peregrine' would not allow '@magento/peregrine/lib' + // so we make a regex that tests for the start of the string + // instead + .map(name => new RegExp(`^${name}`)) + }) + ]; + + const webpackConfig = { + context, + entry, + output: { + filename: 'main.js', + globalObject: 'exports', + libraryTarget: 'commonjs' + }, + module: { + rules: [ + await getModuleRules.graphql(helper), + await getModuleRules.js(helper), + { + test: /\.css$/, + use: ['identity-obj-proxy-loader'] + } + ] + }, + resolve: resolver.config, + resolveLoader: getResolveLoader(), + externals, + ...otherWebpackSettings + }; + + webpackConfig.plugins = webpackConfig.plugins || []; + + if ( + !webpackConfig.plugins.some(plugin => plugin instanceof BuildBusPlugin) + ) { + webpackConfig.plugins.unshift(new BuildBusPlugin(bus)); + } + + const compiler = makeCompiler(webpackConfig, mockFiles); + const results = await compileToPromise(compiler); + const bundle = results.files[webpackConfig.output.filename]; + return { + ...results, + bundle, + run() { + return evalInDom(bundle, require); + } + }; +} + +module.exports = { buildModuleWith }; diff --git a/packages/pwa-buildpack/lib/TestHelpers/testTargets/MockedBuildBus.js b/packages/pwa-buildpack/lib/TestHelpers/testTargets/MockedBuildBus.js new file mode 100644 index 0000000000..b2f68c083a --- /dev/null +++ b/packages/pwa-buildpack/lib/TestHelpers/testTargets/MockedBuildBus.js @@ -0,0 +1,111 @@ +/** + * A mock BuildBus for testing target integrations. Instead of using the local + * `package.json` to detect and order the pertaining dependencies, this takes + * a set of pre-ordered dependencies that can include "virtual dependency" + * objects. + * + * @module Buildpack/TestHelpers + * + */ +const BuildBus = require('../../BuildBus/BuildBus'); +const { ExplicitDependency, resolver } = require('pertain'); +class MockedBuildBus extends BuildBus { + /** @public */ + static clear() { + throw new Error( + 'MockedBuildBus.clear() not supported. More details at https://twitter.com/JamesZetlen/status/1244683087839137792' + ); + } + /** @public */ + static clearAll() { + throw new Error( + 'MockedBuildBus.clearAll() not supported. More details at https://twitter.com/JamesZetlen/status/1244683087839137792' + ); + } + /** + * @public + * @returns {BuildBus} + */ + static for() { + throw new Error( + `MockedBuildBus.for() not supported. To create a MockedBuildBus, use mockBuildBus({ context, dependencies }); + + More details at https://twitter.com/JamesZetlen/status/1244680322442280960` + ); + } + constructor(invoker, context, dependencies) { + super(invoker, context); + this._resolve = resolver(context); + this._mockDependencies = dependencies; + } + _getEnvOverrides() { + this._depsAdditional = []; + } + _getPertaining(phase) { + /** + * Always declare buildpack's base targets. + * If the test declares Buildpack explicitly, don't declare it twice, + * of course. + */ + let buildpackDeclared = false; + const pertaining = []; + const addPertaining = dep => { + if (dep.name === '@magento/pwa-buildpack') { + buildpackDeclared = true; + } + pertaining.push(dep); + }; + + this._mockDependencies.forEach((dep, i) => { + if (typeof dep === 'string') { + const modulePath = this._resolve(dep); + if (!modulePath) { + throw new Error( + `Dependency at index [${i}] is a string "${dep}", indicating a real node_module, but it could not be resolved as a node_module.` + ); + } + const dependency = new ExplicitDependency(modulePath); + const pertainingScript = dependency.pertains( + this._phaseToSubject(phase) + ); + if (pertainingScript) { + addPertaining({ + name: dep, + [phase]: require(pertainingScript) + }); + } + } else if ( + typeof dep === 'object' && + typeof dep.name === 'string' + ) { + if (typeof dep[phase] === 'function') { + addPertaining(dep); + } + } else { + throw new Error( + `${dep} is not a valid dependency. Dependencies argued to MockedBuildBus must be either the names of resolvable modules, or virtual dependencies (objects with a "name" string and "declare" and/or "intercept" functions).` + ); + } + }); + + /** Ensure buildpack. */ + if (!buildpackDeclared) { + pertaining.unshift({ + name: '@magento/pwa-buildpack', + declare: require('../../BuildBus/declare-base'), + intercept: require('../../BuildBus/intercept-base') + }); + } + return pertaining; + } + /** + * + * Get the names of the dependencies that were explicitly argued. + * @returns {string[]} + */ + getMockDependencyNames() { + return this._mockDependencies.map(dep => dep.name || dep); + } +} + +module.exports = MockedBuildBus; diff --git a/packages/pwa-buildpack/lib/TestHelpers/testTargets/index.js b/packages/pwa-buildpack/lib/TestHelpers/testTargets/index.js new file mode 100644 index 0000000000..d263b64eda --- /dev/null +++ b/packages/pwa-buildpack/lib/TestHelpers/testTargets/index.js @@ -0,0 +1,6 @@ +/** @module Buildpack/TestHelpers */ +const MockedBuildBus = require('./MockedBuildBus'); +module.exports = { + ...require('./testTargets'), + MockedBuildBus +}; diff --git a/packages/pwa-buildpack/lib/TestHelpers/testTargets/testTargets.js b/packages/pwa-buildpack/lib/TestHelpers/testTargets/testTargets.js new file mode 100644 index 0000000000..f5ee3e9396 --- /dev/null +++ b/packages/pwa-buildpack/lib/TestHelpers/testTargets/testTargets.js @@ -0,0 +1,73 @@ +/** + * Helper functions for running extension targets in tests. + * @module Buildpack/TestHelpers + */ +const TargetProvider = require('../../BuildBus/TargetProvider'); +const MockedBuildBus = require('./MockedBuildBus'); + +const unimplementedTargetFac = (requestor, requestedName) => { + throw new Error(`${requestor.constructor.name} received request from "${ + requestor.name + }" for external targets "${requestedName}", but no function was supplied for getting external targets. + + More details at https://twitter.com/JamesZetlen/status/1244680319267147783`); +}; + +/** + * An object representing a dependency with targets that participates in a + * build. + * @typedef {Object} MockDependency + * @property {string} name - Module name of the dependency. + * @property {Function} declare - Declare function which will receive the simulated target provider. + * @property {Function} intercept - Intercept function which will receive the simulated target provider. + */ + +/** + * Create a {@link TargetProvider} not bound to a {@link BuildBus}, for testing + * declare and intercept functions in isolation. + * + * @param {string} name + * @param {Function} [getExternalTargets] Function that returns any + * external TargetProviders. To test with an intercept function, which almost + * certainly will use external TargetProviders, you must supply a function + * here that returns them. + * @param {Function} [loggingParent=() => {}] Will be called with detailed logging information. + * @returns {TargetProvider} + */ +function mockTargetProvider( + name, + getExternalTargets = unimplementedTargetFac, + loggingParent = () => {} +) { + return new TargetProvider(loggingParent, name, getExternalTargets); +} + +/** + * + * Create a mock BuildBus for testing target integrations. Instead of using the + * local `package.json` to detect and order the pertaining dependencies, this + * takes a set of pre-ordered dependencies that can include "virtual + * dependency" objects. + * + * You may supply string module names to use the on-disk dependencies, or `{ + * name, declare, intercept }` objects to act as "virtual dependencies". + * + * The modules will be run in the order supplied; therefore, if you're testing + * your own targets, they should come last in the list. + * + * @param {Object} setup + * @param {string} setup.context - Project root, the directory from which + * MockedBuildBus will resolve any on-disk dependencies that are not mocked. + * @param {Array.(string|MockDependency)} setup.dependencies - Dependencies to use. P + * @returns {MockedBuildBus} + */ + +const INVOKE_FLAG = Symbol.for('FORCE_BUILDBUS_CREATE_FACTORY'); +function mockBuildBus({ context, dependencies }) { + return new MockedBuildBus(INVOKE_FLAG, context, dependencies); +} + +module.exports = { + mockBuildBus, + mockTargetProvider +}; diff --git a/packages/pwa-buildpack/lib/TestHelpers/testWebpackCompiler.js b/packages/pwa-buildpack/lib/TestHelpers/testWebpackCompiler.js new file mode 100644 index 0000000000..b943bcb5e3 --- /dev/null +++ b/packages/pwa-buildpack/lib/TestHelpers/testWebpackCompiler.js @@ -0,0 +1,112 @@ +/** + * Helper functions for running a Webpack compiler in tests. + * @module Buildpack/TestHelpers + */ + +const path = require('path'); +const webpack = require('webpack'); +const deepDefaults = require('../util/deep-defaults'); + +const fs = require('fs'); +const { Union } = require('unionfs'); +const { Volume } = require('memfs'); + +/** + * Create a harnessed Webpack compiler for testing. This compiler has some + * tweaks for testing your components, targets, or plugins in a unit test + * framework. It can use a supplied map of virtual files instead of an on-disk fixture, and it records logs and output files in a friendly format on the `.testResults` property. + * @param {Object} config - Webpack configuration object. + * @param {MockFiles} mockFiles - An object of file paths to source code strings, to populate the virtual file system (and lay over the real one) + * @returns {Object} compiler - Webpack compiler object. + * @returns {Object} compiler.testResults - Test metadata. + * + */ +const makeCompiler = (config, mockFiles = {}) => { + const inputFileSystem = new Union(); + inputFileSystem.use(fs).use(Volume.fromJSON(mockFiles, config.context)); + + const defaults = { + mode: 'none', + optimization: { + minimize: false + }, + output: { + path: config.context + } + }; + const finalOptions = deepDefaults(config, defaults); + + const compiler = webpack(finalOptions); + + compiler.inputFileSystem = inputFileSystem; + compiler.resolvers.normal.fileSystem = inputFileSystem; + compiler.resolvers.context.fileSystem = inputFileSystem; + + const files = {}; + const logs = { + mkdirp: [], + writeFile: [] + }; + compiler.outputFileSystem = { + join() { + return [].join.call(arguments, '/').replace(/\/+/g, '/'); + }, + mkdirp(path, callback) { + logs.mkdirp.push(path); + callback(); + }, + writeFile(absPath, content, callback) { + const name = path.relative(finalOptions.context, absPath); + logs.writeFile.push(name, content); + files[name] = content.toString('utf-8'); + callback(); + } + }; + compiler.hooks.compilation.tap( + 'CompilerTest', + compilation => (compilation.bail = true) + ); + + compiler.testResults = { + files, + logs + }; + return compiler; +}; + +/** + * Invoke compiler.run() and return a promise for the Webpack output. + * @async + * @param {Object} compiler - The Webpack compiler instance. + * @returns {Object} testResults - Webpack output and results. + * @returns {Object} testResults.stats - Webpack stats object. + * @returns {Object} testResults.files - An object of output file text. + * @returns {Object} testResults.logs - An array of log messages. + * + */ +const compileToPromise = compiler => + new Promise((res, rej) => { + compiler.run((err, stats) => { + if (err) { + return rej(err); + } + stats = stats.toJson({ + modules: true, + reasons: true + }); + if (stats.errors.length > 0) { + return rej(stats.errors[0]); + } + if (!compiler.testResults) { + rej( + new Error( + 'Compiler was not created with makeCompiler, cannot use compileToPromise on it' + ) + ); + } else { + res({ ...compiler.testResults, stats }); + } + }); + }); + +module.exports = { makeCompiler, compileToPromise }; diff --git a/packages/pwa-buildpack/lib/TestHelpers/testWebpackLoader/MockedWebpackLoaderContext.js b/packages/pwa-buildpack/lib/TestHelpers/testWebpackLoader/MockedWebpackLoaderContext.js new file mode 100644 index 0000000000..2ebb2861c3 --- /dev/null +++ b/packages/pwa-buildpack/lib/TestHelpers/testWebpackLoader/MockedWebpackLoaderContext.js @@ -0,0 +1,52 @@ +/** + * @module Buildpack/TestHelpers + */ + +/** + * A mock Webpack loader context for testing target integrations. + * + */ + +class MockWebpackLoaderContext { + /** + * Creates a loader context and populates. + * @param {Function} callback - Called if the loader runs `this.callback(err, output); + * @param {Object} contextValues - Object that will be copied on to this loader context + * @constructs MockWebpackLoaderContext + */ + constructor(callback, contextValues) { + this.mustReturnSync = true; + this._callback = callback; + this._calls = {}; + Object.assign(this, contextValues); + } + _saveCall(name, args) { + this._calls[name] = this._calls[name] || []; + this._calls[name].push(args); + } + async() { + this.mustReturnSync = false; + return (...args) => this.callback(...args); + } + addDependency(...args) { + this._saveCall('addDependency', args); + } + callback(err, output) { + this.mustReturnSync = false; + return this._callback(err, output); + } + emitWarning(...args) { + this._saveCall('emitWarning', args); + } + emitError(...args) { + this._saveCall('emitError', args); + } + getCalls(name) { + return this._calls[name] || []; + } + resetCalls() { + this._calls = {}; + } +} + +module.exports = MockWebpackLoaderContext; diff --git a/packages/pwa-buildpack/lib/TestHelpers/testWebpackLoader/index.js b/packages/pwa-buildpack/lib/TestHelpers/testWebpackLoader/index.js new file mode 100644 index 0000000000..a893ec5d63 --- /dev/null +++ b/packages/pwa-buildpack/lib/TestHelpers/testWebpackLoader/index.js @@ -0,0 +1,6 @@ +/** @module Buildpack/TestHelpers */ +const MockedWebpackLoaderContext = require('./MockedWebpackLoaderContext'); +module.exports = { + ...require('./testWebpackLoader'), + MockedWebpackLoader: MockedWebpackLoaderContext +}; diff --git a/packages/pwa-buildpack/lib/TestHelpers/testWebpackLoader/testWebpackLoader.js b/packages/pwa-buildpack/lib/TestHelpers/testWebpackLoader/testWebpackLoader.js new file mode 100644 index 0000000000..9bd9fc0ff6 --- /dev/null +++ b/packages/pwa-buildpack/lib/TestHelpers/testWebpackLoader/testWebpackLoader.js @@ -0,0 +1,35 @@ +/** + * @module Buildpack/TestHelpers + */ +const MockWebpackLoaderContext = require('./MockedWebpackLoaderContext'); + +/** + * Test a Webpack loader by simulating Webpack calling it with source code. + * + * @async + * @param {Function} loader - The loader function to test. + * @param {string} content - Source code to be transformed and/or analyzed. + * @param {Object} contextValues - Values to use to populate the Webpack + * `loaderContext`, the `this` object available in loaders. + * @returns Output of the loader. + */ +async function runLoader(loader, content, contextValues) { + return new Promise((res, rej) => { + const callback = (err, output) => { + if (err) { + rej(err); + } else { + res({ context, output }); + } + }; + + const context = new MockWebpackLoaderContext(callback, contextValues); + + const output = loader.call(context, content); + if (context.mustReturnSync) { + res({ context, output }); + } + }); +} + +module.exports = { runLoader }; diff --git a/packages/pwa-buildpack/lib/Utilities/loadEnvironment.js b/packages/pwa-buildpack/lib/Utilities/loadEnvironment.js index 8de5083e12..9951aecd02 100644 --- a/packages/pwa-buildpack/lib/Utilities/loadEnvironment.js +++ b/packages/pwa-buildpack/lib/Utilities/loadEnvironment.js @@ -1,3 +1,7 @@ +/** + * @module Buildpack/Utilities + */ + const debug = require('../util/debug').makeFileLogger(__filename); const { inspect } = require('util'); const path = require('path'); @@ -55,11 +59,16 @@ function throwReport({ errors }) { /** * Wrapper around the camelspace API with convenience methods for making custom * objects out of subsets of configuration values. + * + * @class Buildpack/Utilities~ProjectConfiguration */ -class Configuration { +class ProjectConfiguration { constructor(env, envFilePresent, definitions) { + /** @private */ this.definitions = definitions; + /** Original environment object provided. */ this.env = Object.assign({}, env); + /** @property {boolean} envFilePresent A .env file was detected and used */ this.envFilePresent = envFilePresent; this.isProd = env.isProd; this.isProduction = env.isProduction; @@ -67,9 +76,20 @@ class Configuration { this.isDevelopment = env.isDevelopment; this.isTest = env.isTest; } + /** + * @param {string} sectionName + * @returns camelspaced map of all variables starting with `sectionName` + */ section(sectionName) { return camelspace(sectionName).fromEnv(this.env); } + /** + * + * Convenience wrapper for calling {Configuration#section} multiple times + * and putting the results in a deeper map. + * @param {string[]} sectionNames + * @returns A map of camelspaced section maps, with properties for each argued section name + */ sections(...sectionNames) { const sectionObj = {}; for (const sectionName of sectionNames) { @@ -77,11 +97,24 @@ class Configuration { } return sectionObj; } + /** + * + * @returns All environment properties, camelcased + */ all() { return camelspace.fromEnv(this.env); } } +/** + * Load and validate the configuration environment for a project. + * + * @param {string} dirOrEnv Project root + * @param {Object} [customLogger] Pass a console-like object to log elsewhere. + * @param {Object} [providedDefs] Use provided definitions object instead of + * retrieving definitions from the BuildBus. _Internal only._ + * @returns {ProjectConfiguration} + */ function loadEnvironment(dirOrEnv, customLogger, providedDefs) { const logger = customLogger || prettyLogger; let incomingEnv = process.env; @@ -188,7 +221,7 @@ This call to loadEnvironment() will assume that the working directory ${context} '\n' ); } - return new Configuration(loadedEnv, envFilePresent); + return new ProjectConfiguration(loadedEnv, envFilePresent); } catch (error) { if (!error.validationErrors) { throw error; diff --git a/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/add-badly.es6.js b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/add-badly.es6.js new file mode 100644 index 0000000000..134b54786e --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/add-badly.es6.js @@ -0,0 +1,2 @@ +const addBadly = () => (x, y) => x + y - 1; +export default addBadly; diff --git a/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/math.js b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/math.js new file mode 100644 index 0000000000..f139ab60af --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/math.js @@ -0,0 +1,19 @@ +export let add = (x, y) => { + return x + y; +}; + +export function multiply(x, y) { + let result = y; + while (--x) { + result = add(result, y); + } + return result; +} + +const exps = { + square(x) { + return multiply(x, x); + } +}; + +export default exps.square; diff --git a/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/print-as-binary.es6.js b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/print-as-binary.es6.js new file mode 100644 index 0000000000..42ef62b0bb --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/print-as-binary.es6.js @@ -0,0 +1,3 @@ +export default function printAsBinary(op) { + return (...args) => op(...args).toString(2); +} diff --git a/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/speak-binary.es6.js b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/speak-binary.es6.js new file mode 100644 index 0000000000..e502a3135f --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/speak-binary.es6.js @@ -0,0 +1,13 @@ +const onlyBinary = /^[01]+$/; +export default function speakBinary(op) { + return (...args) => { + const digits = op(...args); + if (typeof digits === 'string' && onlyBinary.test(digits)) { + return digits + .split('') + .map(d => (d === '0' ? 'zero' : 'one')) + .join(' '); + } + throw new Error('I only speak binary'); + }; +} diff --git a/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/square-to-cube.es6.js b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/square-to-cube.es6.js new file mode 100644 index 0000000000..29a63659d3 --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/__tests__/__fixtures__/square-to-cube.es6.js @@ -0,0 +1,3 @@ +export default function squareToCube(square) { + return v => square(v) * v; +} diff --git a/packages/pwa-buildpack/lib/WebpackTools/__tests__/wrap-esm-loader.spec.js b/packages/pwa-buildpack/lib/WebpackTools/__tests__/wrap-esm-loader.spec.js new file mode 100644 index 0000000000..77f0a33cee --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/__tests__/wrap-esm-loader.spec.js @@ -0,0 +1,136 @@ +const wrapEsmLoader = require('../loaders/wrap-esm-loader'); +const { evalEsModule, runLoader } = require('../../TestHelpers'); + +const mathSource = require('fs').readFileSync( + require('path').resolve(__dirname, './__fixtures__/math.js'), + 'utf8' +); + +const addBadly = './__fixtures__/add-badly.es6'; +const printAsBinary = './__fixtures__/print-as-binary.es6'; +const speakBinary = './__fixtures__/speak-binary.es6'; +const squareToCube = './__fixtures__/square-to-cube.es6'; + +const requireModule = content => evalEsModule(content, require); + +const runWrapLoader = async (query, source = mathSource) => + runLoader(wrapEsmLoader, source, { + query, + resourcePath: 'foo' + }); + +test('does nothing if no export map was provided for this resource path', async () => { + const { output } = await runWrapLoader([]); + expect(output).toEqual(mathSource); + const square = requireModule(output); + expect(square(4)).toBe(16); +}); + +test('wraps default export', async () => { + const { output, context } = await runWrapLoader([ + { + defaultExport: true, + wrapperModule: squareToCube + } + ]); + const cube = requireModule(output); + expect(cube(4)).toBe(64); + expect(context.getCalls('emitWarning')).toHaveLength(0); + expect(context.getCalls('addDependency')).toMatchObject([[squareToCube]]); +}); + +test('wraps named exports', async () => { + const { output, context } = await runWrapLoader([ + { + exportName: 'multiply', + wrapperModule: printAsBinary + } + ]); + const cube = requireModule(output); + expect(cube(4)).toBe('10000'); + + expect(context.getCalls('emitWarning')).toHaveLength(0); + expect(context.getCalls('addDependency')).toMatchObject([[printAsBinary]]); +}); + +test('wraps exports multiple times', async () => { + const { output, context } = await runWrapLoader([ + { + exportName: 'multiply', + wrapperModule: printAsBinary + }, + { + exportName: 'multiply', + wrapperModule: speakBinary + } + ]); + const square = requireModule(output); + expect(square(4)).toBe('one zero zero zero zero'); + + expect(context.getCalls('emitWarning')).toHaveLength(0); + expect(context.getCalls('addDependency')).toMatchObject([ + [printAsBinary], + [speakBinary] + ]); +}); + +test('wraps multiple exports', async () => { + const { output, context } = await runWrapLoader([ + { exportName: 'add', wrapperModule: addBadly }, + { exportName: 'multiply', wrapperModule: printAsBinary }, + { + defaultExport: true, + wrapperModule: speakBinary + } + ]); + const square = requireModule(output); + expect(square(4)).toBe('one one zero one'); + expect(context.getCalls('emitWarning')).toHaveLength(0); +}); + +test('reuses imports', async () => { + const { output, context } = await runWrapLoader([ + { exportName: 'add', wrapperModule: printAsBinary }, + { exportName: 'multiply', wrapperModule: printAsBinary } + ]); + const { add, multiply } = requireModule(output); + expect(add(2, 3)).toBe('101'); + expect(multiply(2, 3)).toBe('110'); + expect(output.match(/print\-as\-binary/g)).toHaveLength(1); + expect(context.getCalls('emitWarning')).toHaveLength(0); +}); + +test('warns if anything on export map does not apply', async () => { + const { output, context } = await runWrapLoader([ + { + exportName: 'notARealExport', + wrapperModule: squareToCube + } + ]); + const square = requireModule(output); + expect(square(4)).toBe(16); + expect(context.getCalls('emitWarning')).toMatchObject([ + [ + expect.stringContaining( + 'Cannot wrap export "notARealExport" of "foo"' + ) + ] + ]); +}); + +test('warns if default export does not apply', async () => { + const { output, context } = await runWrapLoader( + [ + { defaultExport: true, wrapperModule: squareToCube }, + { exportName: 'add', wrapperModule: addBadly }, + { exportName: 'fortyTwo', wrapperModule: printAsBinary } + ], + 'export const fortyTwo = () => 42' + ); + const answer = requireModule(output).fortyTwo(); + expect(answer).toBe('101010'); + expect(context.getCalls('emitWarning')).toMatchObject([ + [expect.stringContaining('Cannot wrap default export')], + [expect.stringContaining('Cannot wrap export "add"')] + ]); +}); diff --git a/packages/pwa-buildpack/lib/WebpackTools/loaders/identity-obj-proxy-loader.js b/packages/pwa-buildpack/lib/WebpackTools/loaders/identity-obj-proxy-loader.js new file mode 100644 index 0000000000..0e1144aff4 --- /dev/null +++ b/packages/pwa-buildpack/lib/WebpackTools/loaders/identity-obj-proxy-loader.js @@ -0,0 +1,14 @@ +/** + * Replicates the Jest configuration which mocks all CSS modules. Since most + * testing scenarios don't render or paint the components, loading real CSS + * wastes resources. + * + * Use for CSS modules when testing React components built by Webpack, + * specifically in a target-testing scenario. + */ + +function identityObjProxyLoader() { + return 'exports = module.exports = require("identity-obj-proxy");'; +} + +module.exports = identityObjProxyLoader; diff --git a/packages/pwa-buildpack/lib/index.js b/packages/pwa-buildpack/lib/index.js index 484773e8c8..5535fba499 100644 --- a/packages/pwa-buildpack/lib/index.js +++ b/packages/pwa-buildpack/lib/index.js @@ -1,8 +1,14 @@ +/** + * @module Buildpack + */ const Utilities = require('./Utilities'); const WebpackTools = require('./WebpackTools'); +const TestHelpers = require('./TestHelpers'); module.exports = { + ...TestHelpers, ...Utilities, ...WebpackTools, + TestHelpers, Utilities, WebpackTools }; diff --git a/packages/pwa-buildpack/lib/util/deep-defaults.js b/packages/pwa-buildpack/lib/util/deep-defaults.js new file mode 100644 index 0000000000..3b255cabfe --- /dev/null +++ b/packages/pwa-buildpack/lib/util/deep-defaults.js @@ -0,0 +1,24 @@ +/** + * Simple two-level shallow merge. + * - Only works with objects. + * - Deep merges any second-level objects. + * - Overwrites everything else. + */ + +module.exports = (source, defaults) => { + const target = {}; + Object.assign(target, source); + + // one extra layer of merge depth + for (const [section, defaultValue] of Object.entries(defaults)) { + if (!source.hasOwnProperty(section)) { + target[section] = defaultValue; + } else if ( + typeof source[section] === 'object' && + typeof defaultValue === 'object' + ) { + target[section] = Object.assign({}, defaultValue, source[section]); + } + } + return target; +}; diff --git a/packages/pwa-buildpack/package.json b/packages/pwa-buildpack/package.json index 1652a7a0b4..39b314a5c8 100644 --- a/packages/pwa-buildpack/package.json +++ b/packages/pwa-buildpack/package.json @@ -26,6 +26,7 @@ }, "homepage": "https://github.com/magento/pwa-studio/tree/master/packages/pwa-buildpack#readme", "dependencies": { + "@babel/core": "~7.8.7", "@magento/directive-parser": "~0.1.7", "@magento/upward-js": "~4.0.1", "apicache": "~1.4.0", @@ -49,16 +50,19 @@ "jsdom": "~16.2.2", "klaw": "~3.0.0", "lodash": "~4.17.11", + "memfs": "~3.1.2", "micromatch": "~4.0.2", "node-fetch": "~2.3.0", - "pertain": "~0.1.3", + "pertain": "~0.2.0", "pkg-dir": "~4.1.0", "portscanner": "~2.2.0", "tapable": "~1.1.3", "tar": "~4.4.8", + "unionfs": "~4.4.0", "walk-object": "~4.0.0", "webpack-assets-manifest": "~3.1.1", "webpack-inject-plugin": "~1.5.3", + "webpack-node-externals": "~1.7.2", "word-wrap": "~1.2.3", "write-file-webpack-plugin": "~4.5.0", "yargs": "~13.2.2" diff --git a/packages/venia-concept/webpack.config.js b/packages/venia-concept/webpack.config.js index 15f592ccac..f09f45b324 100644 --- a/packages/venia-concept/webpack.config.js +++ b/packages/venia-concept/webpack.config.js @@ -1,3 +1,4 @@ +const path = require('path'); const { configureWebpack, graphQL: { getMediaURL, getUnionAndInterfaceTypes } @@ -13,6 +14,12 @@ module.exports = async env => { const unionAndInterfaceTypes = await getUnionAndInterfaceTypes(); const { clientConfig, serviceWorkerConfig } = await configureWebpack({ + // For Venia only, in the monorepo, always use the sibling packages. + alias: { + '@magento/pagebuilder': path.resolve(__dirname, '../pagebuilder'), + '@magento/peregrine': path.resolve(__dirname, '../peregrine'), + '@magento/venia-ui': path.resolve(__dirname, '../venia-ui') + }, context: __dirname, vendor: [ '@apollo/react-hooks', diff --git a/packages/venia-ui/lib/targets/__tests__/__snapshots__/venia-ui-targets.spec.js.snap b/packages/venia-ui/lib/targets/__tests__/__snapshots__/venia-ui-targets.spec.js.snap new file mode 100644 index 0000000000..09db7c2767 --- /dev/null +++ b/packages/venia-ui/lib/targets/__tests__/__snapshots__/venia-ui-targets.spec.js.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`uses RichContentRenderers to inject a default strategy into RichContent 1`] = ` +
word up", + } + } +/> +`; diff --git a/packages/venia-ui/lib/targets/__tests__/venia-ui-targets.spec.js b/packages/venia-ui/lib/targets/__tests__/venia-ui-targets.spec.js new file mode 100644 index 0000000000..e847f11bac --- /dev/null +++ b/packages/venia-ui/lib/targets/__tests__/venia-ui-targets.spec.js @@ -0,0 +1,94 @@ +const React = require('react'); +const path = require('path'); +const { + mockBuildBus, + buildModuleWith +} = require('@magento/pwa-buildpack/lib/TestHelpers'); +const { createTestInstance } = require('@magento/peregrine'); +const declare = require('../venia-ui-declare'); +const intercept = require('../venia-ui-intercept'); + +const thisDep = { + name: '@magento/venia-ui', + declare, + intercept +}; + +const mockComponent = name => `function ${name}(props) { return
{props.children}
; +`; +const mockDefault = name => `import React from 'react'; +export default ${mockComponent(name)} } + `; + +jest.doMock('react-router-dom', () => ({ + Switch(p) { + return
{p.children}
; + }, + Route(p) { + return
{p.children}
; + } +})); + +test('declares targets richContentRenderers and routes', () => { + const bus = mockBuildBus({ + context: __dirname, + dependencies: [thisDep] + }); + bus.runPhase('declare'); + const { richContentRenderers, routes } = bus.getTargetsOf( + '@magento/venia-ui' + ); + expect(richContentRenderers.tap).toBeDefined(); + expect(routes.tap).toBeDefined(); + const interceptor = jest.fn(); + // no implementation testing in declare phase + richContentRenderers.tap('test', interceptor); + richContentRenderers.call('woah'); + expect(interceptor).toHaveBeenCalledWith('woah'); + + const divByThree = jest.fn(x => x / 3); + routes.tap('addTwo', x => x + 2); + routes.tap({ name: 'divideByThree', fn: divByThree }); + expect(routes.call(10)).toBe(4); +}); + +test('uses RichContentRenderers to inject a default strategy into RichContent', async () => { + const built = await buildModuleWith('../../components/RichContent', { + context: __dirname, + dependencies: ['@magento/peregrine', thisDep] + }); + + const RichContent = built.run(); + + const wrapper = createTestInstance(); + expect( + wrapper.root.find(c => c.type.name === 'PlainHtmlRenderer') + ).toBeTruthy(); + expect(wrapper.toJSON()).toMatchSnapshot(); +}); + +test('uses routes to inject client-routed pages', async () => { + const routesModule = '../../components/Routes/routes'; + const built = await buildModuleWith(routesModule, { + context: path.dirname(require.resolve(routesModule)), + dependencies: ['@magento/peregrine', thisDep], + mockFiles: { + '../../RootComponents/Search/index.js': mockDefault('SearchPage'), + '../LoadingIndicator/index.js': + 'export const fullPageLoadingIndicator = "Loading";', + '../CartPage/index.js': mockDefault('CartPage'), + '../CreateAccountPage/index.js': mockDefault('CreateAccountPage'), + '../CheckoutPage/index.js': mockDefault('CheckoutPage'), + '../MagentoRoute/index.js': mockDefault('MagentoRoute') + }, + optimization: { + splitChunks: false + } + }); + // Testing this with a shallow renderer is obtusely hard because of + // Suspense, but these strings lurking in the build tell the story. + expect(built.bundle).toContain('SearchPage'); + expect(built.bundle).toContain('CartPage'); + expect(built.bundle).toContain('CreateAccountPage'); + expect(built.bundle).toContain('CheckoutPage'); +}); diff --git a/yarn.lock b/yarn.lock index 92c265cfbc..eddaa33d33 100644 --- a/yarn.lock +++ b/yarn.lock @@ -162,6 +162,27 @@ semver "^5.4.1" source-map "^0.5.0" +"@babel/core@~7.8.7": + version "7.8.7" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.7.tgz#b69017d221ccdeb203145ae9da269d72cf102f3b" + integrity sha512-rBlqF3Yko9cynC5CCFy6+K/w2N+Sq/ff2BPy+Krp7rHlABIr5epbA7OxVeKoMHB39LZOp1UY5SuLjy6uWi35yA== + dependencies: + "@babel/code-frame" "^7.8.3" + "@babel/generator" "^7.8.7" + "@babel/helpers" "^7.8.4" + "@babel/parser" "^7.8.7" + "@babel/template" "^7.8.6" + "@babel/traverse" "^7.8.6" + "@babel/types" "^7.8.7" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.1" + json5 "^2.1.0" + lodash "^4.17.13" + resolve "^1.3.2" + semver "^5.4.1" + source-map "^0.5.0" + "@babel/generator@7.0.0-beta.38": version "7.0.0-beta.38" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.38.tgz#6115a66663e3adfd1d6844029ffb2354680182eb" @@ -193,7 +214,7 @@ lodash "^4.17.13" source-map "^0.5.0" -"@babel/generator@^7.9.0", "@babel/generator@^7.9.5": +"@babel/generator@^7.8.7", "@babel/generator@^7.9.0", "@babel/generator@^7.9.5": version "7.9.5" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.5.tgz#27f0917741acc41e6eaaced6d68f96c3fa9afaf9" integrity sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ== @@ -448,7 +469,7 @@ "@babel/traverse" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/helpers@^7.9.0": +"@babel/helpers@^7.8.4", "@babel/helpers@^7.9.0": version "7.9.2" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.2.tgz#b42a81a811f1e7313b88cba8adc66b3d9ae6c09f" integrity sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA== @@ -471,7 +492,7 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.3.tgz#790874091d2001c9be6ec426c2eed47bc7679081" integrity sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ== -"@babel/parser@^7.7.5", "@babel/parser@^7.9.0": +"@babel/parser@^7.7.5", "@babel/parser@^7.8.7", "@babel/parser@^7.9.0": version "7.9.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8" integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA== @@ -8894,6 +8915,11 @@ fs-minipass@^2.0.0: dependencies: minipass "^3.0.0" +fs-monkey@1.0.0, fs-monkey@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs-monkey/-/fs-monkey-1.0.0.tgz#b1fe36b2d8a78463fd0b8fd1463b355952743bd0" + integrity sha512-nxkkzQ5Ga+ETriXxIof4TncyMSzrV9jFIF+kGN16nw5CiAdWAnG/2FgM7CHhRenW1EBiDx+r1tf/P78HGKCgnA== + fs-readdir-recursive@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" @@ -12489,6 +12515,13 @@ memfs-or-file-map-to-github-branch@^1.1.0: resolved "https://registry.yarnpkg.com/memfs-or-file-map-to-github-branch/-/memfs-or-file-map-to-github-branch-1.1.2.tgz#9d46c02481b7eca8e5ee8a94f170b7e0138cad67" integrity sha512-D2JKK2DTuVYQqquBWco3K6UfSVyVwmd58dgNqh+TgxHOZdTmR8I130gjMbVCkemDl/EzqDA62417cJxKL3/FFA== +memfs@~3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.1.2.tgz#2bb51600dacec67ed35677b1185abb708b7d2ad6" + integrity sha512-YubKuE+RGSdpZcRq2Nih8HcHj3LrqndsDFNB9IFjrgwzdM4eq+fImlDMfNm/HdRhYRkLdUecHGIpdz+wyrqlDg== + dependencies: + fs-monkey "1.0.0" + memoize-one@~5.0.0: version "5.0.5" resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.5.tgz#8cd3809555723a07684afafcd6f756072ac75d7e" @@ -14174,10 +14207,10 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= -pertain@~0.1.3: - version "0.1.11" - resolved "https://registry.yarnpkg.com/pertain/-/pertain-0.1.11.tgz#8fa15a7bf7f04138ec383a2286da71b4e76173c4" - integrity sha512-PmnDz2qhkrntsrSD7r78gGSo6KFjs1O10mEowQo0L3UskjoEMRvKE+T5auM4xmbu4a18a/bBE3tbWv7tzEezkA== +pertain@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/pertain/-/pertain-0.2.0.tgz#ffac6dac6ebf30739bff982d0f8664b2cae5924f" + integrity sha512-CuInHK2wao6QVMvl1Prm6+TtQuaukaA/jkc8lgG06Qvj53/ZXMhPlPCGChdH96JAmVP6bin+FxK6jxpVt2Kz8w== dependencies: debug "~4.1.1" dot-prop "^5.2.0" @@ -18002,6 +18035,13 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^2.0.1" +unionfs@~4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/unionfs/-/unionfs-4.4.0.tgz#b671112e505f70678052345cf5c2a33f0e6edde9" + integrity sha512-N+TuJHJ3PjmzIRCE1d2N3VN4qg/P78eh/nxzwHnzpg3W2Mvf8Wvi7J1mvv6eNkb8neUeSdFSQsKna0eXVyF4+w== + dependencies: + fs-monkey "^1.0.0" + uniq@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" @@ -18628,6 +18668,11 @@ webpack-log@^2.0.0: ansi-colors "^3.0.0" uuid "^3.3.2" +webpack-node-externals@~1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/webpack-node-externals/-/webpack-node-externals-1.7.2.tgz#6e1ee79ac67c070402ba700ef033a9b8d52ac4e3" + integrity sha512-ajerHZ+BJKeCLviLUUmnyd5B4RavLF76uv3cs6KNuO8W+HuQaEs0y0L7o40NQxdPy5w0pcv8Ew7yPUAQG0UdCg== + webpack-sources@^1.0.0, webpack-sources@^1.1.0, webpack-sources@^1.3.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" From b30e790fccd152cfa500241091a1d73cdd7d65bb Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Tue, 28 Apr 2020 11:21:58 -0500 Subject: [PATCH 07/21] docs: targets tutorial --- .../extensibility-with-targets/index.md | 512 ++++++++++++++++++ 1 file changed, 512 insertions(+) create mode 100644 pwa-devdocs/_drafts/pwa-studio-fundamentals/extensibility-with-targets/index.md diff --git a/pwa-devdocs/_drafts/pwa-studio-fundamentals/extensibility-with-targets/index.md b/pwa-devdocs/_drafts/pwa-studio-fundamentals/extensibility-with-targets/index.md new file mode 100644 index 0000000000..e363aee38e --- /dev/null +++ b/pwa-devdocs/_drafts/pwa-studio-fundamentals/extensibility-with-targets/index.md @@ -0,0 +1,512 @@ +--- +title: Modular Extensibility in PWA Studio with Targets +--- + +The new extensibility system for PWA Studio turns your storefront project into a self-organizing app, built by modules you can install, and hand-tuned by as much or as little customization code as you like. + +A few simple new concepts—the network of **Targets**, the **BuildBus**, and the [**Interceptor pattern**](https://web.archive.org/web/20170912094101/http://www.cs.wustl.edu/~schmidt/POSA/POSA2/access-patterns.html)—enable your chosen third-party code to enhance the build toolchain, add new functionality to the storefront, and even _rewrite the code of your published PWA on-the-fly_. + +# Quick Start + +Use Targets to add a new custom route to a Venia-based store, without editing any VeniaUI code. + +## Project Setup + +1. Clone the PWA Studio repository. + + +2. Edit `packages/venia-concept/package.json`. Add a new top-level section: + ```diff + "module": "src/index.js", + "es2015": "src/index.js", + "esnext": "src/index.js", + + "pwa-studio": { + + "targets": { + + "intercept": "targets/local-intercept" + + } + + } + } + ``` + +3. Create `packages/venia-concept/targets/local-intercept.js`: + ```js + module.exports = targets => { + targets.of('@magento/venia-ui').routes.tap(routes => { + routes.push({ + name: 'Greeting', + pattern: '/greeting/:who?', + path: require.resolve('../src/GreetingPage.js') + }); + return routes; + }); + }; + ``` + +4. Create `packages/venia-concept/src/GreetingPage.js`: + ```js + import React from 'react'; + import { useParams } from 'react-router'; + + const hi = { + textAlign: 'center', + margin: '1rem' + }; + const wave = { + ...hi, + fontSize: '5rem' + }; + + export default function GreetingPage() { + const { who = 'nobody' } = useParams(); + return ( +
+

Hello, {who}!

+

{'\uD83D\uDC4B'}

+
+ ); + } + ``` + +That's it! +You've registered your project to use BuildBus, created an interceptor file to add to VeniaUI's routes, and created a new React component to use as a custom route. + +## Watch it work + +1. Run `yarn run watch:venia`. Open the dev-mode storefront in your browser. + +1. Go to `/greeting`. Observe as your `GreetingPage` component is displayed! + +1. Your `GreetingPage` is a React Router `Route`, so it can use route parameters. Go to `/greeting/world`. + +1. Congratulations. Now, your `GreetingPage` has accomplished the [fundamental task of computer programs](https://en.wikipedia.org/wiki/%22Hello,_World!%22_program#/media/File:Hello_World_Brian_Kernighan_1978.jpg). + +## Production efficiency + +Targets that change your storefront only need to run once, at build time. +They affect the build process that generates static application bundles, changing the code it outputs. +That way, there need be _no storefront performance cost_ to augmenting your PWA with Targets. +Prove it to yourself! + +1. Run a full Venia production build: `yarn run build`. + +1. Remove the `"pwa-studio"` section from `packages/venia-concept/package.json`. + +1. Delete `packages/venia-concept/targets`. + +1. Run the staging server: `yarn run stage:venia`. + +1. Visit your staging site and navigate to `/greeting/production`. + +1. It still works, even though your server-side interceptor code is gone! + +Contrast this with a plugin architecture which detects and dispatches plugins as the storefront runs in the shopper's device. +If VeniaUI had to scan extensions for custom routes inside the React app, then the extension system itself would bloat the application. +"Runtime" extension systems can only give developers convenience and customization by sacrificing some performance and shopper experience. +The Targets system is flexible enough to give developers any extension point they can envision, yet it produces a PWA site as fast as hand-optimized code. + +# Concepts + +## Building a PWA from installed extensions + +Magento PWA Studio follows the Magento way of building Web functionality on a simple platform by intelligently merging third-party code. +As the third-party ecosystem grows, a PWA project may contain less and less first-party source code. +A developer can then: + +1. Create a PWA based on VeniaUI +2. Install several third-party modules to automatically enhance VeniaUI +3. Intercept targets to customize those modules +4. Maintain only a minimal "index" component and configuration files in their own source code + +This helps developers 'spin up' new stores and mix in functionality with minimal boilerplate. +In this structure, a developer can manage dependency upgrades and compatibility issues using semantic versioning, by managing the `package.json` file in their project. + +## Intercept files + +Directly interact with Target objects by creating and registering an intercept file. +The file's default export must be a function which receives one argument. +That argument will be a [TargetProvider][]. + +`./targets/intercept.js`: +```js +module.exports = targets => { + // interceptors go here +} +``` + +Intercept files run in NodeJS during the build and other lifecycle scripts. +Write them as CommonJS modules and organize them in another folder, like `./targets` above, alongside your frontend source code. + +To register `./targets/intercept.js` as intercept file, add its path to `package.json` under the `pwa-studio.targets.intercept` section: + ```diff + "module": "src/index.js", + "es2015": "src/index.js", + "esnext": "src/index.js", + + "pwa-studio": { + + "targets": { + + "intercept": "./targets/intercept.js" + + } + + } + } + ``` + +This declaration will alert Buildpack that the package wants to intercept targets. + +Inside its main function, an intercept file uses the `TargetProvider` to retrieve Targets from the various installed modules. +It then runs tap methods on the Target to register interceptor callbacks. +When the module that declared the target chooses to invoke the target, the interceptor callbacks will be invoked. +In those interceptor callbacks, the intercept file can make customizations, gather data, modify the build itself, or implement and call its own declared targets. + +### How and when intercept files run + +PWA Studio's build process, using the `@magento/pwa-buildpack` module, calls intercept files by creating a `BuildBus` and running it. +It looks for intercept files _only in the **named** direct dependencies of the project._ +It does not traverse the whole dependency tree, running intercept files from second-level transitive dependencies and beyond. +Only the modules listed in the project's `package.json` under `dependencies` and `devDependencies` can use Targets in the project. + +The `BuildBus` will load the `targets/intercept.js` module when running targets and call the exported function. + +It runs the intercept files in dependency order. If your module declares another module with Targets in `peerDependencies` (see below), the other module will intercept first. + +The PWA project itself can register an intercept file. +This file should contain specific customizations for this project only. +It runs last, after all dependent modules have declared and intercepted targets. + +### Target dependency management + +A project only runs Targets from its first-level dependencies. +This means that if you're writing a third-party module which uses Targets from another module, like `@magento/peregrine`, then it shouldn't list Peregrine in its own `dependencies` list. +That does not guarantee that Peregrine will be a first-level dependency of the project. +To use those Targets, your module needs its _host PWA project_ to also list a direct dependency on Peregrine. +So it must add `@magento/peregrine` to the package.json `peerDependencies` collection. +This instructs NPM to make sure that the top-level project installing your module is also installing `@magento/peregrine`. +It warns the user strongly if that dependency is missing. +Using `peerDependencies` for frontend libraries is a good practice anyway; it's a safeguard against including multiple copies of the same library in your built PWA. + +## TargetProviders + +A **TargetProvider** is an object that manages the connections between different modules and their Targets. +It is the API that intercept files use to acquire Targets and intercept them. + +Use `targets.of(moduleName)` to get the targets of another module. +This method returns a simple object whose keys are target names, and whose values are Target objects. + +```js +module.exports = targets => { + const builtins = targets.of('@magento/pwa-buildpack'); + builtins.webpackCompiler.tap(compiler => { + // do fun stuff to the compiler using the familiar Tapable interface! + compiler.emit.tap('MyExtension', compilation => { + }); + }); + // use higher-level targets with the same interface if you want! + builtins.transformModules.tap('MyExtension', addTransform => { + }); +} +``` + +Use `targets.own` to get the targets that the current module (whose interceptor file is running right now!) has declared. +The intercept file is a typical place to _implement_ the actual functionality of module's own targets. +Almost all target functionality comes from intercepting other targets, as in the below example. + +```js +module.exports = targets => { + const builtins = targets.of('@magento/pwa-buildpack'); + builtins.webpackCompiler.tap(compiler => { + compiler.emit.tap(targets.name, compilation => { + const totalSize = compilation.getStats().chunks.reduce((sum, chunk) => + sum + chunk.size, + 0 + ); + targets.own.perfReport.call({ totalSize }); + }) + }); + builtins.transformModules.tap('MyExtension', addTransform => { + type: 'source', + fileToTransform: require.resolve('../components/SocialShare'), + transformModule: '@my-extension/targets/codegen-social-icons.js', + options: { + icons: targets.own.socialIcons.call([]) + } + }); +} +``` + +In the example, `MyExtension` implements its own `perfReport` hook by tapping Webpack hooks, building an info object, and invoking the `perfReport` hook inside the Webpack interceptor callback. +It also implements its own `socialIcons` hook by configuring the build process to pass the `SocialShare` component's source code through a transform function implemented in `./codegen-social-icons.js`. + +_Where did `perfReport` and `socialIcons` come from?? See [Declaring targets][] below._ + +## Targets + +A **Target** is an object representing an "extension point", an area in code that can be accessed and intercepted. +All Targets are variants of a simple, common JavaScript pattern called the [Tapable Hook](https://github.com/webpack/tapable). +These objects may resemble event emitters. +They share some functionality with those: a Target lets you choose a point _by name_ in the time and space of a process and run other code in that time and place. +Unlike Event Emitters, Targets have defined behavior for how, and in what order, they will run their interceptors and how those interceptors may change things. + +The concepts of code reusability that already exist in ReactJS apps are still there. +You can import large or small components from VeniaUI and other modules, and put them together in your own React component tree. +You can write components in your own project which utilize third-party code. + +Targets provide an additional layer of customization, which can work smoothly with those plain code composition techniques. +All direct dependencies (listed in `dependencies` or `devDependencies`) of a PWA project can declare and intercept Targets. +An intercept file can access the full library of Targets declared by all direct dependencies. + +### Targets as Public API +Targets present another public API of a package alongside the modules it exports to runtime code. +For example, `@magento/venia-ui` provides components for importing, and talons for making deep changes to those components. +Let's say you want to make all CMS blocks that are only plain text (with no HTML) into big, beautiful blue text. +Your app can import VeniaUI's RichContent component to make its own enhanced one: + +```js +import React from 'react'; +import BaseRichContent from '@magento/venia-ui/lib/components/RichContent'; +import WordArt from 'react-wordart'; + +// If the content is just words, make 'em BLUE words. +export const RichContent = ({ html }) => + /^[^<>]+$/gm.test(html) ? ( // no tags! + + ) : ( + + ); +``` + +Your new component _uses_ RichContent, but it doesn't change RichContent. +It's not going to replace RichContent everywhere that other code uses it. + +RichContent is an important component, and custom content renderers are a popular Magento feature. +So, in addition to exporting the component, VeniaUI declares a Target called `richContentRenderers`. +This allows you to change the behavior of RichContent everywhere it's used, by adding a rendering strategy. + +As documented by VeniaUI, `richContentRenderers` expects a "rendering strategy" module which exports a `canRender` function and a `Component`. +Update your component to implement a rendering strategy, instead of wrapping RichContent itself. + +```js +import React from 'react'; +import WordArt from 'react-wordart'; + +const noTags = /^[^<>]+$/gm; + +const BlueWords = ({ html }) => ( + +); + +export const canRender = html => noTags.test(html); +export { BlueWords as Component }; +``` + +Now, add an interceptor in your intercept file. +```js + targets + .of('@magento/venia-ui') + .richContentRenderers.tap(richContentRenderers => { + richContentRenderers.add({ + componentName: 'BlueWords', + importPath: require.resolve('../BlueWords') + }); + }); +``` + +Since you now explicitly register BlueWords as a `richContentRenderer` via the declared target, the `RichContent` component will attempt to use that renderer everywhere `RichContent` is used. + +Targets are written in JavaScript. +This doesn't mean that they can only work with the JavaScript in the PWA storefront, however. +The built and published PWA contains HTML, CSS, and JavaScript, the main languages of the Web, but all these assets are generated by build process in Webpack, which runs in Node.js. +JavaScript files that declare and intercept Targets work with the build process and can modify the HTML and CSS that the build generates. +If you use module transforms, you'll be using JavaScript that parses and modifies other JavaScript! + +As a Targets-driven codebase grows, it might sometimes be confusing to work with JavaScript files that run as _build scripts_ in NodeJS, alongside similar-looking JavaScript files that are _source code_ to be bundled into the PWA and run on the shopper's device. + +A quick way to get your bearings in a JavaScript file is to look at what kind of module system the code is using. +Storefront JS that will run on the device must use ES6 Modules with `import` and `export`: +```js +import throttle from `lodash.throttle`; +export const everySecond = fn => + throttle(fn, 1000); +``` + +Backend JS that will run in Node,at build time or on the server side, must use CommonJS modules with `require()`: +```js +const throttle = require('lodash.throttle'); +module.exports.everySecond = fn => + throttle(fn, 1000); +``` + +### Declaring targets + +Targets must be defined and created before they are intercepted. +Modules define their own targets by registering a **declare file**. +The declare file also exports a function which receives a TargetProvider object. +The TargetProvider object provides a `declare` function which takes a dictionary of named Targets. +Furthermore, the TargetProvider has a utility collection called `types`, which holds all of the legal constructors for Targets. + +`./targets/declare.js`: +```js +module.exports = targets => { + targets.declare({ + perfReport: new targets.types.AsyncParallel(['report']) + socialIcons: new targets.types.SyncWaterfall(['iconlist']) + }); +} +``` + +The above declare file creates two targets that other modules, as well as the root project, can intercept. +The `perfReport` target runs its interceptors asynchronously and in parallel. +This is appropriate for logging and monitoring interceptors that don't affect functionality. +The `socialIcons` target runs its interceptors synchronously and in subscription order, passing return values as arguments to the next interceptor. +This is appropriate for customizations that must happen in a predictable order. + +Targets are variants of Tapables; see [Tapable](https://github.com/webpack/tapable) for a list of available types and behaviors. +(Note that the Tapable classnames all end with 'Hook', and the Target classnames do not.) + +The BuildBus runs all declare files before running any intercept files, so simply registering a declare file will guarantee that the targets are available to any dependent interceptor. +Like intercept files, declare files are CommonJS modules that run in Node. Organize them together with intercept files. + +To register `./targets/declare.js` as a declare file, add its path to `package.json` under the `pwa-studio.targets.declare` section: + ```diff + "module": "src/index.js", + "es2015": "src/index.js", + "esnext": "src/index.js", + "pwa-studio": { + "targets": { + + "declare": "./targets/declare.js", + "intercept": "./targets/intercept.js" + } + } + } + ``` + +# API + +## List of targets + + + +# Development + +## Extension development + +When writing a PWA-compatible library, to publish on NPM or on the Magento Marketplace, think of Targets as the main tool in your toolbox. +To add new functionality, try adding interceptors to your storefront project and injecting the functionality that way. +When your prototype is working, try separating that work into a separate module and continuing to work on it as an encapsulated, portable extension! + +### Initial phase: in-project interceptors + +As in the [Quick Start][], new functionality may begin by intercepting from the storefront project itself. +The tutorial creates a Greeting Page by implementing the page in the storefront project's source folder. + +The next step is to turn that functionality into a new module. + +### Move the code + +Create a new project folder alongside your storefront. Run `yarn init` and then add the `pwa-studio` section to your `package.json`. + +```json +{ + "name": "@me/pwa-greeting-page", + "version": "1.0.0", + "description": "Hello, anything!", + "author": "PWA Developer", + "main": "./GreetingPage.js", + "license": "MIT", + "pwa-studio": { + "targets": { + "intercept": "./targets/intercept.js" + } + } +} +``` + +Create `./targets/intercept.js`. Now that you're working in a third-party module, you can use default NodeJs module resolution in the route path, and make your greeting component the default export of the package. +Create `packages/venia-concept/targets/local-intercept.js`: +```js +module.exports = targets => { + targets.of('@magento/venia-ui').routes.tap(routes => { + routes.push({ + name: 'Greeting', + pattern: '/greeting/:who?', + path: '@me/pwa-greeting-page' + }); + return routes; + }); +}; +``` + +Create `./GreetingPage.js` and paste in the content from the tutorial above. + +### Manage dependencies + +The GreetingPage uses `react` and `react-router`. +The intercept file depends on targets of `@magento/venia-ui`. +This means that the project using `@me/pwa-greeting-page` must also use `react`, `react-router`, and `@magento/venia-ui` as peers. +Declare these dependencies in `package.json`: + +```json +{ + "name": "@me/pwa-greeting-page", + "version": "1.0.0", + "description": "Hello, anything!", + "author": "PWA Developer", + "main": "./GreetingPage.js", + "license": "MIT", + "pwa-studio": { + "targets": { + "intercept": "./targets/intercept.js" + } + }, + "peerDependencies": { + "@magento/venia-ui": "~6.0.0", + "react": "~16.9.0", + "react-router-dom": "~5.1.0" + } +} +``` + +### Simulate install + +To install and test your `@me/pwa-greeting-page` extension, you would have to add it to the `package.json` dependencies in your storefront. +But since `@me/pwa-greeting-page` has never been published, it would cause errors to manually insert it into those dependencies. +Fortunately, for this exact use-case of developing a new module, Buildpack allows a dev-mode override here. + +First, `yarn link` to symlink your new extension to Yarn's global set. +Then, `cd` back to your store directory and run `yarn link @me/pwa-greeting-page`. This two-step process links the packages together so that Node and Webpack can now resolve `@me/pwa-greeting-page` from the storefront project directory, without it being present in the dependency array. + +The last thing is to make Buildpack use it, even though it's not a listed dependency. +The override for that is simply an environment variable! Set `BUILDBUS_DEPS_ADDITIONAL` to a comma-separated list of the packages Buildpack should check for interceptors, in addition to the listed packages in `package.json`. + +```sh +BUILDBUS_DEPS_ADDITIONAL='@me/pwa-greeting-page' yarn run watch:venia +``` + +It should work! The functionality of the new Greeting Page has been entirely ported into its own dependency. +Simply running `yarn add @me/pwa-greeting-page` to your project will now automatically add the `/greeting` route to your PWA storefront! + +## Contributing + +The Targets system does not automatically expose all of the inner workings of PWA dependencies. +It is an "explicit registration" system; to make something public, a developer must specifically create and employ a Target. +Adding or enhancing Targets to our core libraries is one of the best contributions you can make! +It gives a developer the chance to create their "dream API" that solves their business needs, and then to pull request it for possible inclusion into core. + +### Target dev tips + +- **Buildpack targets are special.** + As the owner of the target system, Buildpack targets are the root pieces of functionality that all other targets use. + Instead of intercepting its own targets, Buildpack invokes them directly with a reference to Buildpack. + When creating or modifying a Buildpack target, consider that it should be very easy and very obvious how to utilize that target at a low level to make higher-level targets. + +- **The transformModules target is powerful.** + This very low-level target is a way to codemod any file in your own package. + Two types of transforms are currently available: a `source` transform, which will run the module code as a string through a function, and a ` + You can only use `transformModules` to transform _your own_ modules; if an intercept file requests to transform an external module file, there will be an error. + This enforces encapsulation and reduces implicit dependencies. + + + +### Help Wanted +- Target full Webpack config before creating compiler +- Target UPWARD file +- Target app shell HTML +- Target header, footer, page wrapper elements +- Target style From 2e813a6a42d1d46e3e9a3f8f1bafac53dc5b9b6d Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Wed, 29 Apr 2020 18:19:06 -0500 Subject: [PATCH 08/21] fixup jest tolerate slow ci --- packages/venia-ui/lib/targets/__tests__/venia-ui-targets.spec.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/venia-ui/lib/targets/__tests__/venia-ui-targets.spec.js b/packages/venia-ui/lib/targets/__tests__/venia-ui-targets.spec.js index e847f11bac..ed95df4ad8 100644 --- a/packages/venia-ui/lib/targets/__tests__/venia-ui-targets.spec.js +++ b/packages/venia-ui/lib/targets/__tests__/venia-ui-targets.spec.js @@ -53,6 +53,7 @@ test('declares targets richContentRenderers and routes', () => { }); test('uses RichContentRenderers to inject a default strategy into RichContent', async () => { + jest.setTimeout(15000); const built = await buildModuleWith('../../components/RichContent', { context: __dirname, dependencies: ['@magento/peregrine', thisDep] From 9857a5f03755824f193407e977eb279fda14b176 Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Fri, 1 May 2020 10:26:21 -0500 Subject: [PATCH 09/21] fixup remove unnecessary alias hack --- packages/venia-concept/webpack.config.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/venia-concept/webpack.config.js b/packages/venia-concept/webpack.config.js index f09f45b324..c80da9505a 100644 --- a/packages/venia-concept/webpack.config.js +++ b/packages/venia-concept/webpack.config.js @@ -14,12 +14,6 @@ module.exports = async env => { const unionAndInterfaceTypes = await getUnionAndInterfaceTypes(); const { clientConfig, serviceWorkerConfig } = await configureWebpack({ - // For Venia only, in the monorepo, always use the sibling packages. - alias: { - '@magento/pagebuilder': path.resolve(__dirname, '../pagebuilder'), - '@magento/peregrine': path.resolve(__dirname, '../peregrine'), - '@magento/venia-ui': path.resolve(__dirname, '../venia-ui') - }, context: __dirname, vendor: [ '@apollo/react-hooks', From 6068ed27b2279e9f47b6718b939b7433dfea2650 Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Fri, 1 May 2020 10:51:34 -0500 Subject: [PATCH 10/21] fixup lint error --- packages/venia-concept/webpack.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/venia-concept/webpack.config.js b/packages/venia-concept/webpack.config.js index c80da9505a..15f592ccac 100644 --- a/packages/venia-concept/webpack.config.js +++ b/packages/venia-concept/webpack.config.js @@ -1,4 +1,3 @@ -const path = require('path'); const { configureWebpack, graphQL: { getMediaURL, getUnionAndInterfaceTypes } From ef159e7ccfb9d72770f5b56a953dd22d44f3f4f2 Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Mon, 4 May 2020 15:12:43 -0500 Subject: [PATCH 11/21] docs: add note on intercept filename --- .../pwa-studio-fundamentals/extensibility-with-targets/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pwa-devdocs/_drafts/pwa-studio-fundamentals/extensibility-with-targets/index.md b/pwa-devdocs/_drafts/pwa-studio-fundamentals/extensibility-with-targets/index.md index e363aee38e..5ee5f13433 100644 --- a/pwa-devdocs/_drafts/pwa-studio-fundamentals/extensibility-with-targets/index.md +++ b/pwa-devdocs/_drafts/pwa-studio-fundamentals/extensibility-with-targets/index.md @@ -136,6 +136,8 @@ module.exports = targets => { Intercept files run in NodeJS during the build and other lifecycle scripts. Write them as CommonJS modules and organize them in another folder, like `./targets` above, alongside your frontend source code. +_All intercept and target paths are changeable; the recommended paths are for convention and organization only. In the above examples, the project-level intercept file is named `local-intercept.js`, to denote that this intercept file is for the local project and is not associated with any extension._ + To register `./targets/intercept.js` as intercept file, add its path to `package.json` under the `pwa-studio.targets.intercept` section: ```diff "module": "src/index.js", From 33abe8981b9711f951a6cc8ccd25252e94b8dd16 Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Mon, 4 May 2020 16:58:57 -0500 Subject: [PATCH 12/21] docs: reorganize contributing section --- .../extensibility-with-targets/index.md | 55 ++++++++++++++----- 1 file changed, 42 insertions(+), 13 deletions(-) diff --git a/pwa-devdocs/_drafts/pwa-studio-fundamentals/extensibility-with-targets/index.md b/pwa-devdocs/_drafts/pwa-studio-fundamentals/extensibility-with-targets/index.md index 5ee5f13433..2d12209225 100644 --- a/pwa-devdocs/_drafts/pwa-studio-fundamentals/extensibility-with-targets/index.md +++ b/pwa-devdocs/_drafts/pwa-studio-fundamentals/extensibility-with-targets/index.md @@ -164,6 +164,7 @@ PWA Studio's build process, using the `@magento/pwa-buildpack` module, calls int It looks for intercept files _only in the **named** direct dependencies of the project._ It does not traverse the whole dependency tree, running intercept files from second-level transitive dependencies and beyond. Only the modules listed in the project's `package.json` under `dependencies` and `devDependencies` can use Targets in the project. +See [Target dependency management][] below. The `BuildBus` will load the `targets/intercept.js` module when running targets and call the exported function. @@ -176,6 +177,7 @@ It runs last, after all dependent modules have declared and intercepted targets. ### Target dependency management A project only runs Targets from its first-level dependencies. + This means that if you're writing a third-party module which uses Targets from another module, like `@magento/peregrine`, then it shouldn't list Peregrine in its own `dependencies` list. That does not guarantee that Peregrine will be a first-level dependency of the project. To use those Targets, your module needs its _host PWA project_ to also list a direct dependency on Peregrine. @@ -491,20 +493,47 @@ It is an "explicit registration" system; to make something public, a developer m Adding or enhancing Targets to our core libraries is one of the best contributions you can make! It gives a developer the chance to create their "dream API" that solves their business needs, and then to pull request it for possible inclusion into core. -### Target dev tips - -- **Buildpack targets are special.** - As the owner of the target system, Buildpack targets are the root pieces of functionality that all other targets use. - Instead of intercepting its own targets, Buildpack invokes them directly with a reference to Buildpack. - When creating or modifying a Buildpack target, consider that it should be very easy and very obvious how to utilize that target at a low level to make higher-level targets. - - **The transformModules target is powerful.** - This very low-level target is a way to codemod any file in your own package. - Two types of transforms are currently available: a `source` transform, which will run the module code as a string through a function, and a ` - You can only use `transformModules` to transform _your own_ modules; if an intercept file requests to transform an external module file, there will be an error. - This enforces encapsulation and reduces implicit dependencies. - - +This very low-level target is a way for dependencies to codemod their own files. +It shares some functionality with the Webpack [module configuration](https://webpack.js.org/configuration/module/), but it is designed to be more distributed and composable than that configuration. +Webpack's API is designed for project owners who have full control over their own source code. +They can design rules that use matching predicates, running all `**/*.js` files through a particular loader chain. +This is an important API and PWA Studio preserves it, but a plugin-driven architecture like Targets requires a different API that is more distributed and encapsulated. +The `transformModules` target builds rules by gathering requirements from multiple dependencies in parallel. +Instead of targeting whole groups of files with regular expressions and predicate functions, `transformModules` requests must target individual files that belong to the requestor's codebase. +Therefore, each file may have its own set of options and overrides. +With this API, `transformModules` enables code changes based on business rules and application logic, rather than the lower-level Webpack paradigm of transpiling and processing different filetypes. + +Where appropriate, consider implementing a new Target using the transformModules target instead of implementing another codemod pathway. +The transformModules target could also be augmented with new abilities! +Currently its features include: + +- The `source` transform type, for changing module source code using custom Webpack loader +- The `babel` transform type, for analyzing or updating a module syntax tree through a custom Babel plugin + +These two options for transforming JavaScript already offer a lot of additional opportunities for new tools. + +Source transforms use Webpack loaders, which can be very general-purpose. +Babel transforms use Babel plugins, which can also be very general-purpose and configurable. +Using these common interfaces, a Target developer can create additional low-level utility Targets that use `transformModules` with a general-purpose Webpack loader or Babel plugin as a `transformModule` +Buildpack offers one custom loader out of the box, for use with the `transformModules` target: the `wrap-esm-loader`. +This loader can be configured to edit the source code of any ES Module to decorate the exports of any module by running them through some other function. +Peregrine uses the Buildpack `transformModules` target plus its `wrap-esm-loader` to expose its individual talon modules for decoration. + +These transform modules can also be highly specific. +VeniaUI implements its own `BabelRouteInjectionPlugin`, a Babel plugin intended only to be used on Venia UI's `Routes` component. +It uses this plugin for its `routes` Target, making one single `transformModules` request to transform the `Routes` component with the `BabelRouteInjectionPlugin`. +A Target developer can create medium or high-level utility Targets by implementing more usage-specific Webpack loaders and Babel plugins, and using `transformModules` to hook them in to the build. + +Another way to contribute to the Targets system would be to propose and/or create additional transform types. +Possibilities: + +- A `postcss` transform type, for manipulating CSS files +- A `dom` transform type, for manipulating HTML, XML, or SVG documents +- A `binary` transform type, for streaming an image file or another asset through some plugin or service +- A `replace` transform type, for replacing a module with another under certain conditions, like a controlled and composable version of Webpack `resolve.alias` +- An `expose` transform type, for making the exports of a module available in a global context + ### Help Wanted - Target full Webpack config before creating compiler From abf01e2abcc837c883d0287dab48a9fcc68b3326 Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Thu, 7 May 2020 11:45:41 -0500 Subject: [PATCH 13/21] chore: upgrade to jest 26 --- package.json | 6 +- packages/pwa-buildpack/lib/BuildBus/Target.js | 59 +- .../lib/BuildBus/__tests__/Target.spec.js | 9 +- yarn.lock | 1191 ++++++++--------- 4 files changed, 598 insertions(+), 667 deletions(-) diff --git a/package.json b/package.json index f026ff2ebb..acd8f3de20 100755 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ }, "devDependencies": { "@magento/eslint-config": "~1.5.0", - "@types/jest": "~24.0.18", + "@types/jest": "~25.2.1", "caller-id": "~0.1.0", "chalk": "~2.4.2", "chokidar": "~2.1.2", @@ -67,9 +67,9 @@ "first-run": "~2.0.0", "graphql-tag": "~2.10.1", "identity-obj-proxy": "~3.0.0", - "jest": "~25.3.0", + "jest": "~26.0.1", "jest-fetch-mock": "~2.1.1", - "jest-junit": "~6.3.0", + "jest-junit": "~10.0.0", "jest-transform-graphql": "~2.1.0", "lodash.debounce": "~4.0.8", "prettier": "~1.16.4", diff --git a/packages/pwa-buildpack/lib/BuildBus/Target.js b/packages/pwa-buildpack/lib/BuildBus/Target.js index 4b0a6908ea..6404fa1549 100644 --- a/packages/pwa-buildpack/lib/BuildBus/Target.js +++ b/packages/pwa-buildpack/lib/BuildBus/Target.js @@ -32,75 +32,34 @@ class Target extends Trackable { this.name = targetName; this.type = tapableType; this.attach(`${targetName}[${tapableType}]`, this._owner); - this._populateFlags(); } /** @ignore */ _invokeTap(method, info, fn) { - const tap = { + const tapInfo = { name: this._requestor }; let customName; if (typeof info === 'object') { - // a tapInfo object was passed! - customName = info.name; - Object.assign(tap, info); + // a tapInfo object was passed! extract its name... + const { name, ...otherInfo } = info; + customName = name; + Object.assign(tapInfo, otherInfo); } else if (fn) { // a custom name and tap function were passed! customName = info; - tap.fn = fn; + tapInfo.fn = fn; } else { // a tap function was passed with no custom name - tap.fn = info; + tapInfo.fn = info; } if (customName) { - tap.name += Target.SOURCE_SEP + customName; + tapInfo.name += Target.SOURCE_SEP + customName; } this.track('intercept', { source: this._requestor, type: interceptionTypes[method] }); - return this._tapable[method](tap); - } - /** @ignore */ - _populateFlags() { - /** - * Runs asynchronously. - * Can only be intercepted with `.tapAsync()` or `.tapPromise()`. - * Can only be run with `.callAsync()` or `.promise()`. - * @type {boolean} - */ - this.async = this.type.includes('Async'); - - /** - * When called, the first interceptor which returns a value will cancel - * the rest of the interceptors and return that value to the caller. - * @type {boolean} - */ - this.bail = this.type.includes('Bail'); - /** - * The first interceptor receives the arguments to the call method. - * Subsequent interceptors receive the return value of the previous - * interceptor to be run. Waterfall hooks allow interceptors to act as - * composed functions. - * @type {boolean} - */ - this.waterfall = this.type.includes('Waterfall'); - - /** - * Runs asynchronously and in parallel. Interceptors are called in - * subscription order, but concurrently without waiting for the previous - * interceptors to finish executing. - * @type {boolean} - */ - this.parallel = this.type.includes('Parallel'); - - /** - * Calls interceptors in subscription order and waits for each - * interceptor to return before calling the next. May run synchronously - * or asynchronously. - * @type {boolean} - */ - this.series = !this.async || this.type.includes('Series'); + return this._tapable[method](tapInfo); } /** * Run `.call(...args)` on the underlying Tapable Hook. diff --git a/packages/pwa-buildpack/lib/BuildBus/__tests__/Target.spec.js b/packages/pwa-buildpack/lib/BuildBus/__tests__/Target.spec.js index c76fe6193a..2db4adc9b2 100644 --- a/packages/pwa-buildpack/lib/BuildBus/__tests__/Target.spec.js +++ b/packages/pwa-buildpack/lib/BuildBus/__tests__/Target.spec.js @@ -20,9 +20,12 @@ test('runs sync interception methods on underlying tapable with default name arg tapInfoSpy(tapInfo); } }); - bailTarget.tap('BailIfAsked', x => { - if (x.bailMe) { - return `${x.txt}: AAAAAAA`; + bailTarget.tap({ + name: 'BailIfAsked', + fn: x => { + if (x.bailMe) { + return `${x.txt}: AAAAAAA`; + } } }); diff --git a/yarn.lock b/yarn.lock index e792866de1..b5d0b69ec5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1393,7 +1393,7 @@ "@babel/parser" "^7.8.3" "@babel/types" "^7.8.3" -"@babel/template@^7.8.6", "@babel/template@~7.8.6": +"@babel/template@^7.3.3", "@babel/template@^7.8.6", "@babel/template@~7.8.6": version "7.8.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg== @@ -1465,6 +1465,15 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" +"@babel/types@^7.3.3": + version "7.9.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7" + integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA== + dependencies: + "@babel/helper-validator-identifier" "^7.9.5" + lodash "^4.17.13" + to-fast-properties "^2.0.0" + "@babel/types@^7.8.6", "@babel/types@^7.8.7": version "7.8.7" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.7.tgz#1fc9729e1acbb2337d5b6977a63979b4819f5d1d" @@ -1682,148 +1691,158 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.2.tgz#26520bf09abe4a5644cd5414e37125a8954241dd" integrity sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw== -"@jest/console@^25.3.0": - version "25.3.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-25.3.0.tgz#33b56b81238427bf3ebe3f7b3378d2f79cdbd409" - integrity sha512-LvSDNqpmZIZyweFaEQ6wKY7CbexPitlsLHGJtcooNECo0An/w49rFhjCJzu6efeb6+a3ee946xss1Jcd9r03UQ== +"@jest/console@^26.0.1": + version "26.0.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-26.0.1.tgz#62b3b2fa8990f3cbffbef695c42ae9ddbc8f4b39" + integrity sha512-9t1KUe/93coV1rBSxMmBAOIK3/HVpwxArCA1CxskKyRiv6o8J70V8C/V3OJminVCTa2M0hQI9AWRd5wxu2dAHw== dependencies: - "@jest/source-map" "^25.2.6" - chalk "^3.0.0" - jest-util "^25.3.0" + "@jest/types" "^26.0.1" + chalk "^4.0.0" + jest-message-util "^26.0.1" + jest-util "^26.0.1" slash "^3.0.0" -"@jest/core@^25.3.0": - version "25.3.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-25.3.0.tgz#80f97a7a8b59dde741a24f30871cc26d0197d426" - integrity sha512-+D5a/tFf6pA/Gqft2DLBp/yeSRgXhlJ+Wpst0X/ZkfTRP54qDR3C61VfHwaex+GzZBiTcE9vQeoZ2v5T10+Mqw== +"@jest/core@^26.0.1": + version "26.0.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-26.0.1.tgz#aa538d52497dfab56735efb00e506be83d841fae" + integrity sha512-Xq3eqYnxsG9SjDC+WLeIgf7/8KU6rddBxH+SCt18gEpOhAGYC/Mq+YbtlNcIdwjnnT+wDseXSbU0e5X84Y4jTQ== dependencies: - "@jest/console" "^25.3.0" - "@jest/reporters" "^25.3.0" - "@jest/test-result" "^25.3.0" - "@jest/transform" "^25.3.0" - "@jest/types" "^25.3.0" + "@jest/console" "^26.0.1" + "@jest/reporters" "^26.0.1" + "@jest/test-result" "^26.0.1" + "@jest/transform" "^26.0.1" + "@jest/types" "^26.0.1" ansi-escapes "^4.2.1" - chalk "^3.0.0" + chalk "^4.0.0" exit "^0.1.2" - graceful-fs "^4.2.3" - jest-changed-files "^25.3.0" - jest-config "^25.3.0" - jest-haste-map "^25.3.0" - jest-message-util "^25.3.0" - jest-regex-util "^25.2.6" - jest-resolve "^25.3.0" - jest-resolve-dependencies "^25.3.0" - jest-runner "^25.3.0" - jest-runtime "^25.3.0" - jest-snapshot "^25.3.0" - jest-util "^25.3.0" - jest-validate "^25.3.0" - jest-watcher "^25.3.0" + graceful-fs "^4.2.4" + jest-changed-files "^26.0.1" + jest-config "^26.0.1" + jest-haste-map "^26.0.1" + jest-message-util "^26.0.1" + jest-regex-util "^26.0.0" + jest-resolve "^26.0.1" + jest-resolve-dependencies "^26.0.1" + jest-runner "^26.0.1" + jest-runtime "^26.0.1" + jest-snapshot "^26.0.1" + jest-util "^26.0.1" + jest-validate "^26.0.1" + jest-watcher "^26.0.1" micromatch "^4.0.2" p-each-series "^2.1.0" - realpath-native "^2.0.0" rimraf "^3.0.0" slash "^3.0.0" strip-ansi "^6.0.0" -"@jest/environment@^25.3.0": - version "25.3.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-25.3.0.tgz#587f28ddb4b0dfe97404d3d4a4c9dbfa0245fb2e" - integrity sha512-vgooqwJTHLLak4fE+TaCGeYP7Tz1Y3CKOsNxR1sE0V3nx3KRUHn3NUnt+wbcfd5yQWKZQKAfW6wqbuwQLrXo3g== +"@jest/environment@^26.0.1": + version "26.0.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-26.0.1.tgz#82f519bba71959be9b483675ee89de8c8f72a5c8" + integrity sha512-xBDxPe8/nx251u0VJ2dFAFz2H23Y98qdIaNwnMK6dFQr05jc+Ne/2np73lOAx+5mSBO/yuQldRrQOf6hP1h92g== + dependencies: + "@jest/fake-timers" "^26.0.1" + "@jest/types" "^26.0.1" + jest-mock "^26.0.1" + +"@jest/fake-timers@^26.0.1": + version "26.0.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-26.0.1.tgz#f7aeff13b9f387e9d0cac9a8de3bba538d19d796" + integrity sha512-Oj/kCBnTKhm7CR+OJSjZty6N1bRDr9pgiYQr4wY221azLz5PHi08x/U+9+QpceAYOWheauLP8MhtSVFrqXQfhg== dependencies: - "@jest/fake-timers" "^25.3.0" - "@jest/types" "^25.3.0" - jest-mock "^25.3.0" + "@jest/types" "^26.0.1" + "@sinonjs/fake-timers" "^6.0.1" + jest-message-util "^26.0.1" + jest-mock "^26.0.1" + jest-util "^26.0.1" -"@jest/fake-timers@^25.3.0": - version "25.3.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-25.3.0.tgz#995aad36d5c8984165ca5db12e740ab8dbf7042a" - integrity sha512-NHAj7WbsyR3qBJPpBwSwqaq2WluIvUQsyzpJTN7XDVk7VnlC/y1BAnaYZL3vbPIP8Nhm0Ae5DJe0KExr/SdMJQ== +"@jest/globals@^26.0.1": + version "26.0.1" + resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-26.0.1.tgz#3f67b508a7ce62b6e6efc536f3d18ec9deb19a9c" + integrity sha512-iuucxOYB7BRCvT+TYBzUqUNuxFX1hqaR6G6IcGgEqkJ5x4htNKo1r7jk1ji9Zj8ZMiMw0oB5NaA7k5Tx6MVssA== dependencies: - "@jest/types" "^25.3.0" - jest-message-util "^25.3.0" - jest-mock "^25.3.0" - jest-util "^25.3.0" - lolex "^5.0.0" + "@jest/environment" "^26.0.1" + "@jest/types" "^26.0.1" + expect "^26.0.1" -"@jest/reporters@^25.3.0": - version "25.3.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-25.3.0.tgz#7f39f0e6911561cc5112a1b54656de18faee269b" - integrity sha512-1u0ZBygs0C9DhdYgLCrRfZfNKQa+9+J7Uo+Z9z0RWLHzgsxhoG32lrmMOtUw48yR6bLNELdvzormwUqSk4H4Vg== +"@jest/reporters@^26.0.1": + version "26.0.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-26.0.1.tgz#14ae00e7a93e498cec35b0c00ab21c375d9b078f" + integrity sha512-NWWy9KwRtE1iyG/m7huiFVF9YsYv/e+mbflKRV84WDoJfBqUrNRyDbL/vFxQcYLl8IRqI4P3MgPn386x76Gf2g== dependencies: "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^25.3.0" - "@jest/test-result" "^25.3.0" - "@jest/transform" "^25.3.0" - "@jest/types" "^25.3.0" - chalk "^3.0.0" + "@jest/console" "^26.0.1" + "@jest/test-result" "^26.0.1" + "@jest/transform" "^26.0.1" + "@jest/types" "^26.0.1" + chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.2" + graceful-fs "^4.2.4" istanbul-lib-coverage "^3.0.0" istanbul-lib-instrument "^4.0.0" istanbul-lib-report "^3.0.0" istanbul-lib-source-maps "^4.0.0" istanbul-reports "^3.0.2" - jest-haste-map "^25.3.0" - jest-resolve "^25.3.0" - jest-util "^25.3.0" - jest-worker "^25.2.6" + jest-haste-map "^26.0.1" + jest-resolve "^26.0.1" + jest-util "^26.0.1" + jest-worker "^26.0.0" slash "^3.0.0" source-map "^0.6.0" - string-length "^3.1.0" + string-length "^4.0.1" terminal-link "^2.0.0" - v8-to-istanbul "^4.0.1" + v8-to-istanbul "^4.1.3" optionalDependencies: - node-notifier "^6.0.0" + node-notifier "^7.0.0" -"@jest/source-map@^25.2.6": - version "25.2.6" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-25.2.6.tgz#0ef2209514c6d445ebccea1438c55647f22abb4c" - integrity sha512-VuIRZF8M2zxYFGTEhkNSvQkUKafQro4y+mwUxy5ewRqs5N/ynSFUODYp3fy1zCnbCMy1pz3k+u57uCqx8QRSQQ== +"@jest/source-map@^26.0.0": + version "26.0.0" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-26.0.0.tgz#fd7706484a7d3faf7792ae29783933bbf48a4749" + integrity sha512-S2Z+Aj/7KOSU2TfW0dyzBze7xr95bkm5YXNUqqCek+HE0VbNNSNzrRwfIi5lf7wvzDTSS0/ib8XQ1krFNyYgbQ== dependencies: callsites "^3.0.0" - graceful-fs "^4.2.3" + graceful-fs "^4.2.4" source-map "^0.6.0" -"@jest/test-result@^25.3.0": - version "25.3.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-25.3.0.tgz#137fab5e5c6fed36e5d40735d1eb029325e3bf06" - integrity sha512-mqrGuiiPXl1ap09Mydg4O782F3ouDQfsKqtQzIjitpwv3t1cHDwCto21jThw6WRRE+dKcWQvLG70GpyLJICfGw== +"@jest/test-result@^26.0.1": + version "26.0.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-26.0.1.tgz#1ffdc1ba4bc289919e54b9414b74c9c2f7b2b718" + integrity sha512-oKwHvOI73ICSYRPe8WwyYPTtiuOAkLSbY8/MfWF3qDEd/sa8EDyZzin3BaXTqufir/O/Gzea4E8Zl14XU4Mlyg== dependencies: - "@jest/console" "^25.3.0" - "@jest/types" "^25.3.0" + "@jest/console" "^26.0.1" + "@jest/types" "^26.0.1" "@types/istanbul-lib-coverage" "^2.0.0" collect-v8-coverage "^1.0.0" -"@jest/test-sequencer@^25.3.0": - version "25.3.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-25.3.0.tgz#271ad5f2b8f8137d092ccedc87e16a50f8676209" - integrity sha512-Xvns3xbji7JCvVcDGvqJ/pf4IpmohPODumoPEZJ0/VgC5gI4XaNVIBET2Dq5Czu6Gk3xFcmhtthh/MBOTljdNg== +"@jest/test-sequencer@^26.0.1": + version "26.0.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-26.0.1.tgz#b0563424728f3fe9e75d1442b9ae4c11da73f090" + integrity sha512-ssga8XlwfP8YjbDcmVhwNlrmblddMfgUeAkWIXts1V22equp2GMIHxm7cyeD5Q/B0ZgKPK/tngt45sH99yLLGg== dependencies: - "@jest/test-result" "^25.3.0" - jest-haste-map "^25.3.0" - jest-runner "^25.3.0" - jest-runtime "^25.3.0" + "@jest/test-result" "^26.0.1" + graceful-fs "^4.2.4" + jest-haste-map "^26.0.1" + jest-runner "^26.0.1" + jest-runtime "^26.0.1" -"@jest/transform@^25.3.0": - version "25.3.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-25.3.0.tgz#083c5447d5307d9b9494d6968115b647460e71f1" - integrity sha512-W01p8kTDvvEX6kd0tJc7Y5VdYyFaKwNWy1HQz6Jqlhu48z/8Gxp+yFCDVj+H8Rc7ezl3Mg0hDaGuFVkmHOqirg== +"@jest/transform@^26.0.1": + version "26.0.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-26.0.1.tgz#0e3ecbb34a11cd4b2080ed0a9c4856cf0ceb0639" + integrity sha512-pPRkVkAQ91drKGbzCfDOoHN838+FSbYaEAvBXvKuWeeRRUD8FjwXkqfUNUZL6Ke48aA/1cqq/Ni7kVMCoqagWA== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^25.3.0" + "@jest/types" "^26.0.1" babel-plugin-istanbul "^6.0.0" - chalk "^3.0.0" + chalk "^4.0.0" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" - graceful-fs "^4.2.3" - jest-haste-map "^25.3.0" - jest-regex-util "^25.2.6" - jest-util "^25.3.0" + graceful-fs "^4.2.4" + jest-haste-map "^26.0.1" + jest-regex-util "^26.0.0" + jest-util "^26.0.1" micromatch "^4.0.2" pirates "^4.0.1" - realpath-native "^2.0.0" slash "^3.0.0" source-map "^0.6.1" write-file-atomic "^3.0.0" @@ -1837,16 +1856,26 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^13.0.0" -"@jest/types@^25.3.0": - version "25.3.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.3.0.tgz#88f94b277a1d028fd7117bc1f74451e0fc2131e7" - integrity sha512-UkaDNewdqXAmCDbN2GlUM6amDKS78eCqiw/UmF5nE0mmLTd6moJkiZJML/X52Ke3LH7Swhw883IRXq8o9nWjVw== +"@jest/types@^25.5.0": + version "25.5.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.5.0.tgz#4d6a4793f7b9599fc3680877b856a97dbccf2a9d" + integrity sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw== dependencies: "@types/istanbul-lib-coverage" "^2.0.0" "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^15.0.0" chalk "^3.0.0" +"@jest/types@^26.0.1": + version "26.0.1" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.0.1.tgz#b78333fbd113fa7aec8d39de24f88de8686dac67" + integrity sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA== + dependencies: + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^1.1.1" + "@types/yargs" "^15.0.0" + chalk "^4.0.0" + "@kbrandwijk/swagger-to-graphql@2.4.3": version "2.4.3" resolved "https://registry.yarnpkg.com/@kbrandwijk/swagger-to-graphql/-/swagger-to-graphql-2.4.3.tgz#7c0fb2410eb0b6b9cc81fad28cc20f9386153cf1" @@ -2063,6 +2092,13 @@ dependencies: type-detect "4.0.8" +"@sinonjs/fake-timers@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz#293674fccb3262ac782c7aadfdeca86b10c75c40" + integrity sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA== + dependencies: + "@sinonjs/commons" "^1.7.0" + "@storybook/addons@5.2.8": version "5.2.8" resolved "https://registry.yarnpkg.com/@storybook/addons/-/addons-5.2.8.tgz#f8bf8bd555b7a69fb1e9a52ab8cdb96384d931ff" @@ -2615,6 +2651,13 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/graceful-fs@^4.1.2": + version "4.1.3" + resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.3.tgz#039af35fe26bec35003e8d86d2ee9c586354348f" + integrity sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ== + dependencies: + "@types/node" "*" + "@types/history@*": version "4.7.3" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.3.tgz#856c99cdc1551d22c22b18b5402719affec9839a" @@ -2652,12 +2695,13 @@ "@types/istanbul-lib-coverage" "*" "@types/istanbul-lib-report" "*" -"@types/jest@~24.0.18": - version "24.0.25" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-24.0.25.tgz#2aba377824ce040114aa906ad2cac2c85351360f" - integrity sha512-hnP1WpjN4KbGEK4dLayul6lgtys6FPz0UfxMeMQCv0M+sTnzN3ConfiO72jHgLxl119guHgI8gLqDOrRLsyp2g== +"@types/jest@~25.2.1": + version "25.2.1" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.2.1.tgz#9544cd438607955381c1bdbdb97767a249297db5" + integrity sha512-msra1bCaAeEdkSyA0CZ6gW1ukMIvZ5YoJkdXw/qhQdsuuDlFTcEUrUw8CLCPt2rVRUfXlClVvK2gvPs9IokZaA== dependencies: - jest-diff "^24.3.0" + jest-diff "^25.2.1" + pretty-format "^25.2.1" "@types/loader-utils@^1.1.3": version "1.1.3" @@ -2726,10 +2770,10 @@ resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== -"@types/prettier@^1.19.0": - version "1.19.1" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" - integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== +"@types/prettier@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.0.0.tgz#dc85454b953178cc6043df5208b9e949b54a3bc4" + integrity sha512-/rM+sWiuOZ5dvuVzV37sUuklsbg+JPOP8d+nNFlo2ZtfpzPiPvh1/gc8liWOLBqe+sR+ZM7guPaIcTt6UZTo7Q== "@types/prop-types@*": version "15.7.3" @@ -3052,7 +3096,7 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -abab@^2.0.0, abab@^2.0.3: +abab@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" integrity sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg== @@ -3070,14 +3114,6 @@ accepts@^1.3.0, accepts@^1.3.5, accepts@^1.3.7, accepts@~1.3.4, accepts@~1.3.5, mime-types "~2.1.24" negotiator "0.6.2" -acorn-globals@^4.3.2: - version "4.3.4" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.4.tgz#9fa1926addc11c97308c4e66d7add0d40c3272e7" - integrity sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A== - dependencies: - acorn "^6.0.1" - acorn-walk "^6.0.1" - acorn-globals@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" @@ -3091,7 +3127,7 @@ acorn-jsx@^5.0.0: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.1.0.tgz#294adb71b57398b0680015f0a38c563ee1db5384" integrity sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw== -acorn-walk@^6.0.1, acorn-walk@^6.1.1: +acorn-walk@^6.1.1: version "6.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" integrity sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA== @@ -3101,7 +3137,7 @@ acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.1.1.tgz#345f0dffad5c735e7373d2fec9a1023e6a44b83e" integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ== -acorn@^6.0.1, acorn@^6.0.7, acorn@^6.2.1: +acorn@^6.0.7, acorn@^6.2.1: version "6.4.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.0.tgz#b659d2ffbafa24baf5db1cdbb2c94a983ecd2784" integrity sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw== @@ -3111,7 +3147,7 @@ acorn@^6.2.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== -acorn@^7.1.0, acorn@^7.1.1: +acorn@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== @@ -3695,11 +3731,6 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= -array-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" - integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= - array-filter@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" @@ -3994,17 +4025,18 @@ babel-helper-to-multiple-sequence-expressions@^0.5.0: resolved "https://registry.yarnpkg.com/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.5.0.tgz#a3f924e3561882d42fcf48907aa98f7979a4588d" integrity sha512-m2CvfDW4+1qfDdsrtf4dwOslQC3yhbgyBFptncp4wvtdrDHqueW7slsYv4gArie056phvQFhT2nRcGS4bnm6mA== -babel-jest@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-25.3.0.tgz#999d0c19e8427f66b796bf9ea233eedf087b957c" - integrity sha512-qiXeX1Cmw4JZ5yQ4H57WpkO0MZ61Qj+YnsVUwAMnDV5ls+yHon11XjarDdgP7H8lTmiEi6biiZA8y3Tmvx6pCg== +babel-jest@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-26.0.1.tgz#450139ce4b6c17174b136425bda91885c397bc46" + integrity sha512-Z4GGmSNQ8pX3WS1O+6v3fo41YItJJZsVxG5gIQ+HuB/iuAQBJxMTHTwz292vuYws1LnHfwSRgoqI+nxdy/pcvw== dependencies: - "@jest/transform" "^25.3.0" - "@jest/types" "^25.3.0" + "@jest/transform" "^26.0.1" + "@jest/types" "^26.0.1" "@types/babel__core" "^7.1.7" babel-plugin-istanbul "^6.0.0" - babel-preset-jest "^25.3.0" - chalk "^3.0.0" + babel-preset-jest "^26.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.4" slash "^3.0.0" babel-jest@~24.1.0: @@ -4123,11 +4155,13 @@ babel-plugin-jest-hoist@^24.9.0: dependencies: "@types/babel__traverse" "^7.0.6" -babel-plugin-jest-hoist@^25.2.6: - version "25.2.6" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.2.6.tgz#2af07632b8ac7aad7d414c1e58425d5fc8e84909" - integrity sha512-qE2xjMathybYxjiGFJg0mLFrz0qNp83aNZycWDY/SuHiZNq+vQfRQtuINqyXyue1ELd8Rd+1OhFSLjms8msMbw== +babel-plugin-jest-hoist@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.0.0.tgz#fd1d35f95cf8849fc65cb01b5e58aedd710b34a8" + integrity sha512-+AuoehOrjt9irZL7DOt2+4ZaTM6dlu1s5TTS46JBa0/qem4dy7VNW3tMb96qeEqcIh20LD73TVNtmVEeymTG7w== dependencies: + "@babel/template" "^7.3.3" + "@babel/types" "^7.3.3" "@types/babel__traverse" "^7.0.6" babel-plugin-macros@2.7.1: @@ -4355,12 +4389,12 @@ babel-preset-jest@^24.1.0: "@babel/plugin-syntax-object-rest-spread" "^7.0.0" babel-plugin-jest-hoist "^24.9.0" -babel-preset-jest@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-25.3.0.tgz#9ab40aee52a19bdc52b8b1ec2403d5914ac3d86b" - integrity sha512-tjdvLKNMwDI9r+QWz9sZUQGTq1dpoxjUqFUpEasAc7MOtHg9XuLT2fx0udFG+k1nvMV0WvHHVAN7VmCZ+1Zxbw== +babel-preset-jest@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-26.0.0.tgz#1eac82f513ad36c4db2e9263d7c485c825b1faa6" + integrity sha512-9ce+DatAa31DpR4Uir8g4Ahxs5K4W4L8refzt+qHWQANb6LhGcAEfIFgLUwk67oya2cCUd6t4eUMtO/z64ocNw== dependencies: - babel-plugin-jest-hoist "^25.2.6" + babel-plugin-jest-hoist "^26.0.0" babel-preset-current-node-syntax "^0.1.2" "babel-preset-minify@^0.5.0 || 0.6.0-alpha.5": @@ -4719,23 +4753,11 @@ brotli-size@0.1.0: duplexer "^0.1.1" iltorb "^2.4.3" -browser-process-hrtime@^0.1.2: - version "0.1.3" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz#616f00faef1df7ec1b5bf9cfe2bdc3170f26c7b4" - integrity sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw== - browser-process-hrtime@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browser-resolve@^1.11.3: - version "1.11.3" - resolved "https://registry.yarnpkg.com/browser-resolve/-/browser-resolve-1.11.3.tgz#9b7cbb3d0f510e4cb86bdbd796124d28b5890af6" - integrity sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ== - dependencies: - resolve "1.1.7" - browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" @@ -5038,6 +5060,11 @@ camelcase@^5.0.0, camelcase@^5.2.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== +camelcase@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.0.0.tgz#5259f7c30e35e278f1bdc2a4d91230b37cad981e" + integrity sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w== + camelspace@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/camelspace/-/camelspace-1.0.2.tgz#ac1ff8f2792536d2908e1fa05d3779208a486d4c" @@ -5135,6 +5162,14 @@ chalk@^3.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.0.0.tgz#6e98081ed2d17faab615eb52ac66ec1fe6209e72" + integrity sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + change-case@^3.0.1, change-case@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/change-case/-/change-case-3.1.0.tgz#0e611b7edc9952df2e8513b27b42de72647dd17e" @@ -5159,6 +5194,11 @@ change-case@^3.0.1, change-case@^3.1.0: upper-case "^1.1.1" upper-case-first "^1.1.0" +char-regex@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" + integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== + character-entities-legacy@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.3.tgz#3c729991d9293da0ede6dddcaf1f2ce1009ee8b4" @@ -6039,7 +6079,7 @@ csso@^4.0.2: dependencies: css-tree "1.0.0-alpha.37" -cssom@^0.4.1, cssom@^0.4.4: +cssom@^0.4.4: version "0.4.4" resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10" integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw== @@ -6049,7 +6089,7 @@ cssom@~0.3.6: resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== -cssstyle@^2.0.0, cssstyle@^2.2.0: +cssstyle@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.2.0.tgz#e4c44debccd6b7911ed617a4395e5754bba59992" integrity sha512-sEb3XFPx3jNnCAMtqrXPDeSgQr+jojtCeNf8cvMNMh1cG970+lljssvQDzPq6lmmJu2Vhqood/gtEomBiHOGnA== @@ -6135,15 +6175,6 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -data-urls@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-1.1.0.tgz#15ee0582baa5e22bb59c77140da8f9c76963bbfe" - integrity sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ== - dependencies: - abab "^2.0.0" - whatwg-mimetype "^2.2.0" - whatwg-url "^7.0.0" - data-urls@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" @@ -6442,16 +6473,16 @@ dicer@0.3.0: dependencies: streamsearch "0.1.2" -diff-sequences@^24.9.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5" - integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew== - diff-sequences@^25.2.6: version "25.2.6" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== +diff-sequences@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.0.0.tgz#0760059a5c287637b842bd7085311db7060e88a6" + integrity sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg== + diff@^1.3.2: version "1.4.0" resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" @@ -6589,13 +6620,6 @@ domelementtype@^2.0.1: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== -domexception@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" - integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug== - dependencies: - webidl-conversions "^4.0.2" - domexception@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" @@ -6995,7 +7019,12 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escodegen@^1.11.1, escodegen@^1.14.1: +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escodegen@^1.14.1: version "1.14.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.1.tgz#ba01d0c8278b5e95a9a45350142026659027a457" integrity sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ== @@ -7309,10 +7338,10 @@ execa@^1.0.0, execa@~1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -execa@^3.2.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-3.4.0.tgz#c08ed4550ef65d858fac269ffc8572446f37eb89" - integrity sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g== +execa@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.0.tgz#7f37d6ec17f09e6b8fc53288611695b6d12b9daf" + integrity sha512-JbDUxwV3BoT5ZVXQrSVbAiaXhXUkIwvbhPIwZ0N13kX+5yCzOhUNdocxB/UQRuYOHRYYwAxKYwJYc0T4D12pDA== dependencies: cross-spawn "^7.0.0" get-stream "^5.0.0" @@ -7321,7 +7350,6 @@ execa@^3.2.0: merge-stream "^2.0.0" npm-run-path "^4.0.0" onetime "^5.1.0" - p-finally "^2.0.0" signal-exit "^3.0.2" strip-final-newline "^2.0.0" @@ -7360,17 +7388,17 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2: dependencies: homedir-polyfill "^1.0.1" -expect@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-25.3.0.tgz#5fd36e51befd05afb7184bc954f8a4792d184c71" - integrity sha512-buboTXML2h/L0Kh44Ys2Cx49mX20ISc5KDirkxIs3Q9AJv0kazweUAbukegr+nHDOvFRKmxdojjIHCjqAceYfg== +expect@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-26.0.1.tgz#18697b9611a7e2725e20ba3ceadda49bc9865421" + integrity sha512-QcCy4nygHeqmbw564YxNbHTJlXh47dVID2BUP52cZFpLU9zHViMFK6h07cC1wf7GYCTIigTdAXhVua8Yl1FkKg== dependencies: - "@jest/types" "^25.3.0" + "@jest/types" "^26.0.1" ansi-styles "^4.0.0" - jest-get-type "^25.2.6" - jest-matcher-utils "^25.3.0" - jest-message-util "^25.3.0" - jest-regex-util "^25.2.6" + jest-get-type "^26.0.0" + jest-matcher-utils "^26.0.1" + jest-message-util "^26.0.1" + jest-regex-util "^26.0.0" express-request-proxy@^2.2.2: version "2.2.2" @@ -8384,11 +8412,16 @@ got@^9.6.0: to-readable-stream "^1.0.0" url-parse-lax "^3.0.0" -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.3: +graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== +graceful-fs@^4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" + integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== + graphcool-json-schema@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/graphcool-json-schema/-/graphcool-json-schema-1.2.1.tgz#6cefb6c8b50543615e6efa43bb54f9e3fbb281f3" @@ -8898,13 +8931,6 @@ html-element-map@^1.0.0: dependencies: array-filter "^1.0.0" -html-encoding-sniffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" - integrity sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw== - dependencies: - whatwg-encoding "^1.0.1" - html-encoding-sniffer@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" @@ -10029,119 +10055,118 @@ jarallax@~1.11.1: rafl "^1.2.2" video-worker "^1.1.6" -jest-changed-files@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-25.3.0.tgz#85d8de6f4bd13dafda9d7f1e3f2565fc0e183c78" - integrity sha512-eqd5hyLbUjIVvLlJ3vQ/MoPxsxfESVXG9gvU19XXjKzxr+dXmZIqCXiY0OiYaibwlHZBJl2Vebkc0ADEMzCXew== +jest-changed-files@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.0.1.tgz#1334630c6a1ad75784120f39c3aa9278e59f349f" + integrity sha512-q8LP9Sint17HaE2LjxQXL+oYWW/WeeXMPE2+Op9X3mY8IEGFVc14xRxFjUuXUbcPAlDLhtWdIEt59GdQbn76Hw== dependencies: - "@jest/types" "^25.3.0" - execa "^3.2.0" + "@jest/types" "^26.0.1" + execa "^4.0.0" throat "^5.0.0" -jest-cli@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-25.3.0.tgz#d9e11f5700cc5946583cf0d01a9bdebceed448d2" - integrity sha512-XpNQPlW1tzpP7RGG8dxpkRegYDuLjzSiENu92+CYM87nEbmEPb3b4+yo8xcsHOnj0AG7DUt9b3uG8LuHI3MDzw== +jest-cli@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-26.0.1.tgz#3a42399a4cbc96a519b99ad069a117d955570cac" + integrity sha512-pFLfSOBcbG9iOZWaMK4Een+tTxi/Wcm34geqZEqrst9cZDkTQ1LZ2CnBrTlHWuYAiTMFr0EQeK52ScyFU8wK+w== dependencies: - "@jest/core" "^25.3.0" - "@jest/test-result" "^25.3.0" - "@jest/types" "^25.3.0" - chalk "^3.0.0" + "@jest/core" "^26.0.1" + "@jest/test-result" "^26.0.1" + "@jest/types" "^26.0.1" + chalk "^4.0.0" exit "^0.1.2" + graceful-fs "^4.2.4" import-local "^3.0.2" is-ci "^2.0.0" - jest-config "^25.3.0" - jest-util "^25.3.0" - jest-validate "^25.3.0" + jest-config "^26.0.1" + jest-util "^26.0.1" + jest-validate "^26.0.1" prompts "^2.0.1" - realpath-native "^2.0.0" yargs "^15.3.1" -jest-config@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-25.3.0.tgz#112b5e2f2e57dec4501dd2fe979044c06fb1317e" - integrity sha512-CmF1JnNWFmoCSPC4tnU52wnVBpuxHjilA40qH/03IHxIevkjUInSMwaDeE6ACfxMPTLidBGBCO3EbxvzPbo8wA== +jest-config@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-26.0.1.tgz#096a3d4150afadf719d1fab00e9a6fb2d6d67507" + integrity sha512-9mWKx2L1LFgOXlDsC4YSeavnblN6A4CPfXFiobq+YYLaBMymA/SczN7xYTSmLaEYHZOcB98UdoN4m5uNt6tztg== dependencies: "@babel/core" "^7.1.0" - "@jest/test-sequencer" "^25.3.0" - "@jest/types" "^25.3.0" - babel-jest "^25.3.0" - chalk "^3.0.0" + "@jest/test-sequencer" "^26.0.1" + "@jest/types" "^26.0.1" + babel-jest "^26.0.1" + chalk "^4.0.0" deepmerge "^4.2.2" glob "^7.1.1" - jest-environment-jsdom "^25.3.0" - jest-environment-node "^25.3.0" - jest-get-type "^25.2.6" - jest-jasmine2 "^25.3.0" - jest-regex-util "^25.2.6" - jest-resolve "^25.3.0" - jest-util "^25.3.0" - jest-validate "^25.3.0" + graceful-fs "^4.2.4" + jest-environment-jsdom "^26.0.1" + jest-environment-node "^26.0.1" + jest-get-type "^26.0.0" + jest-jasmine2 "^26.0.1" + jest-regex-util "^26.0.0" + jest-resolve "^26.0.1" + jest-util "^26.0.1" + jest-validate "^26.0.1" micromatch "^4.0.2" - pretty-format "^25.3.0" - realpath-native "^2.0.0" + pretty-format "^26.0.1" -jest-diff@^24.3.0: - version "24.9.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.9.0.tgz#931b7d0d5778a1baf7452cb816e325e3724055da" - integrity sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ== - dependencies: - chalk "^2.0.1" - diff-sequences "^24.9.0" - jest-get-type "^24.9.0" - pretty-format "^24.9.0" - -jest-diff@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.3.0.tgz#0d7d6f5d6171e5dacde9e05be47b3615e147c26f" - integrity sha512-vyvs6RPoVdiwARwY4kqFWd4PirPLm2dmmkNzKqo38uZOzJvLee87yzDjIZLmY1SjM3XR5DwsUH+cdQ12vgqi1w== +jest-diff@^25.2.1: + version "25.5.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.5.0.tgz#1dd26ed64f96667c068cef026b677dfa01afcfa9" + integrity sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A== dependencies: chalk "^3.0.0" diff-sequences "^25.2.6" jest-get-type "^25.2.6" - pretty-format "^25.3.0" + pretty-format "^25.5.0" -jest-docblock@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-25.3.0.tgz#8b777a27e3477cd77a168c05290c471a575623ef" - integrity sha512-aktF0kCar8+zxRHxQZwxMy70stc9R1mOmrLsT5VO3pIT0uzGRSDAXxSlz4NqQWpuLjPpuMhPRl7H+5FRsvIQAg== +jest-diff@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.0.1.tgz#c44ab3cdd5977d466de69c46929e0e57f89aa1de" + integrity sha512-odTcHyl5X+U+QsczJmOjWw5tPvww+y9Yim5xzqxVl/R1j4z71+fHW4g8qu1ugMmKdFdxw+AtQgs5mupPnzcIBQ== dependencies: - detect-newline "^3.0.0" + chalk "^4.0.0" + diff-sequences "^26.0.0" + jest-get-type "^26.0.0" + pretty-format "^26.0.1" -jest-each@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-25.3.0.tgz#a319eecf1f6076164ab86f99ca166a55b96c0bd4" - integrity sha512-aBfS4VOf/Qs95yUlX6d6WBv0szvOcTkTTyCIaLuQGj4bSHsT+Wd9dDngVHrCe5uytxpN8VM+NAloI6nbPjXfXw== +jest-docblock@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-26.0.0.tgz#3e2fa20899fc928cb13bd0ff68bd3711a36889b5" + integrity sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w== dependencies: - "@jest/types" "^25.3.0" - chalk "^3.0.0" - jest-get-type "^25.2.6" - jest-util "^25.3.0" - pretty-format "^25.3.0" - -jest-environment-jsdom@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-25.3.0.tgz#c493ab8c41f28001520c70ef67dd88b88be6af05" - integrity sha512-jdE4bQN+k2QEZ9sWOxsqDJvMzbdFSCN/4tw8X0TQaCqyzKz58PyEf41oIr4WO7ERdp7WaJGBSUKF7imR3UW1lg== - dependencies: - "@jest/environment" "^25.3.0" - "@jest/fake-timers" "^25.3.0" - "@jest/types" "^25.3.0" - jest-mock "^25.3.0" - jest-util "^25.3.0" - jsdom "^15.2.1" - -jest-environment-node@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-25.3.0.tgz#9845f0e63991e8498448cb0ae804935689533db9" - integrity sha512-XO09S29Nx1NU7TiMPHMoDIkxoGBuKSTbE+sHp0gXbeLDXhIdhysUI25kOqFFSD9AuDgvPvxWCXrvNqiFsOH33g== - dependencies: - "@jest/environment" "^25.3.0" - "@jest/fake-timers" "^25.3.0" - "@jest/types" "^25.3.0" - jest-mock "^25.3.0" - jest-util "^25.3.0" - semver "^6.3.0" + detect-newline "^3.0.0" + +jest-each@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-26.0.1.tgz#633083061619302fc90dd8f58350f9d77d67be04" + integrity sha512-OTgJlwXCAR8NIWaXFL5DBbeS4QIYPuNASkzSwMCJO+ywo9BEa6TqkaSWsfR7VdbMLdgYJqSfQcIyjJCNwl5n4Q== + dependencies: + "@jest/types" "^26.0.1" + chalk "^4.0.0" + jest-get-type "^26.0.0" + jest-util "^26.0.1" + pretty-format "^26.0.1" + +jest-environment-jsdom@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-26.0.1.tgz#217690852e5bdd7c846a4e3b50c8ffd441dfd249" + integrity sha512-u88NJa3aptz2Xix2pFhihRBAatwZHWwSiRLBDBQE1cdJvDjPvv7ZGA0NQBxWwDDn7D0g1uHqxM8aGgfA9Bx49g== + dependencies: + "@jest/environment" "^26.0.1" + "@jest/fake-timers" "^26.0.1" + "@jest/types" "^26.0.1" + jest-mock "^26.0.1" + jest-util "^26.0.1" + jsdom "^16.2.2" + +jest-environment-node@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-26.0.1.tgz#584a9ff623124ff6eeb49e0131b5f7612b310b13" + integrity sha512-4FRBWcSn5yVo0KtNav7+5NH5Z/tEgDLp7VRQVS5tCouWORxj+nI+1tOLutM07Zb2Qi7ja+HEDoOUkjBSWZg/IQ== + dependencies: + "@jest/environment" "^26.0.1" + "@jest/fake-timers" "^26.0.1" + "@jest/types" "^26.0.1" + jest-mock "^26.0.1" + jest-util "^26.0.1" jest-fetch-mock@~2.1.1: version "2.1.2" @@ -10161,18 +10186,24 @@ jest-get-type@^25.2.6: resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877" integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig== -jest-haste-map@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-25.3.0.tgz#b7683031c9c9ddc0521d311564108b244b11e4c6" - integrity sha512-LjXaRa+F8wwtSxo9G+hHD/Cp63PPQzvaBL9XCVoJD2rrcJO0Zr2+YYzAFWWYJ5GlPUkoaJFJtOuk0sL6MJY80A== +jest-get-type@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-26.0.0.tgz#381e986a718998dbfafcd5ec05934be538db4039" + integrity sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg== + +jest-haste-map@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-26.0.1.tgz#40dcc03c43ac94d25b8618075804d09cd5d49de7" + integrity sha512-J9kBl/EdjmDsvyv7CiyKY5+DsTvVOScenprz/fGqfLg/pm1gdjbwwQ98nW0t+OIt+f+5nAVaElvn/6wP5KO7KA== dependencies: - "@jest/types" "^25.3.0" + "@jest/types" "^26.0.1" + "@types/graceful-fs" "^4.1.2" anymatch "^3.0.3" fb-watchman "^2.0.0" - graceful-fs "^4.2.3" - jest-serializer "^25.2.6" - jest-util "^25.3.0" - jest-worker "^25.2.6" + graceful-fs "^4.2.4" + jest-serializer "^26.0.0" + jest-util "^26.0.1" + jest-worker "^26.0.0" micromatch "^4.0.2" sane "^4.0.3" walker "^1.0.7" @@ -10180,205 +10211,214 @@ jest-haste-map@^25.3.0: optionalDependencies: fsevents "^2.1.2" -jest-jasmine2@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-25.3.0.tgz#16ae4f68adef65fb45001b26c864bcbcbf972830" - integrity sha512-NCYOGE6+HNzYFSui52SefgpsnIzvxjn6KAgqw66BdRp37xpMD/4kujDHLNW5bS5i53os5TcMn6jYrzQRO8VPrQ== +jest-jasmine2@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-26.0.1.tgz#947c40ee816636ba23112af3206d6fa7b23c1c1c" + integrity sha512-ILaRyiWxiXOJ+RWTKupzQWwnPaeXPIoLS5uW41h18varJzd9/7I0QJGqg69fhTT1ev9JpSSo9QtalriUN0oqOg== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^25.3.0" - "@jest/source-map" "^25.2.6" - "@jest/test-result" "^25.3.0" - "@jest/types" "^25.3.0" - chalk "^3.0.0" + "@jest/environment" "^26.0.1" + "@jest/source-map" "^26.0.0" + "@jest/test-result" "^26.0.1" + "@jest/types" "^26.0.1" + chalk "^4.0.0" co "^4.6.0" - expect "^25.3.0" + expect "^26.0.1" is-generator-fn "^2.0.0" - jest-each "^25.3.0" - jest-matcher-utils "^25.3.0" - jest-message-util "^25.3.0" - jest-runtime "^25.3.0" - jest-snapshot "^25.3.0" - jest-util "^25.3.0" - pretty-format "^25.3.0" + jest-each "^26.0.1" + jest-matcher-utils "^26.0.1" + jest-message-util "^26.0.1" + jest-runtime "^26.0.1" + jest-snapshot "^26.0.1" + jest-util "^26.0.1" + pretty-format "^26.0.1" throat "^5.0.0" -jest-junit@~6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-6.3.0.tgz#99e64ebc54eddcb21238f0cc49f5820c89a8c785" - integrity sha512-3PH9UkpaomX6CUzqjlnk0m4yBCW/eroxV6v61OM6LkCQFO848P3YUhfIzu8ypZSBKB3vvCbB4WaLTKT0BrIf8A== +jest-junit@~10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-10.0.0.tgz#c94b91c24920a327c9d2a075e897b2dba4af494b" + integrity sha512-dbOVRyxHprdSpwSAR9/YshLwmnwf+RSl5hf0kCGlhAcEeZY9aRqo4oNmaT0tLC16Zy9D0zekDjWkjHGjXlglaQ== dependencies: - jest-validate "^24.0.0" + jest-validate "^24.9.0" mkdirp "^0.5.1" - strip-ansi "^4.0.0" + strip-ansi "^5.2.0" + uuid "^3.3.3" xml "^1.0.1" -jest-leak-detector@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-25.3.0.tgz#5b6bf04903b35be56038915a55f47291771f769f" - integrity sha512-jk7k24dMIfk8LUSQQGN8PyOy9+J0NAfHZWiDmUDYVMctY8FLJQ1eQ8+PjMoN8PgwhLIggUqgYJnyRFvUz3jLRw== +jest-leak-detector@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-26.0.1.tgz#79b19ab3f41170e0a78eb8fa754a116d3447fb8c" + integrity sha512-93FR8tJhaYIWrWsbmVN1pQ9ZNlbgRpfvrnw5LmgLRX0ckOJ8ut/I35CL7awi2ecq6Ca4lL59bEK9hr7nqoHWPA== dependencies: - jest-get-type "^25.2.6" - pretty-format "^25.3.0" + jest-get-type "^26.0.0" + pretty-format "^26.0.1" -jest-matcher-utils@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.3.0.tgz#76765788a26edaa8bc5f0100aea52ae383559648" - integrity sha512-ZBUJ2fchNIZt+fyzkuCFBb8SKaU//Rln45augfUtbHaGyVxCO++ANARdBK9oPGXU3hEDgyy7UHnOP/qNOJXFUg== +jest-matcher-utils@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-26.0.1.tgz#12e1fc386fe4f14678f4cc8dbd5ba75a58092911" + integrity sha512-PUMlsLth0Azen8Q2WFTwnSkGh2JZ8FYuwijC8NR47vXKpsrKmA1wWvgcj1CquuVfcYiDEdj985u5Wmg7COEARw== dependencies: - chalk "^3.0.0" - jest-diff "^25.3.0" - jest-get-type "^25.2.6" - pretty-format "^25.3.0" + chalk "^4.0.0" + jest-diff "^26.0.1" + jest-get-type "^26.0.0" + pretty-format "^26.0.1" -jest-message-util@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.3.0.tgz#e3836826fe5ca538a337b87d9bd2648190867f85" - integrity sha512-5QNy9Id4WxJbRITEbA1T1kem9bk7y2fD0updZMSTNHtbEDnYOGLDPAuFBhFgVmOZpv0n6OMdVkK+WhyXEPCcOw== +jest-message-util@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.0.1.tgz#07af1b42fc450b4cc8e90e4c9cef11b33ce9b0ac" + integrity sha512-CbK8uQREZ8umUfo8+zgIfEt+W7HAHjQCoRaNs4WxKGhAYBGwEyvxuK81FXa7VeB9pwDEXeeKOB2qcsNVCAvB7Q== dependencies: "@babel/code-frame" "^7.0.0" - "@jest/types" "^25.3.0" + "@jest/types" "^26.0.1" "@types/stack-utils" "^1.0.1" - chalk "^3.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.4" micromatch "^4.0.2" slash "^3.0.0" - stack-utils "^1.0.1" + stack-utils "^2.0.2" -jest-mock@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-25.3.0.tgz#d72644509e40987a732a9a2534a1054f4649402c" - integrity sha512-yRn6GbuqB4j3aYu+Z1ezwRiZfp0o9om5uOcBovVtkcRLeBCNP5mT0ysdenUsxAHnQUgGwPOE1wwhtQYe6NKirQ== +jest-mock@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.0.1.tgz#7fd1517ed4955397cf1620a771dc2d61fad8fd40" + integrity sha512-MpYTBqycuPYSY6xKJognV7Ja46/TeRbAZept987Zp+tuJvMN0YBWyyhG9mXyYQaU3SBI0TUlSaO5L3p49agw7Q== dependencies: - "@jest/types" "^25.3.0" + "@jest/types" "^26.0.1" jest-pnp-resolver@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz#ecdae604c077a7fbc70defb6d517c3c1c898923a" integrity sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ== -jest-regex-util@^25.2.6: - version "25.2.6" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-25.2.6.tgz#d847d38ba15d2118d3b06390056028d0f2fd3964" - integrity sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw== +jest-regex-util@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-26.0.0.tgz#d25e7184b36e39fd466c3bc41be0971e821fee28" + integrity sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A== -jest-resolve-dependencies@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-25.3.0.tgz#b0e4ae053dd44ddacc18c6ee12b5b7c28e445a90" - integrity sha512-bDUlLYmHW+f7J7KgcY2lkq8EMRqKonRl0XoD4Wp5SJkgAxKJnsaIOlrrVNTfXYf+YOu3VCjm/Ac2hPF2nfsCIA== +jest-resolve-dependencies@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-26.0.1.tgz#607ba7ccc32151d185a477cff45bf33bce417f0b" + integrity sha512-9d5/RS/ft0vB/qy7jct/qAhzJsr6fRQJyGAFigK3XD4hf9kIbEH5gks4t4Z7kyMRhowU6HWm/o8ILqhaHdSqLw== dependencies: - "@jest/types" "^25.3.0" - jest-regex-util "^25.2.6" - jest-snapshot "^25.3.0" + "@jest/types" "^26.0.1" + jest-regex-util "^26.0.0" + jest-snapshot "^26.0.1" -jest-resolve@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-25.3.0.tgz#cb90a5bbea54a02eccdbbf4126a819595dcf91d6" - integrity sha512-IHoQAAybulsJ+ZgWis+ekYKDAoFkVH5Nx/znpb41zRtpxj4fr2WNV9iDqavdSm8GIpMlsfZxbC/fV9DhW0q9VQ== +jest-resolve@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-26.0.1.tgz#21d1ee06f9ea270a343a8893051aeed940cde736" + integrity sha512-6jWxk0IKZkPIVTvq6s72RH735P8f9eCJW3IM5CX/SJFeKq1p2cZx0U49wf/SdMlhaB/anann5J2nCJj6HrbezQ== dependencies: - "@jest/types" "^25.3.0" - browser-resolve "^1.11.3" - chalk "^3.0.0" + "@jest/types" "^26.0.1" + chalk "^4.0.0" + graceful-fs "^4.2.4" jest-pnp-resolver "^1.2.1" - realpath-native "^2.0.0" - resolve "^1.15.1" - -jest-runner@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-25.3.0.tgz#673ef2ac79d2810eb6b2c1a3f82398375a3d1174" - integrity sha512-csDqSC9qGHYWDrzrElzEgFbteztFeZJmKhSgY5jlCIcN0+PhActzRNku0DA1Xa1HxGOb0/AfbP1EGJlP4fGPtA== - dependencies: - "@jest/console" "^25.3.0" - "@jest/environment" "^25.3.0" - "@jest/test-result" "^25.3.0" - "@jest/types" "^25.3.0" - chalk "^3.0.0" + jest-util "^26.0.1" + read-pkg-up "^7.0.1" + resolve "^1.17.0" + slash "^3.0.0" + +jest-runner@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-26.0.1.tgz#ea03584b7ae4bacfb7e533d680a575a49ae35d50" + integrity sha512-CApm0g81b49Znm4cZekYQK67zY7kkB4umOlI2Dx5CwKAzdgw75EN+ozBHRvxBzwo1ZLYZ07TFxkaPm+1t4d8jA== + dependencies: + "@jest/console" "^26.0.1" + "@jest/environment" "^26.0.1" + "@jest/test-result" "^26.0.1" + "@jest/types" "^26.0.1" + chalk "^4.0.0" exit "^0.1.2" - graceful-fs "^4.2.3" - jest-config "^25.3.0" - jest-docblock "^25.3.0" - jest-haste-map "^25.3.0" - jest-jasmine2 "^25.3.0" - jest-leak-detector "^25.3.0" - jest-message-util "^25.3.0" - jest-resolve "^25.3.0" - jest-runtime "^25.3.0" - jest-util "^25.3.0" - jest-worker "^25.2.6" + graceful-fs "^4.2.4" + jest-config "^26.0.1" + jest-docblock "^26.0.0" + jest-haste-map "^26.0.1" + jest-jasmine2 "^26.0.1" + jest-leak-detector "^26.0.1" + jest-message-util "^26.0.1" + jest-resolve "^26.0.1" + jest-runtime "^26.0.1" + jest-util "^26.0.1" + jest-worker "^26.0.0" source-map-support "^0.5.6" throat "^5.0.0" -jest-runtime@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-25.3.0.tgz#af4d40dbcc590fa5de9910cb6a120a13d131050b" - integrity sha512-gn5KYB1wxXRM3nfw8fVpthFu60vxQUCr+ShGq41+ZBFF3DRHZRKj3HDWVAVB4iTNBj2y04QeAo5cZ/boYaPg0w== - dependencies: - "@jest/console" "^25.3.0" - "@jest/environment" "^25.3.0" - "@jest/source-map" "^25.2.6" - "@jest/test-result" "^25.3.0" - "@jest/transform" "^25.3.0" - "@jest/types" "^25.3.0" +jest-runtime@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-26.0.1.tgz#a121a6321235987d294168e282d52b364d7d3f89" + integrity sha512-Ci2QhYFmANg5qaXWf78T2Pfo6GtmIBn2rRaLnklRyEucmPccmCKvS9JPljcmtVamsdMmkyNkVFb9pBTD6si9Lw== + dependencies: + "@jest/console" "^26.0.1" + "@jest/environment" "^26.0.1" + "@jest/fake-timers" "^26.0.1" + "@jest/globals" "^26.0.1" + "@jest/source-map" "^26.0.0" + "@jest/test-result" "^26.0.1" + "@jest/transform" "^26.0.1" + "@jest/types" "^26.0.1" "@types/yargs" "^15.0.0" - chalk "^3.0.0" + chalk "^4.0.0" collect-v8-coverage "^1.0.0" exit "^0.1.2" glob "^7.1.3" - graceful-fs "^4.2.3" - jest-config "^25.3.0" - jest-haste-map "^25.3.0" - jest-message-util "^25.3.0" - jest-mock "^25.3.0" - jest-regex-util "^25.2.6" - jest-resolve "^25.3.0" - jest-snapshot "^25.3.0" - jest-util "^25.3.0" - jest-validate "^25.3.0" - realpath-native "^2.0.0" + graceful-fs "^4.2.4" + jest-config "^26.0.1" + jest-haste-map "^26.0.1" + jest-message-util "^26.0.1" + jest-mock "^26.0.1" + jest-regex-util "^26.0.0" + jest-resolve "^26.0.1" + jest-snapshot "^26.0.1" + jest-util "^26.0.1" + jest-validate "^26.0.1" slash "^3.0.0" strip-bom "^4.0.0" yargs "^15.3.1" -jest-serializer@^25.2.6: - version "25.2.6" - resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-25.2.6.tgz#3bb4cc14fe0d8358489dbbefbb8a4e708ce039b7" - integrity sha512-RMVCfZsezQS2Ww4kB5HJTMaMJ0asmC0BHlnobQC6yEtxiFKIxohFA4QSXSabKwSggaNkqxn6Z2VwdFCjhUWuiQ== +jest-serializer@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.0.0.tgz#f6c521ddb976943b93e662c0d4d79245abec72a3" + integrity sha512-sQGXLdEGWFAE4wIJ2ZaIDb+ikETlUirEOBsLXdoBbeLhTHkZUJwgk3+M8eyFizhM6le43PDCCKPA1hzkSDo4cQ== + dependencies: + graceful-fs "^4.2.4" -jest-snapshot@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-25.3.0.tgz#d4feb457494f4aaedcc83fbbf1ca21808fc3df71" - integrity sha512-GGpR6Oro2htJPKh5RX4PR1xwo5jCEjtvSPLW1IS7N85y+2bWKbiknHpJJRKSdGXghElb5hWaeQASJI4IiRayGg== +jest-snapshot@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-26.0.1.tgz#1baa942bd83d47b837a84af7fcf5fd4a236da399" + integrity sha512-jxd+cF7+LL+a80qh6TAnTLUZHyQoWwEHSUFJjkw35u3Gx+BZUNuXhYvDqHXr62UQPnWo2P6fvQlLjsU93UKyxA== dependencies: "@babel/types" "^7.0.0" - "@jest/types" "^25.3.0" - "@types/prettier" "^1.19.0" - chalk "^3.0.0" - expect "^25.3.0" - jest-diff "^25.3.0" - jest-get-type "^25.2.6" - jest-matcher-utils "^25.3.0" - jest-message-util "^25.3.0" - jest-resolve "^25.3.0" + "@jest/types" "^26.0.1" + "@types/prettier" "^2.0.0" + chalk "^4.0.0" + expect "^26.0.1" + graceful-fs "^4.2.4" + jest-diff "^26.0.1" + jest-get-type "^26.0.0" + jest-matcher-utils "^26.0.1" + jest-message-util "^26.0.1" + jest-resolve "^26.0.1" make-dir "^3.0.0" natural-compare "^1.4.0" - pretty-format "^25.3.0" - semver "^6.3.0" + pretty-format "^26.0.1" + semver "^7.3.2" jest-transform-graphql@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/jest-transform-graphql/-/jest-transform-graphql-2.1.0.tgz#903cb66bb27bc2772fd3e5dd4f7e9b57230f5829" integrity sha1-kDy2a7J7wncv0+XdT36bVyMPWCk= -jest-util@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-25.3.0.tgz#e3b0064165818f10d78514696fd25efba82cf049" - integrity sha512-dc625P/KS/CpWTJJJxKc4bA3A6c+PJGBAqS8JTJqx4HqPoKNqXg/Ec8biL2Z1TabwK7E7Ilf0/ukSEXM1VwzNA== +jest-util@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.0.1.tgz#72c4c51177b695fdd795ca072a6f94e3d7cef00a" + integrity sha512-byQ3n7ad1BO/WyFkYvlWQHTsomB6GIewBh8tlGtusiylAlaxQ1UpS0XYH0ngOyhZuHVLN79Qvl6/pMiDMSSG1g== dependencies: - "@jest/types" "^25.3.0" - chalk "^3.0.0" + "@jest/types" "^26.0.1" + chalk "^4.0.0" + graceful-fs "^4.2.4" is-ci "^2.0.0" make-dir "^3.0.0" -jest-validate@^24.0.0: +jest-validate@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.9.0.tgz#0775c55360d173cd854e40180756d4ff52def8ab" integrity sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ== @@ -10390,46 +10430,46 @@ jest-validate@^24.0.0: leven "^3.1.0" pretty-format "^24.9.0" -jest-validate@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-25.3.0.tgz#eb95fdee0039647bcd5d4be641b21e4a142a880c" - integrity sha512-3WuXgIZ4HXUvW6gk9twFFkT9j6zUorKnF2oEY8VEsHb7x5LGvVlN3WUsbqazVKuyXwvikO2zFJ/YTySMsMje2w== +jest-validate@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-26.0.1.tgz#a62987e1da5b7f724130f904725e22f4e5b2e23c" + integrity sha512-u0xRc+rbmov/VqXnX3DlkxD74rHI/CfS5xaV2VpeaVySjbb1JioNVOyly5b56q2l9ZKe7bVG5qWmjfctkQb0bA== dependencies: - "@jest/types" "^25.3.0" - camelcase "^5.3.1" - chalk "^3.0.0" - jest-get-type "^25.2.6" + "@jest/types" "^26.0.1" + camelcase "^6.0.0" + chalk "^4.0.0" + jest-get-type "^26.0.0" leven "^3.1.0" - pretty-format "^25.3.0" + pretty-format "^26.0.1" -jest-watcher@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-25.3.0.tgz#fd03fd5ca52f02bd3161ab177466bf1bfdd34e5c" - integrity sha512-dtFkfidFCS9Ucv8azOg2hkiY3sgJEHeTLtGFHS+jfBEE7eRtrO6+2r1BokyDkaG2FOD7485r/SgpC1MFAENfeA== +jest-watcher@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-26.0.1.tgz#5b5e3ebbdf10c240e22a98af66d645631afda770" + integrity sha512-pdZPydsS8475f89kGswaNsN3rhP6lnC3/QDCppP7bg1L9JQz7oU9Mb/5xPETk1RHDCWeqmVC47M4K5RR7ejxFw== dependencies: - "@jest/test-result" "^25.3.0" - "@jest/types" "^25.3.0" + "@jest/test-result" "^26.0.1" + "@jest/types" "^26.0.1" ansi-escapes "^4.2.1" - chalk "^3.0.0" - jest-util "^25.3.0" - string-length "^3.1.0" + chalk "^4.0.0" + jest-util "^26.0.1" + string-length "^4.0.1" -jest-worker@^25.2.6: - version "25.2.6" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.2.6.tgz#d1292625326794ce187c38f51109faced3846c58" - integrity sha512-FJn9XDUSxcOR4cwDzRfL1z56rUofNTFs539FGASpd50RHdb6EVkhxQqktodW2mI49l+W3H+tFJDotCHUQF6dmA== +jest-worker@^26.0.0: + version "26.0.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.0.0.tgz#4920c7714f0a96c6412464718d0c58a3df3fb066" + integrity sha512-pPaYa2+JnwmiZjK9x7p9BoZht+47ecFCDFA/CJxspHzeDvQcfVBLWzCiWyo+EGrSiQMWZtCFo9iSvMZnAAo8vw== dependencies: merge-stream "^2.0.0" supports-color "^7.0.0" -jest@~25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-25.3.0.tgz#7a5e59741d94b8662664c77a9f346246d6bf228b" - integrity sha512-iKd5ShQSHzFT5IL/6h5RZJhApgqXSoPxhp5HEi94v6OAw9QkF8T7X+liEU2eEHJ1eMFYTHmeWLrpBWulsDpaUg== +jest@~26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-26.0.1.tgz#5c51a2e58dff7525b65f169721767173bf832694" + integrity sha512-29Q54kn5Bm7ZGKIuH2JRmnKl85YRigp0o0asTc6Sb6l2ch1DCXIeZTLLFy9ultJvhkTqbswF5DEx4+RlkmCxWg== dependencies: - "@jest/core" "^25.3.0" + "@jest/core" "^26.0.1" import-local "^3.0.2" - jest-cli "^25.3.0" + jest-cli "^26.0.1" js-base64@^2.3.2: version "2.5.1" @@ -10464,39 +10504,7 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jsdom@^15.2.1: - version "15.2.1" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-15.2.1.tgz#d2feb1aef7183f86be521b8c6833ff5296d07ec5" - integrity sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g== - dependencies: - abab "^2.0.0" - acorn "^7.1.0" - acorn-globals "^4.3.2" - array-equal "^1.0.0" - cssom "^0.4.1" - cssstyle "^2.0.0" - data-urls "^1.1.0" - domexception "^1.0.1" - escodegen "^1.11.1" - html-encoding-sniffer "^1.0.2" - nwsapi "^2.2.0" - parse5 "5.1.0" - pn "^1.1.0" - request "^2.88.0" - request-promise-native "^1.0.7" - saxes "^3.1.9" - symbol-tree "^3.2.2" - tough-cookie "^3.0.1" - w3c-hr-time "^1.0.1" - w3c-xmlserializer "^1.1.2" - webidl-conversions "^4.0.2" - whatwg-encoding "^1.0.5" - whatwg-mimetype "^2.3.0" - whatwg-url "^7.0.0" - ws "^7.0.0" - xml-name-validator "^3.0.0" - -jsdom@~16.2.2: +jsdom@^16.2.2, jsdom@~16.2.2: version "16.2.2" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.2.2.tgz#76f2f7541646beb46a938f5dc476b88705bedf2b" integrity sha512-pDFQbcYtKBHxRaP55zGXCJWgFHkDAYbKcsXEK/3Icu9nKYZkutUXfLBwbD+09XDutkYSHcgfQLZ0qvpAAm9mvg== @@ -11123,13 +11131,6 @@ loglevel@^1.4.1: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.6.tgz#0ee6300cc058db6b3551fa1c4bf73b83bb771312" integrity sha512-Sgr5lbboAUBo3eXCSPL4/KoVz3ROKquOjcctxmHIt+vol2DrqTQe3SwkKKuYhEiWB5kYa13YyopJ69deJ1irzQ== -lolex@^5.0.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367" - integrity sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A== - dependencies: - "@sinonjs/commons" "^1.7.0" - long@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" @@ -11921,16 +11922,17 @@ node-modules-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA= -node-notifier@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-6.0.0.tgz#cea319e06baa16deec8ce5cd7f133c4a46b68e12" - integrity sha512-SVfQ/wMw+DesunOm5cKqr6yDcvUTDl/yc97ybGHMrteNEY6oekXpNpS3lZwgLlwz0FLgHoiW28ZpmBHUDg37cw== +node-notifier@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-7.0.0.tgz#513bc42f2aa3a49fce1980a7ff375957c71f718a" + integrity sha512-y8ThJESxsHcak81PGpzWwQKxzk+5YtP3IxR8AYdpXQ1IB6FmcVzFdZXrkPin49F/DKUCfeeiziB8ptY9npzGuA== dependencies: growly "^1.3.0" is-wsl "^2.1.1" - semver "^6.3.0" + semver "^7.2.1" shellwords "^0.1.1" - which "^1.3.1" + uuid "^7.0.3" + which "^2.0.2" node-releases@^1.1.29, node-releases@^1.1.44: version "1.1.45" @@ -12417,11 +12419,6 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= -p-finally@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-2.0.1.tgz#bd6fcaa9c559a096b680806f4d657b3f0f240561" - integrity sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw== - p-is-promise@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" @@ -12627,11 +12624,6 @@ parse-passwd@^1.0.0: resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= -parse5@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" - integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== - parse5@5.1.1, parse5@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" @@ -12868,11 +12860,6 @@ plur@^1.0.0: resolved "https://registry.yarnpkg.com/plur/-/plur-1.0.0.tgz#db85c6814f5e5e5a3b49efc28d604fec62975156" integrity sha1-24XGgU9eXlo7Se/CjWBP7GKXUVY= -pn@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" - integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA== - pnp-webpack-plugin@1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.4.3.tgz#0a100b63f4a1d09cee6ee55a87393b69f03ab5c7" @@ -13102,12 +13089,22 @@ pretty-format@^24.9.0: ansi-styles "^3.2.0" react-is "^16.8.4" -pretty-format@^25.3.0: - version "25.3.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.3.0.tgz#d0a4f988ff4a6cd350342fdabbb809aeb4d49ad5" - integrity sha512-wToHwF8bkQknIcFkBqNfKu4+UZqnrLn/Vr+wwKQwwvPzkBfDDKp/qIabFqdgtoi5PEnM8LFByVsOrHoa3SpTVA== +pretty-format@^25.2.1, pretty-format@^25.5.0: + version "25.5.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a" + integrity sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ== dependencies: - "@jest/types" "^25.3.0" + "@jest/types" "^25.5.0" + ansi-regex "^5.0.0" + ansi-styles "^4.0.0" + react-is "^16.12.0" + +pretty-format@^26.0.1: + version "26.0.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.0.1.tgz#a4fe54fe428ad2fd3413ca6bbd1ec8c2e277e197" + integrity sha512-SWxz6MbupT3ZSlL0Po4WF/KujhQaVehijR2blyRDCzk9e45EaYMVhMBn49fnRuHxtkSpXTes1GxNpVmH86Bxfw== + dependencies: + "@jest/types" "^26.0.1" ansi-regex "^5.0.0" ansi-styles "^4.0.0" react-is "^16.12.0" @@ -13853,6 +13850,15 @@ read-pkg-up@^5.0.0: find-up "^3.0.0" read-pkg "^5.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@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" @@ -13871,7 +13877,7 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -read-pkg@^5.0.0: +read-pkg@^5.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== @@ -13917,11 +13923,6 @@ readline-sync@^1.4.9: resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b" integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw== -realpath-native@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-2.0.0.tgz#7377ac429b6e1fd599dc38d08ed942d0d7beb866" - integrity sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q== - recast@^0.14.7: version "0.14.7" resolved "https://registry.yarnpkg.com/recast/-/recast-0.14.7.tgz#4f1497c2b5826d42a66e8e3c9d80c512983ff61d" @@ -14198,7 +14199,7 @@ request-promise-core@1.1.3: dependencies: lodash "^4.17.15" -request-promise-native@^1.0.7, request-promise-native@^1.0.8: +request-promise-native@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/request-promise-native/-/request-promise-native-1.0.8.tgz#a455b960b826e44e2bf8999af64dff2bfe58cb36" integrity sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ== @@ -14363,11 +14364,6 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" - integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= - resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.8.1, resolve@^1.9.0: version "1.14.2" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.14.2.tgz#dbf31d0fa98b1f29aa5169783b9c290cb865fea2" @@ -14375,10 +14371,10 @@ resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.3. dependencies: path-parse "^1.0.6" -resolve@^1.15.1: - version "1.15.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8" - integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w== +resolve@^1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== dependencies: path-parse "^1.0.6" @@ -14551,13 +14547,6 @@ sax@~1.2.4: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== -saxes@^3.1.9: - version "3.1.11" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-3.1.11.tgz#d59d1fd332ec92ad98a2e0b2ee644702384b1c5b" - integrity sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g== - dependencies: - xmlchars "^2.1.1" - saxes@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.0.tgz#b7d30284d7583a5ca6ad0248b56d8889da53788b" @@ -14642,6 +14631,11 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.2.1, semver@^7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== + semver@~5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" @@ -15215,10 +15209,12 @@ stack-trace@~0.0.7: resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= -stack-utils@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" - integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== +stack-utils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.2.tgz#5cf48b4557becb4638d0bc4f21d23f5d19586593" + integrity sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg== + dependencies: + escape-string-regexp "^2.0.0" state-toggle@^1.0.0: version "1.0.2" @@ -15310,13 +15306,13 @@ string-convert@^0.2.0: resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97" integrity sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c= -string-length@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-3.1.0.tgz#107ef8c23456e187a8abd4a61162ff4ac6e25837" - integrity sha512-Ttp5YvkGm5v9Ijagtaz1BnN+k9ObpvS0eIBblPMp2YWL8FBmi9qblQ9fexc2k/CXFgrTIteU3jAw3payCnwSTA== +string-length@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.1.tgz#4a973bf31ef77c4edbceadd6af2611996985f8a1" + integrity sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw== dependencies: - astral-regex "^1.0.0" - strip-ansi "^5.2.0" + char-regex "^1.0.2" + strip-ansi "^6.0.0" string-width@^1.0.1: version "1.0.2" @@ -15654,7 +15650,7 @@ symbol-observable@^1.0.2, symbol-observable@^1.0.4, symbol-observable@^1.2.0: resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== -symbol-tree@^3.2.2, symbol-tree@^3.2.4: +symbol-tree@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== @@ -16128,13 +16124,6 @@ tough-cookie@~2.4.3: psl "^1.1.24" punycode "^1.4.1" -tr46@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" - integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk= - dependencies: - punycode "^2.1.0" - tr46@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.0.2.tgz#03273586def1595ae08fedb38d7733cee91d2479" @@ -16641,15 +16630,25 @@ uuid@^3.0.1, uuid@^3.1.0, uuid@^3.3.2, uuid@~3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== +uuid@^3.3.3: + version "3.4.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" + integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== + +uuid@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" + integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== + v8-compile-cache@^2.0.2: 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== -v8-to-istanbul@^4.0.1: - version "4.1.3" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.1.3.tgz#22fe35709a64955f49a08a7c7c959f6520ad6f20" - integrity sha512-sAjOC+Kki6aJVbUOXJbcR0MnbfjvBzwKZazEJymA2IX49uoOdEdk+4fBq5cXgYgiyKtAyrrJNtBZdOeDIF+Fng== +v8-to-istanbul@^4.1.3: + version "4.1.4" + resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz#b97936f21c0e2d9996d4985e5c5156e9d4e49cd6" + integrity sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ== dependencies: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^1.6.0" @@ -16723,13 +16722,6 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -w3c-hr-time@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" - integrity sha1-gqwr/2PZUOqeMYmlimViX+3xkEU= - dependencies: - browser-process-hrtime "^0.1.2" - w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" @@ -16737,15 +16729,6 @@ w3c-hr-time@^1.0.2: dependencies: browser-process-hrtime "^1.0.0" -w3c-xmlserializer@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz#30485ca7d70a6fd052420a3d12fd90e6339ce794" - integrity sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg== - dependencies: - domexception "^1.0.1" - webidl-conversions "^4.0.2" - xml-name-validator "^3.0.0" - w3c-xmlserializer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz#3e7104a05b75146cc60f564380b7f683acf1020a" @@ -16814,11 +16797,6 @@ web-namespaces@^1.0.0, web-namespaces@^1.1.2: resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.3.tgz#9bbf5c99ff0908d2da031f1d732492a96571a83f" integrity sha512-r8sAtNmgR0WKOKOxzuSgk09JsHlpKlB+uHi937qypOu3PZ17UxPrierFKDye/uNHjNTTEshu5PId8rojIPj/tA== -webidl-conversions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" - integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== - webidl-conversions@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" @@ -17066,7 +17044,7 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== -whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.5: +whatwg-encoding@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" integrity sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw== @@ -17083,20 +17061,11 @@ whatwg-fetch@>=0.10.0: resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb" integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q== -whatwg-mimetype@^2.2.0, whatwg-mimetype@^2.3.0: +whatwg-mimetype@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== -whatwg-url@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" - integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - whatwg-url@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.0.0.tgz#37f256cb746398e19b107bd6ef820b4ae2d15871" @@ -17394,7 +17363,7 @@ ws@^6.0.0: dependencies: async-limiter "~1.0.0" -ws@^7.0.0, ws@^7.2.3: +ws@^7.2.3: version "7.2.3" resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.3.tgz#a5411e1fb04d5ed0efee76d26d5c46d830c39b46" integrity sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ== @@ -17421,7 +17390,7 @@ xmlbuilder@~4.2.0: dependencies: lodash "^4.0.0" -xmlchars@^2.1.1, xmlchars@^2.2.0: +xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== From a51e9775c33abb44cbdfbdc4f5db38774f84ec3e Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Thu, 7 May 2020 19:14:27 -0500 Subject: [PATCH 14/21] docs: more detail and test helpers --- .../extensibility-with-targets/index.md | 291 +++++++++++++++++- 1 file changed, 289 insertions(+), 2 deletions(-) diff --git a/pwa-devdocs/_drafts/pwa-studio-fundamentals/extensibility-with-targets/index.md b/pwa-devdocs/_drafts/pwa-studio-fundamentals/extensibility-with-targets/index.md index 2d12209225..4dc8b53d20 100644 --- a/pwa-devdocs/_drafts/pwa-studio-fundamentals/extensibility-with-targets/index.md +++ b/pwa-devdocs/_drafts/pwa-studio-fundamentals/extensibility-with-targets/index.md @@ -6,6 +6,45 @@ The new extensibility system for PWA Studio turns your storefront project into a A few simple new concepts—the network of **Targets**, the **BuildBus**, and the [**Interceptor pattern**](https://web.archive.org/web/20170912094101/http://www.cs.wustl.edu/~schmidt/POSA/POSA2/access-patterns.html)—enable your chosen third-party code to enhance the build toolchain, add new functionality to the storefront, and even _rewrite the code of your published PWA on-the-fly_. +- [Quick Start](#quick-start) + - [Project Setup](#project-setup) + - [Watch it work](#watch-it-work) + - [Production efficiency](#production-efficiency) +- [Concepts](#concepts) + - [Building a PWA from installed extensions](#building-a-pwa-from-installed-extensions) + - [Intercept files](#intercept-files) + - [How and when intercept files run](#how-and-when-intercept-files-run) + - [Target dependency management](#target-dependency-management) + - [TargetProviders](#targetproviders) + - [Targets](#targets) + - [Targets as Public API](#targets-as-public-api) + - [Declaring targets](#declaring-targets) +- [API](#api) + - [`Target`](#target) + - [Target names](#target-names) + - [Target Reference API](#target-reference-api) + - [Advanced Target API](#advanced-target-api) + - [TargetProvider](#targetprovider) + - [TargetProvider Reference API](#targetprovider-reference-api) + - [BuildBus](#buildbus) + - [BuildBus Reference API](#buildbus-reference-api) + - [Builtin Targets and their APIs](#builtin-targets-and-their-apis) + - [Buildpack](#buildpack) + - [Peregrine](#peregrine) + - [VeniaUI](#veniaui) +- [Development](#development) + - [Extension development](#extension-development) + - [Initial phase: in-project interceptors](#initial-phase-in-project-interceptors) + - [Move the code](#move-the-code) + - [Manage dependencies](#manage-dependencies) + - [Simulate install](#simulate-install) + - [Testing](#testing) + - [Unit Testing Targets](#unit-testing-targets) + - [Testing Webpack Loaders](#testing-webpack-loaders) + - [Integration Testing: Full Builds](#integration-testing-full-builds) + - [Contributing](#contributing) + - [Help Wanted](#help-wanted) + # Quick Start Use Targets to add a new custom route to a Venia-based store, without editing any VeniaUI code. @@ -383,9 +422,107 @@ To register `./targets/declare.js` as a declare file, add its path to `package.j # API -## List of targets +## `Target` + +A Target is a wrapper around a [Tapable Hook](https://github.com/webpack/tapable). +It reproduces the Hook's API, and tracks activity and the connections between dependencies that use each others' Targets. +Targets can wrap any of the Tapable Hooks described in the above link, except for the `Loop` classes (which Tapable doesn't fully support yet) and the helper classes like `MultiHook` and `HookMap`. +The TargetProvider accessed by declare files and intercept files provides a set of the supported target types. + +```js +module.exports = targets => { + const SyncBail = targets.types.SyncBail; + assert(SyncBailHook === require('tapable').SyncBailHook); +} +``` + +(To avoid confusion with React Hooks, the `target.types` dictionary omits the "Hook" suffix from the supported tapable names.) + +All of the `target.types` constructors require an array of strings as their only argument. +These strings represent represent the arguments sent to interceptors. +If you plan to call the target with 2 arguments, you must supply two strings as argument names to the tapable constructor. + +In this documentation, the words "hook" and "tapable" are used interchangeably. +(**TODO**: Make this documentation _not_ do that.) +Both refer to the classes from the `tapable` library, which PWA Studio wraps with `Targets`. + +A `Target` implements the interface of the tapable used to declare it. If you declared: + +```js +targets.declare({ + cancelLogin: new targets.types.AsyncSeriesBail(['id']) +}) +``` + +Then in your intercept file, you will have access to: + +```js +targets.own.cancelLogin.tapPromise(promiseReturningHandler); + +const canceller = await targets.own.cancelLogin.promise(someId); +``` + +### Target names + +**TODO**: decide what is described here versus in doc comments or Concepts above + +Tap methods in the underlying Tapables always require two arguments: a name for the interceptor, and the callback. +When using Targets' tap methods, the name is optional. +For tracking purposes, all Target interceptors must have names, so the Target class will automatically use the name of the dependency package, such as `@my/pwa-extension`. +If you _do_ supply a string name as the first argument, the Target will concatenate your custom name with the package name. + +Tapables can also accept a single argument that is a `tapInfo` object, with `name` and `fn` properties, as well as other properties that can affect the order of execution. +Those features are available and encouraged! +The same principle applies: unlike raw Tapable instances, Targets don't require a `name` property in the `tapInfo` object. + +### Target Reference API + +**TODO**: Adapt or generate API doc from Target.js doc comments + +### Advanced Target API + +**TODO** Document Tapable concepts like: + - `before` and `stage` parameters + - `intercept` meta-interception possibilities + +## TargetProvider + +**TODO**: decide what is described here versus in doc comments or Concepts above + +### TargetProvider Reference API + +**TODO**: Adapt or generate API doc from Target.js doc comments + +## BuildBus + +The BuildBus, which scans dependencies and executes targets, is an internal-only object. +Only use the BuildBus directly if you are working directly on Buildpack code, which must invoke the BuildBus and call targets manually in order to "kick off" the execution of all targets in the dependency tree. + +### BuildBus Reference API + +**TODO**: Adapt or generate API doc from BuildBus.js doc comments + +## Builtin Targets and their APIs + +**TODO**: Adapt or generate API doc from the JSDoc comments all over all the declare files + +### Buildpack + +- envVarDefinitions +- transformModules +- webpackCompiler +- specialFeatures - +**TODO**: Adapt and move some stuff from the Contributing section below, relating to making higher-level targets from these + +### Peregrine + +- talons + +### VeniaUI + +- richContentRenderers +- routes # Development @@ -486,6 +623,156 @@ BUILDBUS_DEPS_ADDITIONAL='@me/pwa-greeting-page' yarn run watch:venia It should work! The functionality of the new Greeting Page has been entirely ported into its own dependency. Simply running `yarn add @me/pwa-greeting-page` to your project will now automatically add the `/greeting` route to your PWA storefront! +### Testing + +Targets create functionality through the build process. +React components and other frontend code will only show the results of Target functionality after a build. +This makes Target testing more complex than unit testing typical React components. +Buildpack now provides a suite of helpers for testing your Target implementations, in Jest or in any other test framework. + +#### Unit Testing Targets + +To unit test individual Target implementations, use `mockTargetProvider` and `mockBuildBus`. + +`mockTargetProvider` example from Peregrine: +```js +const { mockTargetProvider } = require('@magento/pwa-buildpack'); +const targets = mockTargetProvider( + '@magento/peregrine', + (_, dep) => + ({ + '@magento/pwa-buildpack': { + specialFeatures: { + tap: jest.fn() + }, + transformModules: { + tap: jest.fn() + } + } + }[dep]) +); +declare(targets); +expect(targets.own.talons.tap).toBeDefined(); +const hook = jest.fn(); +// no implementation testing in declare phase +targets.own.talons.tap('test', hook); +targets.own.talons.call('woah'); +expect(hook).toHaveBeenCalledWith('woah'); + +intercept(targets); +const buildpackTargets = targets.of('@magento/pwa-buildpack'); +expect(buildpackTargets.transformModules.tap).toHaveBeenCalled(); +``` + +`mockBuildBus` example from VeniaUI: +```js +const { mockBuildBus } = require('@magento/pwa-buildpack'); +const bus = mockBuildBus({ + context: __dirname, + dependencies: [thisDep] +}); +bus.runPhase('declare'); +const { richContentRenderers, routes } = bus.getTargetsOf( + '@magento/venia-ui' +); +expect(richContentRenderers.tap).toBeDefined(); +expect(routes.tap).toBeDefined(); +const interceptor = jest.fn(); +// no implementation testing in declare phase +richContentRenderers.tap('test', interceptor); +richContentRenderers.call('woah'); +expect(interceptor).toHaveBeenCalledWith('woah'); + +const divByThree = jest.fn(x => x / 3); +routes.tap('addTwo', x => x + 2); +routes.tap({ name: 'divideByThree', fn: divByThree }); +expect(routes.call(10)).toBe(4); +``` + +**TODO**: Adapt or generate API doc from JSDoc comments + +#### Testing Webpack Loaders + +A custom Target implementation that uses `transformModules` may include a custom Webpack loader. +To test Webpack loaders in a simulated Webpack loader context, use `runLoader`. + +Example from Buildpack's `wrap-esm-loader`: +```js +const { runLoader } = require('@magento/pwa-buildpack'); +const { output, context } = await runLoader(wrapEsmLoader, source, { + query: [ + { + defaultExport: true, + wrapperModule: squareToCube + } + ], + resourcePath: 'foo' +}); +const cube = requireModule(output); +expect(cube(4)).toBe(64); +expect(context.getCalls('emitWarning')).toHaveLength(0); +expect(context.getCalls('addDependency')).toMatchObject([[squareToCube]]); +``` + +**TODO**: Adapt or generate API doc from JSDoc comments + +#### Integration Testing: Full Builds + +Most Targets will affect the behavior of React components, which can only be tested by building the React component through Buildpack, which executes those targets. +To simulate this, use `testFullBuild`. + +`buildModuleWith` example from Peregrine: + +```js +const { buildModuleWith } = require('@magento/pwa-buildpack'); +const talonIntegratingDep = { + name: 'goose-app', + declare() {}, + intercept(targets) { + targets.of('@magento/peregrine').talons.tap(talons => { + talons.ProductFullDetail.useProductFullDetail.wrapWith( + 'src/usePFDIntercept' + ); + talons.App.useApp.wrapWith('src/useAppIntercept'); + talons.App.useApp.wrapWith('src/swedish'); + }); + } +}; +const built = await buildModuleWith('src/index.js', { + context: __dirname, + dependencies: [ + { + name: '@magento/peregrine', + declare, + intercept + }, + talonIntegratingDep + ], + mockFiles: { + 'src/index.js': ` +import { useApp } from '@magento/peregrine/lib/talons/App/useApp'; +import { useProductFullDetail } from '@magento/peregrine/lib/talons/ProductFullDetail/useProductFullDetail'; +export default useApp() + useProductFullDetail()`, + 'src/usePFDIntercept': `export default function usePFDIntercept(original) { return function usePFD() { return 'BEEP >o'; } };`, + 'src/useAppIntercept': `export default function useAppIntercept(original) { + return function useApp() { + return 'o< HONK'; + }; +} +`, + 'src/swedish': `export default function swedish(impl) { +return function() { + return impl().replace("O", "Ö") +} +}` + } +}); + +expect(built.run()).toBe('o< HÖNKBEEP >o'); +``` + +**TODO**: Adapt or generate API doc from JSDoc comments + ## Contributing The Targets system does not automatically expose all of the inner workings of PWA dependencies. From 62eeffdd6e34ae4045fff71b1449d680c3e5e22c Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Thu, 7 May 2020 19:46:17 -0500 Subject: [PATCH 15/21] fix better var names --- .../lib/BuildBus/mapHooksToTargets.js | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/pwa-buildpack/lib/BuildBus/mapHooksToTargets.js b/packages/pwa-buildpack/lib/BuildBus/mapHooksToTargets.js index c3c7122d56..c009554354 100644 --- a/packages/pwa-buildpack/lib/BuildBus/mapHooksToTargets.js +++ b/packages/pwa-buildpack/lib/BuildBus/mapHooksToTargets.js @@ -47,18 +47,18 @@ for (const type of allowedTargetTypes) { * Duck typing for async hooks * @private */ -const hasAsyncHookInterface = thing => - typeof thing.tapAsync === 'function' && - typeof thing.tapPromise === 'function' && - typeof thing.callAsync === 'function' && - typeof thing.promise === 'function'; +const hasAsyncHookInterface = hook => + typeof hook.tapAsync === 'function' && + typeof hook.tapPromise === 'function' && + typeof hook.callAsync === 'function' && + typeof hook.promise === 'function'; /** * Duck typing for sync hooks * @private */ -const hasSyncHookInterface = thing => - typeof thing.tap === 'function' && typeof thing.call === 'function'; +const hasSyncHookInterface = hook => + typeof hook.tap === 'function' && typeof hook.call === 'function'; /** * Use duck typing to validate that the passed object seems like a Tapable hook. @@ -80,8 +80,7 @@ const appearsToBeTapable = thing => * the end). Otherwise, returns ''. * @public */ -const getTapableType = thing => - VALID_TYPES.get(thing.constructor) || ''; +const getTapableType = hook => VALID_TYPES.get(hook.constructor) || ''; module.exports = { appearsToBeTapable, From 66295157295cfe1d95f82fcb7149e09b6d11a457 Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Fri, 8 May 2020 12:09:52 -0500 Subject: [PATCH 16/21] fixup run serviceworker through buildbus as well --- .../lib/WebpackTools/configureWebpack/configureWebpack.js | 4 +++- packages/venia-concept/package.json | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/configureWebpack.js b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/configureWebpack.js index 41e6025396..093283c205 100644 --- a/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/configureWebpack.js +++ b/packages/pwa-buildpack/lib/WebpackTools/configureWebpack/configureWebpack.js @@ -189,7 +189,9 @@ async function configureWebpack(options) { vendor: options.vendor || [] }); - clientConfig.plugins.unshift(new BuildBusPlugin(bus, busTrackingQueue)); + const buildBusPlugin = new BuildBusPlugin(bus, busTrackingQueue); + clientConfig.plugins.unshift(buildBusPlugin); + serviceWorkerConfig.plugins.unshift(buildBusPlugin); return { clientConfig, serviceWorkerConfig }; } diff --git a/packages/venia-concept/package.json b/packages/venia-concept/package.json index e72e1ff786..d0f6a08302 100644 --- a/packages/venia-concept/package.json +++ b/packages/venia-concept/package.json @@ -12,6 +12,7 @@ "scripts": { "build": "yarn run build:prod", "build:analyze": "yarn run clean && mkdir dist && webpack -p --profile --no-progress --env.mode production --json > dist/build-stats.json && webpack-bundle-analyzer dist/build-stats.json", + "build:debug": "node --inspect-brk ./node_modules/.bin/webpack --no-progress --env.mode", "build:dev": "yarn run clean && yarn run validate-queries && webpack --no-progress --env.mode development", "build:prod": "yarn run clean && webpack --no-progress --env.mode production", "buildpack": "buildpack", @@ -131,4 +132,4 @@ "module": "src/index.js", "es2015": "src/index.js", "esnext": "src/index.js" -} +} \ No newline at end of file From 64353618296a4bc597f2cf15f803012491bc7bba Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Mon, 11 May 2020 10:52:13 -0500 Subject: [PATCH 17/21] Update packages/peregrine/lib/targets/peregrine-declare.js Co-authored-by: Stephen --- packages/peregrine/lib/targets/peregrine-declare.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/peregrine/lib/targets/peregrine-declare.js b/packages/peregrine/lib/targets/peregrine-declare.js index 8b92c9b654..12abdd7b34 100644 --- a/packages/peregrine/lib/targets/peregrine-declare.js +++ b/packages/peregrine/lib/targets/peregrine-declare.js @@ -32,7 +32,7 @@ module.exports = targets => { * @param {talonIntercept} * * @example Log whenever the `useApp()` hook runs. - * targets.of('@magento/pwa-buildpack').talons.tap(talons => { + * targets.of('@magento/peregrine').talons.tap(talons => { * talons.App.useApp.wrapWith('./log-wrapper'); * }) * // log-wrapper.js: From 68b95e38244d6b70ab107e39c9bb2e237ee2f9dc Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Mon, 11 May 2020 11:44:28 -0500 Subject: [PATCH 18/21] fixup var names i missed --- .../pwa-buildpack/lib/BuildBus/mapHooksToTargets.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/pwa-buildpack/lib/BuildBus/mapHooksToTargets.js b/packages/pwa-buildpack/lib/BuildBus/mapHooksToTargets.js index c009554354..408223dbe3 100644 --- a/packages/pwa-buildpack/lib/BuildBus/mapHooksToTargets.js +++ b/packages/pwa-buildpack/lib/BuildBus/mapHooksToTargets.js @@ -65,14 +65,14 @@ const hasSyncHookInterface = hook => * More robust than doing `instanceof` checks; allows hooks to be proxied and * otherwise hacked by dependencies. * @public - * @param {object} thing - Is it hook-ish? + * @param {object} hookLike - Is it hook-ish? * @returns {boolean} */ -const appearsToBeTapable = thing => - thing && - typeof thing === 'object' && - typeof thing.intercept === 'function' && - (hasSyncHookInterface(thing) || hasAsyncHookInterface(thing)); +const appearsToBeTapable = hookLike => + hookLike && + typeof hookLike === 'object' && + typeof hookLike.intercept === 'function' && + (hasSyncHookInterface(hookLike) || hasAsyncHookInterface(hookLike)); /** * Get the string type name of a provided object. If it is one of the base From ec8fd6cd230aae38e9d96fdad7de47a5c221b87d Mon Sep 17 00:00:00 2001 From: James Zetlen Date: Tue, 19 May 2020 15:57:59 -0500 Subject: [PATCH 19/21] fixup update snapshots after merge --- .../__snapshots__/updateModal.spec.js.snap | 62 +++++-- .../__snapshots__/dialog.spec.js.snap | 154 ++++++++++++++---- 2 files changed, 167 insertions(+), 49 deletions(-) diff --git a/packages/venia-ui/lib/components/CheckoutPage/ShippingMethod/__tests__/__snapshots__/updateModal.spec.js.snap b/packages/venia-ui/lib/components/CheckoutPage/ShippingMethod/__tests__/__snapshots__/updateModal.spec.js.snap index 34b50bd06b..b40fbd84a5 100644 --- a/packages/venia-ui/lib/components/CheckoutPage/ShippingMethod/__tests__/__snapshots__/updateModal.spec.js.snap +++ b/packages/venia-ui/lib/components/CheckoutPage/ShippingMethod/__tests__/__snapshots__/updateModal.spec.js.snap @@ -2,23 +2,34 @@ exports[`it renders correctly 1`] = ` -
-
-
+
+
@@ -88,7 +103,9 @@ exports[`it renders correctly 1`] = ` className="root" />
-
+
-
-
+
+
@@ -251,7 +283,9 @@ exports[`it renders correctly during loading 1`] = `
-
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+