diff --git a/packages/gatsby-source-filesystem/package.json b/packages/gatsby-source-filesystem/package.json index e14601f8d06da..ecc324e29a709 100644 --- a/packages/gatsby-source-filesystem/package.json +++ b/packages/gatsby-source-filesystem/package.json @@ -16,6 +16,7 @@ "md5-file": "^3.1.1", "mime": "^2.2.0", "pretty-bytes": "^4.0.2", + "readdirp-walk": "^1.6.0", "slash": "^1.0.0", "valid-url": "^1.0.9", "xstate": "^3.1.0" diff --git a/packages/gatsby-source-filesystem/src/__tests__/fileFinder.js b/packages/gatsby-source-filesystem/src/__tests__/fileFinder.js new file mode 100644 index 0000000000000..1520e607f87b2 --- /dev/null +++ b/packages/gatsby-source-filesystem/src/__tests__/fileFinder.js @@ -0,0 +1,27 @@ +const fileFinder = require(`../file-finder`) + +describe(`file-finder`, () => { + it(`resolves an array of file paths`, async done => { + expect.assertions(3) + const queue = await fileFinder(`${__dirname}/fixtures/test-fs`) + const expectedQueue = [ + `${__dirname}/fixtures/test-fs/index.md`, + `${__dirname}/fixtures/test-fs/dirA/index.md`, + `${__dirname}/fixtures/test-fs/dirB/index.md`, + `${__dirname}/fixtures/test-fs/dirA/a/index.md`, + `${__dirname}/fixtures/test-fs/dirA/a/A/index.md`, + `${__dirname}/fixtures/test-fs/dirA/b/index.md`, + `${__dirname}/fixtures/test-fs/dirA/b/A/index.md`, + `${__dirname}/fixtures/test-fs/dirA/c/index.md`, + `${__dirname}/fixtures/test-fs/dirA/c/A/index.md`, + ] + + expect(queue.length).toBeGreaterThan(0) + expect(queue.length).toEqual(expectedQueue.length) + const hasAllFiles = expectedQueue.every(expected => + queue.some(item => item === expected) + ) + expect(hasAllFiles).toBe(true) + done() + }) +}) diff --git a/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/.toBeIgnored.md b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/.toBeIgnored.md new file mode 100644 index 0000000000000..c692f9ee8378f --- /dev/null +++ b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/.toBeIgnored.md @@ -0,0 +1 @@ +should never be queued diff --git a/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/a/A/index.md b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/a/A/index.md new file mode 100644 index 0000000000000..10f799646661b --- /dev/null +++ b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/a/A/index.md @@ -0,0 +1 @@ +"DirA a A" content diff --git a/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/a/index.md b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/a/index.md new file mode 100644 index 0000000000000..20a1e322b452c --- /dev/null +++ b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/a/index.md @@ -0,0 +1 @@ +"DirA a" content diff --git a/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/b/A/index.md b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/b/A/index.md new file mode 100644 index 0000000000000..10f799646661b --- /dev/null +++ b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/b/A/index.md @@ -0,0 +1 @@ +"DirA a A" content diff --git a/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/b/index.md b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/b/index.md new file mode 100644 index 0000000000000..3d204ff4c875a --- /dev/null +++ b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/b/index.md @@ -0,0 +1 @@ +"DirA b" content diff --git a/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/c/A/index.md b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/c/A/index.md new file mode 100644 index 0000000000000..10f799646661b --- /dev/null +++ b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/c/A/index.md @@ -0,0 +1 @@ +"DirA a A" content diff --git a/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/c/index.md b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/c/index.md new file mode 100644 index 0000000000000..ee018a1652f2c --- /dev/null +++ b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/c/index.md @@ -0,0 +1 @@ +"DirA c" content diff --git a/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/index.md b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/index.md new file mode 100644 index 0000000000000..5e9dd3ea6f9ee --- /dev/null +++ b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirA/index.md @@ -0,0 +1 @@ +"DirA" content diff --git a/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirB/index.md b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirB/index.md new file mode 100644 index 0000000000000..11256c8a0cf08 --- /dev/null +++ b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/dirB/index.md @@ -0,0 +1 @@ +"DirB" content diff --git a/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/empty/.placeholder.md b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/empty/.placeholder.md new file mode 100644 index 0000000000000..aec7b712a5cf8 --- /dev/null +++ b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/empty/.placeholder.md @@ -0,0 +1 @@ +This file is only so we can commit an empty folder. It should be ignored during testing. diff --git a/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/index.md b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/index.md new file mode 100644 index 0000000000000..fff09d2428d0c --- /dev/null +++ b/packages/gatsby-source-filesystem/src/__tests__/fixtures/test-fs/index.md @@ -0,0 +1 @@ +root dir content diff --git a/packages/gatsby-source-filesystem/src/file-finder.js b/packages/gatsby-source-filesystem/src/file-finder.js new file mode 100644 index 0000000000000..aa5fa3716c781 --- /dev/null +++ b/packages/gatsby-source-filesystem/src/file-finder.js @@ -0,0 +1,34 @@ +const readdirp = require(`readdirp-walk`) + +const ignoredRE = /^\./ +const ignored = [`yarn.lock`, `package-lock.json`, `node_modules`, `dist`] + +function fileFinder(dir) { + return new Promise(resolve => { + let fileList = [] + const stream = readdirp({ root: dir }) + + stream.on(`data`, data => { + const { name, fullPath, stat } = data + + if ( + stat.isDirectory() || + ignoredRE.test(name) || + ignored.includes(name) + ) { + return + } + if (fullPath.includes(`node_modules`) || fullPath.includes(`build`)) { + return + } + fileList.push(fullPath) + return + }) + + stream.on(`end`, () => { + resolve(fileList) + }) + }) +} + +module.exports = fileFinder diff --git a/packages/gatsby-source-filesystem/src/file-watcher.js b/packages/gatsby-source-filesystem/src/file-watcher.js new file mode 100644 index 0000000000000..e77ff5d1688c8 --- /dev/null +++ b/packages/gatsby-source-filesystem/src/file-watcher.js @@ -0,0 +1,212 @@ +const path = require(`path`) +const chokidar = require(`chokidar`) +const { Machine } = require(`xstate`) + +const { createFileNode } = require(`./create-file-node`) +/** + * Create a state machine to manage Chokidar's not-ready/ready states and for + * emitting file system events into Gatsby. + * + * On the latter, this solves the problem where if you call createNode for the + * same File node in quick succession, this can leave Gatsby's internal state + * in disarray causing queries to fail. The latter state machine tracks when + * Gatsby is "processing" a node update or when it's "idle". If updates come in + * while Gatsby is processing, we queue them until the system returns to an + * "idle" state. + */ +const createFSMachine = () => + Machine({ + key: `emitFSEvents`, + parallel: true, + strict: true, + states: { + CHOKIDAR: { + initial: `CHOKIDAR_NOT_READY`, + states: { + CHOKIDAR_NOT_READY: { + on: { + CHOKIDAR_READY: `CHOKIDAR_WATCHING`, + BOOTSTRAP_FINISHED: `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED`, + }, + }, + CHOKIDAR_WATCHING: { + on: { + BOOTSTRAP_FINISHED: `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED`, + CHOKIDAR_READY: `CHOKIDAR_WATCHING`, + }, + }, + CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED: { + on: { + CHOKIDAR_READY: `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED`, + }, + }, + }, + }, + PROCESSING: { + initial: `BOOTSTRAPPING`, + states: { + BOOTSTRAPPING: { + on: { + BOOTSTRAP_FINISHED: `IDLE`, + }, + }, + IDLE: { + on: { + EMIT_FS_EVENT: `PROCESSING`, + }, + }, + PROCESSING: { + on: { + QUERY_QUEUE_DRAINED: `IDLE`, + TOUCH_NODE: `IDLE`, + }, + }, + }, + }, + }, + }) + +function fileWatcher( + { actions, getNode, createNodeId, reporter, emitter }, + pluginOptions +) { + const { createNode, deleteNode } = actions + // Validate that the path is absolute. + // Absolute paths are required to resolve images correctly. + if (!path.isAbsolute(pluginOptions.path)) { + pluginOptions.path = path.resolve(process.cwd(), pluginOptions.path) + } + + const fsMachine = createFSMachine() + let currentState = fsMachine.initialState + let pathQueue = [] + let fileNodeQueue = new Map() + const createAndProcessNode = path => { + const fileNodePromise = createFileNode( + path, + createNodeId, + pluginOptions + ).then(fileNode => { + if (currentState.value.PROCESSING === `PROCESSING`) { + fileNodeQueue.set(fileNode.id, fileNode) + } else { + currentState = fsMachine.transition(currentState.value, `EMIT_FS_EVENT`) + createNode(fileNode) + } + }) + return fileNodePromise + } + + const watcher = chokidar.watch(pluginOptions.path, { + ignored: [ + `**/*.un~`, + `**/.gitignore`, + `**/.npmignore`, + `**/.babelrc`, + `**/yarn.lock`, + `**/node_modules`, + `../**/dist/**`, + ], + }) + + emitter.on(`BOOTSTRAP_FINISHED`, () => { + currentState = fsMachine.transition( + currentState.value, + `BOOTSTRAP_FINISHED` + ) + }) + emitter.on(`TOUCH_NODE`, () => { + // If we create a node which is the same as the previous version, createNode + // returns TOUCH_NODE and then nothing else happens so we listen to that + // to return the state back to IDLE. + currentState = fsMachine.transition(currentState.value, `TOUCH_NODE`) + }) + + emitter.on(`QUERY_QUEUE_DRAINED`, () => { + currentState = fsMachine.transition( + currentState.value, + `QUERY_QUEUE_DRAINED` + ) + // If we have any updates queued, run one of them now. + if (fileNodeQueue.size > 0) { + const toProcess = fileNodeQueue.get(Array.from(fileNodeQueue.keys())[0]) + fileNodeQueue.delete(toProcess.id) + currentState = fsMachine.transition(currentState.value, `EMIT_FS_EVENT`) + createNode(toProcess) + } + }) + watcher.on(`add`, path => { + if (currentState.value.CHOKIDAR !== `CHOKIDAR_NOT_READY`) { + if ( + currentState.value.CHOKIDAR === `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED` + ) { + reporter.info(`added file at ${path}`) + } + createAndProcessNode(path).catch(err => reporter.error(err)) + } else { + pathQueue.push(path) + } + }) + + watcher.on(`change`, path => { + if ( + currentState.value.CHOKIDAR === `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED` + ) { + reporter.info(`changed file at ${path}`) + } + createAndProcessNode(path).catch(err => reporter.error(err)) + }) + + watcher.on(`unlink`, path => { + if ( + currentState.value.CHOKIDAR === `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED` + ) { + reporter.info(`file deleted at ${path}`) + } + const node = getNode(createNodeId(path)) + // It's possible the file node was never created as sometimes tools will + // write and then immediately delete temporary files to the file system. + if (node) { + currentState = fsMachine.transition(currentState.value, `EMIT_FS_EVENT`) + deleteNode({ node }) + } + }) + + watcher.on(`addDir`, path => { + if (currentState.value.CHOKIDAR !== `CHOKIDAR_NOT_READY`) { + if ( + currentState.value.CHOKIDAR === `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED` + ) { + reporter.info(`added directory at ${path}`) + } + createAndProcessNode(path).catch(err => reporter.error(err)) + } else { + pathQueue.push(path) + } + }) + + watcher.on(`unlinkDir`, path => { + if ( + currentState.value.CHOKIDAR === `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED` + ) { + reporter.info(`directory deleted at ${path}`) + } + const node = getNode(createNodeId(path)) + deleteNode({ node }) + }) + + const flushPathQueue = () => { + let queue = pathQueue.slice() + pathQueue = [] + return Promise.all(queue.map(createAndProcessNode)) + } + + return new Promise((resolve, reject) => { + watcher.on(`ready`, () => { + currentState = fsMachine.transition(currentState.value, `CHOKIDAR_READY`) + flushPathQueue().then(resolve, reject) + }) + }) +} + +module.exports = fileWatcher diff --git a/packages/gatsby-source-filesystem/src/gatsby-node.js b/packages/gatsby-source-filesystem/src/gatsby-node.js index fe68f08d80a67..acd9cfc17dbe8 100644 --- a/packages/gatsby-source-filesystem/src/gatsby-node.js +++ b/packages/gatsby-source-filesystem/src/gatsby-node.js @@ -1,78 +1,13 @@ -const chokidar = require(`chokidar`) const fs = require(`fs`) -const path = require(`path`) -const { Machine } = require(`xstate`) -const { createFileNode } = require(`./create-file-node`) +const fileFinder = require(`./file-finder`) +const fileWatcher = require(`./file-watcher`) -/** - * Create a state machine to manage Chokidar's not-ready/ready states and for - * emitting file system events into Gatsby. - * - * On the latter, this solves the problem where if you call createNode for the - * same File node in quick succession, this can leave Gatsby's internal state - * in disarray causing queries to fail. The latter state machine tracks when - * Gatsby is "processing" a node update or when it's "idle". If updates come in - * while Gatsby is processing, we queue them until the system returns to an - * "idle" state. - */ -const createFSMachine = () => - Machine({ - key: `emitFSEvents`, - parallel: true, - strict: true, - states: { - CHOKIDAR: { - initial: `CHOKIDAR_NOT_READY`, - states: { - CHOKIDAR_NOT_READY: { - on: { - CHOKIDAR_READY: `CHOKIDAR_WATCHING`, - BOOTSTRAP_FINISHED: `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED`, - }, - }, - CHOKIDAR_WATCHING: { - on: { - BOOTSTRAP_FINISHED: `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED`, - CHOKIDAR_READY: `CHOKIDAR_WATCHING`, - }, - }, - CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED: { - on: { - CHOKIDAR_READY: `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED`, - }, - }, - }, - }, - PROCESSING: { - initial: `BOOTSTRAPPING`, - states: { - BOOTSTRAPPING: { - on: { - BOOTSTRAP_FINISHED: `IDLE`, - }, - }, - IDLE: { - on: { - EMIT_FS_EVENT: `PROCESSING`, - }, - }, - PROCESSING: { - on: { - QUERY_QUEUE_DRAINED: `IDLE`, - TOUCH_NODE: `IDLE`, - }, - }, - }, - }, - }, - }) +const { createFileNode } = require(`./create-file-node`) -exports.sourceNodes = ( - { actions, getNode, createNodeId, hasNodeChanged, reporter, emitter }, - pluginOptions -) => { - const { createNode, deleteNode } = actions +exports.sourceNodes = async (gatsby, pluginOptions) => { + const { actions, createNodeId, reporter } = gatsby + const { createNode } = actions // Validate that the path exists. if (!fs.existsSync(pluginOptions.path)) { @@ -87,149 +22,16 @@ See docs here - https://www.gatsbyjs.org/packages/gatsby-source-filesystem/ `) } - // Validate that the path is absolute. - // Absolute paths are required to resolve images correctly. - if (!path.isAbsolute(pluginOptions.path)) { - pluginOptions.path = path.resolve(process.cwd(), pluginOptions.path) - } - - const fsMachine = createFSMachine() - let currentState = fsMachine.initialState - let fileNodeQueue = new Map() + if (process.env.NODE_ENV === `production`) { + const pathQueue = await fileFinder(pluginOptions.path) + const createAndProcessNode = path => + createFileNode(path, createNodeId, pluginOptions).then(createNode) - // Once bootstrap is finished, we only let one File node update go through - // the system at a time. - emitter.on(`BOOTSTRAP_FINISHED`, () => { - currentState = fsMachine.transition( - currentState.value, - `BOOTSTRAP_FINISHED` + return new Promise((resolve, reject) => + Promise.all(pathQueue.map(createAndProcessNode)).then(resolve, reject) ) - }) - emitter.on(`TOUCH_NODE`, () => { - // If we create a node which is the same as the previous version, createNode - // returns TOUCH_NODE and then nothing else happens so we listen to that - // to return the state back to IDLE. - currentState = fsMachine.transition(currentState.value, `TOUCH_NODE`) - }) - - emitter.on(`QUERY_QUEUE_DRAINED`, () => { - currentState = fsMachine.transition( - currentState.value, - `QUERY_QUEUE_DRAINED` - ) - // If we have any updates queued, run one of them now. - if (fileNodeQueue.size > 0) { - const toProcess = fileNodeQueue.get(Array.from(fileNodeQueue.keys())[0]) - fileNodeQueue.delete(toProcess.id) - currentState = fsMachine.transition(currentState.value, `EMIT_FS_EVENT`) - createNode(toProcess) - } - }) - - const watcher = chokidar.watch(pluginOptions.path, { - ignored: [ - `**/*.un~`, - `**/.gitignore`, - `**/.npmignore`, - `**/.babelrc`, - `**/yarn.lock`, - `**/node_modules`, - `../**/dist/**`, - ], - }) - - const createAndProcessNode = path => { - const fileNodePromise = createFileNode( - path, - createNodeId, - pluginOptions - ).then(fileNode => { - if (currentState.value.PROCESSING === `PROCESSING`) { - fileNodeQueue.set(fileNode.id, fileNode) - } else { - currentState = fsMachine.transition(currentState.value, `EMIT_FS_EVENT`) - createNode(fileNode) - } - }) - return fileNodePromise } - - // For every path that is reported before the 'ready' event, we throw them - // into a queue and then flush the queue when 'ready' event arrives. - // After 'ready', we handle the 'add' event without putting it into a queue. - let pathQueue = [] - const flushPathQueue = () => { - let queue = pathQueue.slice() - pathQueue = [] - return Promise.all(queue.map(createAndProcessNode)) - } - - watcher.on(`add`, path => { - if (currentState.value.CHOKIDAR !== `CHOKIDAR_NOT_READY`) { - if ( - currentState.value.CHOKIDAR === `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED` - ) { - reporter.info(`added file at ${path}`) - } - createAndProcessNode(path).catch(err => reporter.error(err)) - } else { - pathQueue.push(path) - } - }) - - watcher.on(`change`, path => { - if ( - currentState.value.CHOKIDAR === `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED` - ) { - reporter.info(`changed file at ${path}`) - } - createAndProcessNode(path).catch(err => reporter.error(err)) - }) - - watcher.on(`unlink`, path => { - if ( - currentState.value.CHOKIDAR === `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED` - ) { - reporter.info(`file deleted at ${path}`) - } - const node = getNode(createNodeId(path)) - // It's possible the file node was never created as sometimes tools will - // write and then immediately delete temporary files to the file system. - if (node) { - currentState = fsMachine.transition(currentState.value, `EMIT_FS_EVENT`) - deleteNode({ node }) - } - }) - - watcher.on(`addDir`, path => { - if (currentState.value.CHOKIDAR !== `CHOKIDAR_NOT_READY`) { - if ( - currentState.value.CHOKIDAR === `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED` - ) { - reporter.info(`added directory at ${path}`) - } - createAndProcessNode(path).catch(err => reporter.error(err)) - } else { - pathQueue.push(path) - } - }) - - watcher.on(`unlinkDir`, path => { - if ( - currentState.value.CHOKIDAR === `CHOKIDAR_WATCHING_BOOTSTRAP_FINISHED` - ) { - reporter.info(`directory deleted at ${path}`) - } - const node = getNode(createNodeId(path)) - deleteNode({ node }) - }) - - return new Promise((resolve, reject) => { - watcher.on(`ready`, () => { - currentState = fsMachine.transition(currentState.value, `CHOKIDAR_READY`) - flushPathQueue().then(resolve, reject) - }) - }) + return fileWatcher(gatsby, pluginOptions) } exports.setFieldsOnGraphQLNodeType = require(`./extend-file-node`) diff --git a/yarn.lock b/yarn.lock index ade056a363fb7..61a433fcf0cd6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5662,6 +5662,10 @@ each-async@^1.0.0, each-async@^1.1.1: onetime "^1.0.0" set-immediate-shim "^1.0.0" +each-limit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/each-limit/-/each-limit-1.0.0.tgz#380140083367a8af64299bcac15fdd5b1a977196" + ecc-jsbn@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" @@ -6113,6 +6117,10 @@ event-stream@~3.3.0: stream-combiner "~0.0.4" through "~2.3.1" +eventemitter3@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" + eventemitter3@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163" @@ -9716,6 +9724,10 @@ lodash.isequal@^4.0.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" +lodash.isobject@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d" + lodash.isplainobject@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" @@ -9724,6 +9736,10 @@ lodash.isstring@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" +lodash.isundefined@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz#23ef3d9535565203a66cefd5b830f848911afb48" + lodash.iteratee@^4.5.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.iteratee/-/lodash.iteratee-4.7.0.tgz#be4177db289a8ccc3c0990f1db26b5b22fc1554c" @@ -10212,7 +10228,7 @@ methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" -micromatch@^2.1.5, micromatch@^2.3.11, micromatch@^2.3.7, micromatch@^2.3.8: +micromatch@^2.1.5, micromatch@^2.3.11, micromatch@^2.3.5, micromatch@^2.3.7, micromatch@^2.3.8: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" dependencies: @@ -12639,6 +12655,14 @@ readdir-scoped-modules@^1.0.0: graceful-fs "^4.1.2" once "^1.3.0" +readdirp-walk@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/readdirp-walk/-/readdirp-walk-1.6.0.tgz#07565f57ab93a967d6de644f4663c01356f5a0ba" + dependencies: + eventemitter3 "^1.1.1" + micromatch "^2.3.5" + walk-filtered "^0.8.0" + readdirp@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" @@ -15586,6 +15610,16 @@ wait-for-expect@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wait-for-expect/-/wait-for-expect-1.0.0.tgz#edf2d5790c36dc67c4e21ac6ccedd7d4b79dc6ac" +walk-filtered@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/walk-filtered/-/walk-filtered-0.8.0.tgz#46c31a246ec74cef50c0eb4b4b1563bc761a00a5" + dependencies: + each-limit "^1.0.0" + graceful-fs "^4.1.2" + lodash.isobject "^3.0.2" + lodash.isundefined "^3.0.1" + object-assign "^4.0.1" + walker@~1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"