Skip to content

Commit

Permalink
fix($cssHash): added cssHash feature, updated readme, added tests
Browse files Browse the repository at this point in the history
  • Loading branch information
faceyspacey committed Jul 7, 2017
1 parent 2a0a311 commit 177e65c
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 276 deletions.
1 change: 1 addition & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ __tests__
coverage
src
*.png
*.jpg
.idea
242 changes: 30 additions & 212 deletions README.md

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions __tests__/__snapshots__/createApiWithCss.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,14 @@ Array [
]
`;

exports[`unit tests createCssHash() 1`] = `
Object {
"chunk1": "/static/0.css",
"chunk2": "/static/1.css",
"main": "/static/main.css",
}
`;

exports[`unit tests stylesAsString() 1`] = `
"/Users/jamesgillmore/App/build/main.css- the css!
Expand Down
36 changes: 18 additions & 18 deletions __tests__/__snapshots__/flushChunks.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
exports[`flushChunks() called as pure function babel - chunkNames 1`] = `
Object {
"Css": [Function],
"CssChunks": [Function],
"CssHash": [Function],
"Js": [Function],
"Styles": [Function],
"css": Object {
"toString": [Function],
},
"cssChunks": Object {
"cssHash": Object {
"toString": [Function],
},
"cssChunksHash": Object {
"cssHashRaw": Object {
"chunk1": "/static/0.css",
"chunk2": "/static/1.css",
"main": "/static/main.css",
Expand Down Expand Up @@ -43,16 +43,16 @@ Object {
exports[`flushChunks() called as pure function babel: uses default entries when no named chunks provided via opts.before/after 1`] = `
Object {
"Css": [Function],
"CssChunks": [Function],
"CssHash": [Function],
"Js": [Function],
"Styles": [Function],
"css": Object {
"toString": [Function],
},
"cssChunks": Object {
"cssHash": Object {
"toString": [Function],
},
"cssChunksHash": Object {
"cssHashRaw": Object {
"chunk1": "/static/0.css",
"chunk2": "/static/1.css",
"main": "/static/main.css",
Expand Down Expand Up @@ -85,16 +85,16 @@ Object {
exports[`flushChunks() called as pure function babel: uses entries provided by opts.before/after 1`] = `
Object {
"Css": [Function],
"CssChunks": [Function],
"CssHash": [Function],
"Js": [Function],
"Styles": [Function],
"css": Object {
"toString": [Function],
},
"cssChunks": Object {
"cssHash": Object {
"toString": [Function],
},
"cssChunksHash": Object {
"cssHashRaw": Object {
"chunk1": "/static/0.css",
"chunk2": "/static/1.css",
"main": "/static/main.css",
Expand Down Expand Up @@ -126,16 +126,16 @@ Object {
exports[`flushChunks() called as pure function webpack - chunkNames 1`] = `
Object {
"Css": [Function],
"CssChunks": [Function],
"CssHash": [Function],
"Js": [Function],
"Styles": [Function],
"css": Object {
"toString": [Function],
},
"cssChunks": Object {
"cssHash": Object {
"toString": [Function],
},
"cssChunksHash": Object {
"cssHashRaw": Object {
"chunk1": "/static/0.css",
"chunk2": "/static/1.css",
"main": "/static/main.css",
Expand Down Expand Up @@ -166,16 +166,16 @@ Object {
exports[`flushChunks() called as pure function webpack: uses default entries when no named chunks provided via opts.before/after 1`] = `
Object {
"Css": [Function],
"CssChunks": [Function],
"CssHash": [Function],
"Js": [Function],
"Styles": [Function],
"css": Object {
"toString": [Function],
},
"cssChunks": Object {
"cssHash": Object {
"toString": [Function],
},
"cssChunksHash": Object {
"cssHashRaw": Object {
"chunk1": "/static/0.css",
"chunk2": "/static/1.css",
"main": "/static/main.css",
Expand Down Expand Up @@ -208,16 +208,16 @@ Object {
exports[`flushChunks() called as pure function webpack: uses entries provided by opts.before/after 1`] = `
Object {
"Css": [Function],
"CssChunks": [Function],
"CssHash": [Function],
"Js": [Function],
"Styles": [Function],
"css": Object {
"toString": [Function],
},
"cssChunks": Object {
"cssHash": Object {
"toString": [Function],
},
"cssChunksHash": Object {
"cssHashRaw": Object {
"chunk1": "/static/0.css",
"chunk2": "/static/1.css",
"main": "/static/main.css",
Expand Down
25 changes: 14 additions & 11 deletions __tests__/createApiWithCss.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import {
getJsFileRegex,
isJs,
isCss,
stylesAsString
stylesAsString,
createCssHash
} from '../src/createApiWithCss'

import { stats } from '../__fixtures__/stats'

jest.mock('fs', () => ({
readFileSync: fileName =>
`${fileName}- the css! \n/*# sourceMappingURL=/static/main.js.map */`
Expand All @@ -21,12 +24,7 @@ describe('createApiWithCss()', () => {
it('generates js + style components, strings and arrays', () => {
const files = ['0.js', '0.css', 'main.js', 'main.css']
const filesForCss = ['main.js', 'main.css', '0.js', '0.css']
const api = createApiWithCss(
files,
filesForCss,
publicPath,
outputPath
) /*? $ */
const api = createApiWithCss(files, filesForCss, stats, outputPath) /*? $ */

expect(api.Js() /*? $.props.children */).toMatchSnapshot()
expect(api.Styles() /*? $.props.children */).toMatchSnapshot()
Expand All @@ -45,7 +43,7 @@ describe('createApiWithCss()', () => {

it('uses files with extension "no_css.js" if available', () => {
const files = ['main.js', 'main.no_css.js', 'main.css']
const api = createApiWithCss(files, files, publicPath, outputPath) /*? $ */
const api = createApiWithCss(files, files, stats, outputPath) /*? $ */

expect(api.Js() /*? $.props.children */).toMatchSnapshot()
expect(api.Styles() /*? $.props.children */).toMatchSnapshot()
Expand All @@ -64,7 +62,7 @@ describe('createApiWithCss()', () => {

it('throws when rendering css without outputPath', () => {
const files = ['main.js', 'main.css']
const api = createApiWithCss(files, files, publicPath)
const api = createApiWithCss(files, files, stats)

expect(api.Css /*? */).toThrow()
expect(api.css.toString /*? */).toThrow()
Expand All @@ -73,14 +71,14 @@ describe('createApiWithCss()', () => {
it('adds trailing slash to public path', () => {
const files = ['main.js']
const publicPath = '/static'
const api = createApiWithCss(files, files, publicPath)
const api = createApiWithCss(files, files, stats)

expect(api.js.toString() /*? */).toContain('/static/main.js')
})

it('does not include scripts with extension "hot-update.js"', () => {
const files = ['main.js', 'main.hot-update.js']
const api = createApiWithCss(files, files, publicPath)
const api = createApiWithCss(files, files, stats)

expect(api.scripts /*? */).not.toContain('main.hot-update.js')
})
Expand Down Expand Up @@ -123,4 +121,9 @@ describe('unit tests', () => {
const css = stylesAsString(stylesheets, outputPath) /*? $ */
expect(css).toMatchSnapshot()
})

test('createCssHash()', () => {
const hash = createCssHash(stats) /*? $ */
expect(hash).toMatchSnapshot()
})
})
Binary file added poo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 32 additions & 19 deletions src/createApiWithCss.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
import React from 'react'
import fs from 'fs'

import type { Stats, FilesMap } from './flushChunks'

export type CssChunksHash = {
[key: string]: string
}

type StatelessComponent = () => React.Element<*>
type ObjectString = {
toString: () => string
}
export type CssChunksHash = {
[key: string]: string
}

export type Api = {
Js: StatelessComponent,
Expand All @@ -33,14 +36,14 @@ const DEV = process.env.NODE_ENV === 'development'
export default (
files: Array<string>,
filesOrderedForCss: Array<string>,
publicPath: string,
outputPath: ?string,
cssChunksHash: CssChunksHash
stats: Stats,
outputPath: ?string
): Api => {
const publicPath = stats.publicPath.replace(/\/$/, '')
const regex = getJsFileRegex(files)
const scripts = files.filter(file => isJs(regex, file))
const stylesheets = filesOrderedForCss.filter(isCss)
publicPath = publicPath.replace(/\/$/, '')
const cssHashRaw = createCssHash(stats)

const api = {
// 1) Use as React components using ReactDOM.renderToStaticMarkup, eg:
Expand Down Expand Up @@ -109,16 +112,16 @@ export default (
outputPath,

// 6) special goodness for dual-file import()
cssChunksHash,
CssChunks: () => (
cssHashRaw,
CssHash: () => (
<script
type='text/javascript'
dangerouslySetInnerHTML={{ __html: JSON.stringify(cssChunksHash) }}
dangerouslySetInnerHTML={{ __html: JSON.stringify(cssHashRaw) }}
/>
),
cssChunks: {
cssHash: {
toString: () =>
`<script type='text/javascript'>window.__CSS_CHUNKS__= ${JSON.stringify(cssChunksHash)}</script>`
`<script type='text/javascript'>window.__CSS_CHUNKS__= ${JSON.stringify(cssHashRaw)}</script>`
}
}

Expand All @@ -127,17 +130,17 @@ export default (

/** HELPERS */

const getJsFileRegex = (files: Array<string>): RegExp => {
export const getJsFileRegex = (files: Array<string>): RegExp => {
const isUsingExtractCssChunk = !!files.find(file => file.includes('no_css'))
return isUsingExtractCssChunk ? /\.no_css\.js$/ : /\.js$/
}

const isJs = (regex: RegExp, file: string): boolean =>
export const isJs = (regex: RegExp, file: string): boolean =>
regex.test(file) && !/\.hot-update\.js$/.test(file)

const isCss = (file: string): boolean => /\.css$/.test(file)
export const isCss = (file: string): boolean => /\.css$/.test(file)

const stylesAsString = (
export const stylesAsString = (
stylesheets: Array<string>,
outputPath: ?string
): string => {
Expand All @@ -160,6 +163,16 @@ const stylesAsString = (
.replace(/\/\*# sourceMappingURL=.+\*\//g, '') // hide prod sourcemap err
}

/** EXPORTS FOR TESTING */

export { getJsFileRegex, isJs, isCss, stylesAsString }
export const createCssHash = ({
assetsByChunkName,
publicPath
}: {
assetsByChunkName: FilesMap,
publicPath: string
}): CssChunksHash =>
Object.keys(assetsByChunkName).reduce((hash, name) => {
if (!assetsByChunkName[name] || !assetsByChunkName[name].find) return hash
const file = assetsByChunkName[name].find(file => file.endsWith('.css'))
if (file) hash[name] = `${publicPath}${file}`
return hash
}, {})
20 changes: 4 additions & 16 deletions src/flushChunks.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ declare function __webpack_require__(id: string): any

type Files = Array<string>

type FilesMap = {
export type FilesMap = {
[key: string]: Array<string>
}

Expand All @@ -21,7 +21,7 @@ type Module = {
chunks: Array<number>
}

type Stats = {
export type Stats = {
assetsByChunkName: FilesMap,
chunks: Array<Chunk>,
modules: Array<Module>,
Expand Down Expand Up @@ -77,9 +77,8 @@ const flushChunks = (stats: Stats, isWebpack: boolean, opts: Options = {}) => {
...jsAfter.reverse(), // main.css, someElseYouPutBeforeMain.css, etc
...files // correct incrementing order already
],
stats.publicPath,
opts.outputPath,
createCssHash(stats.assetsByChunkName, stats.publicPath)
stats,
opts.outputPath
)
}

Expand Down Expand Up @@ -200,17 +199,6 @@ const filesFromChunks = (
return [].concat(...chunkNames.filter(hasChunk).map(entryToFiles))
}

const createCssHash = (
assetsByChunkName: FilesMap,
publicPath: string
): CssChunksHash =>
Object.keys(assetsByChunkName).reduce((hash, name) => {
if (!assetsByChunkName[name] || !assetsByChunkName[name].find) return hash
const file = assetsByChunkName[name].find(file => file.endsWith('.css'))
if (file) hash[name] = `${publicPath}${file}`
return hash
}, {})

/** EXPORTS FOR TESTS */

export {
Expand Down

0 comments on commit 177e65c

Please sign in to comment.