diff --git a/packages/generator-fluxible/README.md b/packages/generator-fluxible/README.md index 2e30552d..932f46ac 100644 --- a/packages/generator-fluxible/README.md +++ b/packages/generator-fluxible/README.md @@ -20,21 +20,19 @@ Finally, initiate the generator: yo fluxible ``` -During development, execute `npm run dev` to initiate webpack-dev-server -(with react-hot-loader support) and your application's server using nodemon. -Browse to `http://localhost:3000` to see a very simple Fluxible site with +During development, execute `npm run dev` to initiate webpack-dev-server +(with react-hot-loader support) and your application's server using nodemon. +Browse to `http://localhost:3000` to see a very simple Fluxible site with server-side rendering and client-side navigation. When you change files, -the client will be hot-reloaded (with the exception of stores) and your -application server will restart so that you can see the server-side changes -on the next refresh. +the server will be reloaded and the bundle will be rebuilt. -For other environments, make sure your application is built using +For other environments, make sure your application is built using `npm run build` and then run `npm start`. ## Debugging -Fluxible uses [debug](https://www.npmjs.com/package/debug) to expose debugging -information on the server and client. +Fluxible uses [debug](https://www.npmjs.com/package/debug) to expose debugging +information on the server and client. ### Server @@ -42,6 +40,6 @@ Start the application with the `DEBUG` environment variable: `DEBUG=* grunt`. ### Client -`fluxibleDebug` is exposed to the `window` object to manage debugging. You can -enable it via the browser console: `fluxibleDebug.enable('*');` then refresh +`fluxibleDebug` is exposed to the `window` object to manage debugging. You can +enable it via the browser console: `fluxibleDebug.enable('*');` then refresh the page. To disable, type the following: `fluxibleDebug.disable();`. diff --git a/packages/generator-fluxible/app/index.js b/packages/generator-fluxible/app/index.js index 776b7ab6..1bc65149 100644 --- a/packages/generator-fluxible/app/index.js +++ b/packages/generator-fluxible/app/index.js @@ -41,11 +41,9 @@ module.exports = yeoman.generators.Base.extend({ writing: { config: function () { - this.template('.babelrc', '.babelrc', this.context); - this.template('.editorconfig', '.editorconfig', this.context); + this.template('babel.config.js', 'babel.config.js', this.context); // .gitignore is renamed by npm to .npmignore, so use underscore this.template('_gitignore', '.gitignore', this.context); - this.template('.eslintrc', '.eslintrc', this.context); this.template('package.json', 'package.json', this.context); }, @@ -53,16 +51,11 @@ module.exports = yeoman.generators.Base.extend({ this.template('app.js', 'app.js', this.context); this.template('client.js', 'client.js', this.context); this.template('server.js', 'server.js', this.context); - this.template('start.js', 'start.js', this.context); this.template('webpack.config.js', 'webpack.config.js', this.context); - this.template('webpack.config.production.js', 'webpack.config.production.js', this.context); - this.template('webpack-dev-server.js', 'webpack-dev-server.js', this.context); this.directory('actions', 'actions', this.context); this.directory('components', 'components', this.context); this.directory('configs', 'configs', this.context); this.directory('stores', 'stores', this.context); - // Webpack dev server needs this folder to exist before starting - this.template('_buildgitignore', 'build/js/.gitignore', this.context); } }, diff --git a/packages/generator-fluxible/app/templates/.babelrc b/packages/generator-fluxible/app/templates/.babelrc deleted file mode 100644 index d4e5f8d4..00000000 --- a/packages/generator-fluxible/app/templates/.babelrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "presets": ["es2015", "react"] -} diff --git a/packages/generator-fluxible/app/templates/.editorconfig b/packages/generator-fluxible/app/templates/.editorconfig deleted file mode 100644 index 2536d66b..00000000 --- a/packages/generator-fluxible/app/templates/.editorconfig +++ /dev/null @@ -1,12 +0,0 @@ -# http://editorconfig.org -root = true - -[*] -indent_style = space -indent_size = 4 -charset = utf-8 -trim_trailing_whitespace = true -insert_final_newline = true - -[*.md] -trim_trailing_whitespace = false diff --git a/packages/generator-fluxible/app/templates/.eslintrc b/packages/generator-fluxible/app/templates/.eslintrc deleted file mode 100644 index 0cbafa9d..00000000 --- a/packages/generator-fluxible/app/templates/.eslintrc +++ /dev/null @@ -1,7 +0,0 @@ ---- - parser: "babel-eslint" - env: - node: true - rules: - indent: [2, 4] - quotes: [2, 'single'] diff --git a/packages/generator-fluxible/app/templates/_buildgitignore b/packages/generator-fluxible/app/templates/_buildgitignore deleted file mode 100644 index 72e8ffc0..00000000 --- a/packages/generator-fluxible/app/templates/_buildgitignore +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/packages/generator-fluxible/app/templates/_gitignore b/packages/generator-fluxible/app/templates/_gitignore index eb03e3e1..6b3405c3 100644 --- a/packages/generator-fluxible/app/templates/_gitignore +++ b/packages/generator-fluxible/app/templates/_gitignore @@ -1,2 +1,3 @@ +dist node_modules *.log diff --git a/packages/generator-fluxible/app/templates/app.js b/packages/generator-fluxible/app/templates/app.js index 015d8ee7..6d3c046d 100644 --- a/packages/generator-fluxible/app/templates/app.js +++ b/packages/generator-fluxible/app/templates/app.js @@ -5,11 +5,11 @@ import RouteStore from './stores/RouteStore'; // create new fluxible instance const app = new Fluxible({ - component: Application + component: Application, }); // register stores app.registerStore(RouteStore); app.registerStore(ApplicationStore); -module.exports = app; +export default app; diff --git a/packages/generator-fluxible/app/templates/babel.config.js b/packages/generator-fluxible/app/templates/babel.config.js new file mode 100644 index 00000000..5207fc11 --- /dev/null +++ b/packages/generator-fluxible/app/templates/babel.config.js @@ -0,0 +1,14 @@ +const isNodeTarget = (api) => + api.caller((caller) => caller && caller.target === 'node'); + +module.exports = (api) => ({ + presets: [ + [ + '@babel/preset-env', + { + targets: isNodeTarget(api) ? { node: 'current' } : 'defaults', + }, + ], + '@babel/preset-react', + ], +}); diff --git a/packages/generator-fluxible/app/templates/client.js b/packages/generator-fluxible/app/templates/client.js index 6836bcf0..280d42be 100644 --- a/packages/generator-fluxible/app/templates/client.js +++ b/packages/generator-fluxible/app/templates/client.js @@ -25,9 +25,7 @@ app.rehydrate(dehydratedState, (err, context) => { const mountNode = document.getElementById('app'); debugClient('React Rendering'); - ReactDOM.render( - createElementWithContext(context), - mountNode, - () => debugClient('React Rendered') + ReactDOM.hydrate(createElementWithContext(context), mountNode, () => + debugClient('React Rendered') ); }); diff --git a/packages/generator-fluxible/app/templates/components/Html.js b/packages/generator-fluxible/app/templates/components/Html.js index b37f1b13..942560b3 100644 --- a/packages/generator-fluxible/app/templates/components/Html.js +++ b/packages/generator-fluxible/app/templates/components/Html.js @@ -2,29 +2,33 @@ import React from 'react'; import PropTypes from 'prop-types'; import ApplicationStore from '../stores/ApplicationStore'; -function Html(props) { - return ( - - - - {props.context.getStore(ApplicationStore).getPageTitle()} - - - - -
- - - - - ); -} +const Html = ({ context, markup, state, clientFile }) => ( + + + + {context.getStore(ApplicationStore).getPageTitle()} + + + + +
+ + + + +); Html.propTypes = { clientFile: PropTypes.string, context: PropTypes.object, markup: PropTypes.string, - state: PropTypes.string + state: PropTypes.string, }; export default Html; diff --git a/packages/generator-fluxible/app/templates/package.json b/packages/generator-fluxible/app/templates/package.json index b5cb5146..a6f93a72 100644 --- a/packages/generator-fluxible/app/templates/package.json +++ b/packages/generator-fluxible/app/templates/package.json @@ -1,48 +1,36 @@ { - "name": "<%= name %>", - "version": "0.0.0", - "private": true, - "main": "start.js", - "scripts": { - "build": "webpack & webpack --config webpack.config.production.js", - "dev": "node webpack-dev-server.js", - "lint": "eslint ./*.js ./**/*.js", - "start": "node start.js" - }, - "dependencies": { - "babel": "^6.5.2", - "babel-core": "^6.6.4", - "babel-preset-es2015": "^6.6.0", - "babel-preset-react": "^6.5.0", - "babel-register": "^6.6.0", - "body-parser": "^1.6.4", - "compression": "^1.5.1", - "cookie-parser": "^1.3.3", - "csurf": "^1.6.3", - "debug": "^2.0.0", - "express": "^4.3.2", - "express-state": "^1.2.0", - "fluxible": "^1.0.0", - "fluxible-addons-react": "^0.2.0", - "fluxible-plugin-fetchr": "^0.3.0", - "fluxible-router": "^0.4.0", - "react": "^16.0.0", - "react-dom": "^16.0.0", - "serialize-javascript": "^4.0.0", - "serve-favicon": "^2.1.6" - }, - "devDependencies": { - "babel-eslint": "^10.0.0", - "babel-loader": "^6.2.4", - "bundle-loader": "^0.5.0", - "eslint": "^6.0.0", - "eslint-plugin-babel": "^5.0.0", - "eslint-plugin-react": "^7.0.0", - "json-loader": "^0.5.1", - "nodemon": "^1.2.1", - "react-hot-loader": "^1.2.8", - "shelljs": "^0.6.0", - "webpack": "^1.12.4", - "webpack-dev-server": "^1.6.5" - } + "name": "<%= name %>", + "version": "0.0.0", + "private": true, + "main": "dist/server.js", + "scripts": { + "build": "NODE_ENV=production webpack", + "dev": "npm run dev:browser & npm run dev:server", + "dev:browser": "NODE_ENV=development webpack --watch --no-stats", + "dev:server": "NODE_ENV=development nodemon", + "start": "node ." + }, + "dependencies": { + "body-parser": "^1.19.0", + "compression": "^1.7.4", + "debug": "^4.3.1", + "express": "^4.17.1", + "fluxible": "^1.0.0", + "fluxible-addons-react": "^1.0.0-beta.1", + "fluxible-plugin-fetchr": "^0.3.11", + "fluxible-router": "^2.0.0-beta.9.0", + "react": "^17.0.2", + "react-dom": "^17.0.2", + "serialize-javascript": "^5.0.1" + }, + "devDependencies": { + "@babel/core": "^7.13.16", + "@babel/preset-env": "^7.13.15", + "@babel/preset-react": "^7.13.13", + "babel-loader": "^8.2.2", + "nodemon": "^2.0.7", + "webpack": "^5.36.1", + "webpack-cli": "^4.6.0", + "webpack-node-externals": "^3.0.0" + } } diff --git a/packages/generator-fluxible/app/templates/server.js b/packages/generator-fluxible/app/templates/server.js index 07906e2c..415a5c9c 100644 --- a/packages/generator-fluxible/app/templates/server.js +++ b/packages/generator-fluxible/app/templates/server.js @@ -10,10 +10,10 @@ import compression from 'compression'; import bodyParser from 'body-parser'; import path from 'path'; import serialize from 'serialize-javascript'; -import {navigateAction} from 'fluxible-router'; +import { navigateAction } from 'fluxible-router'; import debugLib from 'debug'; import React from 'react'; -import ReactDOM from 'react-dom/server'; +import ReactDOMServer from 'react-dom/server'; import app from './app'; import HtmlComponent from './components/Html'; import { createElementWithContext } from 'fluxible-addons-react'; @@ -22,7 +22,7 @@ const env = process.env.NODE_ENV; const debug = debugLib('<%= name %>'); const server = express(); -server.use('/public', express['static'](path.join(__dirname, '/build'))); +server.use('/public', express['static'](path.join(__dirname, 'public'))); server.use(compression()); server.use(bodyParser.json()); @@ -30,37 +30,44 @@ server.use((req, res, next) => { const context = app.createContext(); debug('Executing navigate action'); - context.getActionContext().executeAction(navigateAction, { - url: req.url - }, (err) => { - if (err) { - if (err.statusCode && err.statusCode === 404) { - // Pass through to next middleware - next(); - } else { - next(err); + context.getActionContext().executeAction( + navigateAction, + { + url: req.url, + }, + (err) => { + if (err) { + if (err.statusCode && err.statusCode === 404) { + // Pass through to next middleware + next(); + } else { + next(err); + } + return; } - return; - } - debug('Exposing context state'); - const exposed = 'window.App=' + serialize(app.dehydrate(context)) + ';'; + debug('Exposing context state'); + const exposed = + 'window.App=' + serialize(app.dehydrate(context)) + ';'; - debug('Rendering Application component into html'); - const markup = ReactDOM.renderToString(createElementWithContext(context)); - const htmlElement = React.createElement(HtmlComponent, { - clientFile: env === 'production' ? 'main.min.js' : 'main.js', - context: context.getComponentContext(), - state: exposed, - markup: markup - }); - const html = ReactDOM.renderToStaticMarkup(htmlElement); + debug('Rendering Application component into html'); + const markup = ReactDOMServer.renderToString( + createElementWithContext(context) + ); + const htmlElement = React.createElement(HtmlComponent, { + clientFile: env === 'production' ? 'main.min.js' : 'main.js', + context: context.getComponentContext(), + state: exposed, + markup: markup, + }); + const html = ReactDOMServer.renderToStaticMarkup(htmlElement); - debug('Sending markup'); - res.type('html'); - res.write('' + html); - res.end(); - }); + debug('Sending markup'); + res.type('html'); + res.write('' + html); + res.end(); + } + ); }); const port = process.env.PORT || 3000; diff --git a/packages/generator-fluxible/app/templates/start.js b/packages/generator-fluxible/app/templates/start.js deleted file mode 100644 index 22fa4d87..00000000 --- a/packages/generator-fluxible/app/templates/start.js +++ /dev/null @@ -1,3 +0,0 @@ -require('babel-register'); - -module.exports = require('./server'); diff --git a/packages/generator-fluxible/app/templates/webpack-dev-server.js b/packages/generator-fluxible/app/templates/webpack-dev-server.js deleted file mode 100644 index f30be00d..00000000 --- a/packages/generator-fluxible/app/templates/webpack-dev-server.js +++ /dev/null @@ -1,18 +0,0 @@ -var webpack = require('webpack'); -var WebpackDevServer = require('webpack-dev-server'); -var config = require('./webpack.config'); -var shell = require('shelljs'); - -new WebpackDevServer(webpack(config), { - publicPath: config.output.publicPath, - hot: true, - historyApiFallback: true, - //quiet: true, - proxy: { - '*': { target: 'http://localhost:3001' } - } -}).listen(3000, function () { - shell.env.PORT = shell.env.PORT || 3001; - shell.exec('"./node_modules/.bin/nodemon" start.js -e js,jsx', function () {}); - console.log('Webpack Dev Server listening on port 3000'); -}); diff --git a/packages/generator-fluxible/app/templates/webpack.config.js b/packages/generator-fluxible/app/templates/webpack.config.js index 3c3b0539..a85c20d7 100644 --- a/packages/generator-fluxible/app/templates/webpack.config.js +++ b/packages/generator-fluxible/app/templates/webpack.config.js @@ -1,46 +1,42 @@ -var webpack = require('webpack'); -var path = require('path'); +const path = require('path'); +const nodeExternals = require('webpack-node-externals'); -var webpackConfig = { - resolve: { - extensions: ['', '.js', '.jsx'] - }, - entry: [ - 'webpack-dev-server/client?http://localhost:3000', - 'webpack/hot/only-dev-server', - './client.js' - ], - output: { - path: path.resolve('./build/js'), - publicPath: '/public/js/', - filename: 'main.js' - }, +const isProduction = process.env.NODE_ENV !== 'development'; + +const commonConfig = { + mode: isProduction ? 'production' : 'development', module: { - loaders: [ + rules: [ { - test: /\.(js|jsx)$/, + test: /\.m?js$/, exclude: /node_modules/, - loaders: [ - require.resolve('react-hot-loader'), - require.resolve('babel-loader') - ] + use: { loader: 'babel-loader' }, }, - { test: /\.json$/, loader: 'json-loader'} - ] + ], }, - node: { - setImmediate: false +}; + +const browserConfig = { + ...commonConfig, + target: 'web', + entry: './client.js', + output: { + filename: isProduction ? 'main.min.js' : 'main.js', + path: path.resolve('./dist/public'), + publicPath: '/public/', + }, +}; + +const serverConfig = { + ...commonConfig, + target: 'node', + entry: './server.js', + output: { + filename: 'server.js', + path: path.resolve(__dirname, 'dist'), }, - plugins: [ - new webpack.HotModuleReplacementPlugin(), - new webpack.NoErrorsPlugin(), - new webpack.DefinePlugin({ - 'process.env': { - NODE_ENV: JSON.stringify(process.env.NODE_ENV) - } - }) - ], - devtool: 'eval' + externals: [nodeExternals()], + externalsPresets: { node: true }, }; -module.exports = webpackConfig; +module.exports = [browserConfig, serverConfig]; diff --git a/packages/generator-fluxible/app/templates/webpack.config.production.js b/packages/generator-fluxible/app/templates/webpack.config.production.js deleted file mode 100644 index 1411cd70..00000000 --- a/packages/generator-fluxible/app/templates/webpack.config.production.js +++ /dev/null @@ -1,47 +0,0 @@ -var webpack = require('webpack'); -var path = require('path'); - -var webpackConfig = { - resolve: { - extensions: ['', '.js'] - }, - entry: [ - './client.js' - ], - output: { - path: path.resolve('./build/js'), - publicPath: '/public/js/', - filename: 'main.min.js' - }, - module: { - loaders: [ - { - test: /\.(js|jsx)$/, - exclude: /node_modules/, - loaders: [ - require.resolve('babel-loader') - ] - }, - { test: /\.json$/, loader: 'json-loader'} - ] - }, - node: { - setImmediate: false - }, - plugins: [ - new webpack.DefinePlugin({ - 'process.env': { - NODE_ENV: JSON.stringify('production') - } - }), - new webpack.optimize.DedupePlugin(), - new webpack.optimize.UglifyJsPlugin({ - compress: { - warnings: false - } - }) - ], - devtool: 'source-map' -}; - -module.exports = webpackConfig; diff --git a/packages/generator-fluxible/package.json b/packages/generator-fluxible/package.json index 59f2e6e5..4cba3185 100644 --- a/packages/generator-fluxible/package.json +++ b/packages/generator-fluxible/package.json @@ -8,9 +8,6 @@ "name": "Seth Bertalotto", "email": "sbertal@yahoo-inc.com" }, - "engines": { - "node": ">=0.10.0" - }, "scripts": { "cover": "BABEL_ENV=test ../../node_modules/.bin/nyc ../../node_modules/.bin/_mocha tests/unit --recursive --reporter spec", "lint": "../../node_modules/.bin/eslint app/ tests/", diff --git a/packages/generator-fluxible/tests/unit/test-app.js b/packages/generator-fluxible/tests/unit/test-app.js index f139a24d..49d2a893 100644 --- a/packages/generator-fluxible/tests/unit/test-app.js +++ b/packages/generator-fluxible/tests/unit/test-app.js @@ -12,7 +12,8 @@ var os = require('os'); describe('generator-fluxible', function () { describe('app', function () { before(function (done) { - helpers.run(path.join(__dirname, '../../app')) + helpers + .run(path.join(__dirname, '../../app')) .inDir(path.join(os.tmpdir(), './temp-test')) .withOptions({ 'skip-install': true }) .on('end', done); @@ -21,11 +22,9 @@ describe('generator-fluxible', function () { it('creates files', function () { assert.file([ 'package.json', - '.editorconfig', - '.babelrc', - '.eslintrc', + 'babel.config.js', 'app.js', - 'components/Application.js' + 'components/Application.js', ]); }); });