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 Original file line 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 Spy = jasmine.Spy
import { headComponentsData, postBodyComponentsData, scriptsData } from './fake-data.spec' import { headComponentsData, postBodyComponentsData, scriptsData } from './fake-data.spec'


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


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

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


onPreRenderHTML({ getHeadComponents, replaceHeadComponents: replaceHeadComponentsSpy, getPostBodyComponents, replacePostBodyComponents: replacePostBodyComponentsSpy }, {}) onPreRenderHTML({ getHeadComponents, pathname, replaceHeadComponents: replaceHeadComponentsSpy, getPostBodyComponents, replacePostBodyComponents: replacePostBodyComponentsSpy }, {})
expect(replaceHeadComponentsSpy).toHaveBeenCalledWith([headComponentsData[2], headComponentsData[3]]) 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', () => { it('does not remove scripts in the head marked as excluded from plugin options', () => {
onRenderBody({ scripts: scriptsData }) onRenderBody({ scripts: scriptsData })
function getHeadComponents () { function getHeadComponents () {
Expand All @@ -44,6 +52,7 @@ describe('gatsby-ssr.js', () => {
} }


onPreRenderHTML({ onPreRenderHTML({
pathname: '/my-cool-page',
getHeadComponents, getHeadComponents,
replaceHeadComponents: replaceHeadComponentsSpy, replaceHeadComponents: replaceHeadComponentsSpy,
getPostBodyComponents, 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', () => { 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: [] }) onRenderBody({ scripts: [] })
function getHeadComponents () { function getHeadComponents () {
return [headComponentsData[0], headComponentsData[1], headComponentsData[14]] return [headComponentsData[0], headComponentsData[1], headComponentsData[14]]
Expand All @@ -61,12 +72,14 @@ describe('gatsby-ssr.js', () => {
return [] return []
} }


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


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

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


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


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


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


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


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


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

onRenderBody({ scripts: [] }) onRenderBody({ scripts: [] })
function getHeadComponents () { function getHeadComponents () {
return [] return []
Expand All @@ -135,11 +152,13 @@ describe('gatsby-ssr.js', () => {
return [postBodyComponentsData[0], postBodyComponentsData[1], postBodyComponentsData[2]] 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]]) expect(replacePostBodyComponentsSpy).toHaveBeenCalledWith([postBodyComponentsData[0]])
}) })


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

onRenderBody({ scripts: scriptsData }) onRenderBody({ scripts: scriptsData })
function getHeadComponents () { function getHeadComponents () {
return [] return []
Expand All @@ -148,14 +167,16 @@ describe('gatsby-ssr.js', () => {
return [postBodyComponentsData[0], postBodyComponentsData[3], postBodyComponentsData[4], postBodyComponentsData[5], postBodyComponentsData[6], postBodyComponentsData[7]] 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).toHaveBeenCalledTimes(1)
expect(replacePostBodyComponentsSpy.calls.argsFor(0)[0].length).toEqual(1) expect(replacePostBodyComponentsSpy.calls.argsFor(0)[0].length).toEqual(1)
expect(replacePostBodyComponentsSpy).toHaveBeenCalledWith([postBodyComponentsData[0]]) expect(replacePostBodyComponentsSpy).toHaveBeenCalledWith([postBodyComponentsData[0]])
}) })


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

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


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


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


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


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


let pageScripts: Script[] 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. // 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) { export function onPreRenderHTML ({ getHeadComponents, pathname, replaceHeadComponents, getPostBodyComponents, replacePostBodyComponents }: OnPreRenderHTMLArgs, pluginOptions: PluginOptions) {
if (process.env.NODE_ENV !== 'production') { // During a gatsby development build (gatsby develop) we do nothing. if (process.env.NODE_ENV !== 'production' || checkPathExclusion(pathname, pluginOptions)) { // During a gatsby development build (gatsby develop) we do nothing.
return return
} }
replaceHeadComponents(getHeadComponentsNoJS(getHeadComponents(), pluginOptions)) replaceHeadComponents(getHeadComponentsNoJS(getHeadComponents(), pluginOptions))
replacePostBodyComponents(getPostBodyComponentsNoJS(getPostBodyComponents(), 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[] { function getHeadComponentsNoJS (headComponents: ReactNode[], pluginOptions: PluginOptions): ReactNode[] {
return headComponents.filter((headComponent) => { return headComponents.filter((headComponent) => {
// Not a react component and therefore not a <script>. // Not a react component and therefore not a <script>.
Expand Down

0 comments on commit 2719468

Please sign in to comment.