From 3c723d3f1224990b6356670aa6ec3e23dd246d6b Mon Sep 17 00:00:00 2001 From: Jared Palmer Date: Tue, 16 Jan 2018 16:16:47 -0500 Subject: [PATCH 1/2] Add very basic hooks before and after compile, allow paths to be modifed --- packages/razzle/config/createConfig.js | 5 ++-- packages/razzle/config/createJestConfig.js | 1 + packages/razzle/config/env.js | 18 ++++++++++-- packages/razzle/scripts/build.js | 34 ++++++++++++++++------ packages/razzle/scripts/start.js | 22 ++++++++++---- packages/razzle/scripts/test.js | 20 ++++++++++++- 6 files changed, 81 insertions(+), 19 deletions(-) diff --git a/packages/razzle/config/createConfig.js b/packages/razzle/config/createConfig.js index de5ad4dfb..d7d4f68c2 100644 --- a/packages/razzle/config/createConfig.js +++ b/packages/razzle/config/createConfig.js @@ -11,7 +11,7 @@ const FriendlyErrorsPlugin = require('razzle-dev-utils/FriendlyErrorsPlugin'); const eslintFormatter = require('react-dev-utils/eslintFormatter'); const autoprefixer = require('autoprefixer'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); -const paths = require('./paths'); + const getClientEnv = require('./env').getClientEnv; const nodePath = require('./env').nodePath; const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware'); @@ -20,7 +20,8 @@ const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware') module.exports = ( target = 'web', env = 'dev', - { clearConsole = true, host = 'localhost', port = 3000 } + { clearConsole = true, host = 'localhost', port = 3000 }, + paths ) => { // First we check to see if the user has a custom .babelrc file, otherwise // we just use babel-preset-razzle. diff --git a/packages/razzle/config/createJestConfig.js b/packages/razzle/config/createJestConfig.js index e0e338b61..65dcf0a8d 100644 --- a/packages/razzle/config/createJestConfig.js +++ b/packages/razzle/config/createJestConfig.js @@ -16,6 +16,7 @@ module.exports = (resolve, rootDir) => { const config = { collectCoverageFrom: ['src/**/*.{js,jsx}'], setupTestFrameworkScriptFile: setupTestsFile, + setupFiles: [resolve('config/polyfills.js')], testMatch: [ '/src/**/__tests__/**/*.js?(x)', '/src/**/?(*.)(spec|test).js?(x)', diff --git a/packages/razzle/config/env.js b/packages/razzle/config/env.js index cdffc0ac5..b27315f5e 100644 --- a/packages/razzle/config/env.js +++ b/packages/razzle/config/env.js @@ -1,9 +1,23 @@ 'use strict'; -const paths = require('./paths'); -const fs = require('fs'); +const defaultPaths = require('./paths'); +const fs = require('fs-extra'); const path = require('path'); +let paths = defaultPaths; +// Check for razzle.config.js file +if (fs.existsSync(defaultPaths.appRazzleConfig)) { + try { + const mods = require(defaultPaths.appRazzleConfig); + if (mods.modifyPaths) { + paths = mods.modifyPaths(defaultPaths); + } + } catch (e) { + console.error('Invalid razzle.config.js file.', e); + process.exit(1); + } +} + // Make sure that including paths.js after env.js will read .env variables. delete require.cache[require.resolve('./paths')]; diff --git a/packages/razzle/scripts/build.js b/packages/razzle/scripts/build.js index 29f90ff53..8e50be5a9 100755 --- a/packages/razzle/scripts/build.js +++ b/packages/razzle/scripts/build.js @@ -16,7 +16,7 @@ require('../config/env'); const webpack = require('webpack'); const fs = require('fs-extra'); const chalk = require('chalk'); -const paths = require('../config/paths'); +const defaultPaths = require('../config/paths'); const createConfig = require('../config/createConfig'); const printErrors = require('razzle-dev-utils/printErrors'); const logger = require('razzle-dev-utils/logger'); @@ -26,6 +26,27 @@ const measureFileSizesBeforeBuild = FileSizeReporter.measureFileSizesBeforeBuild; const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild; +let razzle = {}; + +// Check for razzle.config.js file +if (fs.existsSync(defaultPaths.appRazzleConfig)) { + try { + razzle = require(defaultPaths.appRazzleConfig); + } catch (e) { + logger.error('Invalid razzle.config.js file.', e); + process.exit(1); + } +} + +// Allow overriding paths +const paths = razzle.modifyPaths + ? razzle.modifyPaths('prod', defaultPaths) + : defaultPaths; + +if (razzle.preCompile) { + razzle.preCompile('prod'); +} + // First, read the current file sizes in build directory. // This lets us display how much they changed later. measureFileSizesBeforeBuild(paths.appBuildPublic) @@ -70,14 +91,6 @@ measureFileSizesBeforeBuild(paths.appBuildPublic) ); function build(previousFileSizes) { - // Check if razzle.config.js exists - let razzle = {}; - try { - razzle = require(paths.appRazzleConfig); - /* eslint-disable no-empty */ - } catch (e) {} - /* eslint-enable */ - if (razzle.clearConsole === false || !!razzle.host || !!razzle.port) { logger.warn(`Specifying options \`port\`, \`host\`, and \`clearConsole\` in razzle.config.js has been deprecated. Please use a .env file instead. @@ -166,6 +179,9 @@ ${razzle.port !== '3000' && `PORT=${razzle.port}`} return reject(new Error(serverMessages.warnings.join('\n\n'))); } console.log(chalk.green('Compiled server successfully.')); + if (razzle.postCompile) { + razzle.postCompile('prod'); + } return resolve({ stats: clientStats, previousFileSizes, diff --git a/packages/razzle/scripts/start.js b/packages/razzle/scripts/start.js index 806ffecd9..08936e601 100755 --- a/packages/razzle/scripts/start.js +++ b/packages/razzle/scripts/start.js @@ -4,7 +4,7 @@ process.env.NODE_ENV = 'development'; const fs = require('fs-extra'); const webpack = require('webpack'); -const paths = require('../config/paths'); +const defaultPaths = require('../config/paths'); const createConfig = require('../config/createConfig'); const devServer = require('webpack-dev-server'); const printErrors = require('razzle-dev-utils/printErrors'); @@ -28,9 +28,9 @@ function main() { let razzle = {}; // Check for razzle.config.js file - if (fs.existsSync(paths.appRazzleConfig)) { + if (fs.existsSync(defaultPaths.appRazzleConfig)) { try { - razzle = require(paths.appRazzleConfig); + razzle = require(defaultPaths.appRazzleConfig); } catch (e) { clearConsole(); logger.error('Invalid razzle.config.js file.', e); @@ -38,13 +38,22 @@ function main() { } } + // Allow overriding paths + const paths = razzle.modifyPaths + ? razzle.modifyPaths('dev', defaultPaths) + : defaultPaths; + + if (razzle.preCompile) { + razzle.preCompile('dev'); + } + // Delete assets.json to always have a manifest up to date fs.removeSync(paths.appManifest); // Create dev configs using our config factory, passing in razzle file as // options. - let clientConfig = createConfig('web', 'dev', razzle); - let serverConfig = createConfig('node', 'dev', razzle); + let clientConfig = createConfig('web', 'dev', razzle, paths); + let serverConfig = createConfig('node', 'dev', razzle, paths); // Check if razzle.config has a modify function. If it does, call it on the // configs we just created. @@ -87,6 +96,9 @@ function main() { if (err) { logger.error(err); } + if (razzle.postCompile) { + razzle.postCompile('dev'); + } } ); } diff --git a/packages/razzle/scripts/test.js b/packages/razzle/scripts/test.js index 7cb17390d..e898966a1 100755 --- a/packages/razzle/scripts/test.js +++ b/packages/razzle/scripts/test.js @@ -35,7 +35,25 @@ if (!process.env.CI && argv.indexOf('--coverage') < 0) { const createJestConfig = require('../config/createJestConfig'); const path = require('path'); -const paths = require('../config/paths'); +const fs = require('fs-extra'); +const defaultPaths = require('../config/paths'); + +let razzle = {}; +// Check for razzle.config.js file +if (fs.existsSync(defaultPaths.appRazzleConfig)) { + try { + razzle = require(defaultPaths.appRazzleConfig); + } catch (e) { + console.error('Invalid razzle.config.js file.', e); + process.exit(1); + } +} + +// Allow overriding paths +const paths = razzle.modifyPaths + ? razzle.modifyPaths('test', defaultPaths) + : defaultPaths; + argv.push( '--config', JSON.stringify( From 231c937008123420b83f116186e5dae4a286cd1b Mon Sep 17 00:00:00 2001 From: Jared Palmer Date: Wed, 17 Jan 2018 10:42:21 -0500 Subject: [PATCH 2/2] Add afterjs example --- examples/with-afterjs/.gitignore | 13 + examples/with-afterjs/README.md | 45 + examples/with-afterjs/lib/_app.js | 84 + examples/with-afterjs/lib/_document.js | 49 + examples/with-afterjs/lib/client.js | 22 + examples/with-afterjs/lib/ensureReady.js | 16 + examples/with-afterjs/lib/index.js | 22 + examples/with-afterjs/lib/loadInitialProps.js | 23 + examples/with-afterjs/lib/server.js | 57 + examples/with-afterjs/package.json | 24 + examples/with-afterjs/public/favicon.ico | Bin 0 -> 32988 bytes examples/with-afterjs/public/robots.txt | 2 + examples/with-afterjs/razzle.config.js | 55 + examples/with-afterjs/src/Home.js | 9 + examples/with-afterjs/src/_routes.js | 9 + examples/with-afterjs/yarn.lock | 7190 +++++++++++++++++ 16 files changed, 7620 insertions(+) create mode 100644 examples/with-afterjs/.gitignore create mode 100644 examples/with-afterjs/README.md create mode 100644 examples/with-afterjs/lib/_app.js create mode 100644 examples/with-afterjs/lib/_document.js create mode 100644 examples/with-afterjs/lib/client.js create mode 100644 examples/with-afterjs/lib/ensureReady.js create mode 100644 examples/with-afterjs/lib/index.js create mode 100644 examples/with-afterjs/lib/loadInitialProps.js create mode 100644 examples/with-afterjs/lib/server.js create mode 100644 examples/with-afterjs/package.json create mode 100644 examples/with-afterjs/public/favicon.ico create mode 100644 examples/with-afterjs/public/robots.txt create mode 100644 examples/with-afterjs/razzle.config.js create mode 100644 examples/with-afterjs/src/Home.js create mode 100644 examples/with-afterjs/src/_routes.js create mode 100644 examples/with-afterjs/yarn.lock diff --git a/examples/with-afterjs/.gitignore b/examples/with-afterjs/.gitignore new file mode 100644 index 000000000..52273a920 --- /dev/null +++ b/examples/with-afterjs/.gitignore @@ -0,0 +1,13 @@ +logs +*.log +npm-debug.log* +.DS_Store + +coverage +node_modules +build +public/static +.env.local +.env.development.local +.env.test.local +.env.production.local \ No newline at end of file diff --git a/examples/with-afterjs/README.md b/examples/with-afterjs/README.md new file mode 100644 index 000000000..73ddc7fb9 --- /dev/null +++ b/examples/with-afterjs/README.md @@ -0,0 +1,45 @@ +# Razzle Custom Webpack Configuration Example + +## How to use +Download the example [or clone the whole project](https://github.com/jaredpalmer/razzle.git): + +```bash +curl https://codeload.github.com/jaredpalmer/razzle/tar.gz/master | tar -xz --strip=2 razzle-master/examples/with-custom-webpack-config +cd with-custom-webpack-config +``` + +Install it and run: + +```bash +yarn install +yarn start +``` + +## Idea behind the example +This example demonstrates how to use a `razzle.config.js` file to modify Razzle's +underlying webpack configuration. It modifies the name of the server's output file +in production (`razzle build`). + +Note that this file is not transpiled, and so you must write it with vanilla +Node.js-compatible JavaScript. + +```js +// razzle.config.js +'use strict'; + +module.exports = { + modify(config, { target, dev }, webpack) { + const appConfig = config; // stay immutable here + + // Change the name of the server output file in production + if (target === 'node' && !dev) { + appConfig.output.filename = 'custom.js'; + } + + return appConfig; + }, +}; + + + +``` \ No newline at end of file diff --git a/examples/with-afterjs/lib/_app.js b/examples/with-afterjs/lib/_app.js new file mode 100644 index 000000000..bb725e9cf --- /dev/null +++ b/examples/with-afterjs/lib/_app.js @@ -0,0 +1,84 @@ +import React from 'react'; +import { Switch, Route, withRouter } from 'react-router-dom'; +import loadInitialProps from './loadInitialProps'; + +class App extends React.Component { + constructor(props) { + super(props); + this.state = { + data: props.data, + previousLocation: null, + }; + this.prefetcherCache = {}; + } + + // only runs clizzient + componentWillReceiveProps(nextProps, nextState) { + const navigated = nextProps.location !== this.props.location; + if (navigated) { + window.scrollTo(0, 0); + // save the location so we can render the old screen + this.setState({ + previousLocation: this.props.location, + data: undefined, // unless you want to keep it + }); + loadInitialProps(this.props.routes, nextProps.location.pathname, { + location: nextProps.location, + history: nextProps.history, + }) + .then(data => { + this.setState({ previousLocation: null, data: data[0] }); + }) + .catch(e => { + // @todo we should more cleverly handle errors??? + console.log(e); + }); + } + } + + prefetch = pathname => { + loadInitialProps(this.props.routes, pathname, { + history: this.props.history, + }) + .then(data => { + this.prefetcherCache = { ...this.prefetcherCache, [pathname]: data[0] }; + }) + .catch(e => console.log(e)); + }; + + updateData = data => { + this.setState({ data }); + }; + + render() { + const { previousLocation, data } = this.state; + const { location, history, match } = this.props; + const initialData = this.prefetcherCache[location.pathname] + ? this.prefetcherCache[location.pathname] + : data; + + return ( + + {this.props.routes.map((r, i) => ( + { + return React.createElement(r.component, { + ...initialData, + history, + location: previousLocation || location, + match, + prefetch: this.prefetch, + }); + }} + /> + ))} + + ); + } +} + +export default withRouter(App); diff --git a/examples/with-afterjs/lib/_document.js b/examples/with-afterjs/lib/_document.js new file mode 100644 index 000000000..3b4f2ae5e --- /dev/null +++ b/examples/with-afterjs/lib/_document.js @@ -0,0 +1,49 @@ +import React from 'react'; + +class Document extends React.Component { + static getInitialProps({ assets, data, renderPage }) { + const page = renderPage(); + return { assets, data, ...page }; + } + + render() { + const { helmet, assets, data } = this.props; + // get attributes from React Helmet + const htmlAttrs = helmet.htmlAttributes.toComponent(); + const bodyAttrs = helmet.bodyAttributes.toComponent(); + + return ( + + + + + Welcome to the Afterparty + + {helmet.title.toComponent()} + {helmet.meta.toComponent()} + {helmet.link.toComponent()} + {assets.client.css && ( + + )} + + +
DO_NOT_DELETE_THIS_YOU_WILL_BREAK_YOUR_APP
+