Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat(gatsby): add assetPrefix to support deploying assets separate from html #12128

Merged
merged 70 commits into from
May 2, 2019
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
860f920
chore: add validation schema, start tweaking webpack config
DSchau Feb 25, 2019
f070da7
chore: keep iterating
DSchau Feb 25, 2019
c32d7f1
chore: yadda
DSchau Feb 25, 2019
4351700
chore: keep working
DSchau Feb 25, 2019
5b8e714
keep doing stuff
DSchau Feb 25, 2019
5e7516f
chore: get mostly done (let's see!)
DSchau Feb 26, 2019
50c4841
chore: remove unused package
DSchau Feb 26, 2019
e6ad72f
chore: ensure url is normalized correctly
DSchau Feb 26, 2019
23749b6
chore: try try again
DSchau Feb 26, 2019
7f9e076
chore: fix for base path
DSchau Feb 26, 2019
6d61a3d
test: tests are important; fix them
DSchau Feb 27, 2019
9a33fa6
chore: remove a silly change
DSchau Feb 27, 2019
e8b6d71
chore: fix linter
DSchau Feb 27, 2019
6687692
fix(gatsby-plugin-offline): hard fail if assetPrefix is used
DSchau Feb 27, 2019
ad04a43
refactor: add a publicPath helper
DSchau Feb 27, 2019
de7e73b
test: add some get public path tests
DSchau Feb 27, 2019
a665cc4
chore: use correct name
DSchau Feb 27, 2019
9acb7cd
docs: add asset prefix doc, and tweak path prefix
DSchau Feb 27, 2019
26c2f33
chore: allow relative url for assetPrefix
DSchau Feb 27, 2019
7d27649
test: add a few more unit tests
DSchau Feb 27, 2019
723bb82
Merge branch 'master' into gatsby/asset-prefix-improved
DSchau Feb 27, 2019
f58d9a3
test: clean up test
DSchau Feb 27, 2019
6af9bad
Merge branch 'gatsby/asset-prefix-improved' of github.com:DSchau/gats…
DSchau Feb 27, 2019
52d3e21
chore: fix e2e-test
DSchau Feb 27, 2019
b144bd2
fix: fall back to empty string, not slash
DSchau Feb 27, 2019
fe0f94a
Update docs/docs/asset-prefix.md
DSchau Feb 28, 2019
20eae0b
Merge remote-tracking branch 'upstream/master' into gatsby/asset-pref…
DSchau Mar 8, 2019
4ac9648
fix: handle relative paths
DSchau Mar 8, 2019
8f25776
Merge branch 'gatsby/asset-prefix-improved' of github.com:DSchau/gats…
DSchau Mar 8, 2019
7e3b1e2
feat: add withAssetPrefix helper for gatsby-link
DSchau Mar 8, 2019
da45256
fix: use withAssetPrefix (if available) for gatsby-plugin-manifest
DSchau Mar 8, 2019
c7c6859
Allow using gatsby-plugin-offline with assetPrefix
Mar 18, 2019
87c9815
Add docs for using offline-plugin with asset prefix
Mar 18, 2019
6110801
clarify docs
Mar 18, 2019
5ec39f7
Merge remote-tracking branch 'upstream/master' into gatsby/asset-pref…
DSchau Mar 26, 2019
82be716
Merge branch 'gatsby/asset-prefix-improved' of github.com:DSchau/gats…
DSchau Mar 26, 2019
7734c5e
feat(*): use withAssetPrefix helper from gatsby-link
DSchau Mar 26, 2019
9234e33
Merge remote-tracking branch 'upstream/master' into gatsby/asset-pref…
DSchau Mar 28, 2019
b8a1c2c
test: get tests passing
DSchau Mar 28, 2019
39403b8
test: add a test for assetPrefix with nesting
DSchau Mar 28, 2019
933f148
Update docs/docs/path-prefix.md
muescha Apr 30, 2019
bbebb41
Merge remote-tracking branch 'upstream/master' into gatsby/asset-pref…
DSchau Apr 30, 2019
dff0e72
chore: fix up merge conflicts/get tests passing
DSchau Apr 30, 2019
f8cfef0
Merge branch 'gatsby/asset-prefix-improved' of github.com:DSchau/gats…
DSchau Apr 30, 2019
560eb79
chore: tweak version
DSchau Apr 30, 2019
37293e3
fix(gatsby-plugin-sitemap): work with asset prefix
DSchau Apr 30, 2019
45ec7c8
fix(gatsby): disallow both relative assetPrefix and pathPrefix
DSchau Apr 30, 2019
b0fef6e
chore: fallback to withPathPrefix, bump peerDep
DSchau Apr 30, 2019
0eb6c85
chore: remove caveat re: trailing slash
DSchau Apr 30, 2019
f0d9a91
fix: gatsby-plugin-sitemap regression
DSchau Apr 30, 2019
0603c31
chore: revert peer dep
DSchau Apr 30, 2019
b22da6e
chore: use basePath if it's defined
DSchau Apr 30, 2019
272551a
chore: remove eslint global comment
DSchau Apr 30, 2019
c3a1230
chore: ensure prefixPaths is set to enable pathPrefix
DSchau Apr 30, 2019
b88c2f8
chore: fix read-only error (can't reassign imports ya dingus)
DSchau Apr 30, 2019
5521302
chore: actually fallback
DSchau Apr 30, 2019
08ede03
Update docs/docs/asset-prefix.md
m-allanson May 1, 2019
d28d6f1
Update docs/docs/path-prefix.md
pieh May 1, 2019
d277729
Update docs/docs/asset-prefix.md
m-allanson May 1, 2019
4d073da
chore: simply/merely remove the easy term ;)
DSchau May 1, 2019
b1241c2
Update docs/docs/asset-prefix.md
m-allanson May 1, 2019
3b8c23a
Merge remote-tracking branch 'upstream/master' into gatsby/asset-pref…
DSchau May 1, 2019
a84aeb9
test: write e2e test for asset prefix
DSchau May 1, 2019
78374b2
chore: fix package json and make isURL test stricter
DSchau May 1, 2019
d226f5e
chore: fix yarn and stuff hopefully
DSchau May 1, 2019
983f010
chore: minor clean up
DSchau May 2, 2019
60c2408
fix(gatsby): fix initial navigation not registering in history
DSchau May 2, 2019
ae9b34c
chore: remove unneccessary dep
DSchau May 2, 2019
4075e95
fix: use __BASE_PATH__ in development runtime too; add a test
DSchau May 2, 2019
2a761db
chore: fix @pieh nit before he finds it
DSchau May 2, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
"globals": {
"before": true,
"spyOn": true,
"__PATH_PREFIX__": true
"__PATH_PREFIX__": true,
"__BASE_PATH__": true,
"__ASSET_PREFIX__": true
},
"rules": {
"arrow-body-style": [
Expand Down
79 changes: 79 additions & 0 deletions docs/docs/asset-prefix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
title: Adding an Asset Prefix
---

Gatsby produces static content that can be hosted _anywhere_ at scale in a cost-effective manner. This static content is comprised of HTML files, JavaScript, CSS, images, and more that power your great, Gatsby application.

There are scenarios in which it may be advantageous or necessary to deploy _assets_ (where an asset is a non-HTML resource, e.g. JavaScript, CSS, etc.) to a separate domain. This can oftentimes be used to enable functionality like a CDN or a particular hosting strategy that your company may employ where assets need to be separate from the core application.

This `assetPrefix` functionality is available starting in gatsby@2.2.0, so that you can seamlessly use Gatsby with assets hosted from a separate domain. To use this functionality, ensure that your version of `gatsby` specified in `package.json` is at least `2.2.0`.

## Usage

### Adding to `gatsby-config.js`

```js:title=gatsby-config.js
module.exports = {
assetPrefix: `https://cdn.example.com`,
}
```

That was easy! One more step - when we build out this application, we need to add a flag so that Gatsby picks up this option.

### The `--prefix-paths` flag

When building with an `assetPrefix`, we require a `--prefix-paths` flag. If this flag is not specified, the build will ignore this option, and build out content as if it was hosted on the same domain. To ensure we build out successfully, use the following command:

```shell
gatsby build --prefix-paths
```

That's it! We now have an application that is ready to have its assets deployed from a CDN and its core files (e.g. HTML files) can be hosted on a separate domain.

## Building / Deploying

Once your application is built out, all assets will be automatically prefixed by this asset prefix. For example, if we have a JavaScript file `app-common-1234.js`, the script tag will look something like:

```html
<script src="https://cdn.example.com/app-common-1234.js"></script>
```

However - if we were to deploy our application as-is, those assets would not be available! We can do this in a few ways, but the general approach will be to deploy the contents of the `public` folder to _both_ your core domain, and the CDN/asset prefix location.

### Using `onPostBuild`

We expose an [`onPostBuild`](/docs/node-apis/#onPostBuild) API hook. This can be used to deploy your content to the CDN, like so:

```js:title=gatsby-node.js
const assetsDirectory = `public`

exports.onPostBuild = async function onPostBuild() {
// do something with public
// e.g. upload to S3
}
```

### Using `package.json` scripts

Additionally, we can use an npm script, which will let us use some command line interfaces/executables to perform some action, in this case, deploying our assets directory!

In this example, I'll use the `aws-cli` and `s3` to sync the `public` folder (containing all our assets) to the `s3` bucket.

```json:title=package.json
{
"scripts": {
"build": "gatsby build --prefix-paths",
"postbuild": "aws s3 sync public s3://mybucket"
}
}
```

Now whenever the `build` script is invoked, e.g. `npm run build`, the `postbuild` script will be invoked _after_ the build completes, therefore making our assets available on a _separate_ domain after we have finished building out our appliacation with prefixed assets.

## Additional Considerations

### Usage with `pathPrefix`

The [`pathPrefix`](/docs/path-prefix/) feature can be thought of as semi-related to this feature. That feature allows _all_ your website content to be prefixed with some constant prefix, for example you may want your blog to be hosted from `/blog` rather than the project root.

This feature works seamlessly with `pathPrefix`. Build out your application with the `--prefix-paths` flag and you'll be well on your way to hosting an application with its assets hosted on a CDN, and its core functionality available behind a path prefix.
86 changes: 61 additions & 25 deletions docs/docs/path-prefix.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,82 @@
title: Adding a Path Prefix
---

Many sites are hosted at something other than the root of their domain.
Many applications are hosted at something other than the root (`/`) of their domain.

E.g. a Gatsby blog could live at `example.com/blog/` or a site could be hosted
on GitHub Pages at `example.github.io/my-gatsby-site/`
For example, a Gatsby blog could live at `example.com/blog/` or a site could be hosted on GitHub Pages at `example.github.io/my-gatsby-site/`

Each of these sites need a prefix added to all paths on the site. So a link to
`/my-sweet-blog-post/` should be rewritten to `/blog/my-sweet-blog-post`.

In addition links to various resources (JavaScript, images, CSS) need the same
prefix added (this is accomplished by setting the `publicPath` in webpack).
In addition links to various resources (JavaScript, CSS, images, and other static content) need the same prefix, so that the site continues to function and display correctly, even if served from this path prefix.

Luckily, for most sites, this work can be offloaded to Gatsby. Using
[Gatsby's Link component](/docs/gatsby-link/) for internal links ensures those links
will be prefixed correctly. Gatsby ensures that paths created internally and by
webpack are also correctly prefixed.
Let's get this functionality implemented. We'll add an option to our `gatsby-config.js`, and add a flag to our build command.

## Development
### Add to `gatsby-config.js`

During development, write paths as if there was no path prefix e.g. for a blog
hosted at `example.com/blog`, don't add `/blog` to your links. The prefix will
be added when you build for deployment.

## Production build

There are two steps for building a site with path prefixes.

First define the prefix in your site's `gatsby-config.js`.

```javascript:title=gatsby-config.js
```js:title=gatsby-config.js
module.exports = {
// Note: it must *not* have a trailing slash.
pathPrefix: `/blog`,
}
```

Then pass `--prefix-paths` cmd option to Gatsby.
### Build

Once the `pathPrefix` is specified in `gastby-config.js`, we are well on our way to a prefixed app. The final step is to build out your application with a flag `--prefix-paths`, like so:

```shell
gatsby build --prefix-paths
```

NOTE: When running the command without the `--prefix-paths` flag, Gatsby ignores
your `pathPrefix`.
If this flag is not passed, Gatsby will ignore your `pathPrefix` and build out your site as if it were hosted from the root domain.

### In-app linking
Copy link
Contributor

Choose a reason for hiding this comment

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

👍 for this example.


As a developer using this feature, it should be seamless. We provide APIs and libraries to make using this functionality a breeze. Specifically, the [`Link`](/docs/gatsby-link/) component has built-in functionality to handle path prefixing.

For example, if we want to link to our `/page-2` link (but the actual link will be prefixed, e.g. `/blog/page-2`) we don't want to hard code this path prefix in all of our links. We have your back! Merely by using the `Link` component, we will automatically prefix your paths for you. If you later migrate off of `pathPrefix` your links will _still_ work seamlessly.

Let's look at a quick example.

```jsx:title=src/pages/index.js
import React from "react"
import { Link } from "gatsby"
import Layout from "../components/layout"

function Index() {
return (
<Layout>
{/* highlight-next-line */}
<Link to="page-2">Page 2</Link>
</Layout>
)
}
```

Without doing _anything_ and merely using the `Link` component, this link will be prefixed with our specified `pathPrefix` in `gatsby-config.js`. Woo hoo!

If we want to do programattic/dynamic navigation, totally possible too! We expose a `navigate` helper, and this too automatically handles path prefixing.

```jsx:title=src/pages/index.js
import React from "react"
import { navigate } from "gatsby"
import Layout from "../components/layout"

export default function Index() {
return (
<Layout>
{/* Note: this is an intentionally contrived example, but you get the idea! */}
{/* highlight-next-line */}
<button onClick={() => navigate("/page-2")}>
Go to page 2, dynamically
</button>
</Layout>
)
}
```

### Additional Considerations

The [`assetPrefix`](/docs/asset-prefix/) feature can be thought of as semi-related to this feature. That feature allows your assets (non-HTML files, e.g. images, JavaScript, etc.) to be hosted on a separate domain, for example a CDN.

This feature works seamlessly with `assetPrefix`. Build out your application with the `--prefix-paths` flag and you'll be well on your way to hosting an application with its assets hosted on a CDN, and its core functionality available behind a path prefix.
1 change: 0 additions & 1 deletion e2e-tests/gatsby-image/gatsby-config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
const path = require(`path`)

module.exports = {
pathPrefix: `/blog`,
siteMetadata: {
title: `Gatsby Image e2e`,
},
Expand Down
10 changes: 5 additions & 5 deletions packages/gatsby-link/src/__tests__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
import Link, { navigate, push, replace, withPrefix } from "../"

afterEach(() => {
global.__PATH_PREFIX__ = ``
global.__BASE_PATH__ = ``
Copy link
Contributor Author

Choose a reason for hiding this comment

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

BASE_PATH is the new PATH_PREFIX. PATH_PREFIX is now overloaded with assetPrefix + pathPrefix.

cleanup()
})

Expand All @@ -34,12 +34,12 @@ const getReplace = () => {
}

const getWithPrefix = (pathPrefix = ``) => {
global.__PATH_PREFIX__ = pathPrefix
global.__BASE_PATH__ = pathPrefix
return withPrefix
}

const setup = ({ sourcePath = `/active`, linkProps, pathPrefix = `` } = {}) => {
global.__PATH_PREFIX__ = pathPrefix
global.__BASE_PATH__ = pathPrefix
const source = createMemorySource(sourcePath)
const history = createHistory(source)

Expand Down Expand Up @@ -151,11 +151,11 @@ describe(`navigate`, () => {

it(`respects pathPrefix`, () => {
const to = `/some-path`
global.__PATH_PREFIX__ = `/blog`
global.__BASE_PATH__ = `/blog`
getNavigate()(to)

expect(global.___navigate).toHaveBeenCalledWith(
`${global.__PATH_PREFIX__}${to}`,
`${global.__BASE_PATH__}${to}`,
undefined
)
})
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby-link/src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*global __PATH_PREFIX__ */
/*global __BASE_PATH__ */
import PropTypes from "prop-types"
import React from "react"
import { Link } from "@reach/router"
Expand All @@ -8,7 +8,7 @@ import { parsePath } from "./parse-path"
export { parsePath }

export function withPrefix(path) {
return normalizePath(`${__PATH_PREFIX__}/${path}`)
return normalizePath(`${__BASE_PATH__}/${path}`)
}

function normalizePath(path) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { navigate } from "gatsby"
import * as catchLinks from "../catch-links"

beforeAll(() => {
global.__PATH_PREFIX__ = ``
global.__BASE_PATH__ = ``
// Set the base URL we will be testing against to http://localhost:8000/blog
window.history.pushState({}, `APP Url`, `${pathPrefix}`)
})
Expand Down Expand Up @@ -365,13 +365,13 @@ describe(`pathPrefix is handled if catched link to ${pathPrefix}/article navigat
})

afterAll(() => {
global.__PATH_PREFIX__ = ``
global.__BASE_PATH__ = ``
eventDestroyer()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

🔥

})

test(`on sites with pathPrefix '${pathPrefix}'`, done => {
// simulate case with --prefix-paths and prefix /blog
global.__PATH_PREFIX__ = pathPrefix
global.__BASE_PATH__ = pathPrefix

// create the element with href /blog/article
const clickElement = document.createElement(`a`)
Expand Down Expand Up @@ -408,7 +408,7 @@ describe(`pathPrefix is handled if catched link to ${pathPrefix}/article navigat

test(`on sites without pathPrefix`, done => {
// simulate default case without --prefix-paths
global.__PATH_PREFIX__ = ``
global.__BASE_PATH__ = ``

// create the element with href /blog/article
const clickElement = document.createElement(`a`)
Expand Down
11 changes: 5 additions & 6 deletions packages/gatsby-plugin-feed/src/__tests__/gatsby-ssr.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
const { onRenderBody } = require(`../gatsby-ssr`)

const defaultPathPrefix = global.__PATH_PREFIX__

describe(`Adds <Link> for feed to head`, () => {
const prefix = global.__BASE_PATH__
beforeEach(() => {
global.__PATH_PREFIX__ = ``
global.__BASE_PATH__ = ``
})

afterEach(() => {
global.__PATH_PREFIX__ = defaultPathPrefix
afterAll(() => {
global.__BASE_PATH__ = prefix
})

it(`creates Link if feeds does exist`, async () => {
Expand Down Expand Up @@ -55,7 +54,7 @@ describe(`Adds <Link> for feed to head`, () => {
expect(setHeadComponents).toHaveBeenCalledTimes(1)
})
it(`creates Link href with path prefix when __PATH_PREFIX__ sets`, async () => {
global.__PATH_PREFIX__ = `/hogwarts`
global.__BASE_PATH__ = `/hogwarts`

const pluginOptions = {
feeds: [
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby-plugin-manifest/src/__tests__/gatsby-ssr.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const ssrArgs = {

describe(`gatsby-plugin-manifest`, () => {
beforeEach(() => {
global.__PATH_PREFIX__ = ``
global.__BASE_PATH__ = ``
headComponents = []
})

Expand Down Expand Up @@ -130,7 +130,7 @@ describe(`gatsby-plugin-manifest`, () => {
})

it(`Creates href attributes using pathPrefix`, () => {
global.__PATH_PREFIX__ = `/path-prefix`
global.__BASE_PATH__ = `/path-prefix`

onRenderBody(ssrArgs, {
icon: true,
Expand Down
6 changes: 6 additions & 0 deletions packages/gatsby-plugin-offline/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ outdated version registered in users' browsers.

## Notes

### Empty View Source and SEO

Gatsby offers great SEO capabilities and that is no different with `gatsby-plugin-offline`. However, you shouldn't think that Gatsby doesn't serve HTML tags anymore when looking at your source code in the browser (with `Right click` => `View source`). `View source` doesn't represent the actual HTML data since `gatsby-plugin-offline` registers and loads a service worker that will cache and handle this differently. Your site is loaded from the service worker, not from its actual source (check your `Network` tab in the DevTools for that).

To see the HTML data that crawlers will receive, run this in your terminal:
Expand All @@ -98,3 +100,7 @@ curl https://www.yourdomain.tld
```

Alternatively you can have a look at the `/public/index.html` file in your project folder.

### `assetPrefix`

The [asset-prefix](https://gatsbyjs.app/docs/asset-prefix) feature _cannot_ be used in conjunction with this plugin. Service workers must exist on the same domain, and so therefore they cannot be hosted via a CDN or any other asset prefix.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`it throws error if assetPrefix is in config 1`] = `
"gatsby-plugin-offline cannot be used with the \`assetPrefix\` option.
A service worker specifies that it must be served on the same domain.
Either remove \`assetPrefix\` from gatsby-config.js or remove this plugin."
`;
Loading