Skip to content

Commit

Permalink
feat: support multiple react apps (#317)
Browse files Browse the repository at this point in the history
Closes #311
  • Loading branch information
debugpai authored and gregberge committed Apr 23, 2019
1 parent 4ba71dd commit dc54050
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 16 deletions.
9 changes: 6 additions & 3 deletions packages/component/src/loadableReady.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
/* eslint-disable no-underscore-dangle, camelcase */
/* eslint-env browser */
import { warn } from './util'
import { LOADABLE_REQUIRED_CHUNKS_KEY } from './sharedInternals'
import { getRequiredChunkKey } from './sharedInternals'

const BROWSER = typeof window !== 'undefined'

export default function loadableReady(done = () => {}) {
export default function loadableReady(
done = () => {},
{ namespace = '' } = {},
) {
if (!BROWSER) {
warn('`loadableReady()` must be called in browser only')
done()
Expand All @@ -14,7 +17,7 @@ export default function loadableReady(done = () => {}) {

let requiredChunks = null
if (BROWSER) {
const dataElement = document.getElementById(LOADABLE_REQUIRED_CHUNKS_KEY)
const dataElement = document.getElementById(getRequiredChunkKey(namespace))
if (dataElement) {
requiredChunks = JSON.parse(dataElement.textContent)
}
Expand Down
5 changes: 4 additions & 1 deletion packages/component/src/sharedInternals.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export { invariant } from './util'
export { default as Context } from './Context'
export const LOADABLE_REQUIRED_CHUNKS_KEY = '__LOADABLE_REQUIRED_CHUNKS__'
const LOADABLE_REQUIRED_CHUNKS_KEY = '__LOADABLE_REQUIRED_CHUNKS__'
export function getRequiredChunkKey(namespace) {
return `${namespace}${LOADABLE_REQUIRED_CHUNKS_KEY}`
}
10 changes: 7 additions & 3 deletions packages/server/src/ChunkExtractor.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import uniq from 'lodash/uniq'
import uniqBy from 'lodash/uniqBy'
import flatMap from 'lodash/flatMap'
import React from 'react'
import { invariant, LOADABLE_REQUIRED_CHUNKS_KEY } from './sharedInternals'
import { invariant, getRequiredChunkKey } from './sharedInternals'
import ChunkExtractorManager from './ChunkExtractorManager'
import { smartRequire, joinURLPath } from './util'

Expand Down Expand Up @@ -159,9 +159,11 @@ class ChunkExtractor {
statsFile,
stats,
entrypoints = ['main'],
namespace = '',
outputPath,
publicPath,
} = {}) {
this.namespace = namespace
this.stats = stats || smartRequire(statsFile)
this.publicPath = publicPath || this.stats.publicPath
this.outputPath = outputPath || this.stats.outputPath
Expand Down Expand Up @@ -260,7 +262,9 @@ class ChunkExtractor {
}

getRequiredChunksScriptTag(extraProps) {
return `<script id="${LOADABLE_REQUIRED_CHUNKS_KEY}" type="application/json"${extraPropsToString(
return `<script id="${getRequiredChunkKey(
this.namespace,
)}" type="application/json"${extraPropsToString(
null,
extraProps,
)}>${this.getRequiredChunksScriptContent()}</script>`
Expand All @@ -270,7 +274,7 @@ class ChunkExtractor {
return (
<script
key="required"
id={LOADABLE_REQUIRED_CHUNKS_KEY}
id={getRequiredChunkKey(this.namespace)}
type="application/json"
dangerouslySetInnerHTML={{
__html: this.getRequiredChunksScriptContent(),
Expand Down
38 changes: 38 additions & 0 deletions packages/server/src/ChunkExtractor.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,18 @@ describe('ChunkExtrator', () => {
`)
})

