diff --git a/benchmarks/query/gatsby-node.js b/benchmarks/query/gatsby-node.js index 40bd221ac4e04..73efaa2af17c3 100644 --- a/benchmarks/query/gatsby-node.js +++ b/benchmarks/query/gatsby-node.js @@ -85,9 +85,9 @@ function allTypeQuery(typeName) { } // Create template in .cache for the received type -function createTemplateFile(typeName) { +function createTemplateFile(cache, typeName) { const templateSrc = createPageTemplateJs(typeName) - const templateFilename = `./.cache/${typeName}Template.js` + const templateFilename = cache.rootPath(`${typeName}Template.js`) fs.writeFileSync(templateFilename, templateSrc) return templateFilename } @@ -99,10 +99,10 @@ async function createNode(graphql, typeName) { } // Create page for each type -exports.createPages = async ({ actions, graphql }) => { +exports.createPages = async ({ actions, graphql, cache }) => { for (let i = 0; i < types.length; i++) { const typeName = types[i] - const templateFilename = createTemplateFile(typeName) + const templateFilename = createTemplateFile(cache, typeName) const edges = await createNode(graphql, typeName) _.forEach(edges, ({ node }) => { actions.createPage({ diff --git a/docs/docs/building-with-components.md b/docs/docs/building-with-components.md index b9f0c666ded63..4a67020b3b9c6 100644 --- a/docs/docs/building-with-components.md +++ b/docs/docs/building-with-components.md @@ -129,7 +129,7 @@ import favicon from "./favicon.png" let inlinedStyles = "" if (process.env.NODE_ENV === "production") { try { - inlinedStyles = require("!raw-loader!../public/styles.css") + inlinedStyles = require("!raw-loader!gatsby-public-dir/styles.css") } catch (e) { console.log(e) } diff --git a/docs/docs/environment-variables.md b/docs/docs/environment-variables.md index 4184e82893642..0c671bd753b59 100644 --- a/docs/docs/environment-variables.md +++ b/docs/docs/environment-variables.md @@ -139,6 +139,13 @@ If set to true, this will expose a `/__refresh` webhook that is able to receive You can trigger this endpoint locally for example on Unix-based operating systems (like Ubuntu and MacOS) you can use `curl -X POST http://localhost:8000/__refresh`. +Gatsby also allows you to use environment variables instead of using command line options for: + +- `--build-dir`: `GATSBY_BUILD_DIR` +- `--cache-dir`: `GATSBY_CACHE_DIR` + +The command line options will take precedence over the environment variables. + ## Additional Environments (Staging, Test, etc) As noted above `NODE_ENV` is a reserved environment variable in Gatsby as it is needed by the build system to make key optimizations when compiling React and other modules. For this reason it is necessary to make use of a secondary environment variable for additional environment support, and manually make the environment variables available to the client-side code. diff --git a/docs/docs/gatsby-cli.md b/docs/docs/gatsby-cli.md index 2c8d0e8903bb4..014a96de737ec 100644 --- a/docs/docs/gatsby-cli.md +++ b/docs/docs/gatsby-cli.md @@ -47,12 +47,14 @@ Once you've installed a Gatsby site, go to the root directory of your project an #### Options -| Option | Description | -| :-------------: | ----------------------------------------------- | -| `-H`, `--host` | Set host. Defaults to localhost | -| `-p`, `--port` | Set port. Defaults to 8000 | -| `-o`, `--open` | Open the site in your (default) browser for you | -| `-S`, `--https` | Use HTTPS | +| Option | Description | +| :-------------: | ---------------------------------------------------- | +| `-H`, `--host` | Set host. Defaults to localhost | +| `-p`, `--port` | Set port. Defaults to 8000 | +| `-o`, `--open` | Open the site in your (default) browser for you | +| `-S`, `--https` | Use HTTPS | +| `--build-dir` | Set the build directory location. Defaults to public | +| `--cache-dir` | Set the cache directory location. Defaults to .cache | Follow the [Local HTTPS guide](/docs/local-https/) to find out how you can set up an HTTPS development server using Gatsby. @@ -70,6 +72,8 @@ At the root of a Gatsby site, compile your application and make it ready for dep | `--prefix-paths` | Build site with link paths prefixed (set pathPrefix in your config) | | `--no-uglify` | Build site without uglifying JS bundles (for debugging) | | `--open-tracing-config-file` | Tracer configuration file (open tracing compatible). See [Performance Tracing](/docs/performance-tracing/) | +| `--build-dir` | Set the build directory location. Defaults to public | +| `--cache-dir` | Set the cache directory location. Defaults to .cache | ### `serve` @@ -85,6 +89,8 @@ At the root of a Gatsby site, serve the production build of your site for testin | `-p`, `--port` | Set port. Defaults to 9000 | | `-o`, `--open` | Open the site in your (default) browser for you | | `--prefix-paths` | Serve site with link paths prefixed (if built with pathPrefix in your gatsby-config.js). | +| `--build-dir` | Set the build directory location. Defaults to public | +| `--cache-dir` | Set the cache directory location. Defaults to .cache | ### `info` diff --git a/docs/docs/production-app.md b/docs/docs/production-app.md index 2889b4fba9859..00192a003c171 100644 --- a/docs/docs/production-app.md +++ b/docs/docs/production-app.md @@ -89,7 +89,7 @@ The first thing our app does is run the [onClientEntry](/docs/browser-apis/#onCl It's worth noting that the browser API runner is completely different to `api-runner-node` which is explained in [How APIs/Plugins Are Run](/docs/how-plugins-apis-are-run/). `api-runner-node` runs in Node.js and has to deal with complex server based execution paths. Whereas running APIs on the browser is simply a matter of iterating through the site's registered browser plugins and running them one after the other (see [api-runner-browser.js](https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/cache-dir/api-runner-browser.js#L9)). -One thing to note is that it gets the list of plugins from `./cache/api-runner-browser-plugins.js`, which is generated [early in bootstrap](https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/src/bootstrap/index.js#L338). +One thing to note is that it gets the list of plugins from `./.cache/api-runner-browser-plugins.js`, which is generated [early in bootstrap](https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby/src/bootstrap/index.js#L338). ### DOM Hydration diff --git a/examples/styleguide/gatsby-node.js b/examples/styleguide/gatsby-node.js index f9392a2d1ff65..c24284f28d831 100644 --- a/examples/styleguide/gatsby-node.js +++ b/examples/styleguide/gatsby-node.js @@ -1,13 +1,12 @@ const path = require(`path`) const fs = require(`fs`) -const appRootDir = require(`app-root-dir`).get() const componentPageTemplate = path.resolve( `src/templates/ComponentPage/index.js` ) const tableOfContentsTemplate = path.resolve(`src/templates/TOC/index.js`) -exports.createPages = ({ graphql, actions }) => { +exports.createPages = ({ graphql, actions, cache }) => { const { createPage } = actions return new Promise((resolve, reject) => { @@ -83,10 +82,7 @@ exports.createPages = ({ graphql, actions }) => { }, []) .join(`\n`) + `\n` - fs.writeFileSync( - path.join(appRootDir, `.cache/components.js`), - exportFileContents - ) + fs.writeFileSync(cache.rootPath(`components.js`), exportFileContents) allComponents.forEach(data => { const { filePath } = data diff --git a/examples/styleguide/src/templates/ComponentPage/components/ComponentPreview/ComponentPreview.js b/examples/styleguide/src/templates/ComponentPage/components/ComponentPreview/ComponentPreview.js index bece88b2a048b..e57d91dec0db3 100644 --- a/examples/styleguide/src/templates/ComponentPage/components/ComponentPreview/ComponentPreview.js +++ b/examples/styleguide/src/templates/ComponentPage/components/ComponentPreview/ComponentPreview.js @@ -2,7 +2,7 @@ import React from "react" import PropTypes from "prop-types" import { LiveProvider, LiveEditor, LiveError, LivePreview } from "react-live" -import * as components from "../../../../../.cache/components" +import * as components from "gatsby-cache-dir/components" import "./prism-theme.css" diff --git a/packages/babel-plugin-remove-graphql-queries/src/__tests__/index.js b/packages/babel-plugin-remove-graphql-queries/src/__tests__/index.js index 510c1535c6aaa..0345076c85b60 100644 --- a/packages/babel-plugin-remove-graphql-queries/src/__tests__/index.js +++ b/packages/babel-plugin-remove-graphql-queries/src/__tests__/index.js @@ -43,7 +43,7 @@ it(`Transforms queries in useStaticQuery`, () => { export default () => { const siteTitle = useStaticQuery(graphql\`{site { siteMetadata { title }}}\`) - + return (

