diff --git a/package.json b/package.json
index df9d4597d..193f1dacd 100644
--- a/package.json
+++ b/package.json
@@ -41,6 +41,7 @@
"lerna": "^3.13.2",
"prettier": "^1.16.4",
"react": "^16.8.6",
+ "regenerator-runtime": "^0.13.2",
"rollup": "^1.9.3",
"rollup-plugin-babel": "^4.3.2",
"rollup-plugin-commonjs": "^9.3.4",
diff --git a/packages/server/src/ChunkExtractor.js b/packages/server/src/ChunkExtractor.js
index 582930095..46b547720 100644
--- a/packages/server/src/ChunkExtractor.js
+++ b/packages/server/src/ChunkExtractor.js
@@ -51,9 +51,9 @@ function assetToScriptElement(asset, extraProps) {
)
}
-function assetToStyleString(asset) {
+function assetToStyleString(asset, { inputFileSystem }) {
return new Promise((resolve, reject) => {
- fs.readFile(asset.path, 'utf8', (err, data) => {
+ inputFileSystem.readFile(asset.path, 'utf8', (err, data) => {
if (err) {
reject(err)
return
@@ -69,9 +69,9 @@ function assetToStyleTag(asset, extraProps) {
}"${extraPropsToString(asset, extraProps)}>`
}
-function assetToStyleTagInline(asset, extraProps) {
+function assetToStyleTagInline(asset, extraProps, { inputFileSystem }) {
return new Promise((resolve, reject) => {
- fs.readFile(asset.path, 'utf8', (err, data) => {
+ inputFileSystem.readFile(asset.path, 'utf8', (err, data) => {
if (err) {
reject(err)
return
@@ -100,9 +100,9 @@ function assetToStyleElement(asset, extraProps) {
)
}
-function assetToStyleElementInline(asset, extraProps) {
+function assetToStyleElementInline(asset, extraProps, { inputFileSystem }) {
return new Promise((resolve, reject) => {
- fs.readFile(asset.path, 'utf8', (err, data) => {
+ inputFileSystem.readFile(asset.path, 'utf8', (err, data) => {
if (err) {
reject(err)
return
@@ -162,6 +162,7 @@ class ChunkExtractor {
namespace = '',
outputPath,
publicPath,
+ inputFileSystem = fs,
} = {}) {
this.namespace = namespace
this.stats = stats || smartRequire(statsFile)
@@ -170,6 +171,7 @@ class ChunkExtractor {
this.statsFile = statsFile
this.entrypoints = Array.isArray(entrypoints) ? entrypoints : [entrypoints]
this.chunks = []
+ this.inputFileSystem = inputFileSystem
}
resolvePublicUrl(filename) {
@@ -342,7 +344,7 @@ class ChunkExtractor {
getCssString() {
const mainAssets = this.getMainAssets('style')
const promises = mainAssets.map(asset =>
- assetToStyleString(asset).then(data => data),
+ assetToStyleString(asset, this).then(data => data),
)
return Promise.all(promises).then(results => joinTags(results))
}
@@ -355,7 +357,7 @@ class ChunkExtractor {
getInlineStyleTags(extraProps = {}) {
const mainAssets = this.getMainAssets('style')
const promises = mainAssets.map(asset =>
- assetToStyleTagInline(asset, extraProps).then(data => data),
+ assetToStyleTagInline(asset, extraProps, this).then(data => data),
)
return Promise.all(promises).then(results => joinTags(results))
}
@@ -368,7 +370,7 @@ class ChunkExtractor {
getInlineStyleElements(extraProps = {}) {
const mainAssets = this.getMainAssets('style')
const promises = mainAssets.map(asset =>
- assetToStyleElementInline(asset, extraProps).then(data => data),
+ assetToStyleElementInline(asset, extraProps, this).then(data => data),
)
return Promise.all(promises).then(results => results)
}
diff --git a/packages/server/src/ChunkExtractor.test.js b/packages/server/src/ChunkExtractor.test.js
index 8953f79d6..331a76f04 100644
--- a/packages/server/src/ChunkExtractor.test.js
+++ b/packages/server/src/ChunkExtractor.test.js
@@ -1,3 +1,5 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import 'regenerator-runtime/runtime'
import path from 'path'
import stats from '../__fixtures__/stats.json'
import ChunkExtractor from './ChunkExtractor'
@@ -63,9 +65,9 @@ describe('ChunkExtrator', () => {
describe('#getScriptTags', () => {
it('should return main script tag without chunk', () => {
expect(extractor.getScriptTags()).toMatchInlineSnapshot(`
-"
-"
-`)
+ "
+ "
+ `)
})
it('should return main script tag without chunk with namespaced required chunks id', () => {
@@ -75,37 +77,37 @@ describe('ChunkExtrator', () => {
outputPath: path.resolve(__dirname, '../__fixtures__'),
})
expect(extractor.getScriptTags()).toMatchInlineSnapshot(`
-"
-"
-`)
+ "
+ "
+ `)
})
it('should return other chunks if referenced', () => {
extractor.addChunk('letters-A')
expect(extractor.getScriptTags()).toMatchInlineSnapshot(`
-"
-
-"
-`)
+ "
+
+ "
+ `)
})
it('should allow for query params in chunk names', () => {
extractor.addChunk('letters-E')
expect(extractor.getScriptTags()).toMatchInlineSnapshot(`
-"
-
-"
-`)
+ "
+
+ "
+ `)
})
it('should add extra props if specified - object argument', () => {
extractor.addChunk('letters-A')
expect(extractor.getScriptTags({ nonce: 'testnonce' }))
.toMatchInlineSnapshot(`
-"
-
-"
-`)
+ "
+
+ "
+ `)
})
it('should add extra props if specified - function argument', () => {
@@ -115,10 +117,10 @@ describe('ChunkExtrator', () => {
return { nonce: asset ? asset.chunk : 'anonymous' }
}),
).toMatchInlineSnapshot(`
-"
-
-"
-`)
+ "
+
+ "
+ `)
})
})
@@ -130,129 +132,129 @@ describe('ChunkExtrator', () => {
outputPath: path.resolve(__dirname, '../__fixtures__'),
})
expect(extractor.getScriptElements()).toMatchInlineSnapshot(`
-Array [
- ,
- ,
-]
-`)
+ Array [
+ ,
+ ,
+ ]
+ `)
})
it('should return main script tag without chunk', () => {
expect(extractor.getScriptElements()).toMatchInlineSnapshot(`
-Array [
- ,
- ,
-]
-`)
+ Array [
+ ,
+ ,
+ ]
+ `)
})
it('should return other chunks if referenced', () => {
extractor.addChunk('letters-A')
expect(extractor.getScriptElements()).toMatchInlineSnapshot(`
-Array [
- ,
- ,
- ,
-]
-`)
+ Array [
+ ,
+ ,
+ ,
+ ]
+ `)
})
it('should allow for query params in chunk names', () => {
extractor.addChunk('letters-E')
expect(extractor.getScriptElements()).toMatchInlineSnapshot(`
-Array [
- ,
- ,
- ,
-]
-`)
+ Array [
+ ,
+ ,
+ ,
+ ]
+ `)
})
it('should add extra props if specified - object argument', () => {
extractor.addChunk('letters-A')
expect(extractor.getScriptElements({ nonce: 'testnonce' }))
.toMatchInlineSnapshot(`
-Array [
- ,
- ,
- ,
-]
-`)
+ Array [
+ ,
+ ,
+ ,
+ ]
+ `)
})
it('should add extra props if specified - function argument', () => {
@@ -262,31 +264,31 @@ Array [
return { nonce: asset ? asset.chunk : 'anonymous' }
}),
).toMatchInlineSnapshot(`
-Array [
- ,
- ,
- ,
-]
-`)
+ Array [
+ ,
+ ,
+ ,
+ ]
+ `)
})
it('should use publicPath from options', () => {
@@ -297,23 +299,23 @@ Array [
})
expect(extractor.getScriptElements()).toMatchInlineSnapshot(`
-Array [
- ,
- ,
-]
-`)
+ Array [
+ ,
+ ,
+ ]
+ `)
})
})
@@ -327,26 +329,26 @@ Array [
it('should return other chunks if referenced', () => {
extractor.addChunk('letters-A')
expect(extractor.getStyleTags()).toMatchInlineSnapshot(`
-"
-"
-`)
+ "
+ "
+ `)
})
it('should allow for query params in chunk names', () => {
extractor.addChunk('letters-E')
expect(extractor.getStyleTags()).toMatchInlineSnapshot(`
-"
-"
-`)
+ "
+ "
+ `)
})
it('should add extraProps if specified - object argument', () => {
extractor.addChunk('letters-A')
expect(extractor.getStyleTags({ nonce: 'testnonce' }))
.toMatchInlineSnapshot(`
-"
-"
-`)
+ "
+ "
+ `)
})
it('should add extraProps if specified - function argument', () => {
@@ -356,450 +358,457 @@ Array [
nonce: asset ? asset.chunk : 'anonymous',
})),
).toMatchInlineSnapshot(`
-"
-"
-`)
+ "
+ "
+ `)
})
})
describe('#getInlineStyleTags', () => {
- it('should return inline style tags as a promise', () => {
+ it('should return inline style tags as a promise', async () => {
extractor.addChunk('letters-A')
- expect.assertions(1)
- return extractor.getInlineStyleTags().then(data =>
- expect(data).toMatchInlineSnapshot(`
-"
-"
-`),
- )
- })
-
- it('should add extraProps if specified - object argument', () => {
+ const data = await extractor.getInlineStyleTags()
+ expect(data).toMatchInlineSnapshot(`
+ "
+ "
+ `)
+ })
+
+ it('should add extraProps if specified - object argument', async () => {
extractor.addChunk('letters-A')
- expect.assertions(1)
- return extractor.getInlineStyleTags({ nonce: 'testnonce' }).then(data =>
- expect(data).toMatchInlineSnapshot(`
-"
-"
-`),
- )
- })
-
- it('should add extraProps if specified - function argument', () => {
+ const data = await extractor.getInlineStyleTags({ nonce: 'testnonce' })
+ expect(data).toMatchInlineSnapshot(`
+ "
+ "
+ `)
+ })
+
+ it('should add extraProps if specified - function argument', async () => {
extractor.addChunk('letters-A')
- expect.assertions(1)
- return extractor
- .getInlineStyleTags(asset => ({ nonce: asset.chunk }))
- .then(data =>
- expect(data).toMatchInlineSnapshot(`
-"
-"
-`),
- )
+ const data = await extractor.getInlineStyleTags(asset => ({
+ nonce: asset.chunk,
+ }))
+ expect(data).toMatchInlineSnapshot(`
+ "
+ "
+ `)
})
})
describe('#getStyleElements', () => {
it('should return main style tag without chunk', () => {
expect(extractor.getStyleElements()).toMatchInlineSnapshot(`
-Array [
- ,
-]
-`)
+ Array [
+ ,
+ ]
+ `)
})
it('should return other chunks if referenced', () => {
extractor.addChunk('letters-A')
expect(extractor.getStyleElements()).toMatchInlineSnapshot(`
-Array [
- ,
- ,
-]
-`)
+ Array [
+ ,
+ ,
+ ]
+ `)
})
it('should allow for query params in chunk names', () => {
extractor.addChunk('letters-E')
expect(extractor.getStyleElements()).toMatchInlineSnapshot(`
-Array [
- ,
- ,
-]
-`)
+ Array [
+ ,
+ ,
+ ]
+ `)
})
it('should add extraProps if specified - object argument', () => {
extractor.addChunk('letters-A')
expect(extractor.getStyleElements({ nonce: 'testnonce' }))
.toMatchInlineSnapshot(`
-Array [
- ,
- ,
-]
-`)
+ Array [
+ ,
+ ,
+ ]
+ `)
})
it('should add extraProps if specified - function argument', () => {
extractor.addChunk('letters-A')
expect(extractor.getStyleElements(asset => ({ nonce: asset.chunk })))
.toMatchInlineSnapshot(`
-Array [
- ,
- ,
-]
-`)
+ Array [
+ ,
+ ,
+ ]
+ `)
})
})
describe('#getInlineStyleElements', () => {
- it('should return inline style elements as a promise', () => {
+ it('should return inline style elements as a promise', async () => {
extractor.addChunk('letters-A')
- expect.assertions(1)
- return extractor.getInlineStyleElements().then(data =>
- expect(data).toMatchInlineSnapshot(`
-Array [
- ,
- ,
-]
-`),
- )
+ const data = await extractor.getInlineStyleElements()
+ expect(data).toMatchInlineSnapshot(`
+ Array [
+ ,
+ ,
+ ]
+ `)
})
})
describe('#getCssString', () => {
- it('should return a string of the referenced css files as a promise', () => {
+ it('should return a string of the referenced css files as a promise', async () => {
extractor.addChunk('letters-A')
- expect.assertions(1)
- return extractor.getCssString().then(data =>
- expect(data).toMatchInlineSnapshot(`
-"h1 {
- color: cyan;
-}
-body {
- background: pink;
-}
-"
-`),
- )
+ const data = await extractor.getCssString()
+ expect(data).toMatchInlineSnapshot(`
+ "h1 {
+ color: cyan;
+ }
+ body {
+ background: pink;
+ }
+ "
+ `)
+ })
+
+ it('should work with custom fs', async () => {
+ extractor.inputFileSystem = {
+ readFile: jest.fn((file, encoding, callback) =>
+ callback(null, 'foo\n'),
+ ),
+ }
+ extractor.addChunk('letters-A')
+ const data = await extractor.getCssString()
+ expect(extractor.inputFileSystem.readFile).toHaveBeenCalledTimes(2)
+ expect(data).toMatchInlineSnapshot(`
+ "foo
+
+ foo
+ "
+ `)
})
})
describe('#getLinkTags', () => {
it('should return main script tag without chunk', () => {
expect(extractor.getLinkTags()).toMatchInlineSnapshot(`
-"
-
-
-"
-`)
+ "
+
+
+ "
+ `)
})
it('should return other chunks if referenced', () => {
extractor.addChunk('letters-A')
expect(extractor.getLinkTags()).toMatchInlineSnapshot(`
-"
-
-
-
-
-"
-`)
+ "
+
+
+
+
+ "
+ `)
})
it('should allow for query params in chunk names', () => {
extractor.addChunk('letters-E')
expect(extractor.getLinkTags()).toMatchInlineSnapshot(`
-"
-
-
-
-
-"
-`)
+ "
+
+
+
+
+ "
+ `)
})
it('should add extraProps if specified - object argument', () => {
extractor.addChunk('letters-A')
expect(extractor.getLinkTags({ nonce: 'testnonce' }))
.toMatchInlineSnapshot(`
-"
-
-
-
-
-"
-`)
+ "
+
+
+
+
+ "
+ `)
})
it('should add extraProps if specified - function argument', () => {
extractor.addChunk('letters-A')
expect(extractor.getLinkTags(asset => ({ nonce: asset.chunk })))
.toMatchInlineSnapshot(`
-"
-
-
-
-
-"
-`)
+ "
+
+
+
+
+ "
+ `)
})
})
describe('#getLinkElements', () => {
it('should return main script tag without chunk', () => {
expect(extractor.getLinkElements()).toMatchInlineSnapshot(`
-Array [
- ,
- ,
- ,
- ,
-]
-`)
+ Array [
+ ,
+ ,
+ ,
+ ,
+ ]
+ `)
})
it('should return other chunks if referenced', () => {
extractor.addChunk('letters-A')
expect(extractor.getLinkElements()).toMatchInlineSnapshot(`
-Array [
- ,
- ,
- ,
- ,
- ,
- ,
-]
-`)
+ Array [
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ]
+ `)
})
it('should allow for query params in chunk names', () => {
extractor.addChunk('letters-E')
expect(extractor.getLinkElements()).toMatchInlineSnapshot(`
-Array [
- ,
- ,
- ,
- ,
- ,
- ,
-]
-`)
+ Array [
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ]
+ `)
})
it('should add extraProps if specified', () => {
extractor.addChunk('letters-A')
expect(extractor.getLinkElements({ nonce: 'testnonce' }))
.toMatchInlineSnapshot(`
-Array [
- ,
- ,
- ,
- ,
- ,
- ,
-]
-`)
+ Array [
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ]
+ `)
})
})
diff --git a/website/src/pages/docs/api-loadable-server.mdx b/website/src/pages/docs/api-loadable-server.mdx
index a0b73dcf4..837ad8e54 100644
--- a/website/src/pages/docs/api-loadable-server.mdx
+++ b/website/src/pages/docs/api-loadable-server.mdx
@@ -10,14 +10,15 @@ 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`). |
-| `options.namespace` | Namespace of your application (use only if you have several React apps on the same page). |
+| 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). |
+| `options.inputFileSystem` | File system used to read files (default to `fs`). |
You must specify either `statsFile` or `stats` to be able to use `ChunkExtractor`.
diff --git a/yarn.lock b/yarn.lock
index 24c5c7262..c32f3ff66 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7566,6 +7566,11 @@ regenerator-runtime@^0.11.1:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
+regenerator-runtime@^0.13.2:
+ version "0.13.2"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447"
+ integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA==
+
regenerator-transform@^0.13.4:
version "0.13.4"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.4.tgz#18f6763cf1382c69c36df76c6ce122cc694284fb"