it('should return main script tag without chunk with namespaced required chunks id', () => {
extractor = new ChunkExtractor({
namespace: 'testapp',
stats,
outputPath: path.resolve(__dirname, '../__fixtures__'),
})
expect(extractor.getScriptTags()).toMatchInlineSnapshot(`
"<script id=\\"testapp__LOADABLE_REQUIRED_CHUNKS__\\" type=\\"application/json\\">[]</script>
<script async data-chunk=\\"main\\" src=\\"/dist/node/main.js\\"></script>"
`)
})

it('should return other chunks if referenced', () => {
extractor.addChunk('letters-A')
expect(extractor.getScriptTags()).toMatchInlineSnapshot(`
Expand Down Expand Up @@ -111,6 +123,32 @@ describe('ChunkExtrator', () => {
})

describe('#getScriptElements', () => {
it('should return main script tag without chunk with namespaced id for loadable chunks', () => {
extractor = new ChunkExtractor({
namespace: 'testapp',
stats,
outputPath: path.resolve(__dirname, '../__fixtures__'),
})
expect(extractor.getScriptElements()).toMatchInlineSnapshot(`
Array [
<script
dangerouslySetInnerHTML={
Object {
"__html": "[]",
}
}
id="testapp__LOADABLE_REQUIRED_CHUNKS__"
type="application/json"
/>,
<script
async={true}
data-chunk="main"
src="/dist/node/main.js"
/>,
]
`)
})

it('should return main script tag without chunk', () => {
expect(extractor.getScriptElements()).toMatchInlineSnapshot(`
Array [
Expand Down
4 changes: 2 additions & 2 deletions packages/server/src/sharedInternals.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED } from '@loadable/co
const {
invariant,
Context,
LOADABLE_REQUIRED_CHUNKS_KEY,
getRequiredChunkKey,
} = __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED

export { invariant, Context, LOADABLE_REQUIRED_CHUNKS_KEY }
export { invariant, Context, getRequiredChunkKey }
19 changes: 19 additions & 0 deletions website/src/pages/docs/api-loadable-component.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,22 @@ A component created using `loadable.lib` or `lazy.lib`.
| `ref` | Accepts a ref, populated when the library is loaded. |
| `fallback` | Fallback displayed during the loading. |
| `...` | Props are forwarded as first argument of `loadFn` |

## loadableReady

Wait for all loadable components to be loaded. This method must only be used with Server Side Rendering, see [Server Side Rendering guide](/docs/server-side-rendering/).

| Arguments | Description |
| ------------------- | ----------------------------------------------------------------------------------------- |
| `done` | Function called when all components are loaded. |
| `options` | Optional options. |
| `options.namespace` | Namespace of your application (use only if you have several React apps on the same page). |

```js
import { loadableReady } from '@loadable/component'

loadableReady(() => {
const root = document.getElementById('main')
hydrate(<App />, root)
})
```
15 changes: 8 additions & 7 deletions website/src/pages/docs/api-loadable-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ order: 20

Used to collect chunks server-side and get them as script tags or script elements.

| Arguments | Description |
| --------------------- | ----------------------------------------------------------- |
| `options` | An object options. |
| `options.statsFile` | Stats file path generated using `@loadable/webpack-plugin`. |
| `options.stats` | Stats generated using `@loadable/webpack-plugin`. |
| `options.entrypoints` | Webpack entrypoints to load (default to `["main"]`). |
| `options.outputPath` | Optional output path (only for `requireEntrypoint`). |
| Arguments | Description |
| --------------------- | ----------------------------------------------------------------------------------------- |
| `options` | An object options. |
| `options.statsFile` | Stats file path generated using `@loadable/webpack-plugin`. |
| `options.stats` | Stats generated using `@loadable/webpack-plugin`. |
| `options.entrypoints` | Webpack entrypoints to load (default to `["main"]`). |
| `options.outputPath` | Optional output path (only for `requireEntrypoint`). |
| `options.namespace` | Namespace of your application (use only if you have several React apps on the same page). |

You must specify either `statsFile` or `stats` to be able to use `ChunkExtractor`.

Expand Down

0 comments on commit dc54050

Please sign in to comment.