{siteTitle.site.siteMetadata.title}

) @@ -79,7 +79,7 @@ it(`Transforms queries defined in own variable in useStaticQuery`, () => { export default () => { const query = graphql\`{site { siteMetadata { title }}}\` const siteTitle = useStaticQuery(query) - + return (

{siteTitle.site.siteMetadata.title}

) @@ -95,7 +95,7 @@ it(`Transforms queries and preserves destructuring in useStaticQuery`, () => { export default () => { const query = graphql\`{site { siteMetadata { title }}}\` const { site } = useStaticQuery(query) - + return (

{site.siteMetadata.title}

) @@ -111,7 +111,7 @@ it(`Transforms queries and preserves variable type in useStaticQuery`, () => { export default () => { const query = graphql\`{site { siteMetadata { title }}}\` let { site } = useStaticQuery(query) - + return (

{site.siteMetadata.title}

) @@ -142,18 +142,18 @@ it(`Transforms only the call expression in useStaticQuery`, () => { matchesSnapshot(` import React from "react" import { graphql, useStaticQuery } from "gatsby" - + const useSiteMetadata = () => { return useStaticQuery( graphql\`{site { siteMetadata { title }}}\` ).site.siteMetadata } - + export default () => { const siteMetadata = useSiteMetadata() - + return

{siteMetadata.title}

- } + } `) }) @@ -165,7 +165,7 @@ it(`Only runs transforms if useStaticQuery is imported from gatsby`, () => { export default () => { const query = graphql\`{site { siteMetadata { title }}}\` const siteTitle = useStaticQuery(query) - + return (

{siteTitle.site.siteMetadata.title}

) diff --git a/packages/babel-plugin-remove-graphql-queries/src/index.js b/packages/babel-plugin-remove-graphql-queries/src/index.js index 40055485d1cd9..5b45e13a6fb1a 100644 --- a/packages/babel-plugin-remove-graphql-queries/src/index.js +++ b/packages/babel-plugin-remove-graphql-queries/src/index.js @@ -141,8 +141,12 @@ export default function({ types: t }) { ) { const identifier = t.identifier(`staticQueryData`) const filename = state.file.opts.filename - const shortResultPath = `public/static/d/${this.queryHash}.json` - const resultPath = nodePath.join(process.cwd(), shortResultPath) + const shortResultPath = `static/d/${this.queryHash}.json` + const resultPath = nodePath.join( + (state.opts || {}).publicPath || + nodePath.join(process.cwd(), `public`), + shortResultPath + ) // Add query path2.parent.attributes.push( t.jSXAttribute( @@ -162,7 +166,7 @@ export default function({ types: t }) { nodePath.parse(filename).dir, resultPath ) - : shortResultPath + : `public/${shortResultPath}` ) ) path.unshiftContainer(`body`, importDeclaration) @@ -179,8 +183,12 @@ export default function({ types: t }) { ) { const identifier = t.identifier(`staticQueryData`) const filename = state.file.opts.filename - const shortResultPath = `public/static/d/${this.queryHash}.json` - const resultPath = nodePath.join(process.cwd(), shortResultPath) + const shortResultPath = `static/d/${this.queryHash}.json` + const resultPath = nodePath.join( + (state.opts || {}).publicPath || + nodePath.join(process.cwd(), `public`), + shortResultPath + ) // Remove query variable since it is useless now if (this.templatePath.parentPath.isVariableDeclarator()) { @@ -211,7 +219,7 @@ export default function({ types: t }) { nodePath.parse(filename).dir, resultPath ) - : shortResultPath + : `public/${shortResultPath}` ) ) path.unshiftContainer(`body`, importDeclaration) diff --git a/packages/babel-preset-gatsby/README.md b/packages/babel-preset-gatsby/README.md index 3a398510736b6..2547f9342d92a 100644 --- a/packages/babel-preset-gatsby/README.md +++ b/packages/babel-preset-gatsby/README.md @@ -34,3 +34,9 @@ npm install --dev babel-preset-gatsby `{ [string]: number | string }`, defaults to `{ "browsers": ["last 4 versions", "safari >= 7", "ie >= 9"] }` in production and `{ "browsers": ["last 2 versions", "not ie <= 11", "not android 4.4.3"] }` in development when targeting the browser and `{ "node": 6 }` in production and `{ "node": "current" }` in development when targeting Node.js. Use this option to configure [custom target browsers](https://www.gatsbyjs.org/docs/babel/). + +### `cachePath` + +`string`, defaults to `path.join(process.cwd(), '.cache')`. + +Use this option to configure cache location. diff --git a/packages/babel-preset-gatsby/src/index.js b/packages/babel-preset-gatsby/src/index.js index d0842a53188b8..381f8f7e00481 100644 --- a/packages/babel-preset-gatsby/src/index.js +++ b/packages/babel-preset-gatsby/src/index.js @@ -2,13 +2,13 @@ const path = require(`path`) const resolve = m => require.resolve(m) -const loadCachedConfig = () => { +const loadCachedConfig = cachePath => { let pluginBabelConfig = {} if (process.env.NODE_ENV !== `test`) { try { pluginBabelConfig = require(path.join( - process.cwd(), - `./.cache/babelState.json` + cachePath || path.join(process.cwd(), `.cache`), + `babelState.json` )) } catch (err) { if (err.message.includes(`Cannot find module`)) { @@ -25,9 +25,9 @@ const loadCachedConfig = () => { } module.exports = function preset(_, options = {}) { - let { targets = null } = options + let { targets = null, cachePath } = options - const pluginBabelConfig = loadCachedConfig() + const pluginBabelConfig = loadCachedConfig(cachePath) const stage = process.env.GATSBY_BUILD_STAGE || `test` if (!targets) { diff --git a/packages/gatsby-cli/src/create-cli.js b/packages/gatsby-cli/src/create-cli.js index 943e81639fde6..345f2da874fc4 100644 --- a/packages/gatsby-cli/src/create-cli.js +++ b/packages/gatsby-cli/src/create-cli.js @@ -15,6 +15,8 @@ const handlerP = fn => (...args) => { function buildLocalCommands(cli, isLocalSite) { const defaultHost = `localhost` + const defaultCacheDir = `.cache` + const defaultBuildDir = `public` const directory = path.resolve(`.`) // 'not dead' query not available in browserslist used in Gatsby v1 @@ -142,10 +144,22 @@ function buildLocalCommands(cli, isLocalSite) { .option(`open-tracing-config-file`, { type: `string`, describe: `Tracer configuration file (open tracing compatible). See https://www.gatsbyjs.org/docs/performance-tracing/`, + }) + .option(`cache-dir`, { + type: `string`, + default: defaultCacheDir, + describe: `Set cache location. Defaults to ${defaultCacheDir}`, + }) + .option(`build-dir`, { + type: `string`, + default: defaultBuildDir, + describe: `Set build directory location. Defaults to ${defaultBuildDir}`, }), handler: handlerP( getCommandHandler(`develop`, (args, cmd) => { process.env.NODE_ENV = process.env.NODE_ENV || `development` + process.env.GATSBY_CACHE_DIR = args.cacheDir + process.env.GATSBY_BUILD_DIR = args.buildDir cmd(args) // Return an empty promise to prevent handlerP from exiting early. // The development server shouldn't ever exit until the user directly @@ -172,10 +186,22 @@ function buildLocalCommands(cli, isLocalSite) { .option(`open-tracing-config-file`, { type: `string`, describe: `Tracer configuration file (open tracing compatible). See https://www.gatsbyjs.org/docs/performance-tracing/`, + }) + .option(`cache-dir`, { + type: `string`, + default: defaultCacheDir, + describe: `Set cache location. Defaults to ${defaultCacheDir}`, + }) + .option(`build-dir`, { + type: `string`, + default: defaultBuildDir, + describe: `Set build directory location. Defaults to ${defaultBuildDir}`, }), handler: handlerP( getCommandHandler(`build`, (args, cmd) => { process.env.NODE_ENV = `production` + process.env.GATSBY_CACHE_DIR = args.cacheDir + process.env.GATSBY_BUILD_DIR = args.buildDir return cmd(args) }) ), @@ -206,9 +232,23 @@ function buildLocalCommands(cli, isLocalSite) { type: `boolean`, default: false, describe: `Serve site with link paths prefixed (if built with pathPrefix in your gatsby-config.js).`, + }) + .option(`cache-dir`, { + type: `string`, + default: defaultCacheDir, + describe: `Set cache location. Defaults to ${defaultCacheDir}`, + }) + .option(`build-dir`, { + type: `string`, + default: defaultBuildDir, + describe: `Set build directory location. Defaults to ${defaultBuildDir}`, }), - handler: getCommandHandler(`serve`), + handler: getCommandHandler(`serve`, (args, cmd) => { + process.env.GATSBY_CACHE_DIR = args.cacheDir + process.env.GATSBY_BUILD_DIR = args.buildDir + return cmd(args) + }), }) cli.command({ diff --git a/packages/gatsby-plugin-feed/src/__tests__/gatsby-node.js b/packages/gatsby-plugin-feed/src/__tests__/gatsby-node.js index 4956576e6b243..c31ac0e249f09 100644 --- a/packages/gatsby-plugin-feed/src/__tests__/gatsby-node.js +++ b/packages/gatsby-plugin-feed/src/__tests__/gatsby-node.js @@ -9,6 +9,12 @@ global.Date = jest.fn(() => DATE_TO_USE) global.Date.UTC = _Date.UTC global.Date.now = _Date.now +const cache = { + publicPath(filePath) { + return path.join(`public`, filePath) + }, +} + describe(`Test plugin feed`, async () => { fs.existsSync = jest.fn() fs.existsSync.mockReturnValue(true) @@ -40,7 +46,7 @@ describe(`Test plugin feed`, async () => { }, }, }) - await onPostBuild({ graphql }, {}) + await onPostBuild({ graphql, cache }, {}) const [filePath, contents] = internals.writeFile.mock.calls[0] expect(filePath).toEqual(path.join(`public`, `rss.xml`)) expect(contents).toMatchSnapshot() @@ -113,7 +119,7 @@ describe(`Test plugin feed`, async () => { }, ], } - await onPostBuild({ graphql }, options) + await onPostBuild({ graphql, cache }, options) const [filePath, contents] = internals.writeFile.mock.calls[0] expect(filePath).toEqual(path.join(`public`, `rss_new.xml`)) expect(contents).toMatchSnapshot() diff --git a/packages/gatsby-plugin-feed/src/gatsby-node.js b/packages/gatsby-plugin-feed/src/gatsby-node.js index 17fd5bd58bc5b..7996a81dc3edc 100644 --- a/packages/gatsby-plugin-feed/src/gatsby-node.js +++ b/packages/gatsby-plugin-feed/src/gatsby-node.js @@ -5,8 +5,6 @@ import merge from "lodash.merge" import mkdirp from "mkdirp" import { defaultOptions, runQuery, writeFile } from "./internals" -const publicPath = `./public` - // A default function to transform query data into feed entries. const serialize = ({ query: { site, allMarkdownRemark } }) => allMarkdownRemark.edges.map(edge => { @@ -19,7 +17,7 @@ const serialize = ({ query: { site, allMarkdownRemark } }) => } }) -exports.onPostBuild = async ({ graphql }, pluginOptions) => { +exports.onPostBuild = async ({ graphql, cache }, pluginOptions) => { delete pluginOptions.plugins /* @@ -55,7 +53,7 @@ exports.onPostBuild = async ({ graphql }, pluginOptions) => { items.forEach(i => feed.item(i)) - const outputPath = path.join(publicPath, f.output) + const outputPath = cache.publicPath(f.output) const outputDir = path.dirname(outputPath) if (!fs.existsSync(outputDir)) { mkdirp.sync(outputDir) diff --git a/packages/gatsby-plugin-guess-js/src/gatsby-ssr.js b/packages/gatsby-plugin-guess-js/src/gatsby-ssr.js index d0c0e96b1ddaf..5d354479ea4c3 100644 --- a/packages/gatsby-plugin-guess-js/src/gatsby-ssr.js +++ b/packages/gatsby-plugin-guess-js/src/gatsby-ssr.js @@ -1,5 +1,4 @@ const _ = require(`lodash`) -const nodePath = require(`path`) const fs = require(`fs`) const React = require(`react`) @@ -13,36 +12,34 @@ function urlJoin(...parts) { } let pd = [] -const readPageData = () => { +const readPageData = cache => { if (pd.length > 0) { return pd } else { - pd = JSON.parse( - fs.readFileSync(nodePath.join(process.cwd(), `.cache`, `data.json`)) - ) + pd = JSON.parse(fs.readFileSync(cache.rootPath(`data.json`))) return pd } } let s -const readStats = () => { +const readStats = cache => { if (s) { return s } else { s = JSON.parse( - fs.readFileSync(`${process.cwd()}/public/webpack.stats.json`, `utf-8`) + fs.readFileSync(cache.publicPath(`webpack.stats.json`, `utf-8`)) ) return s } } exports.onRenderBody = ( - { setHeadComponents, pathname, pathPrefix }, + { setHeadComponents, pathname, pathPrefix, cache }, pluginOptions ) => { if (process.env.NODE_ENV === `production`) { - const pagesData = readPageData() - const stats = readStats() + const pagesData = readPageData(cache) + const stats = readStats(cache) const matchedPaths = Object.keys( guess({ path: pathname, threshold: pluginOptions.minimumThreshold }) ) diff --git a/packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js b/packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js index 7ae8021f9e184..752232fa53c09 100644 --- a/packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/__tests__/gatsby-node.js @@ -31,6 +31,11 @@ const fs = require(`fs`) const path = require(`path`) const sharp = require(`sharp`) const { onPostBootstrap } = require(`../gatsby-node`) +const cache = { + publicPath(filePath) { + return path.join(`public`, filePath) + }, +} const manifestOptions = { name: `GatsbyJS`, @@ -60,14 +65,17 @@ describe(`Test plugin manifest options`, () => { }) it(`correctly works with default parameters`, async () => { - await onPostBootstrap([], { - name: `GatsbyJS`, - short_name: `GatsbyJS`, - start_url: `/`, - background_color: `#f7f0eb`, - theme_color: `#a2466c`, - display: `standalone`, - }) + await onPostBootstrap( + { cache }, + { + name: `GatsbyJS`, + short_name: `GatsbyJS`, + start_url: `/`, + background_color: `#f7f0eb`, + theme_color: `#a2466c`, + display: `standalone`, + } + ) const [filePath, contents] = fs.writeFileSync.mock.calls[0] expect(filePath).toEqual(path.join(`public`, `manifest.webmanifest`)) expect(sharp).toHaveBeenCalledTimes(0) @@ -80,22 +88,25 @@ describe(`Test plugin manifest options`, () => { const icon = `pretend/this/exists.png` const size = 48 - await onPostBootstrap([], { - name: `GatsbyJS`, - short_name: `GatsbyJS`, - start_url: `/`, - background_color: `#f7f0eb`, - theme_color: `#a2466c`, - display: `standalone`, - icon, - icons: [ - { - src: `icons/icon-48x48.png`, - sizes: `${size}x${size}`, - type: `image/png`, - }, - ], - }) + await onPostBootstrap( + { cache }, + { + name: `GatsbyJS`, + short_name: `GatsbyJS`, + start_url: `/`, + background_color: `#f7f0eb`, + theme_color: `#a2466c`, + display: `standalone`, + icon, + icons: [ + { + src: `icons/icon-48x48.png`, + sizes: `${size}x${size}`, + type: `image/png`, + }, + ], + } + ) expect(sharp).toHaveBeenCalledWith(icon, { density: size }) expect(sharp).toHaveBeenCalledTimes(1) @@ -104,22 +115,25 @@ describe(`Test plugin manifest options`, () => { it(`fails on non existing icon`, async () => { fs.statSync.mockReturnValueOnce({ isFile: () => false }) - return onPostBootstrap([], { - name: `GatsbyJS`, - short_name: `GatsbyJS`, - start_url: `/`, - background_color: `#f7f0eb`, - theme_color: `#a2466c`, - display: `standalone`, - icon: `non/existing/path`, - icons: [ - { - src: `icons/icon-48x48.png`, - sizes: `48x48`, - type: `image/png`, - }, - ], - }).catch(err => { + return onPostBootstrap( + { cache }, + { + name: `GatsbyJS`, + short_name: `GatsbyJS`, + start_url: `/`, + background_color: `#f7f0eb`, + theme_color: `#a2466c`, + display: `standalone`, + icon: `non/existing/path`, + icons: [ + { + src: `icons/icon-48x48.png`, + sizes: `48x48`, + type: `image/png`, + }, + ], + } + ).catch(err => { expect(sharp).toHaveBeenCalledTimes(0) expect(err).toBe( `icon (non/existing/path) does not exist as defined in gatsby-config.js. Make sure the file exists relative to the root of the site.` @@ -135,10 +149,13 @@ describe(`Test plugin manifest options`, () => { theme_color_in_head: false, cache_busting_mode: `name`, } - await onPostBootstrap([], { - ...manifestOptions, - ...pluginSpecificOptions, - }) + await onPostBootstrap( + { cache }, + { + ...manifestOptions, + ...pluginSpecificOptions, + } + ) expect(sharp).toHaveBeenCalledTimes(0) const content = JSON.parse(fs.writeFileSync.mock.calls[0][1]) expect(content).toEqual(manifestOptions) @@ -152,10 +169,13 @@ describe(`Test plugin manifest options`, () => { legacy: true, cache_busting_mode: `name`, } - await onPostBootstrap([], { - ...manifestOptions, - ...pluginSpecificOptions, - }) + await onPostBootstrap( + { cache }, + { + ...manifestOptions, + ...pluginSpecificOptions, + } + ) expect(sharp).toHaveBeenCalledTimes(1) const content = JSON.parse(fs.writeFileSync.mock.calls[0][1]) @@ -170,10 +190,13 @@ describe(`Test plugin manifest options`, () => { legacy: true, cache_busting_mode: `none`, } - await onPostBootstrap([], { - ...manifestOptions, - ...pluginSpecificOptions, - }) + await onPostBootstrap( + { cache }, + { + ...manifestOptions, + ...pluginSpecificOptions, + } + ) expect(sharp).toHaveBeenCalledTimes(1) const content = JSON.parse(fs.writeFileSync.mock.calls[0][1]) diff --git a/packages/gatsby-plugin-manifest/src/gatsby-node.js b/packages/gatsby-plugin-manifest/src/gatsby-node.js index b7a0c8252e016..9510b53b3276a 100644 --- a/packages/gatsby-plugin-manifest/src/gatsby-node.js +++ b/packages/gatsby-plugin-manifest/src/gatsby-node.js @@ -22,10 +22,10 @@ try { // doesn't support cpu-core-count utility. } -function generateIcons(icons, srcIcon) { +function generateIcons(cache, icons, srcIcon) { return Promise.map(icons, icon => { const size = parseInt(icon.sizes.substring(0, icon.sizes.lastIndexOf(`x`))) - const imgPath = path.join(`public`, icon.src) + const imgPath = cache.publicPath(icon.src) // For vector graphics, instruct sharp to use a pixel density // suitable for the resolution we're rasterizing to. @@ -39,7 +39,7 @@ function generateIcons(icons, srcIcon) { }) } -exports.onPostBootstrap = async (args, pluginOptions) => { +exports.onPostBootstrap = async ({ cache }, pluginOptions) => { const { icon, ...manifest } = pluginOptions // Delete options we won't pass to the manifest.webmanifest. @@ -77,7 +77,7 @@ exports.onPostBootstrap = async (args, pluginOptions) => { //if cacheBusting is being done via url query icons must be generated before cache busting runs if (cacheMode === `query`) { - await generateIcons(manifest.icons, icon) + await generateIcons(cache, manifest.icons, icon) } if (cacheMode !== `none`) { @@ -90,7 +90,7 @@ exports.onPostBootstrap = async (args, pluginOptions) => { //if file names are being modified by cacheBusting icons must be generated after cache busting runs if (cacheMode !== `query`) { - await generateIcons(manifest.icons, icon) + await generateIcons(cache, manifest.icons, icon) } } diff --git a/packages/gatsby-plugin-netlify-cms/src/gatsby-node.js b/packages/gatsby-plugin-netlify-cms/src/gatsby-node.js index c2ba06b7758a5..fb0f1d38c63c3 100644 --- a/packages/gatsby-plugin-netlify-cms/src/gatsby-node.js +++ b/packages/gatsby-plugin-netlify-cms/src/gatsby-node.js @@ -1,4 +1,3 @@ -import path from "path" import { get, mapValues, isPlainObject, trim } from "lodash" import webpack from "webpack" import HtmlWebpackPlugin from "html-webpack-plugin" @@ -32,22 +31,18 @@ function deepMap(obj, fn) { return obj } -exports.onCreateDevServer = ({ app, store }) => { - const { program } = store.getState() +exports.onCreateDevServer = ({ app, cache }) => { app.get(`/admin`, function(req, res) { - res.sendFile( - path.join(program.directory, `public/admin/index.html`), - err => { - if (err) { - res.status(500).end(err.message) - } + res.sendFile(cache.publicPath(`admin/index.html`), err => { + if (err) { + res.status(500).end(err.message) } - ) + }) }) } exports.onCreateWebpackConfig = ( - { store, stage, getConfig, plugins, pathPrefix }, + { store, stage, getConfig, plugins, pathPrefix, cache }, { modulePath, publicPath = `admin`, @@ -71,7 +66,7 @@ exports.onCreateWebpackConfig = ( ].filter(p => p), }, output: { - path: path.join(program.directory, `public`, publicPathClean), + path: cache.publicPath(publicPathClean), }, module: { /** diff --git a/packages/gatsby-plugin-netlify/src/gatsby-node.js b/packages/gatsby-plugin-netlify/src/gatsby-node.js index afe7787de7c35..fc28aa3ef8e20 100644 --- a/packages/gatsby-plugin-netlify/src/gatsby-node.js +++ b/packages/gatsby-plugin-netlify/src/gatsby-node.js @@ -25,8 +25,11 @@ exports.onCreateWebpackConfig = ({ actions, stage }) => { }) } -exports.onPostBuild = async ({ store, pathPrefix }, userPluginOptions) => { - const pluginData = makePluginData(store, assetsManifest, pathPrefix) +exports.onPostBuild = async ( + { store, cache, pathPrefix }, + userPluginOptions +) => { + const pluginData = makePluginData(store, cache, assetsManifest, pathPrefix) const pluginOptions = { ...DEFAULT_OPTIONS, ...userPluginOptions } const { redirects } = store.getState() diff --git a/packages/gatsby-plugin-netlify/src/plugin-data.js b/packages/gatsby-plugin-netlify/src/plugin-data.js index 079c86f89d971..1b42d4f142115 100644 --- a/packages/gatsby-plugin-netlify/src/plugin-data.js +++ b/packages/gatsby-plugin-netlify/src/plugin-data.js @@ -1,9 +1,4 @@ import _ from "lodash" -import path from "path" - -export function buildPrefixer(prefix, ...paths) { - return (...subpaths) => path.join(prefix, ...paths, ...subpaths) -} // Webpack stats map to an array if source maps are enabled. // We normalize to make direct map. @@ -16,9 +11,14 @@ function normalizeStats(stats) { // This function assembles data across the manifests and store to match a similar // shape of `static-entry.js`. With it, we can build headers that point to the correct // hashed filenames and ensure we pull in the componentChunkName. -export default function makePluginData(store, assetsManifest, pathPrefix) { - const { program, pages: storePages } = store.getState() - const publicFolder = buildPrefixer(program.directory, `public`) +export default function makePluginData( + store, + cache, + assetsManifest, + pathPrefix +) { + const { pages: storePages } = store.getState() + const publicFolder = cache.publicPath const stats = require(publicFolder(`webpack.stats.json`)) const chunkManifest = normalizeStats(stats) const pages = storePages diff --git a/packages/gatsby-plugin-offline/src/gatsby-node.js b/packages/gatsby-plugin-offline/src/gatsby-node.js index 4d014c439ed8a..5e229534d7336 100644 --- a/packages/gatsby-plugin-offline/src/gatsby-node.js +++ b/packages/gatsby-plugin-offline/src/gatsby-node.js @@ -17,30 +17,29 @@ exports.createPages = ({ actions }) => { } let s -const readStats = () => { +const readStats = cache => { if (s) { return s } else { s = JSON.parse( - fs.readFileSync(`${process.cwd()}/public/webpack.stats.json`, `utf-8`) + fs.readFileSync(cache.publicPath(`webpack.stats.json`), `utf-8`) ) return s } } -const getAssetsForChunks = chunks => { +const getAssetsForChunks = (cache, chunks) => { const files = _.flatten( - chunks.map(chunk => readStats().assetsByChunkName[chunk]) + chunks.map(chunk => readStats(cache).assetsByChunkName[chunk]) ) return _.compact(files) } exports.onPostBuild = (args, pluginOptions) => { - const { pathPrefix } = args - const rootDir = `public` + const { pathPrefix, cache } = args // Get exact asset filenames for app and offline app shell chunks - const files = getAssetsForChunks([ + const files = getAssetsForChunks(cache, [ `app`, `webpack-runtime`, `component---node-modules-gatsby-plugin-offline-app-shell-js`, @@ -53,9 +52,9 @@ exports.onPostBuild = (args, pluginOptions) => { const criticalFilePaths = _.uniq( _.concat( - getResourcesFromHTML(`${process.cwd()}/${rootDir}/404.html`), + getResourcesFromHTML(cache.publicPath(`404.html`)), getResourcesFromHTML( - `${process.cwd()}/${rootDir}/offline-plugin-app-shell-fallback/index.html` + cache.publicPath(`offline-plugin-app-shell-fallback/index.html`) ) ) ).map(omitPrefix) @@ -67,12 +66,12 @@ exports.onPostBuild = (args, pluginOptions) => { const manifests = [`manifest.json`, `manifest.webmanifest`] manifests.forEach(file => { - if (fs.existsSync(`${rootDir}/${file}`)) globPatterns.push(file) + if (fs.existsSync(cache.publicPath(file))) globPatterns.push(file) }) const options = { importWorkboxFrom: `local`, - globDirectory: rootDir, + globDirectory: cache.publicPath(), globPatterns, modifyUrlPrefix: { // If `pathPrefix` is configured by user, we should replace @@ -113,10 +112,10 @@ exports.onPostBuild = (args, pluginOptions) => { const idbKeyvalFile = `idb-keyval-iife.min.js` const idbKeyvalSource = require.resolve(`idb-keyval/dist/${idbKeyvalFile}`) - const idbKeyvalDest = `public/${idbKeyvalFile}` + const idbKeyvalDest = cache.publicPath(idbKeyvalFile) fs.createReadStream(idbKeyvalSource).pipe(fs.createWriteStream(idbKeyvalDest)) - const swDest = `public/sw.js` + const swDest = cache.publicPath(`sw.js`) return workboxBuild .generateSW({ swDest, ...combinedOptions }) .then(({ count, size, warnings }) => { @@ -126,7 +125,7 @@ exports.onPostBuild = (args, pluginOptions) => { .readFileSync(`${__dirname}/sw-append.js`, `utf8`) .replace(/%pathPrefix%/g, pathPrefix) - fs.appendFileSync(`public/sw.js`, `\n` + swAppend) + fs.appendFileSync(swDest, `\n` + swAppend) console.log( `Generated ${swDest}, which will precache ${count} files, totaling ${size} bytes.` ) diff --git a/packages/gatsby-plugin-sharp/src/index.js b/packages/gatsby-plugin-sharp/src/index.js index 3805260eb7df3..0fb74557d1541 100644 --- a/packages/gatsby-plugin-sharp/src/index.js +++ b/packages/gatsby-plugin-sharp/src/index.js @@ -127,7 +127,27 @@ const healOptions = ( return options } -function queueImageResizing({ file, args = {}, reporter }) { +function getPublicPath(cache, file, argsDigestShort) { + const filePath = path.join( + `static`, + file.internal.contentDigest, + argsDigestShort + ) + + // Not all tranformer plugins are going to provide cache + if (cache) { + return cache.publicPath(filePath) + } + + const { GATSBY_BUILD_DIR } = process.env + const buildDir = GATSBY_BUILD_DIR || `public` + if (path.isAbsolute(buildDir)) { + return path.join(buildDir, filePath) + } + return path.join(process.cwd(), buildDir, filePath) +} + +function queueImageResizing({ file, args = {}, reporter, cache }) { const options = healOptions(pluginOptions, args, file.extension) // Filter out false args, and args not for this extension and put width at // end (for the file path) @@ -156,13 +176,7 @@ function queueImageResizing({ file, args = {}, reporter }) { const argsDigestShort = argsDigest.substr(argsDigest.length - 5) const imgSrc = `/${file.name}.${fileExtension}` - const dirPath = path.join( - process.cwd(), - `public`, - `static`, - file.internal.contentDigest, - argsDigestShort - ) + const dirPath = getPublicPath(cache, file, argsDigestShort) const filePath = path.join(dirPath, imgSrc) fs.ensureDirSync(dirPath) @@ -457,6 +471,7 @@ async function fluid({ file, args = {}, reporter, cache }) { file, args: arrrgs, // matey reporter, + cache, }) }) @@ -577,6 +592,7 @@ async function fixed({ file, args = {}, reporter, cache }) { file, args: arrrgs, reporter, + cache, }) }) diff --git a/packages/gatsby-plugin-sitemap/src/__tests__/gatsby-node.js b/packages/gatsby-plugin-sitemap/src/__tests__/gatsby-node.js index 690f1888cf837..dab343fd34a9b 100644 --- a/packages/gatsby-plugin-sitemap/src/__tests__/gatsby-node.js +++ b/packages/gatsby-plugin-sitemap/src/__tests__/gatsby-node.js @@ -6,6 +6,15 @@ const { onPostBuild } = require(`../gatsby-node`) const internals = require(`../internals`) const pathPrefix = `` +const cache = { + publicPath(filePath) { + if (!filePath) { + return `public` + } + return path.join(`public`, filePath) + }, +} + describe(`Test plugin sitemap`, async () => { it(`default settings work properly`, async () => { internals.writeFile = jest.fn() @@ -34,7 +43,7 @@ describe(`Test plugin sitemap`, async () => { }, }, }) - await onPostBuild({ graphql, pathPrefix }, {}) + await onPostBuild({ graphql, pathPrefix, cache }, {}) const [filePath, contents] = internals.writeFile.mock.calls[0] expect(filePath).toEqual(path.join(`public`, `sitemap.xml`)) expect(contents).toMatchSnapshot() @@ -80,7 +89,7 @@ describe(`Test plugin sitemap`, async () => { path } } - } + } }` const options = { output: `custom-sitemap.xml`, @@ -95,7 +104,7 @@ describe(`Test plugin sitemap`, async () => { exclude: [`/post/exclude-page`], query: customQuery, } - await onPostBuild({ graphql, pathPrefix }, options) + await onPostBuild({ graphql, pathPrefix, cache }, options) const [filePath, contents] = internals.writeFile.mock.calls[0] expect(filePath).toEqual(path.join(`public`, `custom-sitemap.xml`)) expect(contents).toMatchSnapshot() @@ -153,15 +162,15 @@ describe(`Test plugin sitemap`, async () => { const options = { sitemapSize: 1, } - await onPostBuild({ graphql, pathPrefix }, options) + await onPostBuild({ graphql, pathPrefix, cache }, options) expect(fs.createWriteStream.mock.calls[0][0]).toEqual( - `./public/sitemap-0.xml` + `public/sitemap-0.xml` ) expect(fs.createWriteStream.mock.calls[1][0]).toEqual( - `./public/sitemap-1.xml` + `public/sitemap-1.xml` ) expect(fs.createWriteStream.mock.calls[2][0]).toEqual( - `./public/sitemap-index.xml` + `public/sitemap-index.xml` ) const [originalFile, newFile] = internals.renameFile.mock.calls[0] expect(originalFile).toEqual(path.join(`public`, `sitemap-index.xml`)) @@ -171,7 +180,7 @@ describe(`Test plugin sitemap`, async () => { const options = { sitemapSize: 100, } - await onPostBuild({ graphql, pathPrefix }, options) + await onPostBuild({ graphql, pathPrefix, cache }, options) const [filePath, contents] = internals.writeFile.mock.calls[0] expect(filePath).toEqual(path.join(`public`, `sitemap.xml`)) expect(contents).toMatchSnapshot() diff --git a/packages/gatsby-plugin-sitemap/src/gatsby-node.js b/packages/gatsby-plugin-sitemap/src/gatsby-node.js index 1ab7344c1a603..d04142e2a2f1e 100644 --- a/packages/gatsby-plugin-sitemap/src/gatsby-node.js +++ b/packages/gatsby-plugin-sitemap/src/gatsby-node.js @@ -1,4 +1,3 @@ -import path from "path" import sitemap from "sitemap" import { defaultOptions, @@ -8,9 +7,7 @@ import { withoutTrailingSlash, } from "./internals" -const publicPath = `./public` - -exports.onPostBuild = async ({ graphql, pathPrefix }, pluginOptions) => { +exports.onPostBuild = async ({ graphql, pathPrefix, cache }, pluginOptions) => { const options = { ...pluginOptions } delete options.plugins delete options.createLinkInHead @@ -20,7 +17,7 @@ exports.onPostBuild = async ({ graphql, pathPrefix }, pluginOptions) => { ...options, } - const saved = path.join(publicPath, output) + const saved = cache.publicPath(output) // Paths we're excluding... const excludeOptions = exclude.concat(defaultOptions.exclude) @@ -46,14 +43,13 @@ exports.onPostBuild = async ({ graphql, pathPrefix }, pluginOptions) => { } = queryRecords return new Promise(resolve => { // sitemap-index.xml is default file name. (https://git.io/fhNgG) - const indexFilePath = path.join( - publicPath, + const indexFilePath = cache.publicPath( `${rest.sitemapName || `sitemap`}-index.xml` ) const sitemapIndexOptions = { ...rest, hostname: hostname || withoutTrailingSlash(siteUrl), - targetFolder: publicPath, + targetFolder: cache.publicPath(), urls, callback: error => { if (error) throw new Error(error) diff --git a/packages/gatsby-plugin-sitemap/src/internals.js b/packages/gatsby-plugin-sitemap/src/internals.js index 3b646be9848c4..5ffe31c8c6ac3 100644 --- a/packages/gatsby-plugin-sitemap/src/internals.js +++ b/packages/gatsby-plugin-sitemap/src/internals.js @@ -49,7 +49,7 @@ export const defaultOptions = { } } }`, - output: `/sitemap.xml`, + output: `sitemap.xml`, exclude: [ `/dev-404-page`, `/404`, diff --git a/packages/gatsby-plugin-subfont/src/gatsby-node.js b/packages/gatsby-plugin-subfont/src/gatsby-node.js index 20b012e0fb8d1..bd5da89f0f9c6 100644 --- a/packages/gatsby-plugin-subfont/src/gatsby-node.js +++ b/packages/gatsby-plugin-subfont/src/gatsby-node.js @@ -1,16 +1,17 @@ const path = require(`path`) const { execSync } = require(`child_process`) -exports.onPostBuild = ({ store }) => { - const root = path.join(store.getState().program.directory, `public`) +exports.onPostBuild = ({ store, cache }) => { // TODO make this configurable const urlPaths = [`/`] const filePaths = urlPaths.reduce( (accumulator, currentPath) => - `${accumulator} ${path.join(root, currentPath, `index.html`)}`, + `${accumulator} ${cache.publicPath( + path.join(currentPath, `index.html`) + )}`, `` ) - const command = `node_modules/.bin/subfont -i --no-recursive --inline-css --root file://${root}${filePaths}` + const command = `node_modules/.bin/subfont -i --no-recursive --inline-css --root file://${cache.publicPath()}${filePaths}` execSync(command) } diff --git a/packages/gatsby-remark-copy-linked-files/src/__tests__/index.js b/packages/gatsby-remark-copy-linked-files/src/__tests__/index.js index 15661be036605..eb2581dfd0129 100644 --- a/packages/gatsby-remark-copy-linked-files/src/__tests__/index.js +++ b/packages/gatsby-remark-copy-linked-files/src/__tests__/index.js @@ -244,8 +244,7 @@ describe(`gatsby-remark-copy-linked-files`, () => { const markdownAST = remark.parse(`![some absolute image](${imagePath})`) const validDestinationDir = `path/to/dir` const expectedNewPath = path.posix.join( - process.cwd(), - `public`, + path.join(process.cwd(), `public`), validDestinationDir, `/undefined-undefined.gif` ) @@ -269,8 +268,7 @@ describe(`gatsby-remark-copy-linked-files`, () => { const pathPrefix = `/blog` const validDestinationDir = `path/to/dir` const expectedNewPath = path.posix.join( - process.cwd(), - `public`, + path.join(process.cwd(), `public`), validDestinationDir, `/undefined-undefined.gif` ) @@ -298,8 +296,7 @@ describe(`gatsby-remark-copy-linked-files`, () => { it(`copies file to root dir when not supplied'`, async () => { const markdownAST = remark.parse(`![some absolute image](${imagePath})`) const expectedNewPath = path.posix.join( - process.cwd(), - `public`, + path.join(process.cwd(), `public`), `/undefined-undefined.gif` ) expect.assertions(3) diff --git a/packages/gatsby-remark-copy-linked-files/src/index.js b/packages/gatsby-remark-copy-linked-files/src/index.js index 7e1c8011fed71..679447617c8f9 100644 --- a/packages/gatsby-remark-copy-linked-files/src/index.js +++ b/packages/gatsby-remark-copy-linked-files/src/index.js @@ -6,8 +6,7 @@ const path = require(`path`) const _ = require(`lodash`) const cheerio = require(`cheerio`) const imageSize = require(`probe-image-size`) - -const DEPLOY_DIR = `public` +const { publicPath } = require(`gatsby/dist/utils/cache`) const invalidDestinationDirMessage = dir => `[gatsby-remark-copy-linked-files You have supplied an invalid destination directory. The destination directory must be a child but was: ${dir}` @@ -23,14 +22,9 @@ const newFileName = linkNode => const newPath = (linkNode, destinationDir) => { if (destinationDir) { - return path.posix.join( - process.cwd(), - DEPLOY_DIR, - destinationDir, - newFileName(linkNode) - ) + return path.posix.join(publicPath(), destinationDir, newFileName(linkNode)) } - return path.posix.join(process.cwd(), DEPLOY_DIR, newFileName(linkNode)) + return path.posix.join(publicPath(), newFileName(linkNode)) } const newLinkURL = (linkNode, destinationDir, pathPrefix) => { diff --git a/packages/gatsby-source-contentful/src/cache-image.js b/packages/gatsby-source-contentful/src/cache-image.js index a0a9bb30b04d5..805a0806e63fc 100644 --- a/packages/gatsby-source-contentful/src/cache-image.js +++ b/packages/gatsby-source-contentful/src/cache-image.js @@ -1,12 +1,13 @@ const crypto = require(`crypto`) -const { resolve, parse } = require(`path`) +const { join, parse } = require(`path`) const axios = require(`axios`) const { pathExists, createWriteStream } = require(`fs-extra`) -module.exports = async function cacheImage(store, image, options) { - const program = store.getState().program - const CACHE_DIR = resolve(`${program.directory}/.cache/contentful/assets/`) +const { CACHE_NAME } = require(`./constants`) + +module.exports = async function cacheImage(cache, image, options) { + const CACHE_DIR = cache.rootPath(CACHE_NAME) const { file: { url, fileName, details }, } = image @@ -43,7 +44,7 @@ module.exports = async function cacheImage(store, image, options) { .digest(`hex`) const { name, ext } = parse(fileName) - const absolutePath = resolve(CACHE_DIR, `${name}-${optionsHash}${ext}`) + const absolutePath = join(CACHE_DIR, `${name}-${optionsHash}${ext}`) const alreadyExists = await pathExists(absolutePath) diff --git a/packages/gatsby-source-contentful/src/constants.js b/packages/gatsby-source-contentful/src/constants.js new file mode 100644 index 0000000000000..8f9d574516b95 --- /dev/null +++ b/packages/gatsby-source-contentful/src/constants.js @@ -0,0 +1 @@ +module.exports.CACHE_NAME = `contentful/assets/` diff --git a/packages/gatsby-source-contentful/src/extend-node-type.js b/packages/gatsby-source-contentful/src/extend-node-type.js index 7c45c2f7e17a1..fa6f2b58f0215 100644 --- a/packages/gatsby-source-contentful/src/extend-node-type.js +++ b/packages/gatsby-source-contentful/src/extend-node-type.js @@ -458,7 +458,7 @@ const fluidNodeType = ({ name, getTracedSVG }) => { } } -exports.extendNodeType = ({ type, store }) => { +exports.extendNodeType = ({ type, cache }) => { if (type.name.match(/contentful.*RichTextNode/)) { return { nodeType: { @@ -491,7 +491,7 @@ exports.extendNodeType = ({ type, store }) => { return null } - const absolutePath = await cacheImage(store, image, options) + const absolutePath = await cacheImage(cache, image, options) const extension = path.extname(absolutePath) return traceSVG({ diff --git a/packages/gatsby-source-contentful/src/gatsby-node.js b/packages/gatsby-source-contentful/src/gatsby-node.js index ee68091e34669..585910aef6027 100644 --- a/packages/gatsby-source-contentful/src/gatsby-node.js +++ b/packages/gatsby-source-contentful/src/gatsby-node.js @@ -1,4 +1,3 @@ -const path = require(`path`) const isOnline = require(`is-online`) const _ = require(`lodash`) const fs = require(`fs-extra`) @@ -6,6 +5,8 @@ const fs = require(`fs-extra`) const normalize = require(`./normalize`) const fetchData = require(`./fetch`) +const { CACHE_NAME } = require(`./constants`) + const conflictFieldPrefix = `contentful` // restrictedNodeFields from here https://www.gatsbyjs.org/docs/node-interface/ @@ -215,12 +216,8 @@ exports.sourceNodes = async ( // Check if there are any ContentfulAsset nodes and if gatsby-image is installed. If so, // add fragments for ContentfulAsset and gatsby-image. The fragment will cause an error // if there's not ContentfulAsset nodes and without gatsby-image, the fragment is useless. -exports.onPreExtractQueries = async ({ store, getNodesByType }) => { - const program = store.getState().program - - const CACHE_DIR = path.resolve( - `${program.directory}/.cache/contentful/assets/` - ) +exports.onPreExtractQueries = async ({ cache, getNodesByType }) => { + const CACHE_DIR = cache.rootPath(CACHE_NAME) await fs.ensureDir(CACHE_DIR) if (getNodesByType(`ContentfulAsset`).length == 0) { @@ -243,6 +240,6 @@ exports.onPreExtractQueries = async ({ store, getNodesByType }) => { // add our fragments to .cache/fragments. await fs.copy( require.resolve(`gatsby-source-contentful/src/fragments.js`), - `${program.directory}/.cache/fragments/contentful-asset-fragments.js` + cache.rootPath(`fragments/contentful-asset-fragments.js`) ) } diff --git a/packages/gatsby-source-filesystem/README.md b/packages/gatsby-source-filesystem/README.md index 177433c737b3a..12791ab4d5ec8 100644 --- a/packages/gatsby-source-filesystem/README.md +++ b/packages/gatsby-source-filesystem/README.md @@ -197,7 +197,6 @@ const { createRemoteFileNode } = require(`gatsby-source-filesystem`) exports.downloadMediaFiles = ({ nodes, - store, cache, createNode, createNodeId, @@ -212,7 +211,6 @@ exports.downloadMediaFiles = ({ fileNode = await createRemoteFileNode({ url: node.source_url, parentNodeId: node.id, - store, cache, createNode, createNodeId, @@ -243,7 +241,6 @@ createRemoteFileNode({ // The source url of the remote file url: `https://example.com/a-file-without-an-extension`, parentNodeId: node.id, - store, cache, createNode, createNodeId, diff --git a/packages/gatsby-source-filesystem/src/__tests__/create-remote-file-node.js b/packages/gatsby-source-filesystem/src/__tests__/create-remote-file-node.js index 92f4622844bf3..f2a3052cd7e73 100644 --- a/packages/gatsby-source-filesystem/src/__tests__/create-remote-file-node.js +++ b/packages/gatsby-source-filesystem/src/__tests__/create-remote-file-node.js @@ -41,15 +41,4 @@ describe(`create-remote-file-node`, () => { `"cache must be the Gatsby cache, was undefined"` ) }) - - it(`throws on invalid inputs: store`, () => { - expect(() => { - createRemoteFileNode({ - ...defaultArgs, - store: undefined, - }) - }).toThrowErrorMatchingInlineSnapshot( - `"store must be the redux store, was undefined"` - ) - }) }) diff --git a/packages/gatsby-source-filesystem/src/create-remote-file-node.js b/packages/gatsby-source-filesystem/src/create-remote-file-node.js index 7f6d1cf09ae96..808a84ac37164 100644 --- a/packages/gatsby-source-filesystem/src/create-remote-file-node.js +++ b/packages/gatsby-source-filesystem/src/create-remote-file-node.js @@ -47,7 +47,6 @@ const bar = new ProgressBar( * @description Create Remote File Node Payload * * @param {String} options.url - * @param {Redux} options.store * @param {GatsbyCache} options.cache * @param {Function} options.createNode * @param {Auth} [options.auth] @@ -71,7 +70,6 @@ const createHash = str => .update(str) .digest(`hex`) -const CACHE_DIR = `.cache` const FS_PLUGIN_DIR = `gatsby-source-filesystem` /** @@ -180,7 +178,6 @@ const requestRemoteNode = (url, headers, tmpFilename) => */ async function processRemoteNode({ url, - store, cache, createNode, parentNodeId, @@ -190,11 +187,7 @@ async function processRemoteNode({ name, }) { // Ensure our cache directory exists. - const pluginCacheDir = path.join( - store.getState().program.directory, - CACHE_DIR, - FS_PLUGIN_DIR - ) + const pluginCacheDir = cache.rootPath(FS_PLUGIN_DIR) await fs.ensureDir(pluginCacheDir) // See if there's response headers for this url @@ -307,7 +300,6 @@ let totalJobs = 0 */ module.exports = ({ url, - store, cache, createNode, parentNodeId = null, @@ -327,9 +319,6 @@ module.exports = ({ if (typeof createNode !== `function`) { throw new Error(`createNode must be a function, was ${typeof createNode}`) } - if (typeof store !== `object`) { - throw new Error(`store must be the redux store, was ${typeof store}`) - } if (typeof cache !== `object`) { throw new Error(`cache must be the Gatsby cache, was ${typeof cache}`) } @@ -351,7 +340,6 @@ module.exports = ({ const fileDownloadPromise = pushTask({ url, - store, cache, createNode, parentNodeId, diff --git a/packages/gatsby-source-filesystem/src/extend-file-node.js b/packages/gatsby-source-filesystem/src/extend-file-node.js index 52991f4d1616f..44d06467fd423 100644 --- a/packages/gatsby-source-filesystem/src/extend-file-node.js +++ b/packages/gatsby-source-filesystem/src/extend-file-node.js @@ -2,7 +2,12 @@ const { GraphQLString } = require(`gatsby/graphql`) const fs = require(`fs-extra`) const path = require(`path`) -module.exports = ({ type, getNodeAndSavePathDependency, pathPrefix = `` }) => { +module.exports = ({ + type, + getNodeAndSavePathDependency, + pathPrefix = ``, + cache, +}) => { if (type.name !== `File`) { return {} } @@ -18,12 +23,7 @@ module.exports = ({ type, getNodeAndSavePathDependency, pathPrefix = `` }) => { details.ext }` - const publicPath = path.join( - process.cwd(), - `public`, - `static`, - fileName - ) + const publicPath = cache.publicPath(path.join(`static`, fileName)) if (!fs.existsSync(publicPath)) { fs.copy(details.absolutePath, publicPath, err => { diff --git a/packages/gatsby-transformer-sharp/src/extend-node-type.js b/packages/gatsby-transformer-sharp/src/extend-node-type.js index 519c23a00d1c6..a52f46b6b4464 100644 --- a/packages/gatsby-transformer-sharp/src/extend-node-type.js +++ b/packages/gatsby-transformer-sharp/src/extend-node-type.js @@ -374,12 +374,7 @@ module.exports = ({ const imageName = `${details.name}-${image.internal.contentDigest}${ details.ext }` - const publicPath = path.join( - process.cwd(), - `public`, - `static`, - imageName - ) + const publicPath = cache.publicPath(path.join(`static`, imageName)) if (!fsExtra.existsSync(publicPath)) { fsExtra.copy(details.absolutePath, publicPath, err => { diff --git a/packages/gatsby-transformer-sharp/src/gatsby-node.js b/packages/gatsby-transformer-sharp/src/gatsby-node.js index 3f9433c15f639..cfdbfbdef1947 100644 --- a/packages/gatsby-transformer-sharp/src/gatsby-node.js +++ b/packages/gatsby-transformer-sharp/src/gatsby-node.js @@ -3,18 +3,16 @@ const fs = require(`fs-extra`) exports.onCreateNode = require(`./on-node-create`) exports.setFieldsOnGraphQLNodeType = require(`./extend-node-type`) -exports.onPreExtractQueries = async ({ store, getNodesByType }) => { - const program = store.getState().program - +exports.onPreExtractQueries = async ({ cache, getNodesByType }) => { // Check if there are any ImageSharp nodes. If so add fragments for ImageSharp. // The fragment will cause an error if there are no ImageSharp nodes. if (getNodesByType(`ImageSharp`).length == 0) { return } - // We have ImageSharp nodes so let's add our fragments to .cache/fragments. + // We have ImageSharp nodes so let's add our fragments to cache/fragments. await fs.copy( require.resolve(`gatsby-transformer-sharp/src/fragments.js`), - `${program.directory}/.cache/fragments/image-sharp-fragments.js` + cache.rootPath(`fragments/image-sharp-fragments.js`) ) } diff --git a/packages/gatsby-transformer-sqip/src/extend-node-type.js b/packages/gatsby-transformer-sqip/src/extend-node-type.js index 5ed8261a51c2c..2e541a8c5f553 100644 --- a/packages/gatsby-transformer-sqip/src/extend-node-type.js +++ b/packages/gatsby-transformer-sqip/src/extend-node-type.js @@ -1,5 +1,3 @@ -const { resolve } = require(`path`) - const { DuotoneGradientType, ImageCropFocusType, @@ -22,6 +20,8 @@ const generateSqip = require(`./generate-sqip`) const debug = Debug(`gatsby-transformer-sqip`) const SUPPORTED_NODES = [`ImageSharp`, `ContentfulAsset`] +const cacheName = `sqip` + module.exports = async args => { const { type: { name }, @@ -41,9 +41,8 @@ module.exports = async args => { return {} } -async function sqipSharp({ type, cache, getNodeAndSavePathDependency, store }) { - const program = store.getState().program - const cacheDir = resolve(`${program.directory}/.cache/sqip/`) +async function sqipSharp({ type, cache, getNodeAndSavePathDependency }) { + const cacheDir = cache.rootPath(cacheName) await ensureDir(cacheDir) @@ -130,15 +129,14 @@ async function sqipSharp({ type, cache, getNodeAndSavePathDependency, store }) { } } -async function sqipContentful({ type, cache, store }) { +async function sqipContentful({ type, cache }) { const { schemes: { ImageResizingBehavior, ImageCropFocusType }, } = require(`gatsby-source-contentful`) const cacheImage = require(`gatsby-source-contentful/cache-image`) - const program = store.getState().program - const cacheDir = resolve(`${program.directory}/.cache/sqip/`) + const cacheDir = cache.rootPath(cacheName) await ensureDir(cacheDir) @@ -217,7 +215,7 @@ async function sqipContentful({ type, cache, store }) { background, } - const absolutePath = await cacheImage(store, asset, options) + const absolutePath = await cacheImage(cache, asset, options) return generateSqip({ cache, diff --git a/packages/gatsby/cache-dir/__tests__/static-entry.js b/packages/gatsby/cache-dir/__tests__/static-entry.js index 03d857d15a1de..8746b464987a3 100644 --- a/packages/gatsby/cache-dir/__tests__/static-entry.js +++ b/packages/gatsby/cache-dir/__tests__/static-entry.js @@ -8,6 +8,21 @@ jest.mock(`gatsby/package.json`, () => { } }) +jest.mock( + `gatsby-public-dir/webpack.stats.json`, + () => { + return {} + }, + { virtual: true } +) +jest.mock( + `gatsby-public-dir/chunk-map.json`, + () => { + return {} + }, + { virtual: true } +) + jest.mock( `../sync-requires`, () => { @@ -41,13 +56,6 @@ jest.mock( { virtual: true } ) -const MOCK_FILE_INFO = { - [`${process.cwd()}/public/webpack.stats.json`]: `{}`, - [`${process.cwd()}/public/chunk-map.json`]: `{}`, -} - -require(`fs`).__setMockFiles(MOCK_FILE_INFO) - // Needs to be imported after __setMockFiles is called, and imports get hoisted. const StaticEntry = require(`../static-entry`).default diff --git a/packages/gatsby/cache-dir/static-entry.js b/packages/gatsby/cache-dir/static-entry.js index 3e0cb708ba184..4dce7e25b3c67 100644 --- a/packages/gatsby/cache-dir/static-entry.js +++ b/packages/gatsby/cache-dir/static-entry.js @@ -1,6 +1,5 @@ const React = require(`react`) const fs = require(`fs`) -const { join } = require(`path`) const { renderToString, renderToStaticMarkup } = require(`react-dom/server`) const { ServerLocation, Router, isRedirect } = require(`@reach/router`) const { get, merge, isObject, flatten, uniqBy } = require(`lodash`) @@ -14,13 +13,9 @@ const { version: gatsbyVersion } = require(`gatsby/package.json`) const pagesObjectMap = new Map() pages.forEach(p => pagesObjectMap.set(p.path, p)) -const stats = JSON.parse( - fs.readFileSync(`${process.cwd()}/public/webpack.stats.json`, `utf-8`) -) +const stats = require(`gatsby-public-dir/webpack.stats.json`) -const chunkMapping = JSON.parse( - fs.readFileSync(`${process.cwd()}/public/chunk-map.json`, `utf-8`) -) +const chunkMapping = require(`gatsby-public-dir/chunk-map.json`) // const testRequireError = require("./test-require-error") // For some extremely mysterious reason, webpack adds the above module *after* @@ -116,11 +111,9 @@ export default (pagePath, callback) => { if (page.jsonName in dataPaths) { const pathToJsonData = `../public/` + dataPaths[page.jsonName] try { - dataAndContext = JSON.parse( - fs.readFileSync( - `${process.cwd()}/public/static/d/${dataPaths[page.jsonName]}.json` - ) - ) + dataAndContext = require(`gatsby-public-dir/static/d/${ + dataPaths[page.jsonName] + }.json`) } catch (e) { console.log(`error`, pathToJsonData, e) process.exit() @@ -312,7 +305,7 @@ export default (pagePath, callback) => { data-href={`${__PATH_PREFIX__}/${style.name}`} dangerouslySetInnerHTML={{ __html: fs.readFileSync( - join(process.cwd(), `public`, style.name), + require.resolve(`gatsby-public-dir/${style.name}`), `utf-8` ), }} diff --git a/packages/gatsby/src/bootstrap/index.js b/packages/gatsby/src/bootstrap/index.js index f29a1b63beb1d..1b227bd352b7f 100644 --- a/packages/gatsby/src/bootstrap/index.js +++ b/packages/gatsby/src/bootstrap/index.js @@ -12,6 +12,7 @@ const Promise = require(`bluebird`) const apiRunnerNode = require(`../utils/api-runner-node`) const getBrowserslist = require(`../utils/browserslist`) +const { publicPath, cachePath } = require(`../utils/cache`) const { graphql } = require(`graphql`) const { store, emitter } = require(`../redux`) const loadPlugins = require(`./load-plugins`) @@ -129,12 +130,15 @@ module.exports = async (args: BootstrapArgs) => { } ) activity.start() - await del([ - `public/*.{html,css}`, - `public/**/*.{html,css}`, - `!public/static`, - `!public/static/**/*.{html,css}`, - ]) + await del( + [ + `${publicPath()}/*.{html,css}`, + `${publicPath()}/**/*.{html,css}`, + `!${publicPath()}/static`, + `!${publicPath()}/static/**/*.{html,css}`, + ], + { force: true } + ) activity.end() } @@ -165,7 +169,7 @@ module.exports = async (args: BootstrapArgs) => { let state = store.getState() const oldPluginsHash = state && state.status ? state.status.PLUGINS_HASH : `` - // Check if anything has changed. If it has, delete the site's .cache + // Check if anything has changed. If it has, delete the site's cache // directory and tell reducers to empty themselves. // // Also if the hash isn't there, then delete things just in case something @@ -177,14 +181,14 @@ module.exports = async (args: BootstrapArgs) => { data `) } - const cacheDirectory = `${program.directory}/.cache` + const cacheDirectory = cachePath() if (!oldPluginsHash || pluginsHash !== oldPluginsHash) { try { // Attempt to empty dir if remove fails, // like when directory is mount point await fs.remove(cacheDirectory).catch(() => fs.emptyDir(cacheDirectory)) } catch (e) { - report.error(`Failed to remove .cache files.`, e) + report.error(`Failed to remove cache files.`, e) } // Tell reducers to delete their data (the store will already have // been loaded from the file system cache). @@ -199,12 +203,12 @@ module.exports = async (args: BootstrapArgs) => { payload: pluginsHash, }) - // Now that we know the .cache directory is safe, initialize the cache + // Now that we know the cache directory is safe, initialize the cache // directory. await fs.ensureDir(cacheDirectory) // Ensure the public/static directory - await fs.ensureDir(`${program.directory}/public/static`) + await fs.ensureDir(publicPath(`static`)) activity.end() @@ -217,7 +221,7 @@ module.exports = async (args: BootstrapArgs) => { parentSpan: bootstrapSpan, }) activity.start() - const dbSaveFile = `${cacheDirectory}/loki/loki.db` + const dbSaveFile = cachePath(`loki/loki.db`) try { await loki.start({ saveFile: dbSaveFile, @@ -246,17 +250,17 @@ module.exports = async (args: BootstrapArgs) => { await fs.copy(srcDir, siteDir, { clobber: true, }) - await fs.copy(tryRequire, `${siteDir}/test-require-error.js`, { + await fs.copy(tryRequire, cachePath(`test-require-error.js`), { clobber: true, }) - await fs.ensureDirSync(`${cacheDirectory}/json`) + await fs.ensureDirSync(cachePath(`json`)) - // Ensure .cache/fragments exists and is empty. We want fragments to be + // Ensure cache/fragments exists and is empty. We want fragments to be // added on every run in response to data as fragments can only be added if // the data used to create the schema they're dependent on is available. - await fs.emptyDir(`${cacheDirectory}/fragments`) + await fs.emptyDir(cachePath(`fragments`)) } catch (err) { - report.panic(`Unable to copy site files to .cache`, err) + report.panic(`Unable to copy site files to cache`, err) } // Find plugins which implement gatsby-browser and gatsby-ssr and write diff --git a/packages/gatsby/src/commands/build-html.js b/packages/gatsby/src/commands/build-html.js index 2a1548bc77240..cdbad74c41d3f 100644 --- a/packages/gatsby/src/commands/build-html.js +++ b/packages/gatsby/src/commands/build-html.js @@ -7,6 +7,7 @@ const webpackConfig = require(`../utils/webpack.config`) const { store } = require(`../redux`) const { createErrorFromString } = require(`gatsby-cli/lib/reporter/errors`) const renderHTMLQueue = require(`../utils/html-renderer-queue`) +const { publicPath } = require(`../utils/cache`) module.exports = async (program: any, activity: any) => { const { directory } = program @@ -28,7 +29,7 @@ module.exports = async (program: any, activity: any) => { if (e) { return reject(e) } - const outputFile = `${directory}/public/render-page.js` + const outputFile = publicPath(`render-page.js`) if (stats.hasErrors()) { let webpackErrors = stats.toJson().errors.filter(Boolean) return reject( diff --git a/packages/gatsby/src/commands/clean.js b/packages/gatsby/src/commands/clean.js index b9e0752c21694..f915f9eb526d4 100644 --- a/packages/gatsby/src/commands/clean.js +++ b/packages/gatsby/src/commands/clean.js @@ -1,10 +1,11 @@ const fs = require(`fs-extra`) const path = require(`path`) +const { cachePath, publicPath } = require(`../utils/cache`) module.exports = async function clean(args) { const { directory, report } = args - const directories = [`.cache`, `public`] + const directories = [cachePath(``, directory), publicPath(``, directory)] report.info(`Deleting ${directories.join(`, `)}`) diff --git a/packages/gatsby/src/commands/develop-html.js b/packages/gatsby/src/commands/develop-html.js index ef2c89c19035f..0e56427fdbcd1 100644 --- a/packages/gatsby/src/commands/develop-html.js +++ b/packages/gatsby/src/commands/develop-html.js @@ -5,6 +5,7 @@ const { createErrorFromString } = require(`gatsby-cli/lib/reporter/errors`) const debug = require(`debug`)(`gatsby:html`) const webpackConfig = require(`../utils/webpack.config`) const renderHTMLQueue = require(`../utils/html-renderer-queue`) +const { publicPath } = require(`../utils/cache`) module.exports = async (program: any) => { const { directory } = program @@ -24,7 +25,7 @@ module.exports = async (program: any) => { if (e) { return reject(e) } - const outputFile = `${directory}/public/render-page.js` + const outputFile = publicPath(`render-page.js`) if (stats.hasErrors()) { let webpackErrors = stats.toJson().errors console.log(`here`, webpackErrors[0]) diff --git a/packages/gatsby/src/commands/develop.js b/packages/gatsby/src/commands/develop.js index 24087560d5325..3a4573dce4469 100644 --- a/packages/gatsby/src/commands/develop.js +++ b/packages/gatsby/src/commands/develop.js @@ -29,6 +29,7 @@ const getSslCert = require(`../utils/get-ssl-cert`) const slash = require(`slash`) const { initTracer } = require(`../utils/tracer`) const apiRunnerNode = require(`../utils/api-runner-node`) +const { publicPath } = require(`../utils/cache`) // const isInteractive = process.stdout.isTTY @@ -148,7 +149,9 @@ async function startServer(program) { // This can lead to serving stale html files during development. // // We serve by default an empty index.html that sets up the dev environment. - app.use(require(`./develop-static`)(`public`, { index: false })) + app.use( + require(`./develop-static`)(publicPath(``, directory), { index: false }) + ) app.use( require(`webpack-dev-middleware`)(compiler, { @@ -190,7 +193,7 @@ async function startServer(program) { // Render an HTML page and serve it. app.use((req, res, next) => { - res.sendFile(directoryPath(`public/index.html`), err => { + res.sendFile(publicPath(`index.html`, directory), err => { if (err) { res.status(500).end() } diff --git a/packages/gatsby/src/commands/serve.js b/packages/gatsby/src/commands/serve.js index 9ed5fe0a74d36..ed778708a38f7 100644 --- a/packages/gatsby/src/commands/serve.js +++ b/packages/gatsby/src/commands/serve.js @@ -7,12 +7,13 @@ const compression = require(`compression`) const express = require(`express`) const getConfigFile = require(`../bootstrap/get-config-file`) const preferDefault = require(`../bootstrap/prefer-default`) +const { cachePath, publicPath } = require(`../utils/cache`) const chalk = require(`chalk`) const { match: reachMatch } = require(`@reach/router/lib/utils`) const getPages = directory => fs - .readFile(path.join(directory, `.cache`, `pages.json`)) + .readFile(path.join(cachePath(``, directory), `pages.json`)) .then(contents => JSON.parse(contents)) .catch(() => []) @@ -51,13 +52,13 @@ module.exports = async program => { let pathPrefix = config && config.pathPrefix pathPrefix = prefixPaths && pathPrefix ? pathPrefix : `/` - const root = path.join(program.directory, `public`) + const root = publicPath(``, program.directory) const pages = await getPages(program.directory) const app = express() const router = express.Router() router.use(compression()) - router.use(express.static(`public`)) + router.use(express.static(root)) router.use(clientOnlyPathsRouter(pages, { root })) router.use((req, res, next) => { if (req.accepts(`html`)) { diff --git a/packages/gatsby/src/internal-plugins/dev-404-page/gatsby-node.js b/packages/gatsby/src/internal-plugins/dev-404-page/gatsby-node.js index 1204fa110c985..ebf91ec1f87df 100644 --- a/packages/gatsby/src/internal-plugins/dev-404-page/gatsby-node.js +++ b/packages/gatsby/src/internal-plugins/dev-404-page/gatsby-node.js @@ -2,16 +2,11 @@ const path = require(`path`) const fs = require(`fs-extra`) const chokidar = require(`chokidar`) -exports.createPagesStatefully = async ({ store, actions }, options, done) => { +exports.createPagesStatefully = async ({ actions, cache }, options, done) => { if (process.env.NODE_ENV !== `production`) { - const { program } = store.getState() const { createPage } = actions const source = path.join(__dirname, `./raw_dev-404-page.js`) - const destination = path.join( - program.directory, - `.cache`, - `dev-404-page.js` - ) + const destination = cache.rootPath(`dev-404-page.js`) const copy = () => fs.copy(source, destination) await copy() createPage({ diff --git a/packages/gatsby/src/internal-plugins/load-babel-config/gatsby-node.js b/packages/gatsby/src/internal-plugins/load-babel-config/gatsby-node.js index 5f0cf62307ef6..5042a271245b2 100644 --- a/packages/gatsby/src/internal-plugins/load-babel-config/gatsby-node.js +++ b/packages/gatsby/src/internal-plugins/load-babel-config/gatsby-node.js @@ -3,11 +3,9 @@ const fs = require(`fs-extra`) const apiRunnerNode = require(`../../utils/api-runner-node`) -const { withBasePath } = require(`../../utils/path`) -exports.onPreBootstrap = async ({ store }) => { - const { directory, browserslist } = store.getState().program - const directoryPath = withBasePath(directory) +exports.onPreBootstrap = async ({ cache, store }) => { + const { browserslist } = store.getState().program await apiRunnerNode(`onCreateBabelConfig`, { stage: `develop`, @@ -31,5 +29,5 @@ exports.onPreBootstrap = async ({ store }) => { 2 ) - await fs.writeFile(directoryPath(`.cache/babelState.json`), babelState) + await fs.writeFile(cache.rootPath(`babelState.json`), babelState) } diff --git a/packages/gatsby/src/internal-plugins/query-runner/__tests__/pages-writer.js b/packages/gatsby/src/internal-plugins/query-runner/__tests__/pages-writer.js index f3449e9ae10a5..4ab64bf7933ce 100644 --- a/packages/gatsby/src/internal-plugins/query-runner/__tests__/pages-writer.js +++ b/packages/gatsby/src/internal-plugins/query-runner/__tests__/pages-writer.js @@ -35,6 +35,12 @@ jest.mock(`../../../redux/`, () => { } }) +jest.mock(`../../../utils/cache`, () => { + return { + cachePath: () => `my/gatsby/project/.cache`, + } +}) + const expectedResult = JSON.stringify({ pages: [ { path: `/amet`, matchPath: null }, diff --git a/packages/gatsby/src/internal-plugins/query-runner/pages-writer.js b/packages/gatsby/src/internal-plugins/query-runner/pages-writer.js index 10e8c21e76bd9..63778673c602b 100644 --- a/packages/gatsby/src/internal-plugins/query-runner/pages-writer.js +++ b/packages/gatsby/src/internal-plugins/query-runner/pages-writer.js @@ -3,6 +3,7 @@ const fs = require(`fs-extra`) const crypto = require(`crypto`) const { store, emitter } = require(`../../redux/`) +const { cachePath } = require(`../../utils/cache`) import { joinPath } from "../../utils/path" @@ -58,6 +59,8 @@ const writePages = async () => { return Promise.resolve() } + const cacheDirectory = cachePath() + lastHash = newHash // Get list of components, and json files. @@ -102,13 +105,12 @@ const preferDefault = m => m && m.default || m }\n\n` asyncRequires += `exports.data = () => import(/* webpackChunkName: "pages-manifest" */ "${joinPath( - program.directory, - `.cache`, + cacheDirectory, `data.json` )}")\n\n` const writeAndMove = (file, data) => { - const destination = joinPath(program.directory, `.cache`, file) + const destination = joinPath(cacheDirectory, file) const tmp = `${destination}.${Date.now()}` return fs .writeFile(tmp, data) diff --git a/packages/gatsby/src/internal-plugins/query-runner/query-compiler.js b/packages/gatsby/src/internal-plugins/query-runner/query-compiler.js index 9965885e72762..d6d3705ed6a8f 100644 --- a/packages/gatsby/src/internal-plugins/query-runner/query-compiler.js +++ b/packages/gatsby/src/internal-plugins/query-runner/query-compiler.js @@ -21,6 +21,7 @@ import { } from "./graphql-errors" import report from "gatsby-cli/lib/reporter" const websocketManager = require(`../../utils/websocket-manager`) +const { cachePath } = require(`../../utils/cache`) import type { DocumentNode, GraphQLSchema } from "graphql" @@ -99,10 +100,7 @@ class Runner { async parseEverything() { const filesRegex = path.join(`/**`, `*.+(t|j)s?(x)`) - let files = [ - path.join(this.base, `src`), - path.join(this.base, `.cache`, `fragments`), - ] + let files = [path.join(this.base, `src`), cachePath(`fragments`, this.base)] .concat(this.additional.map(additional => path.join(additional, `src`))) .reduce( (merged, folderPath) => diff --git a/packages/gatsby/src/internal-plugins/query-runner/query-runner.js b/packages/gatsby/src/internal-plugins/query-runner/query-runner.js index 1e3197efc43f4..8779c463601e3 100644 --- a/packages/gatsby/src/internal-plugins/query-runner/query-runner.js +++ b/packages/gatsby/src/internal-plugins/query-runner/query-runner.js @@ -5,11 +5,11 @@ const fs = require(`fs-extra`) const report = require(`gatsby-cli/lib/reporter`) const websocketManager = require(`../../utils/websocket-manager`) -const path = require(`path`) const { store } = require(`../../redux`) const { generatePathChunkName } = require(`../../utils/js-chunk-names`) const { formatErrorDetails } = require(`./utils`) const mod = require(`hash-mod`)(999) +const { publicPath } = require(`../../utils/cache`) const resultHashes = {} @@ -25,7 +25,7 @@ type QueryJob = { // Run query module.exports = async (queryJob: QueryJob, component: Any) => { - const { schema, program } = store.getState() + const { schema } = store.getState() const graphql = (query, context) => graphqlFunction(schema, query, context, context, context) @@ -121,14 +121,7 @@ ${formatErrorDetails(errorDetails)}`) } // Always write file to public/static/d/ folder. - const resultPath = path.join( - program.directory, - `public`, - `static`, - `d`, - modInt, - `${dataPath}.json` - ) + const resultPath = publicPath(`static/d/${modInt}/${dataPath}.json`) if (queryJob.isPage) { dataPath = `${modInt}/${dataPath}` diff --git a/packages/gatsby/src/internal-plugins/query-runner/redirects-writer.js b/packages/gatsby/src/internal-plugins/query-runner/redirects-writer.js index da2452107af2d..c0bff3fb8e929 100644 --- a/packages/gatsby/src/internal-plugins/query-runner/redirects-writer.js +++ b/packages/gatsby/src/internal-plugins/query-runner/redirects-writer.js @@ -2,7 +2,7 @@ import _ from "lodash" import crypto from "crypto" import fs from "fs-extra" import { store, emitter } from "../../redux/" -import { joinPath } from "../../utils/path" +const { cachePath } = require(`../../utils/cache`) let lastHash = null @@ -26,7 +26,7 @@ const writeRedirects = async () => { lastHash = newHash return await fs.writeFile( - joinPath(program.directory, `.cache/redirects.json`), + cachePath(`redirects.json`, program.directory), JSON.stringify(browserRedirects, null, 2) ) } diff --git a/packages/gatsby/src/redux/__tests__/__snapshots__/babelrc.js.snap b/packages/gatsby/src/redux/__tests__/__snapshots__/babelrc.js.snap index b5e94554957f9..944aef326b05e 100644 --- a/packages/gatsby/src/redux/__tests__/__snapshots__/babelrc.js.snap +++ b/packages/gatsby/src/redux/__tests__/__snapshots__/babelrc.js.snap @@ -160,6 +160,9 @@ Array [ Array [ Array [ "/path/to/module/babel-plugin-remove-graphql-queries", + Object { + "publicPath": "/public", + }, ], Object { "type": "plugin", @@ -168,6 +171,9 @@ Array [ Array [ Array [ "/path/to/module/babel-preset-gatsby", + Object { + "cachePath": "/.cache", + }, ], Object { "type": "preset", diff --git a/packages/gatsby/src/redux/actions.js b/packages/gatsby/src/redux/actions.js index 313a47c9e8677..60402a93a02fb 100644 --- a/packages/gatsby/src/redux/actions.js +++ b/packages/gatsby/src/redux/actions.js @@ -17,6 +17,7 @@ const { store } = require(`./index`) const fileExistsSync = require(`fs-exists-cached`).sync const joiSchemas = require(`../joi-schemas/joi`) const { generateComponentChunkName } = require(`../utils/js-chunk-names`) +const { cachePath } = require(`../utils/cache`) const actions = {} @@ -288,7 +289,7 @@ ${reservedFields.map(f => ` * "${f}"`).join(`\n`)} // // Only run validation once during builds. if ( - !internalPage.component.includes(`/.cache/`) && + !internalPage.component.includes(cachePath()) && (process.env.NODE_ENV === `production` && !fileOkCache[internalPage.component]) ) { diff --git a/packages/gatsby/src/redux/index.js b/packages/gatsby/src/redux/index.js index 856c58c6ec5d0..6dc0666f2ceff 100644 --- a/packages/gatsby/src/redux/index.js +++ b/packages/gatsby/src/redux/index.js @@ -3,6 +3,7 @@ const _ = require(`lodash`) const fs = require(`fs-extra`) const mitt = require(`mitt`) const stringify = require(`json-stringify-safe`) +const { cachePath } = require(`../utils/cache`) // Create event emitter for actions const emitter = mitt() @@ -29,7 +30,7 @@ const mapToObject = map => { // Read from cache the old node data. let initialState = {} try { - const file = fs.readFileSync(`${process.cwd()}/.cache/redux-state.json`) + const file = fs.readFileSync(cachePath(`redux-state.json`)) // Apparently the file mocking in node-tracking-test.js // can override the file reading replacing the mocked string with // an already parsed object. @@ -89,7 +90,7 @@ function saveState() { pickedState.components = mapToObject(pickedState.components) pickedState.nodes = pickedState.nodes ? mapToObject(pickedState.nodes) : [] const stringified = stringify(pickedState, null, 2) - return fs.writeFile(`${process.cwd()}/.cache/redux-state.json`, stringified) + return fs.writeFile(cachePath(`redux-state.json`), stringified) } exports.saveState = saveState diff --git a/packages/gatsby/src/utils/babel-loader-helpers.js b/packages/gatsby/src/utils/babel-loader-helpers.js index 8d7b9ea837494..ae816e81a5ce8 100644 --- a/packages/gatsby/src/utils/babel-loader-helpers.js +++ b/packages/gatsby/src/utils/babel-loader-helpers.js @@ -1,5 +1,5 @@ -const path = require(`path`) const _ = require(`lodash`) +const { cachePath, publicPath } = require(`./cache`) const loadCachedConfig = () => { let pluginBabelConfig = { @@ -8,10 +8,7 @@ const loadCachedConfig = () => { }, } if (process.env.NODE_ENV !== `test`) { - pluginBabelConfig = require(path.join( - process.cwd(), - `./.cache/babelState.json` - )) + pluginBabelConfig = require(cachePath(`./babelState.json`)) } return pluginBabelConfig } @@ -29,9 +26,17 @@ const prepareOptions = (babel, resolve = require.resolve) => { // Required plugins/presets const requiredPlugins = [ - babel.createConfigItem([resolve(`babel-plugin-remove-graphql-queries`)], { - type: `plugin`, - }), + babel.createConfigItem( + [ + resolve(`babel-plugin-remove-graphql-queries`), + { + publicPath: publicPath(), + }, + ], + { + type: `plugin`, + } + ), ] const requiredPresets = [] @@ -56,9 +61,17 @@ const prepareOptions = (babel, resolve = require.resolve) => { const fallbackPresets = [] fallbackPresets.push( - babel.createConfigItem([resolve(`babel-preset-gatsby`)], { - type: `preset`, - }) + babel.createConfigItem( + [ + resolve(`babel-preset-gatsby`), + { + cachePath: cachePath(), + }, + ], + { + type: `preset`, + } + ) ) // Go through babel state and create config items for presets/plugins from. const reduxPlugins = [] diff --git a/packages/gatsby/src/utils/cache.js b/packages/gatsby/src/utils/cache.js index 7dbac24859a62..7693596cea3ec 100644 --- a/packages/gatsby/src/utils/cache.js +++ b/packages/gatsby/src/utils/cache.js @@ -6,6 +6,42 @@ const path = require(`path`) const MAX_CACHE_SIZE = 250 const TTL = Number.MAX_SAFE_INTEGER +function getCachePath(cwd) { + const { GATSBY_CACHE_DIR } = process.env + if (GATSBY_CACHE_DIR) { + if (path.isAbsolute(GATSBY_CACHE_DIR)) { + return GATSBY_CACHE_DIR + } + return path.join(cwd || process.cwd() || `.`, GATSBY_CACHE_DIR) + } + return path.join(cwd || process.cwd() || `.`, `./.cache`) +} + +function cachePath(filePath, cwd) { + if (!filePath) { + return getCachePath(cwd) + } + return path.join(getCachePath(cwd), filePath) +} + +function getPublicPath(cwd) { + const { GATSBY_BUILD_DIR } = process.env + if (GATSBY_BUILD_DIR) { + if (path.isAbsolute(GATSBY_BUILD_DIR)) { + return GATSBY_BUILD_DIR + } + return path.join(cwd || process.cwd(), GATSBY_BUILD_DIR) + } + return path.join(cwd || process.cwd(), `./public`) +} + +function publicPath(filePath, cwd) { + if (!filePath) { + return getPublicPath(cwd) + } + return path.join(getPublicPath(cwd), filePath) +} + class Cache { constructor({ name = `db`, store = fsStore } = {}) { this.name = name @@ -13,7 +49,19 @@ class Cache { } get directory() { - return path.join(process.cwd(), `.cache/caches/${this.name}`) + return cachePath(`caches/${this.name}`) + } + + get rootDirectory() { + return getCachePath() + } + + rootPath(filePath) { + return cachePath(filePath) + } + + publicPath(filePath) { + return publicPath(filePath) } init() { @@ -56,3 +104,5 @@ class Cache { } module.exports = Cache +module.exports.cachePath = cachePath +module.exports.publicPath = publicPath diff --git a/packages/gatsby/src/utils/gatsby-webpack-stats-extractor.js b/packages/gatsby/src/utils/gatsby-webpack-stats-extractor.js index 4c0186cf8bfdd..660b7c9238a7e 100644 --- a/packages/gatsby/src/utils/gatsby-webpack-stats-extractor.js +++ b/packages/gatsby/src/utils/gatsby-webpack-stats-extractor.js @@ -1,5 +1,5 @@ const fs = require(`fs-extra`) -const path = require(`path`) +const { publicPath } = require(`./cache`) class GatsbyWebpackStatsExtractor { constructor(options) { @@ -31,11 +31,11 @@ class GatsbyWebpackStatsExtractor { assetsByChunkName: assets, } fs.writeFile( - path.join(`public`, `chunk-map.json`), + publicPath(`chunk-map.json`), JSON.stringify(assetsMap), () => { fs.writeFile( - path.join(`public`, `webpack.stats.json`), + publicPath(`webpack.stats.json`), JSON.stringify(webpackStats), done ) diff --git a/packages/gatsby/src/utils/get-static-dir.js b/packages/gatsby/src/utils/get-static-dir.js index 3280d0497f40f..de3222f8209b4 100644 --- a/packages/gatsby/src/utils/get-static-dir.js +++ b/packages/gatsby/src/utils/get-static-dir.js @@ -1,6 +1,7 @@ const fs = require(`fs-extra`) const chokidar = require(`chokidar`) const nodePath = require(`path`) +const { publicPath } = require(`./cache`) /** * copyStaticDir @@ -10,7 +11,7 @@ const nodePath = require(`path`) exports.copyStaticDir = () => { const staticDir = nodePath.join(process.cwd(), `static`) if (!fs.existsSync(staticDir)) return Promise.resolve() - return fs.copySync(staticDir, nodePath.join(process.cwd(), `public`)) + return fs.copySync(staticDir, publicPath()) } /** @@ -24,10 +25,10 @@ exports.syncStaticDir = () => { .watch(staticDir) .on(`add`, path => { const relativePath = nodePath.relative(staticDir, path) - fs.copy(path, `${process.cwd()}/public/${relativePath}`) + fs.copy(path, publicPath(relativePath)) }) .on(`change`, path => { const relativePath = nodePath.relative(staticDir, path) - fs.copy(path, `${process.cwd()}/public/${relativePath}`) + fs.copy(path, publicPath(relativePath)) }) } diff --git a/packages/gatsby/src/utils/test-require-error.js b/packages/gatsby/src/utils/test-require-error.js index 7f44a2f87ad05..64e2f0e0b7b84 100644 --- a/packages/gatsby/src/utils/test-require-error.js +++ b/packages/gatsby/src/utils/test-require-error.js @@ -1,4 +1,4 @@ -// This module is also copied into the .cache directory some modules copied there +// This module is also copied into the cache directory some modules copied there // from cache-dir can also use this module. export default (moduleName, err) => { const regex = new RegExp( diff --git a/packages/gatsby/src/utils/webpack.config.js b/packages/gatsby/src/utils/webpack.config.js index c3cbcb05db7e0..3fdde201338e8 100644 --- a/packages/gatsby/src/utils/webpack.config.js +++ b/packages/gatsby/src/utils/webpack.config.js @@ -8,11 +8,11 @@ const { store } = require(`../redux`) const { actions } = require(`../redux/actions`) const debug = require(`debug`)(`gatsby:webpack-config`) const report = require(`gatsby-cli/lib/reporter`) -const { withBasePath } = require(`./path`) const apiRunnerNode = require(`./api-runner-node`) const createUtils = require(`./webpack-utils`) const hasLocalEslint = require(`./local-eslint-config-finder`) +const { cachePath, publicPath } = require(`./cache`) // Four stages or modes: // 1) develop: for `gatsby develop` command, hot reload and CSS injection into page @@ -26,8 +26,6 @@ module.exports = async ( suppliedStage, webpackPort = 1500 ) => { - const directoryPath = withBasePath(directory) - process.env.GATSBY_BUILD_STAGE = suppliedStage // We combine develop & develop-html stages for purposes of generating the @@ -64,7 +62,7 @@ module.exports = async ( // Don't allow overwriting of NODE_ENV, PUBLIC_DIR as to not break gatsby things envObject.NODE_ENV = JSON.stringify(env) - envObject.PUBLIC_DIR = JSON.stringify(`${process.cwd()}/public`) + envObject.PUBLIC_DIR = JSON.stringify(publicPath()) envObject.BUILD_STAGE = JSON.stringify(stage) envObject.CYPRESS_SUPPORT = JSON.stringify(process.env.CYPRESS_SUPPORT) @@ -120,7 +118,7 @@ module.exports = async ( // A temp file required by static-site-generator-plugin. See plugins() below. // Deleted by build-html.js, since it's not needed for production. return { - path: directoryPath(`public`), + path: publicPath(``, directory), filename: `render-page.js`, libraryTarget: `umd`, library: `lib`, @@ -134,7 +132,7 @@ module.exports = async ( return { filename: `[name]-[contenthash].js`, chunkFilename: `[name]-[contenthash].js`, - path: directoryPath(`public`), + path: publicPath(``, directory), publicPath: program.prefixPaths ? `${store.getState().config.pathPrefix}/` : `/`, @@ -153,20 +151,20 @@ module.exports = async ( `${require.resolve( `webpack-hot-middleware/client` )}?path=${getHmrPath()}`, - directoryPath(`.cache/app`), + cachePath(`app`, directory), ], } case `develop-html`: return { - main: directoryPath(`.cache/develop-static-entry`), + main: cachePath(`develop-static-entry`, directory), } case `build-html`: return { - main: directoryPath(`.cache/static-entry`), + main: cachePath(`static-entry`, directory), } case `build-javascript`: return { - app: directoryPath(`.cache/production-app`), + app: cachePath(`production-app`, directory), } default: throw new Error(`The state requested ${stage} doesn't exist.`) @@ -316,7 +314,7 @@ module.exports = async ( // 'resolvableExtensions' API hook). extensions: [...program.extensions], alias: { - gatsby$: directoryPath(path.join(`.cache`, `gatsby-browser-entry.js`)), + gatsby$: cachePath(`gatsby-browser-entry.js`, directory), // Using directories for module resolution is mandatory because // relative path imports are used sometimes // See https://stackoverflow.com/a/49455609/6420957 for more details @@ -327,10 +325,13 @@ module.exports = async ( "react-hot-loader": path.dirname( require.resolve(`react-hot-loader/package.json`) ), - "react-lifecycles-compat": directoryPath( - `.cache/react-lifecycles-compat.js` + "react-lifecycles-compat": cachePath( + `react-lifecycles-compat.js`, + directory ), - "create-react-context": directoryPath(`.cache/create-react-context.js`), + "create-react-context": cachePath(`create-react-context.js`, directory), + "gatsby-cache-dir": cachePath(``, directory), + "gatsby-public-dir": publicPath(``, directory), }, } } diff --git a/packages/gatsby/src/utils/websocket-manager.js b/packages/gatsby/src/utils/websocket-manager.js index 93812e795d6fb..4ab8934be1a9e 100644 --- a/packages/gatsby/src/utils/websocket-manager.js +++ b/packages/gatsby/src/utils/websocket-manager.js @@ -1,8 +1,8 @@ // @flow -const path = require(`path`) const { store } = require(`../redux`) const fs = require(`fs`) +const { publicPath } = require(`./cache`) type QueryResult = { id: string, @@ -17,13 +17,7 @@ type QueryResultsMap = Map * @param {string} directory Root directory of current project. */ const readCachedResults = (dataFileName: string, directory: string): object => { - const filePath = path.join( - directory, - `public`, - `static`, - `d`, - `${dataFileName}.json` - ) + const filePath = publicPath(`static/d/${dataFileName}.json`, directory) return JSON.parse(fs.readFileSync(filePath, `utf-8`)) } diff --git a/packages/gatsby/src/utils/worker.js b/packages/gatsby/src/utils/worker.js index ac74894853aa3..780692ddbcdaf 100644 --- a/packages/gatsby/src/utils/worker.js +++ b/packages/gatsby/src/utils/worker.js @@ -1,6 +1,7 @@ const fs = require(`fs-extra`) const path = require(`path`) const Promise = require(`bluebird`) +const { publicPath } = require(`./cache`) // copied from https://github.com/markdalgleish/static-site-generator-webpack-plugin/blob/master/index.js#L161 const generatePathToOutput = outputPath => { @@ -10,7 +11,7 @@ const generatePathToOutput = outputPath => { outputFileName = path.join(outputFileName, `index.html`) } - return path.join(process.cwd(), `public`, outputFileName) + return publicPath(outputFileName) } export function renderHTML({ htmlComponentRendererPath, paths, envVars }) {