Skip to content

Commit

Permalink
feat(gatsby-ssr): add excludePaths option
Browse files Browse the repository at this point in the history
some paths should be excluded from what gets JS stripped from it, so this will give the app the
option to do exactly that
  • Loading branch information
wmontgomery committed Jul 19, 2019
1 parent 625ac1c commit 2719468
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 10 deletions.
37 changes: 29 additions & 8 deletions src/gatsby-ssr.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { onPreRenderHTML, onRenderBody } from './gatsby-ssr'
import { onPreRenderHTML, onRenderBody, checkPathExclusion } from './gatsby-ssr'
import Spy = jasmine.Spy
import { headComponentsData, postBodyComponentsData, scriptsData } from './fake-data.spec'

Expand All @@ -15,8 +15,10 @@ describe('gatsby-ssr.js', () => {
})

describe('onPreRenderHTML', () => {
const pathname = '/my-cool-page'
let replaceHeadComponentsSpy: Spy
let replacePostBodyComponentsSpy: Spy

beforeEach(() => {
replaceHeadComponentsSpy = jasmine.createSpy<any>('replaceHeadComponents')
replacePostBodyComponentsSpy = jasmine.createSpy<any>('replacePostBodyComponents')
Expand All @@ -30,10 +32,16 @@ describe('gatsby-ssr.js', () => {
return []
}

onPreRenderHTML({ getHeadComponents, replaceHeadComponents: replaceHeadComponentsSpy, getPostBodyComponents, replacePostBodyComponents: replacePostBodyComponentsSpy }, {})
onPreRenderHTML({ getHeadComponents, pathname, replaceHeadComponents: replaceHeadComponentsSpy, getPostBodyComponents, replacePostBodyComponents: replacePostBodyComponentsSpy }, {})
expect(replaceHeadComponentsSpy).toHaveBeenCalledWith([headComponentsData[2], headComponentsData[3]])
})

it('does the pathname match any of the exclusions', () => {
expect(checkPathExclusion('/client/nerds', { excludePaths: ['/client/', '/tacos'] })).toBeTruthy()
expect(checkPathExclusion('/about/blerns', { excludePaths: ['/client/', '/tacos'] })).toBeFalsy()
expect(checkPathExclusion('/blerkstorm', {})).toBeFalsy()
})

it('does not remove scripts in the head marked as excluded from plugin options', () => {
onRenderBody({ scripts: scriptsData })
function getHeadComponents () {
Expand All @@ -44,6 +52,7 @@ describe('gatsby-ssr.js', () => {
}

onPreRenderHTML({
pathname: '/my-cool-page',
getHeadComponents,
replaceHeadComponents: replaceHeadComponentsSpy,
getPostBodyComponents,
Expand All @@ -53,6 +62,8 @@ describe('gatsby-ssr.js', () => {
})

it('should remove static files like JSON from the head as these files are always added by Gatsby', () => {
const pathname = '/my-cool-page'

onRenderBody({ scripts: [] })
function getHeadComponents () {
return [headComponentsData[0], headComponentsData[1], headComponentsData[14]]
Expand All @@ -61,12 +72,14 @@ describe('gatsby-ssr.js', () => {
return []
}

onPreRenderHTML({ getHeadComponents, replaceHeadComponents: replaceHeadComponentsSpy, getPostBodyComponents, replacePostBodyComponents: replacePostBodyComponentsSpy }, {})
onPreRenderHTML({ getHeadComponents, pathname, replaceHeadComponents: replaceHeadComponentsSpy, getPostBodyComponents, replacePostBodyComponents: replacePostBodyComponentsSpy }, {})
expect(replaceHeadComponentsSpy).toHaveBeenCalledWith([headComponentsData[0], headComponentsData[1]])
})

it('should remove preload scripts from the head that are called out by Gatsby during onRenderBody', () => {
const pathname = '/my-cool-page'
onRenderBody({ scripts: scriptsData })

function getHeadComponents () {
return [
headComponentsData[0], headComponentsData[1], headComponentsData[8], headComponentsData[9], headComponentsData[10], headComponentsData[11],
Expand All @@ -78,13 +91,14 @@ describe('gatsby-ssr.js', () => {
return []
}

onPreRenderHTML({ getHeadComponents, replaceHeadComponents: replaceHeadComponentsSpy, getPostBodyComponents, replacePostBodyComponents: replacePostBodyComponentsSpy }, {})
onPreRenderHTML({ getHeadComponents, pathname, replaceHeadComponents: replaceHeadComponentsSpy, getPostBodyComponents, replacePostBodyComponents: replacePostBodyComponentsSpy }, {})
expect(replaceHeadComponentsSpy).toHaveBeenCalledTimes(1)
expect(replaceHeadComponentsSpy.calls.argsFor(0)[0].length).toEqual(2)
expect(replaceHeadComponentsSpy).toHaveBeenCalledWith([headComponentsData[0], headComponentsData[1]])
})

it('does not remove non react components from the body, (checks for props)', function () {
const pathname = '/my-cool-page'
const fakeBodyComponents = [
{
type: 'link',
Expand All @@ -104,7 +118,7 @@ describe('gatsby-ssr.js', () => {
return fakeBodyComponents
}

onPreRenderHTML({ getHeadComponents, replaceHeadComponents: replaceHeadComponentsSpy, getPostBodyComponents, replacePostBodyComponents: replacePostBodyComponentsSpy }, {})
onPreRenderHTML({ getHeadComponents, pathname, replaceHeadComponents: replaceHeadComponentsSpy, getPostBodyComponents, replacePostBodyComponents: replacePostBodyComponentsSpy }, {})
expect(replacePostBodyComponentsSpy).toHaveBeenCalledWith(fakeBodyComponents)
})

Expand All @@ -118,6 +132,7 @@ describe('gatsby-ssr.js', () => {
}

onPreRenderHTML({
pathname: '/my-cool-page',
getHeadComponents,
replaceHeadComponents: replaceHeadComponentsSpy,
getPostBodyComponents,
Expand All @@ -127,6 +142,8 @@ describe('gatsby-ssr.js', () => {
})

it('should remove special Gatsby scripts from the body', () => {
const pathname = '/my-cool-page'

onRenderBody({ scripts: [] })
function getHeadComponents () {
return []
Expand All @@ -135,11 +152,13 @@ describe('gatsby-ssr.js', () => {
return [postBodyComponentsData[0], postBodyComponentsData[1], postBodyComponentsData[2]]
}

onPreRenderHTML({ getHeadComponents, replaceHeadComponents: replaceHeadComponentsSpy, getPostBodyComponents, replacePostBodyComponents: replacePostBodyComponentsSpy }, {})
onPreRenderHTML({ getHeadComponents, pathname, replaceHeadComponents: replaceHeadComponentsSpy, getPostBodyComponents, replacePostBodyComponents: replacePostBodyComponentsSpy }, {})
expect(replacePostBodyComponentsSpy).toHaveBeenCalledWith([postBodyComponentsData[0]])
})

it('should remove preload scripts from the body that are called out by Gatsby during onRenderBody', () => {
const pathname = '/my-cool-page'

onRenderBody({ scripts: scriptsData })
function getHeadComponents () {
return []
Expand All @@ -148,14 +167,16 @@ describe('gatsby-ssr.js', () => {
return [postBodyComponentsData[0], postBodyComponentsData[3], postBodyComponentsData[4], postBodyComponentsData[5], postBodyComponentsData[6], postBodyComponentsData[7]]
}

onPreRenderHTML({ getHeadComponents, replaceHeadComponents: replaceHeadComponentsSpy, getPostBodyComponents, replacePostBodyComponents: replacePostBodyComponentsSpy }, {})
onPreRenderHTML({ getHeadComponents, pathname, replaceHeadComponents: replaceHeadComponentsSpy, getPostBodyComponents, replacePostBodyComponents: replacePostBodyComponentsSpy }, {})
expect(replacePostBodyComponentsSpy).toHaveBeenCalledTimes(1)
expect(replacePostBodyComponentsSpy.calls.argsFor(0)[0].length).toEqual(1)
expect(replacePostBodyComponentsSpy).toHaveBeenCalledWith([postBodyComponentsData[0]])
})

it('should not remove anything during a non production build', () => {
const pathname = '/my-cool-page'
const oldEnv = process.env.NODE_ENV

process.env.NODE_ENV = 'development'
onRenderBody({})
function getHeadComponents () {
Expand All @@ -165,7 +186,7 @@ describe('gatsby-ssr.js', () => {
return postBodyComponentsData
}

onPreRenderHTML({ getHeadComponents, replaceHeadComponents: replaceHeadComponentsSpy, getPostBodyComponents, replacePostBodyComponents: replacePostBodyComponentsSpy }, {})
onPreRenderHTML({ getHeadComponents, pathname, replaceHeadComponents: replaceHeadComponentsSpy, getPostBodyComponents, replacePostBodyComponents: replacePostBodyComponentsSpy }, {})

expect(replaceHeadComponentsSpy).toHaveBeenCalledTimes(0)
expect(replacePostBodyComponentsSpy).toHaveBeenCalledTimes(0)
Expand Down
11 changes: 9 additions & 2 deletions src/gatsby-ssr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface OnPreRenderHTMLArgs {
replaceHeadComponents (reactNodes: ReactNode[]): void
getPostBodyComponents (): ReactNode[]
replacePostBodyComponents (ReactNode: ReactNode[]): void
pathname: string
}

export interface Script {
Expand All @@ -18,6 +19,7 @@ export interface Script {

export interface PluginOptions {
exclude?: RegExp | string
excludePaths?: any[]
}

let pageScripts: Script[]
Expand All @@ -39,14 +41,19 @@ export function onRenderBody ({ scripts }: OnRenderBodyArgs) {
}

// Here we rely on the fact that onPreRenderHTML is called after onRenderBody so we have access to the scripts Gatsby inserted into the HTML.
export function onPreRenderHTML ({ getHeadComponents, replaceHeadComponents, getPostBodyComponents, replacePostBodyComponents }: OnPreRenderHTMLArgs, pluginOptions: PluginOptions) {
if (process.env.NODE_ENV !== 'production') { // During a gatsby development build (gatsby develop) we do nothing.
export function onPreRenderHTML ({ getHeadComponents, pathname, replaceHeadComponents, getPostBodyComponents, replacePostBodyComponents }: OnPreRenderHTMLArgs, pluginOptions: PluginOptions) {
if (process.env.NODE_ENV !== 'production' || checkPathExclusion(pathname, pluginOptions)) { // During a gatsby development build (gatsby develop) we do nothing.
return
}
replaceHeadComponents(getHeadComponentsNoJS(getHeadComponents(), pluginOptions))
replacePostBodyComponents(getPostBodyComponentsNoJS(getPostBodyComponents(), pluginOptions))
}

export function checkPathExclusion (pathname: string, pluginOptions: PluginOptions): boolean {
const exclusion = pluginOptions.excludePaths || []
return exclusion.some(p => pathname.includes(p))
}

function getHeadComponentsNoJS (headComponents: ReactNode[], pluginOptions: PluginOptions): ReactNode[] {
return headComponents.filter((headComponent) => {
// Not a react component and therefore not a <script>.
Expand Down

0 comments on commit 2719468

Please sign in to comment.