Skip to content

Commit

Permalink
Merge pull request #826 from Zexron-xu/feature/extract-to-json
Browse files Browse the repository at this point in the history
feat(ssr): support extract to json and restore state
  • Loading branch information
Brooooooklyn committed Sep 2, 2021
2 parents 6c1c468 + 4ac7936 commit a4c3f0a
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ exports[`StateToPersist specs should be able extract actionsToRetry 1`] = `"<htm
exports[`StateToPersist specs should be able to extractScriptString without tag 1`] = `"window['SIGI_STATE']={\\"bar\\":\\"baz\\"};window['SIGI_RETRY']={}"`;
exports[`StateToPersist specs should be able to extractToJSONScriptString 1`] = `"<script id=\\"SIGI_STATE\\" type=\\"application/json\\">{\\"foo\\":\\"bar\\"}</script><script id=\\"SIGI_RETRY\\" type=\\"application/json\\">{}</script>"`;
exports[`StateToPersist specs should be able to renderToJSX 1`] = `"<html data-reactroot=\\"\\"><head><link rel=\\"stylesheet\\" href=\\"https://sigi.how/style.css\\"/></head><body><div id=\\"app\\"><img src=\\"https://sigi.how/image.jpeg\\" alt=\\"\\"/></div><script id=\\"sigi-persisted-data\\">window['SIGI_STATE']={\\"foo\\":1};window['SIGI_RETRY']={}</script></body></html>"`;
exports[`StateToPersist specs should prevent xss 1`] = `"<html data-reactroot=\\"\\"><head><link rel=\\"stylesheet\\" href=\\"https://sigi.how/style.css\\"/></head><body><div id=\\"app\\"><img src=\\"https://sigi.how/image.jpeg\\" alt=\\"\\"/></div><script id=\\"sigi-persisted-data\\">window['SIGI_STATE']={\\"xss\\":\\"\\\\u003C\\\\u002Fscript\\\\u003E\\\\u003Cscript\\\\u003Ealert(1);\\\\u003C\\\\u002Fscript\\\\u003E\\"};window['SIGI_RETRY']={}</script></body></html>"`;
Expand Down
31 changes: 31 additions & 0 deletions packages/ssr/src/__tests__/browser.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { restoreState } from '../browser'

beforeEach(() => {
// @ts-expect-error
global.document = {}
// @ts-expect-error
global.window = {}
})

afterEach(() => {
// @ts-expect-error
delete global.document
// @ts-expect-error
delete global.window
})

describe('Browser function test', () => {
it('should add data into window', () => {
global.document.getElementById = jest.fn().mockReturnValue({ textContent: '{}' })
restoreState()
expect(global.window['SIGI_STATE']).toEqual({})
expect(global.window['SIGI_RETRY']).toEqual({})
})

it('should not add data into window', () => {
global.document.getElementById = jest.fn().mockReturnValue(null)
restoreState()
expect(global.window['SIGI_STATE']).toEqual(undefined)
expect(global.window['SIGI_RETRY']).toEqual(undefined)
})
})
10 changes: 10 additions & 0 deletions packages/ssr/src/__tests__/state-to-persist.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,14 @@ describe('StateToPersist specs', () => {
const state = createStateToPersist({ foo: 1 }, { home: ['initAppContext'] })
expect(renderToString(renderDocumentJSX(state.renderToJSX()))).toMatchSnapshot()
})

it('empty data should return empty string', () => {
const state = createStateToPersist()
expect(state.extractToJSONScriptString()).toBe('')
})

it('should be able to extractToJSONScriptString', () => {
const state = createStateToPersist({ foo: 'bar' })
expect(state.extractToJSONScriptString()).toMatchSnapshot()
})
})
14 changes: 14 additions & 0 deletions packages/ssr/src/browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { GLOBAL_KEY_SYMBOL, RETRY_KEY_SYMBOL } from '@sigi/core'

export function restoreState() {
const sigiStateContent = document.getElementById(GLOBAL_KEY_SYMBOL)?.textContent
const sigiRetryContent = document.getElementById(RETRY_KEY_SYMBOL)?.textContent

if (sigiStateContent) {
window[GLOBAL_KEY_SYMBOL] = JSON.parse(sigiStateContent)
}

if (sigiRetryContent) {
window[RETRY_KEY_SYMBOL] = JSON.parse(sigiRetryContent)
}
}
1 change: 1 addition & 0 deletions packages/ssr/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { runSSREffects as emitSSREffects, ModuleMeta } from './run'
export { match } from './match'
export { restoreState } from './browser'
10 changes: 10 additions & 0 deletions packages/ssr/src/state-to-persist.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ export class StateToPersist<T> {
return doc.substr(0, endBodyPosition) + this.extractToScriptString() + doc.substring(endBodyPosition)
}

extractToJSONScriptString() {
if (this.dataToPersist == null) {
return ''
}
const stateContent = serialize(this.dataToPersist, { isJSON: true })
return `<script id="${GLOBAL_KEY_SYMBOL}" type="application/json">${stateContent}</script><script id="${RETRY_KEY_SYMBOL}" type="application/json">${JSON.stringify(
this.actionsToRetry,
)}</script>`
}

private serialize() {
const content = serialize(this.dataToPersist, { isJSON: true })
return `window['${GLOBAL_KEY_SYMBOL}']=${content};window['${RETRY_KEY_SYMBOL}']=${JSON.stringify(
Expand Down

0 comments on commit a4c3f0a

Please sign in to comment.