diff --git a/packages/gatsby-dev-cli/src/__tests__/watch.js b/packages/gatsby-dev-cli/src/__tests__/watch.js new file mode 100644 index 0000000000000..0af1b6c425228 --- /dev/null +++ b/packages/gatsby-dev-cli/src/__tests__/watch.js @@ -0,0 +1,140 @@ +jest.mock(`chokidar`, () => { + return { + watch: jest.fn(), + } +}) +jest.mock(`fs-extra`, () => { + return { + copy: jest.fn(), + existsSync: jest.fn(), + } +}) +const chokidar = require(`chokidar`) +const fs = require(`fs-extra`) +const path = require(`path`) +const watch = require(`../watch`) + +let on +beforeEach(() => { + fs.copy.mockReset() + fs.existsSync.mockImplementation(() => true) + chokidar.watch.mockImplementation(() => { + const mock = { + on: jest.fn().mockImplementation(() => mock), + } + on = mock.on + return mock + }) +}) + +describe(`watching`, () => { + const callEventCallback = (...args) => + on.mock.calls[0].slice(-1).pop()(...args) + const callReadyCallback = (...args) => + on.mock.calls[1].slice(-1).pop()(...args) + + const args = [process.cwd(), [`gatsby`], {}] + + it(`watches files`, () => { + watch(...args) + expect(chokidar.watch).toHaveBeenCalledTimes(1) + expect(chokidar.watch).toHaveBeenCalledWith(expect.any(Array), { + ignored: [expect.any(Function)], + }) + }) + + it(`registers on handlers`, () => { + watch(...args) + + expect(on).toHaveBeenCalledTimes(2) + expect(on).toHaveBeenLastCalledWith(`ready`, expect.any(Function)) + }) + + describe(`copying files`, () => { + it(`does not copy files on non-watch event`, () => { + watch(...args) + + callEventCallback(`test`) + + expect(fs.copy).not.toHaveBeenCalled() + }) + + it(`copies files on watch event`, () => { + const filePath = path.join(process.cwd(), `packages/gatsby/dist/index.js`) + watch(...args) + callEventCallback(`add`, filePath) + + expect(fs.copy).toHaveBeenCalledTimes(1) + expect(fs.copy).toHaveBeenCalledWith( + filePath, + `node_modules/gatsby/dist/index.js`, + expect.any(Function) + ) + }) + + it(`copies cache-dir files`, () => { + watch(...args) + + const filePath = path.join( + process.cwd(), + `packages/gatsby/cache-dir/register-service-worker.js` + ) + callEventCallback(`add`, filePath) + + expect(fs.copy).toHaveBeenCalledTimes(2) + expect(fs.copy).toHaveBeenLastCalledWith( + filePath, + `.cache/register-service-worker.js`, + expect.any(Function) + ) + }) + + it(`filters non-existant files/directories`, () => { + fs.existsSync.mockReset().mockImplementation(file => false) + + watch(...args) + + expect(chokidar.watch).toHaveBeenCalledWith([], expect.any(Object)) + }) + + it(`filters duplicate directories`, () => { + watch(process.cwd(), [`gatsby`, `gatsby`], {}) + + expect(chokidar.watch).toHaveBeenCalledWith( + [expect.stringContaining(`gatsby`)], + expect.any(Object) + ) + }) + }) + + describe(`exiting`, () => { + let realProcess + beforeAll(() => { + realProcess = global.process + + global.process = { + ...realProcess, + exit: jest.fn(), + } + }) + + afterAll(() => { + global.process = realProcess + }) + + it(`does not exit if scanOnce is not defined`, () => { + watch(...args) + callReadyCallback() + + expect(process.exit).not.toHaveBeenCalled() + }) + + it(`exits if scanOnce is defined`, async () => { + watch(process.cwd(), [`gatsby`], { scanOnce: true }) + + await callReadyCallback() + + expect(process.exit).toHaveBeenCalledTimes(1) + }) + }) +}) diff --git a/packages/gatsby-dev-cli/src/watch.js b/packages/gatsby-dev-cli/src/watch.js index a4d940d38a398..1c256175ff3ef 100644 --- a/packages/gatsby-dev-cli/src/watch.js +++ b/packages/gatsby-dev-cli/src/watch.js @@ -26,24 +26,33 @@ const copyPath = (oldPath, newPath, quiet) => }) }) +/* + * non-existant packages break on('ready') + * See: https://github.com/paulmillr/chokidar/issues/449 + */ function watch(root, packages, { scanOnce, quiet }) { - const ignored = [ - /[/\\]node_modules[/\\]/i, - /\.git/i, - /\.DS_Store/, - ].concat( + const ignored = [/[/\\]node_modules[/\\]/i, /\.git/i, /\.DS_Store/].concat( packages.map(p => new RegExp(`${p}[\\/\\\\]src[\\/\\\\]`, `i`)) ) - const watchers = packages.map(p => path.join(root, `/packages/`, p)) + const watchers = _.uniq( + packages + .map(p => path.join(root, `/packages/`, p)) + .filter(p => fs.existsSync(p)) + ) let allCopies = [] - chokidar.watch(watchers, { - ignored: [filePath => _.some(ignored, reg => reg.test(filePath))], - }) + chokidar + .watch(watchers, { + ignored: [filePath => _.some(ignored, reg => reg.test(filePath))], + }) .on(`all`, (event, filePath) => { - if (event === `change` || event === `add`) { - const packageName = path.basename(path.dirname(filePath.split(`packages/`).pop())) + const watchEvents = [`change`, `add`] + if (_.includes(watchEvents, event)) { + const [packageName] = filePath + .split(`packages/`) + .pop() + .split(`/`) const prefix = path.join(root, `/packages/`, packageName) // Copy it over local version. @@ -71,14 +80,14 @@ function watch(root, packages, { scanOnce, quiet }) { allCopies = allCopies.concat(localCopies) } }) - .on(`ready`, () => { + .on(`ready`, () => // all files watched, quit once all files are copied if necessary Promise.all(allCopies).then(() => { if (scanOnce) { quit() } }) - }) + ) } module.exports = watch diff --git a/scripts/publish-site.sh b/scripts/publish-site.sh index b134ffd4c8b48..79915c15a01b7 100755 --- a/scripts/publish-site.sh +++ b/scripts/publish-site.sh @@ -12,7 +12,7 @@ cd "$1" || exit yarn echo "=== Copying built Gatsby to website." -gatsby-dev --scan-once --quiet +gatsby-dev --scan-once # copy file if target dir exists FRAGMENTSDIR="node_modules/gatsby-transformer-sharp/src"