diff --git a/gulpfile.js b/gulpfile.js index 8676a54d2..daff4cf9b 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -30,7 +30,7 @@ const runAppTest = (dir, forceLocal) => { const appPkgFile = `${dir}/package.json`; let appPkgData; - if (forceLocal || process.env.BUILD_TEST) { + if (forceLocal || process.env.BUILD_TEST || process.env.CI) { appPkgData = fs.readFileSync(appPkgFile).toString(); const appPkg = JSON.parse(appPkgData); updateToLocalPkgs(appPkg["dependencies"], localPkgs); diff --git a/packages/electrode-archetype-react-app-dev/config/archetype.js b/packages/electrode-archetype-react-app-dev/config/archetype.js new file mode 100644 index 000000000..956784dbc --- /dev/null +++ b/packages/electrode-archetype-react-app-dev/config/archetype.js @@ -0,0 +1 @@ +module.exports = require("electrode-archetype-react-app/config/archetype"); diff --git a/packages/electrode-archetype-react-app-dev/config/babel/babelrc-client b/packages/electrode-archetype-react-app-dev/config/babel/babelrc-client new file mode 100644 index 000000000..8e0e37a61 --- /dev/null +++ b/packages/electrode-archetype-react-app-dev/config/babel/babelrc-client @@ -0,0 +1,23 @@ +{ + "presets": [ + "es2015", + "es2015-loose", + "react" + ], + "plugins": [ + "babel-plugin-transform-object-rest-spread", + ["babel-plugin-react-intl", { + "messagesDir": "./tmp/messages/", + "enforceDescriptions": true + }], + "lodash", + "transform-runtime" + ], + "env": { + "production": { + "plugins": [ + "babel-plugin-transform-react-constant-elements" + ] + } + } +} diff --git a/packages/electrode-archetype-react-app-dev/config/babel/babelrc-server b/packages/electrode-archetype-react-app-dev/config/babel/babelrc-server new file mode 100644 index 000000000..7c19bb4b6 --- /dev/null +++ b/packages/electrode-archetype-react-app-dev/config/babel/babelrc-server @@ -0,0 +1,5 @@ +{ + "presets": [ + "es2015-node" + ] +} diff --git a/packages/electrode-archetype-react-app/config/browser_test/test.html b/packages/electrode-archetype-react-app-dev/config/browser_test/test.html similarity index 100% rename from packages/electrode-archetype-react-app/config/browser_test/test.html rename to packages/electrode-archetype-react-app-dev/config/browser_test/test.html diff --git a/packages/electrode-archetype-react-app/config/eslint/.eslintrc-base b/packages/electrode-archetype-react-app-dev/config/eslint/.eslintrc-base similarity index 100% rename from packages/electrode-archetype-react-app/config/eslint/.eslintrc-base rename to packages/electrode-archetype-react-app-dev/config/eslint/.eslintrc-base diff --git a/packages/electrode-archetype-react-app/config/eslint/.eslintrc-mocha-test b/packages/electrode-archetype-react-app-dev/config/eslint/.eslintrc-mocha-test similarity index 100% rename from packages/electrode-archetype-react-app/config/eslint/.eslintrc-mocha-test rename to packages/electrode-archetype-react-app-dev/config/eslint/.eslintrc-mocha-test diff --git a/packages/electrode-archetype-react-app/config/eslint/.eslintrc-node b/packages/electrode-archetype-react-app-dev/config/eslint/.eslintrc-node similarity index 100% rename from packages/electrode-archetype-react-app/config/eslint/.eslintrc-node rename to packages/electrode-archetype-react-app-dev/config/eslint/.eslintrc-node diff --git a/packages/electrode-archetype-react-app/config/eslint/.eslintrc-react b/packages/electrode-archetype-react-app-dev/config/eslint/.eslintrc-react similarity index 100% rename from packages/electrode-archetype-react-app/config/eslint/.eslintrc-react rename to packages/electrode-archetype-react-app-dev/config/eslint/.eslintrc-react diff --git a/packages/electrode-archetype-react-app/config/eslint/.eslintrc-react-demo b/packages/electrode-archetype-react-app-dev/config/eslint/.eslintrc-react-demo similarity index 100% rename from packages/electrode-archetype-react-app/config/eslint/.eslintrc-react-demo rename to packages/electrode-archetype-react-app-dev/config/eslint/.eslintrc-react-demo diff --git a/packages/electrode-archetype-react-app/config/eslint/.eslintrc-react-test b/packages/electrode-archetype-react-app-dev/config/eslint/.eslintrc-react-test similarity index 100% rename from packages/electrode-archetype-react-app/config/eslint/.eslintrc-react-test rename to packages/electrode-archetype-react-app-dev/config/eslint/.eslintrc-react-test diff --git a/packages/electrode-archetype-react-app/config/gitignore/.gitignore b/packages/electrode-archetype-react-app-dev/config/gitignore/.gitignore similarity index 100% rename from packages/electrode-archetype-react-app/config/gitignore/.gitignore rename to packages/electrode-archetype-react-app-dev/config/gitignore/.gitignore diff --git a/packages/electrode-archetype-react-app/config/istanbul/.istanbul.50.yml b/packages/electrode-archetype-react-app-dev/config/istanbul/.istanbul.50.yml similarity index 100% rename from packages/electrode-archetype-react-app/config/istanbul/.istanbul.50.yml rename to packages/electrode-archetype-react-app-dev/config/istanbul/.istanbul.50.yml diff --git a/packages/electrode-archetype-react-app/config/istanbul/.istanbul.70.yml b/packages/electrode-archetype-react-app-dev/config/istanbul/.istanbul.70.yml similarity index 100% rename from packages/electrode-archetype-react-app/config/istanbul/.istanbul.70.yml rename to packages/electrode-archetype-react-app-dev/config/istanbul/.istanbul.70.yml diff --git a/packages/electrode-archetype-react-app/config/istanbul/.istanbul.85.yml b/packages/electrode-archetype-react-app-dev/config/istanbul/.istanbul.85.yml similarity index 100% rename from packages/electrode-archetype-react-app/config/istanbul/.istanbul.85.yml rename to packages/electrode-archetype-react-app-dev/config/istanbul/.istanbul.85.yml diff --git a/packages/electrode-archetype-react-app/config/istanbul/.istanbul.95.yml b/packages/electrode-archetype-react-app-dev/config/istanbul/.istanbul.95.yml similarity index 100% rename from packages/electrode-archetype-react-app/config/istanbul/.istanbul.95.yml rename to packages/electrode-archetype-react-app-dev/config/istanbul/.istanbul.95.yml diff --git a/packages/electrode-archetype-react-app/config/istanbul/.istanbul.yml b/packages/electrode-archetype-react-app-dev/config/istanbul/.istanbul.yml similarity index 100% rename from packages/electrode-archetype-react-app/config/istanbul/.istanbul.yml rename to packages/electrode-archetype-react-app-dev/config/istanbul/.istanbul.yml diff --git a/packages/electrode-archetype-react-app/config/karma/entry.js b/packages/electrode-archetype-react-app-dev/config/karma/entry.js similarity index 94% rename from packages/electrode-archetype-react-app/config/karma/entry.js rename to packages/electrode-archetype-react-app-dev/config/karma/entry.js index 5416f66ef..1863e6922 100644 --- a/packages/electrode-archetype-react-app/config/karma/entry.js +++ b/packages/electrode-archetype-react-app-dev/config/karma/entry.js @@ -42,6 +42,7 @@ window.mocha.setup({ // -------------------------------------------------------------------------- // Use webpack to include all app code _except_ the entry point so we can get // code coverage in the bundle, whether tested or not. +// NOTE: No need to specify src even in src mode since webpack should handle that already var srcReq = require.context("client", true, /^((?!app).)*\.jsx?$/); srcReq.keys().map(srcReq); diff --git a/packages/electrode-archetype-react-app/config/karma/karma.conf.coverage.js b/packages/electrode-archetype-react-app-dev/config/karma/karma.conf.coverage.js similarity index 91% rename from packages/electrode-archetype-react-app/config/karma/karma.conf.coverage.js rename to packages/electrode-archetype-react-app-dev/config/karma/karma.conf.coverage.js index c7b34aae2..dd0d28bd4 100644 --- a/packages/electrode-archetype-react-app/config/karma/karma.conf.coverage.js +++ b/packages/electrode-archetype-react-app-dev/config/karma/karma.conf.coverage.js @@ -20,7 +20,7 @@ module.exports = function (config) { { type: "lcov" }, { type: "text" } ], - dir: path.join(process.cwd(), "coverage/client") + dir: path.resolve("coverage", "client") } }); }; diff --git a/packages/electrode-archetype-react-app/config/karma/karma.conf.dev.js b/packages/electrode-archetype-react-app-dev/config/karma/karma.conf.dev.js similarity index 100% rename from packages/electrode-archetype-react-app/config/karma/karma.conf.dev.js rename to packages/electrode-archetype-react-app-dev/config/karma/karma.conf.dev.js diff --git a/packages/electrode-archetype-react-app/config/karma/karma.conf.js b/packages/electrode-archetype-react-app-dev/config/karma/karma.conf.js similarity index 93% rename from packages/electrode-archetype-react-app/config/karma/karma.conf.js rename to packages/electrode-archetype-react-app-dev/config/karma/karma.conf.js index 255bbe6fe..856e5a3b5 100644 --- a/packages/electrode-archetype-react-app/config/karma/karma.conf.js +++ b/packages/electrode-archetype-react-app-dev/config/karma/karma.conf.js @@ -1,5 +1,5 @@ "use strict"; -var Path = require("path"); +var path = require("path"); var webpackCfg = require("../webpack/webpack.config.test"); @@ -46,7 +46,7 @@ module.exports = function (config) { { type: "lcov" }, { type: "text" } ], - dir: Path.join(process.cwd(), "coverage/client") + dir: path.resolve("coverage", "client") }, captureTimeout: 100000, singleRun: true diff --git a/packages/electrode-archetype-react-app/config/karma/karma.conf.watch.js b/packages/electrode-archetype-react-app-dev/config/karma/karma.conf.watch.js similarity index 96% rename from packages/electrode-archetype-react-app/config/karma/karma.conf.watch.js rename to packages/electrode-archetype-react-app-dev/config/karma/karma.conf.watch.js index 1ca75dbaa..69117642e 100644 --- a/packages/electrode-archetype-react-app/config/karma/karma.conf.watch.js +++ b/packages/electrode-archetype-react-app-dev/config/karma/karma.conf.watch.js @@ -18,7 +18,7 @@ module.exports = function (config) { // Watch these files but do not add them to the bundle. ].concat( [ - "client/**", + "src/client/**", "test/**" ].map(function (pattern) { return { @@ -32,4 +32,3 @@ module.exports = function (config) { }); }; - diff --git a/packages/electrode-archetype-react-app-dev/config/mocha/mocha.opts b/packages/electrode-archetype-react-app-dev/config/mocha/mocha.opts new file mode 100644 index 000000000..7e60ebc1b --- /dev/null +++ b/packages/electrode-archetype-react-app-dev/config/mocha/mocha.opts @@ -0,0 +1,4 @@ +--require electrode-archetype-react-app-dev/config/mocha/setup.js +--reporter spec +--recursive +--ui bdd diff --git a/packages/electrode-archetype-react-app/config/mocha/setup.js b/packages/electrode-archetype-react-app-dev/config/mocha/setup.js similarity index 100% rename from packages/electrode-archetype-react-app/config/mocha/setup.js rename to packages/electrode-archetype-react-app-dev/config/mocha/setup.js diff --git a/packages/electrode-archetype-react-app/config/webpack/add-dll-references.js b/packages/electrode-archetype-react-app-dev/config/webpack/add-dll-references.js similarity index 73% rename from packages/electrode-archetype-react-app/config/webpack/add-dll-references.js rename to packages/electrode-archetype-react-app-dev/config/webpack/add-dll-references.js index 9869fe548..d20a89a7a 100644 --- a/packages/electrode-archetype-react-app/config/webpack/add-dll-references.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/add-dll-references.js @@ -1,16 +1,16 @@ "use strict"; -var archetype = require("../archetype"); var _ = require("lodash"); var fs = require("fs"); -var path = require("path"); -var glob = archetype.devRequire("glob"); +var glob = require("glob"); var webpack = require("webpack"); +var archetype = require("../archetype"); +var Path = archetype.Path; module.exports = function (config) { try { - var exists = fs.accessSync(path.join(process.cwd(), "client/dll.config.js")); - var filenames = glob.sync(path.join(process.cwd(), "dll/js/*-manifest.*.json")); + var exists = fs.existsSync(Path.resolve(archetype.AppMode.src.client, "dll.config.js")); + var filenames = glob.sync(Path.resolve("dll", "js", "*-manifest.*.json")); if (exists && filenames.length) { config.plugins = _.concat(config.plugins || [], filenames.map(function (filename) { diff --git a/packages/electrode-archetype-react-app/config/webpack/base-test.js b/packages/electrode-archetype-react-app-dev/config/webpack/base-test.js similarity index 83% rename from packages/electrode-archetype-react-app/config/webpack/base-test.js rename to packages/electrode-archetype-react-app-dev/config/webpack/base-test.js index abd1e831d..ef5377cc4 100644 --- a/packages/electrode-archetype-react-app/config/webpack/base-test.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/base-test.js @@ -1,8 +1,7 @@ "use strict"; - var archetype = require("../archetype"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; -var Path = archetype.PlatformPath; +var mergeWebpackConfig = require("webpack-partial").default; +var Path = archetype.Path; var baseConfig = require("./base.js"); @@ -19,7 +18,7 @@ var testConfig = { /node_modules\/sinon\// ] }, - context: Path.join(process.cwd(), "test/client"), + context: Path.resolve("test", "client"), debug: false, devServer: { stats: "errors-only" // only show errors @@ -34,7 +33,7 @@ var testConfig = { alias: { // Allow root import of `src/FOO` from ROOT/src. src: process.cwd(), - sinon: archetype.devRequire.resolve("sinon/pkg/sinon") + sinon: require.resolve("sinon/pkg/sinon") } }, // Enzyme depends jsdom and cheerio being global to render their DOM. diff --git a/packages/electrode-archetype-react-app/config/webpack/base.js b/packages/electrode-archetype-react-app-dev/config/webpack/base.js similarity index 73% rename from packages/electrode-archetype-react-app/config/webpack/base.js rename to packages/electrode-archetype-react-app-dev/config/webpack/base.js index 50599df5c..53344ba01 100644 --- a/packages/electrode-archetype-react-app/config/webpack/base.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/base.js @@ -2,9 +2,7 @@ var _ = require("lodash"); var fs = require("fs"); -var archetype = require("../archetype"); -var Path = archetype.PlatformPath; -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; +var mergeWebpackConfig = require("webpack-partial").default; // config partials var babelConfig = require("./partial/babel"); @@ -15,13 +13,15 @@ var statsConfig = require("./partial/stats"); var isomorphicConfig = require("./partial/isomorphic"); var jsonConfig = require("./partial/json"); var pwaConfig = require("./partial/pwa"); +var archetype = require("../archetype"); +var Path = archetype.Path; +var AppMode = archetype.AppMode; +var context = Path.resolve(AppMode.src.client); -var archetypeNodeModules = Path.join(__dirname, "../../node_modules"); -var archetypeDevNodeModules = Path.join(archetype.devPath, "node_modules"); +var archetypeNodeModules = Path.join(archetype.dir, "node_modules"); +var archetypeDevNodeModules = Path.join(archetype.devDir, "node_modules"); var inspectpack = process.env.INSPECTPACK_DEBUG === "true"; -var context = Path.resolve(archetype.clientSrcDir); - /* eslint-disable func-style */ /* @@ -57,7 +57,7 @@ var baseConfig = { debug: false, entry: entry, output: { - path: Path.resolve("dist/js"), + path: Path.resolve("dist", "js"), pathinfo: inspectpack, // Enable path information for inspectpack publicPath: "/js/", chunkFilename: "[hash].[name].js", @@ -66,12 +66,22 @@ var baseConfig = { : "bundle.[hash].js" }, resolve: { - root: [archetypeNodeModules, archetypeDevNodeModules, process.cwd()], - modulesDirectories: ["client", "node_modules"].concat(archetype.webpack.modulesDirectories), + root: [ + archetypeNodeModules, + archetypeDevNodeModules, + AppMode.isSrc && Path.resolve(AppMode.src.dir) || null, + process.cwd() + ].filter((x) => x), + modulesDirectories: ["node_modules"].concat(archetype.webpack.modulesDirectories), extensions: ["", ".js", ".jsx"] }, resolveLoader: { - root: [archetypeNodeModules, archetypeDevNodeModules, process.cwd()] + root: [ + archetypeNodeModules, + archetypeDevNodeModules, + Path.resolve("lib"), + process.cwd() + ].filter((x) => x) } }; diff --git a/packages/electrode-archetype-react-app/config/webpack/get-root-config.js b/packages/electrode-archetype-react-app-dev/config/webpack/get-root-config.js similarity index 69% rename from packages/electrode-archetype-react-app/config/webpack/get-root-config.js rename to packages/electrode-archetype-react-app-dev/config/webpack/get-root-config.js index 180b913ac..691cb5216 100644 --- a/packages/electrode-archetype-react-app/config/webpack/get-root-config.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/get-root-config.js @@ -10,9 +10,7 @@ module.exports = function (rootConfigFileName) { var rootConfig; try { - rootConfig = require( // eslint-disable-line global-require - path.join(process.cwd(), rootConfigFileName) - ); + rootConfig = require(path.resolve(rootConfigFileName)); // eslint-disable-line global-require } catch (err) { rootConfig = {}; } diff --git a/packages/electrode-archetype-react-app-dev/config/webpack/partial/babel.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/babel.js new file mode 100644 index 000000000..785123f8d --- /dev/null +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/babel.js @@ -0,0 +1,29 @@ +"use strict"; + +var mergeWebpackConfig = require("webpack-partial").default; +var archetype = require("../../archetype"); +var AppMode = archetype.AppMode; +var Path = archetype.Path; + +module.exports = function (babel) { + var babelExcludeRegex = new RegExp(`(node_modules|\b${Path.join(AppMode.src.client, "vendor")}\b)`); + return function (config) { + return mergeWebpackConfig(config, { + module: { + loaders: [{ + name: "babel", + test: /\.jsx?$/, + exclude: babelExcludeRegex, + // NOTE: webpack.config.hot.js inserts "react-hot" into loaders array + loader: "babel-loader", + query: babel + }, + { + name: "json", + test: /\.json$/, + loader: "json" + }] + } + }); + }; +}; diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/coverage.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/coverage.js similarity index 72% rename from packages/electrode-archetype-react-app/config/webpack/partial/coverage.js rename to packages/electrode-archetype-react-app-dev/config/webpack/partial/coverage.js index f51b7c6b2..96908cf10 100644 --- a/packages/electrode-archetype-react-app/config/webpack/partial/coverage.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/coverage.js @@ -1,8 +1,7 @@ "use strict"; -var archetype = require("../../archetype"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; -var ispartaLoader = archetype.devRequire.resolve("isparta-loader"); +var ispartaLoader = require.resolve("isparta-loader"); +var mergeWebpackConfig = require("webpack-partial").default; module.exports = function () { return function (config) { diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/define.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/define.js similarity index 71% rename from packages/electrode-archetype-react-app/config/webpack/partial/define.js rename to packages/electrode-archetype-react-app-dev/config/webpack/partial/define.js index 1ec18b897..db517b147 100644 --- a/packages/electrode-archetype-react-app/config/webpack/partial/define.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/define.js @@ -1,8 +1,7 @@ "use strict"; -var archetype = require("../../archetype"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; -var DefinePlugin = archetype.devRequire("webpack").DefinePlugin; +var mergeWebpackConfig = require("webpack-partial").default; +var DefinePlugin = require("webpack").DefinePlugin; module.exports = function () { return function (config) { @@ -18,4 +17,3 @@ module.exports = function () { }); }; }; - diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/dev.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/dev.js similarity index 82% rename from packages/electrode-archetype-react-app/config/webpack/partial/dev.js rename to packages/electrode-archetype-react-app-dev/config/webpack/partial/dev.js index 53b3847e0..6eac591a5 100644 --- a/packages/electrode-archetype-react-app/config/webpack/partial/dev.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/dev.js @@ -1,15 +1,16 @@ "use strict"; +var mergeWebpackConfig = require("webpack-partial").default; +var ExtractTextPlugin = require("extract-text-webpack-plugin"); +var WebpackReporter = require("electrode-webpack-reporter"); +var webpack = require("webpack"); +var Fs = require("fs"); var archetype = require("../../archetype"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; -var ExtractTextPlugin = archetype.devRequire("extract-text-webpack-plugin"); -var webpack = archetype.devRequire("webpack"); -var WebpackReporter = archetype.devRequire("electrode-webpack-reporter"); -var fs = require("fs"); +var Path = archetype.Path; function notifyBundleValid() { setTimeout(function () { - fs.writeFileSync(".etmp/bundle.valid.log", `${Date.now()}`); + Fs.writeFileSync(Path.resolve(archetype.eTmpDir, "bundle.valid.log"), `${Date.now()}`); }, 100); } diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/extract.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/extract.js similarity index 73% rename from packages/electrode-archetype-react-app/config/webpack/partial/extract.js rename to packages/electrode-archetype-react-app-dev/config/webpack/partial/extract.js index 25d7791fe..5cebaa4c6 100644 --- a/packages/electrode-archetype-react-app/config/webpack/partial/extract.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/extract.js @@ -1,20 +1,22 @@ "use strict"; var archetype = require("../../archetype"); -var Path = archetype.PlatformPath; -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; +var Path = archetype.Path; +var mergeWebpackConfig = require("webpack-partial").default; -var glob = archetype.devRequire("glob"); -var ExtractTextPlugin = archetype.devRequire("extract-text-webpack-plugin"); -var CSSSplitPlugin = archetype.devRequire("css-split-webpack-plugin").default; -var atImport = archetype.devRequire("postcss-import"); -var cssnext = archetype.devRequire("postcss-cssnext"); +var glob = require("glob"); +var ExtractTextPlugin = require("extract-text-webpack-plugin"); +var CSSSplitPlugin = require("css-split-webpack-plugin").default; +var atImport = require("postcss-import"); +var cssnext = require("postcss-cssnext"); -var autoprefixer = archetype.devRequire("autoprefixer-stylus"); -var cssLoader = archetype.devRequire.resolve("css-loader"); -var styleLoader = archetype.devRequire.resolve("style-loader"); -var stylusLoader = archetype.devRequire.resolve("stylus-relative-loader"); -var postcssLoader = archetype.devRequire.resolve("postcss-loader"); +var autoprefixer = require("autoprefixer-stylus"); +var cssLoader = require.resolve("css-loader"); +var styleLoader = require.resolve("style-loader"); +var stylusLoader = require.resolve("stylus-relative-loader"); +var postcssLoader = require.resolve("postcss-loader"); + +var AppMode = archetype.AppMode; /** * [cssModuleSupport By default, this archetype assumes you are using CSS-Modules + CSS-Next] @@ -28,8 +30,8 @@ var postcssLoader = archetype.devRequire.resolve("postcss-loader"); * case 4: *none* *.css & *.styl exists => CSS-Modules + CSS-Next takes priority */ -var cssNextExists = (glob.sync(Path.join(process.cwd() + "/client/**/*.css")).length > 0); -var stylusExists = (glob.sync(Path.join(process.cwd() + "/client/**/*.styl")).length > 0); +var cssNextExists = (glob.sync(Path.resolve(AppMode.src.client, "**", "*.css")).length > 0); +var stylusExists = (glob.sync(Path.resolve(AppMode.src.client, "**", "*.styl")).length > 0); // By default, this archetype assumes you are using CSS-Modules + CSS-Next var cssModuleSupport = true; diff --git a/packages/electrode-archetype-react-app-dev/config/webpack/partial/fail.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/fail.js new file mode 100644 index 000000000..b76b353b2 --- /dev/null +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/fail.js @@ -0,0 +1,14 @@ +"use strict"; + +var mergeWebpackConfig = require("webpack-partial").default; +var FailPlugin = require("webpack-fail-plugin"); + +module.exports = function () { + return function (config) { + return mergeWebpackConfig(config, { + plugins: [ + FailPlugin + ] + }); + }; +}; diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/fonts.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/fonts.js similarity index 68% rename from packages/electrode-archetype-react-app/config/webpack/partial/fonts.js rename to packages/electrode-archetype-react-app-dev/config/webpack/partial/fonts.js index 0292a7e87..1dbfb368a 100644 --- a/packages/electrode-archetype-react-app/config/webpack/partial/fonts.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/fonts.js @@ -1,10 +1,9 @@ "use strict"; -var archetype = require("../../archetype"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; +var mergeWebpackConfig = require("webpack-partial").default; -var urlLoader = archetype.devRequire.resolve("url-loader"); -var fileLoader = archetype.devRequire.resolve("file-loader"); +var urlLoader = require.resolve("url-loader"); +var fileLoader = require.resolve("file-loader"); module.exports = function () { return function (config) { diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/hot.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/hot.js similarity index 91% rename from packages/electrode-archetype-react-app/config/webpack/partial/hot.js rename to packages/electrode-archetype-react-app-dev/config/webpack/partial/hot.js index bdb9b6a77..bf522b93d 100644 --- a/packages/electrode-archetype-react-app/config/webpack/partial/hot.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/hot.js @@ -1,7 +1,7 @@ "use strict"; var archetype = require("../../archetype"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; +var mergeWebpackConfig = require("webpack-partial").default; var getDefaultEntry = function (entry) { return [ diff --git a/packages/electrode-archetype-react-app-dev/config/webpack/partial/images.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/images.js new file mode 100644 index 000000000..64ac4b422 --- /dev/null +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/images.js @@ -0,0 +1,33 @@ +"use strict"; + +var archetype = require("../../archetype"); +var mergeWebpackConfig = require("webpack-partial").default; +var isomorphicLoader = require.resolve("isomorphic-loader"); + +var _ = require("lodash"); + +function getCdnLoader() { + var loader = _(["electrode-cdn-file-loader", "cdn-file-loader", "file-loader"]).find(function(x) { + try { + return require.resolve(x); + } catch (e) { + return undefined; + } + }); + + return loader && require.resolve(loader) || "file-loader"; +} + +module.exports = function() { + return function(config) { + return mergeWebpackConfig(config, { + module: { + loaders: [{ + name: "images", + test: /\.(jpe?g|png|gif|svg)(\?\S*)?$/i, + loader: getCdnLoader() + "?limit=10000!" + isomorphicLoader + }] + } + }); + }; +}; diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/isomorphic.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/isomorphic.js similarity index 87% rename from packages/electrode-archetype-react-app/config/webpack/partial/isomorphic.js rename to packages/electrode-archetype-react-app-dev/config/webpack/partial/isomorphic.js index 27687f3b5..a62bd8b8d 100644 --- a/packages/electrode-archetype-react-app/config/webpack/partial/isomorphic.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/isomorphic.js @@ -2,7 +2,7 @@ var archetype = require("../../archetype"); var IsomorphicLoaderPlugin = require("isomorphic-loader/lib/webpack-plugin"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; +var mergeWebpackConfig = require("webpack-partial").default; module.exports = function () { return function (config) { diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/json.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/json.js similarity index 100% rename from packages/electrode-archetype-react-app/config/webpack/partial/json.js rename to packages/electrode-archetype-react-app-dev/config/webpack/partial/json.js diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/locales.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/locales.js similarity index 78% rename from packages/electrode-archetype-react-app/config/webpack/partial/locales.js rename to packages/electrode-archetype-react-app-dev/config/webpack/partial/locales.js index b142a12c4..3857457eb 100644 --- a/packages/electrode-archetype-react-app/config/webpack/partial/locales.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/locales.js @@ -1,8 +1,7 @@ "use strict"; -var archetype = require("../../archetype"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; -var ContextReplacementPlugin = archetype.devRequire("webpack").ContextReplacementPlugin; +var mergeWebpackConfig = require("webpack-partial").default; +var ContextReplacementPlugin = require("webpack").ContextReplacementPlugin; // Note that in modern versions of `moment`, there is actually no // `locale/en.js`, `en` is simply the default and included in `moment` itself. diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/optimize.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/optimize.js similarity index 82% rename from packages/electrode-archetype-react-app/config/webpack/partial/optimize.js rename to packages/electrode-archetype-react-app-dev/config/webpack/partial/optimize.js index 49676f43d..877e94ee4 100644 --- a/packages/electrode-archetype-react-app/config/webpack/partial/optimize.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/optimize.js @@ -1,9 +1,9 @@ "use strict"; var archetype = require("../../archetype"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; -var optimize = archetype.devRequire("webpack").optimize; -var _ = archetype.devRequire("lodash"); +var mergeWebpackConfig = require("webpack-partial").default; +var optimize = require("webpack").optimize; +var _ = require("lodash"); var inspectpack = process.env.INSPECTPACK_DEBUG === "true"; // var LodashModuleReplacementPlugin = require("lodash-webpack-plugin"); diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/pwa.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/pwa.js similarity index 90% rename from packages/electrode-archetype-react-app/config/webpack/partial/pwa.js rename to packages/electrode-archetype-react-app-dev/config/webpack/partial/pwa.js index ae9d161a4..0880591fc 100644 --- a/packages/electrode-archetype-react-app/config/webpack/partial/pwa.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/pwa.js @@ -1,17 +1,20 @@ "use strict"; -var path = require("path"); -var assign = require("lodash/assign"); + var archetype = require("../../archetype"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; -var fileLoader = archetype.devRequire.resolve("file-loader"); +var Path = archetype.Path; +var AppMode = archetype.AppMode; + +var assign = require("lodash/assign"); +var mergeWebpackConfig = require("webpack-partial").default; +var fileLoader = require.resolve("file-loader"); var webAppManifestLoader = require.resolve("web-app-manifest-loader"); var SWPrecacheWebpackPlugin = require("sw-precache-webpack-plugin"); var FaviconsWebpackPlugin = require("favicons-webpack-plugin"); var AddManifestFieldsPlugin = require('../plugins/add-manifest-fields'); var DiskPlugin = require('webpack-disk-plugin'); -var swConfigPath = path.resolve(process.cwd(), "config/sw-config.js"); -var serverConfigPath = path.resolve(process.cwd(), "config/default.json"); +var swConfigPath = Path.resolve("config", "sw-config.js"); +var serverConfigPath = Path.resolve("config", "default.json"); /** @@ -39,7 +42,7 @@ function requireModuleSafelyWithFallback(path, fallback) { * @return {string} parsed file path */ function getHashedPath(filepath) { - var parsed = path.parse(filepath); + var parsed = Path.parse(filepath); var name = parsed.name; var ext = parsed.ext; return name + '.[hash]' + ext; @@ -52,7 +55,7 @@ function getHashedPath(filepath) { * @return {string} parsed file path */ function getDevelopmentPath(filepath) { - var parsed = path.parse(filepath); + var parsed = Path.parse(filepath); var name = parsed.name; var ext = parsed.ext; return name + '.bundle.dev' + ext; @@ -77,7 +80,7 @@ function createEntryConfigFromScripts(importScripts, entry) { ? Object.assign({}, entry) : { main: entry }; return importScripts.reduce(function(acc, script) { - var name = path.parse(script).name; + var name = Path.parse(script).name; acc[name] = script; return acc; }, newEntry); @@ -138,7 +141,7 @@ module.exports = function () { }; } - var logoPath = `${process.cwd()}/client/${manifestConfig.logo}`; + var logoPath = Path.resolve(AppMode.src.client, manifestConfig.logo); var plugins = [ new FaviconsWebpackPlugin({ logo: logoPath, @@ -172,10 +175,7 @@ module.exports = function () { plugins.push( new DiskPlugin({ output: { - path: path.resolve( - process.cwd(), - severConfig.buildArtifactsPath || ".build" - ) + path: Path.resolve(severConfig.buildArtifactsPath || ".build") }, files: [{ asset: /\/stats.json$/, diff --git a/packages/electrode-archetype-react-app-dev/config/webpack/partial/simple-progress.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/simple-progress.js new file mode 100644 index 000000000..aee45d56c --- /dev/null +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/simple-progress.js @@ -0,0 +1,36 @@ +"use strict"; + +var mergeWebpackConfig = require("webpack-partial").default; +var webpack = require("webpack"); + +var lastPct; + +module.exports = function () { + return function (config) { + return mergeWebpackConfig(config, { + plugins: [ + new webpack.ProgressPlugin((pct, msg) => { + pct = Math.ceil(pct * 100); + if (lastPct === pct) { + return; + } + if (pct === 0) { + process.stdout.write("\nwebpack " + msg + ": "); + } + process.stdout.write("."); + if (pct > 0 && pct % 20 === 0) { + process.stdout.write("\n"); + if (msg && pct < 100) { + process.stdout.write(" " + msg + ": "); + } + } + if (pct === 100) { + lastPct = undefined; + } else { + lastPct = pct; + } + }) + ] + }); + }; +}; diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/sourcemaps-inline.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/sourcemaps-inline.js similarity index 58% rename from packages/electrode-archetype-react-app/config/webpack/partial/sourcemaps-inline.js rename to packages/electrode-archetype-react-app-dev/config/webpack/partial/sourcemaps-inline.js index f55fe0310..286d8a505 100644 --- a/packages/electrode-archetype-react-app/config/webpack/partial/sourcemaps-inline.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/sourcemaps-inline.js @@ -1,7 +1,6 @@ "use strict"; -var archetype = require("../../archetype"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; +var mergeWebpackConfig = require("webpack-partial").default; module.exports = function () { return function (config) { diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/sourcemaps-remote.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/sourcemaps-remote.js similarity index 71% rename from packages/electrode-archetype-react-app/config/webpack/partial/sourcemaps-remote.js rename to packages/electrode-archetype-react-app-dev/config/webpack/partial/sourcemaps-remote.js index 3f45e8a07..01f9dade4 100644 --- a/packages/electrode-archetype-react-app/config/webpack/partial/sourcemaps-remote.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/sourcemaps-remote.js @@ -1,8 +1,8 @@ "use strict"; var archetype = require("../../archetype"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; -var SourceMapDevToolPlugin = archetype.devRequire("webpack").SourceMapDevToolPlugin; +var mergeWebpackConfig = require("webpack-partial").default; +var SourceMapDevToolPlugin = require("webpack").SourceMapDevToolPlugin; module.exports = function () { return function (config) { diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/stats.js b/packages/electrode-archetype-react-app-dev/config/webpack/partial/stats.js similarity index 77% rename from packages/electrode-archetype-react-app/config/webpack/partial/stats.js rename to packages/electrode-archetype-react-app-dev/config/webpack/partial/stats.js index c04162f4f..8c6cea756 100644 --- a/packages/electrode-archetype-react-app/config/webpack/partial/stats.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/partial/stats.js @@ -1,8 +1,7 @@ "use strict"; -var archetype = require("../../archetype"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; -var StatsWriterPlugin = archetype.devRequire("webpack-stats-plugin").StatsWriterPlugin; +var mergeWebpackConfig = require("webpack-partial").default; +var StatsWriterPlugin = require("webpack-stats-plugin").StatsWriterPlugin; module.exports = function (opts) { var statsOptions = { diff --git a/packages/electrode-archetype-react-app/config/webpack/plugins/add-manifest-fields.js b/packages/electrode-archetype-react-app-dev/config/webpack/plugins/add-manifest-fields.js similarity index 100% rename from packages/electrode-archetype-react-app/config/webpack/plugins/add-manifest-fields.js rename to packages/electrode-archetype-react-app-dev/config/webpack/plugins/add-manifest-fields.js diff --git a/packages/electrode-archetype-react-app/config/webpack/remove-dll-references.js b/packages/electrode-archetype-react-app-dev/config/webpack/remove-dll-references.js similarity index 100% rename from packages/electrode-archetype-react-app/config/webpack/remove-dll-references.js rename to packages/electrode-archetype-react-app-dev/config/webpack/remove-dll-references.js diff --git a/packages/electrode-archetype-react-app/config/webpack/webpack.config.browsercoverage.js b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.browsercoverage.js similarity index 74% rename from packages/electrode-archetype-react-app/config/webpack/webpack.config.browsercoverage.js rename to packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.browsercoverage.js index a734758d1..411b3c69a 100644 --- a/packages/electrode-archetype-react-app/config/webpack/webpack.config.browsercoverage.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.browsercoverage.js @@ -1,9 +1,8 @@ "use strict"; -var archetype = require("../archetype"); -var _ = archetype.devRequire("lodash"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; -var WebpackConfig = archetype.devRequire("webpack-config").default; +var _ = require("lodash"); +var mergeWebpackConfig = require("webpack-partial").default; +var WebpackConfig = require("webpack-config").default; var getRootConfig = require("./get-root-config"); var baseConfig = require("./base.js"); diff --git a/packages/electrode-archetype-react-app/config/webpack/webpack.config.coverage.js b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.coverage.js similarity index 62% rename from packages/electrode-archetype-react-app/config/webpack/webpack.config.coverage.js rename to packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.coverage.js index 294102493..5b2acb199 100644 --- a/packages/electrode-archetype-react-app/config/webpack/webpack.config.coverage.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.coverage.js @@ -1,17 +1,18 @@ "use strict"; -var archetype = require("../archetype"); -var _ = archetype.devRequire("lodash"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; -var WebpackConfig = archetype.devRequire("webpack-config").default; +var _ = require("lodash"); +var mergeWebpackConfig = require("webpack-partial").default; +var WebpackConfig = require("webpack-config").default; var getRootConfig = require("./get-root-config"); var coverageConfig = require("./partial/coverage"); var inlineSourcemapsConfig = require("./partial/sourcemaps-inline"); var testConfig = require("./base-test.js"); +var simpleProgress = require("./partial/simple-progress"); module.exports = new WebpackConfig().merge(_.flow( mergeWebpackConfig.bind(null, {}, testConfig), coverageConfig(), - inlineSourcemapsConfig() + inlineSourcemapsConfig(), + simpleProgress() )()).merge(getRootConfig("webpack.config.coverage.js")); diff --git a/packages/electrode-archetype-react-app/config/webpack/webpack.config.dev.js b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.dev.js similarity index 65% rename from packages/electrode-archetype-react-app/config/webpack/webpack.config.dev.js rename to packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.dev.js index 7a993de9a..b927b61eb 100644 --- a/packages/electrode-archetype-react-app/config/webpack/webpack.config.dev.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.dev.js @@ -2,10 +2,9 @@ /** * Webpack dev configuration */ -var archetype = require("../archetype"); -var _ = archetype.devRequire("lodash"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; -var WebpackConfig = archetype.devRequire("webpack-config").default; +var _ = require("lodash"); +var mergeWebpackConfig = require("webpack-partial").default; +var WebpackConfig = require("webpack-config").default; var getRootConfig = require("./get-root-config"); var baseConfig = require("./base.js"); diff --git a/packages/electrode-archetype-react-app/config/webpack/webpack.config.dev.static.js b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.dev.static.js similarity index 66% rename from packages/electrode-archetype-react-app/config/webpack/webpack.config.dev.static.js rename to packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.dev.static.js index 778010d18..fb62df9de 100644 --- a/packages/electrode-archetype-react-app/config/webpack/webpack.config.dev.static.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.dev.static.js @@ -2,10 +2,9 @@ /** * Webpack dev static configuration */ -var archetype = require("../archetype"); -var _ = archetype.devRequire("lodash"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; -var WebpackConfig = archetype.devRequire("webpack-config").default; +var _ = require("lodash"); +var mergeWebpackConfig = require("webpack-partial").default; +var WebpackConfig = require("webpack-config").default; var getRootConfig = require("./get-root-config"); var baseConfig = require("./base.js"); diff --git a/packages/electrode-archetype-react-app/config/webpack/webpack.config.dll.js b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.dll.js similarity index 80% rename from packages/electrode-archetype-react-app/config/webpack/webpack.config.dll.js rename to packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.dll.js index 06f8b8ea8..48623b0c0 100644 --- a/packages/electrode-archetype-react-app/config/webpack/webpack.config.dll.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.dll.js @@ -1,19 +1,19 @@ "use strict"; var _ = require("lodash"); -var path = require("path"); var webpack = require("webpack"); var WebpackConfig = require("webpack-config").default; var removeDllReferences = require("./remove-dll-references"); var getRootConfig = require("./get-root-config"); +var archetype = require("../archetype"); +var Path = archetype.Path; +var AppMode = archetype.AppMode; /* eslint-disable func-style */ function getDllEntry() { try { - return require( // eslint-disable-line global-require - path.join(process.cwd(), "client/dll.config.js") - ); + return require(Path.resolve(AppMode.src.client, "dll.config.js")); // eslint-disable-line global-require } catch (err) { return {}; } @@ -44,14 +44,14 @@ extensions[baseConfigPath] = function (config) { var dllConfig = new WebpackConfig().extend(extensions).merge({ entry: getDllEntry(), output: { - path: path.join(process.cwd(), "dll/js"), + path: Path.resolve("dll/js"), filename: "[name].bundle.[hash].js", library: "[name]_[hash]" }, plugins: [ new webpack.DllPlugin({ name: "[name]_[hash]", - path: path.join(process.cwd(), "dll/js/[name]-manifest.[hash].json") + path: Path.resolve("dll/js/[name]-manifest.[hash].json") }) ] }).merge(getRootConfig("webpack.config.dll.js")); diff --git a/packages/electrode-archetype-react-app/config/webpack/webpack.config.hot.js b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.hot.js similarity index 73% rename from packages/electrode-archetype-react-app/config/webpack/webpack.config.hot.js rename to packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.hot.js index 51e989f6f..b3789e9ba 100644 --- a/packages/electrode-archetype-react-app/config/webpack/webpack.config.hot.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.hot.js @@ -2,16 +2,17 @@ /** * Webpack hot configuration */ -var archetype = require("../archetype"); -var _ = archetype.devRequire("lodash"); -var Path = archetype.PlatformPath; -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; -var WebpackConfig = archetype.devRequire("webpack-config").default; +var _ = require("lodash"); +var path = require("path"); +var mergeWebpackConfig = require("webpack-partial").default; var hotConfig = require("./partial/hot"); var baseConfig = require("./base.js"); var defineConfig = require("./partial/define.js"); var devConfig = require("./partial/dev.js"); +var WebpackConfig = require("webpack-config").default; var getRootConfig = require("./get-root-config"); +var fs = require("fs"); +var archetype = require("../archetype"); var config = module.exports = _.flow( mergeWebpackConfig.bind(null, {}, baseConfig), @@ -27,6 +28,6 @@ var babel = _.find(config.module.loaders, {name: "babel"}); // update babel loaders for hot loading babel.loaders = [].concat(["react-hot"], babel.loaders); -babel.include = Path.resolve(archetype.clientSrcDir); +babel.include = path.resolve(archetype.AppMode.src.client); module.exports = new WebpackConfig().merge(config).merge(getRootConfig("webpack.config.hot.js")); diff --git a/packages/electrode-archetype-react-app/config/webpack/webpack.config.js b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.js similarity index 83% rename from packages/electrode-archetype-react-app/config/webpack/webpack.config.js rename to packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.js index c6621ff1f..fa459cf39 100644 --- a/packages/electrode-archetype-react-app/config/webpack/webpack.config.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.js @@ -1,8 +1,7 @@ "use strict"; var _ = require("lodash"); -var archetype = require("../archetype"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; +var mergeWebpackConfig = require("webpack-partial").default; var WebpackConfig = require("webpack-config").default; var getRootConfig = require("./get-root-config"); var addDllReferences = require("./add-dll-references"); @@ -13,6 +12,7 @@ var optimizeConfig = require("./partial/optimize"); var localesConfig = require("./partial/locales"); var productionSourcemapsConfig = require("./partial/sourcemaps-remote"); var failConfig = require("./partial/fail"); +var simpleProgress = require("./partial/simple-progress"); var config = new WebpackConfig().merge(_.flow( mergeWebpackConfig.bind(null, {}, baseConfig), @@ -20,7 +20,8 @@ var config = new WebpackConfig().merge(_.flow( localesConfig(), defineConfig(), productionSourcemapsConfig(), - failConfig() + failConfig(), + simpleProgress() )()).merge(getRootConfig("webpack.config.js")); addDllReferences(config); diff --git a/packages/electrode-archetype-react-app/config/webpack/webpack.config.stats.electrify.js b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.stats.electrify.js similarity index 63% rename from packages/electrode-archetype-react-app/config/webpack/webpack.config.stats.electrify.js rename to packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.stats.electrify.js index 89d539de3..19b86eb18 100644 --- a/packages/electrode-archetype-react-app/config/webpack/webpack.config.stats.electrify.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.stats.electrify.js @@ -2,9 +2,8 @@ /** * Webpack dev static configuration */ -var archetype = require("../archetype"); -var _ = archetype.devRequire("lodash"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; +var _ = require("lodash"); +var mergeWebpackConfig = require("webpack-partial").default; var baseConfig = require("./base.js"); var statsConfig = require("./partial/stats.js"); diff --git a/packages/electrode-archetype-react-app/config/webpack/webpack.config.test.js b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.test.js similarity index 61% rename from packages/electrode-archetype-react-app/config/webpack/webpack.config.test.js rename to packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.test.js index c58c92cf5..97f9bad8c 100644 --- a/packages/electrode-archetype-react-app/config/webpack/webpack.config.test.js +++ b/packages/electrode-archetype-react-app-dev/config/webpack/webpack.config.test.js @@ -1,9 +1,8 @@ "use strict"; -var archetype = require("../archetype"); -var _ = archetype.devRequire("lodash"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; -var WebpackConfig = archetype.devRequire("webpack-config").default; +var _ = require("lodash"); +var mergeWebpackConfig = require("webpack-partial").default; +var WebpackConfig = require("webpack-config").default; var getRootConfig = require("./get-root-config"); var testConfig = require("./base-test.js"); diff --git a/packages/electrode-archetype-react-app-dev/package.json b/packages/electrode-archetype-react-app-dev/package.json index a583ec34e..29f88e0cd 100644 --- a/packages/electrode-archetype-react-app-dev/package.json +++ b/packages/electrode-archetype-react-app-dev/package.json @@ -12,10 +12,26 @@ }, "license": "Apache-2.0", "scripts": {}, + "peerDependencies": { + "electrode-archetype-react-app": "1.8.0" + }, "dependencies": { "autoprefixer-stylus": "^0.9.1", - "babel-eslint": "^7.1.0", + "babel-cli": "^6.4.5", + "babel-core": "^6.4.0", + "babel-eslint": "^7.0.0", "babel-loader": "^6.2.1", + "babel-plugin-lodash": "^3.1.3", + "babel-plugin-react-intl": "^2.1.2", + "babel-plugin-transform-object-rest-spread": "^6.3.13", + "babel-plugin-transform-react-constant-elements": "^6.5.0", + "babel-plugin-transform-react-inline-elements": "^6.6.5", + "babel-plugin-transform-runtime": "^6.8.0", + "babel-polyfill": "^6.5.0", + "babel-preset-es2015": "^6.3.13", + "babel-preset-es2015-loose": "^7.0.0", + "babel-preset-es2015-node": "^6.1.1", + "babel-preset-react": "^6.3.13", "chai": "^3.2.0", "chai-shallowly": "^0.9.10", "chalk": "^1.1.1", diff --git a/packages/electrode-archetype-react-app/scripts/l10n/flatten-messages.js b/packages/electrode-archetype-react-app-dev/scripts/l10n/flatten-messages.js similarity index 100% rename from packages/electrode-archetype-react-app/scripts/l10n/flatten-messages.js rename to packages/electrode-archetype-react-app-dev/scripts/l10n/flatten-messages.js diff --git a/packages/electrode-archetype-react-app/scripts/map-isomorphic-cdn.js b/packages/electrode-archetype-react-app-dev/scripts/map-isomorphic-cdn.js similarity index 100% rename from packages/electrode-archetype-react-app/scripts/map-isomorphic-cdn.js rename to packages/electrode-archetype-react-app-dev/scripts/map-isomorphic-cdn.js diff --git a/packages/electrode-archetype-react-app-dev/scripts/merge-isomorphic-assets.js b/packages/electrode-archetype-react-app-dev/scripts/merge-isomorphic-assets.js new file mode 100644 index 000000000..0f8969b11 --- /dev/null +++ b/packages/electrode-archetype-react-app-dev/scripts/merge-isomorphic-assets.js @@ -0,0 +1,29 @@ +"use strict"; + +var fs = require("fs"); +var glob = require("glob"); +var path = require("path"); + +var assetsPattern = path.resolve("@(dist|dll)/isomorphic-assets.*"); +var assetsPath = path.resolve("dist/isomorphic-assets.json"); + +glob(assetsPattern, function (readErr, filenames) { + var assets = filenames.reduce(function (result, filename) { + var fileAssets = require(filename); + + Object.assign(result.marked, fileAssets.marked); + Object.assign(result.chunks, fileAssets.chunks); + + return result; + }, { + chunks: {}, + marked: {} + }); + + fs.writeFile(assetsPath, JSON.stringify(assets, null, 2), function (err) { + if (err) { + throw err; + } + }); +}); + diff --git a/packages/electrode-archetype-react-app/FAQ.md b/packages/electrode-archetype-react-app/FAQ.md new file mode 100644 index 000000000..60e940674 --- /dev/null +++ b/packages/electrode-archetype-react-app/FAQ.md @@ -0,0 +1,123 @@ +#### What is this for? + +This "app archetype" provides for common patterns across all app projects so that each app project can standardize on common development behavior and patterns. Its essentially pre-made patterns for build scripts. + +#### How do I start developing in my application project after installing? + +```bash +# This runs both the node server and webpack (in hot mode) +$ gulp hot + +# Also try `dev` mode when running off battery power and you wish to maximize battery life. +$ gulp dev +``` + +#### What is `hot mode`? + +`Hot mode` enables Hot module reloading(HMR), it is where webpack transpiles your javascript and css code and continues to watch for any changes, and, builds and loads only the code that has changed on disk. It allows you to develop without re-loading your browser page as the changes will be automagically piped in. + +#### How do I run my application tests? + +```bash +# This will run test eslint and your spec tests +$ gulp check +``` + +#### How do I run my application tests without going through eslint (i.e., while I'm developing)? + +```bash +# This will run only your spec tests +$ gulp test-dev +``` + +#### Why can't my test and code changes get automatically run with the tests? Why do the tests take so long to start? + +```bash +# This will start a webpack-dev-server to hot watch your code and also start a karma test browser that auto-reruns when specs or client code changes. +$ gulp test-watch-all +``` + +#### How do I use and/or view the final build files without minifying/uglifying but also with sourcemaps? + +```bash +# This will build your code and save to disk, and then start a node server (without using webpack-dev-server). +$ gulp dev-static +``` + +#### Is there anything else that might be nice for my development? + +```bash +# This will start the node server in debug mode so that you can place breakpoints, "debugger" statements, or use `node-inspector`. +$ gulp debug +``` + +#### How do I view my test result in the browser? + +Run either of the below commands before opening the link. + +``` +gulp server-test +gulp dev # (OR) (which includes `server-test`) +gulp hot # (OR) (which includes `server-test`) +``` +This will serve the static assets for test.html + +open [test.html]((http://localhost:3001/node_modules/electrode-archetype-react-app/config/browser_test/test.html)) to view test result. + +#### How do I generate a manifest.json and a service-worker file? + +First we need to add a `sw-config.js` file under the app's `config` folder. + +This file contains two sections: + +##### 1. Manifest + ``` +manifest: { + logo: "./images/icon.png", + title: "Electrode Progressive App", + short_name: "EPA", + start_url: "/" +} + ``` + Manifest gives you control over how your web app is installed on user's home screen with `short_name, title and logo` properties. You can also specify a starting path to launch your app with `start_url` property. Manifest defines how your app appears to the user and more importantly how they can launch it. + +##### 2. Cache + +``` +cache: { + runtimeCaching: [{ + handler: "fastest", + urlPattern: /\/home$/ + }, { + handler: "networkFirst", + urlPattern: /getBeer/ + }], + staticFileGlobs: ['dist/**/*'] +} +``` + + Seamlessly cache your static assets or run time routes or cache them together! + Precache your static assets generated by webpack using the `staticFileGlobs` property. Or use the `runtimeCaching` property to cache specific react routes in from your `routes.jsx`. + +Once this file is added, running + +``` +gulp build +``` + +will generate a `manifest.json` file inside of `dist/js/icons-[hash]` and a service worker file `dist/sw.js`. + +`Service Worker` file is generated during the build step and it will precache all the static resources as per the configuration in `sw-config.js`. +Using `AboveTheFoldOnlyServerRender` you can avoid caching of certain components inside the crucial pages of your app to make your _App shell_ even lighter. + +``` + +
+ this will not be a part of your app shell +
+
+``` + +AboveTheFoldOnlyServerRender [example](https://github.com/docs-code-examples-electrode-io/electrode-progressive-beer/blob/master/client/components/home.jsx#L69-L83) +Sample [config.js](https://github.com/electrode-io/electrode-boilerplate-universal-react-node/blob/master/config/sw-config.js) +More on [PWA](https://developers.google.com/web/progressive-web-apps/) diff --git a/packages/electrode-archetype-react-app/README.md b/packages/electrode-archetype-react-app/README.md index 0aeb1b779..b114f2a4d 100644 --- a/packages/electrode-archetype-react-app/README.md +++ b/packages/electrode-archetype-react-app/README.md @@ -4,155 +4,215 @@ A Walmart Labs flavored React Universal App archetype. -## tl;dr +## Have a question? Check the [FAQ](./FAQ.md) -#### What is this for? +## Installation + +> NOTE: Generally you don't need to install these modules directly. You should start your app by using our Yeoman [generator-electrode] or check our [Electrode getting started] guide. -This "app archetype" provides for common patterns across all app projects so that each app project can standardize on common development behavior and patterns. Its essentially pre-made patterns for build scripts. +However, if you are manually creating your app: -#### How do I start developing in my application project after installing? +###### Install the two complementary modules ```bash -# This runs both the node server and webpack (in hot mode) -$ gulp hot +$ npm install --save electrode-archetype-react-app +$ npm install --save-dev electrode-archetype-react-app-dev +``` -# Also try `dev` mode when running off battery power and you wish to maximize battery life. -$ gulp dev +###### Add a `gulpfile.js` + +The `gulpfile.js` should contain the following and be located in the root of your project + +```js +require("electrode-archetype-react-app")(); ``` -#### What is `hot mode`? +## Usage -`Hot mode` enables Hot module reloading(HMR), it is where webpack transpiles your javascript and css code and continues to watch for any changes, and, builds and loads only the code that has changed on disk. It allows you to develop without re-loading your browser page as the changes will be automagically piped in. +The primary interface to the archetype is a list of tasks you can invoke with gulp to do your bidding. -#### How do I run my application tests? +To see the tasks, simply run: ```bash -# This will run test eslint and your spec tests -$ gulp check +$ gulp ``` -#### How do I run my application tests without going through eslint (i.e., while I'm developing)? +Some tasks are internal and hidden, but if you are curious, you can see them with: ```bash -# This will run only your spec tests -$ gulp test-dev +$ gulp help --all ``` -#### Why can't my test and code changes get automatically run with the tests? Why do the tests take so long to start? +To invoke a task, for example, the `dev` task, run: ```bash -# This will start a webpack-dev-server to hot watch your code and also start a karma test browser that auto-reruns when specs or client code changes. -$ gulp test-watch-all +$ gulp dev ``` -#### How do I use and/or view the final build files without minifying/uglifying but also with sourcemaps? +## AppMode -```bash -# This will build your code and save to disk, and then start a node server (without using webpack-dev-server). -$ gulp dev-static -``` +The archetype supports two app modes. The default legacy babel-register mode and the [src/lib mode](#srclib-mode). -#### Is there anything else that might be nice for my development? +In babel-register mode, you put your `client` and `server` code under your project's top level and your Node server requires installing babel-register to transpile your code during run time. This is not recommended due to babel-register consuming resources. -```bash -# This will start the node server in debug mode so that you can place breakpoints, "debugger" statements, or use `node-inspector`. -$ gulp debug -``` +In the [src/lib mode](#srclib-mode), you put your `client` and `server` code under the `src` directory and the `build` task will transpile them into the `lib` directory during build time. -#### How do I view my test result in the browser? +> In the next major release, we plan to remove the babel-register mode. -Run either of the below commands before opening the link. +#### babel-register mode `.babelrc` -``` -gulp server-test -gulp dev # (OR) (which includes `server-test`) -gulp hot # (OR) (which includes `server-test`) -``` -This will serve the static assets for test.html +> Note: If you opt-in to use the src/lib mode, then this is not applicable. See [here](#srclib-babelrc) for more details. -open [test.html]((http://localhost:3001/node_modules/electrode-archetype-react-app/config/browser_test/test.html)) to view test result. +If you are using babel-register mode, then you need to add a `.babelrc` in your app's top level directory to extend +[the archetype's babel configuration](config/babel/.babelrc) in order to apply the presets (ES2015, React) and the plugins like i18n. If your project needs additional Babel settings (like using stage 0 features) you can add them to this file. See the [Babel docs](https://babeljs.io/docs/usage/babelrc/) for more information. -#### How do I generate a manifest.json and a service-worker file? -First we need to add a `sw-config.js` file under the app's `config` folder. -This file contains two sections: -##### 1. Manifest - ``` -manifest: { - logo: "./images/icon.png", - title: "Electrode Progressive App", - short_name: "EPA", - start_url: "/" -} - ``` - Manifest gives you control over how your web app is installed on user's home screen with `short_name, title and logo` properties. You can also specify a starting path to launch your app with `start_url` property. Manifest defines how your app appears to the user and more importantly how they can launch it. -##### 2. Cache -``` -cache: { - runtimeCaching: [{ - handler: "fastest", - urlPattern: /\/home$/ - }, { - handler: "networkFirst", - urlPattern: /getBeer/ - }], - staticFileGlobs: ['dist/**/*'] +```json +{ + "extends": "electrode-archetype-react-app/config/babel/.babelrc" } ``` - Seamlessly cache your static assets or run time routes or cache them together! - Precache your static assets generated by webpack using the `staticFileGlobs` property. Or use the `runtimeCaching` property to cache specific react routes in from your `routes.jsx`. -Once this file is added, running -``` -gulp build -``` -will generate a `manifest.json` file inside of `dist/js/icons-[hash]` and a service worker file `dist/sw.js`. +## Opt-in features -`Service Worker` file is generated during the build step and it will precache all the static resources as per the configuration in `sw-config.js`. -Using `AboveTheFoldOnlyServerRender` you can avoid caching of certain components inside the crucial pages of your app to make your _App shell_ even lighter. -``` - -
- this will not be a part of your app shell -
-
-``` +### Run time support API -AboveTheFoldOnlyServerRender [example](https://github.com/docs-code-examples-electrode-io/electrode-progressive-beer/blob/master/client/components/home.jsx#L69-L83) -Sample [config.js](https://github.com/electrode-io/electrode-boilerplate-universal-react-node/blob/master/config/sw-config.js) -More on [PWA](https://developers.google.com/web/progressive-web-apps/) +For the things that should be initialized at your Node server's startup, they are all combined into a single API in the archetype as `support.load`. -## Installation +Here is how you can use it: -We use [gulp] as a task invoker. You should install it globally. +In your `server/index.js`: -```bash -$ sudo npm install -g gulp -``` +```js +const support = require("electrode-archetype-react-app/support"); +const electrodeServer = require("electrode-server"); +const electrodeConfippet = require("electrode-confippet"); -###### run the following in your project -```bash -$ npm install --save-dev gulp electrode-archetype-react-app electrode-archetype-react-app-dev +support.load() + .then(() => { + return electrodeServer(electrodeConfippet.config); + }) + .catch((e) => { + console.log("Server start failed", e); // eslint-disable-line no-console + }); ``` -###### Add a `gulpfile.js` -The `gulpfile.js` should contain the following and be located in the root of your project +If you are using this API, then things like isomorphic support and React module optimization are handled for you. + +It accepts a single `options` object, with the following supported fields: + + - `babelRegister` - Set to `false` to disable loading `babel-register` + - `babel-register` is loaded by default only in babel-register app mode, and off in src/lib app mode. + - `optimizeModulesForProduction` - Set to `false` to disable loading optimized copy of React modules. + - also disabled unless `NODE_ENV=production` + - If this is an object, then it's used as `options` for `optimizeModulesForProduction` + - `cssModuleHook` - Set to `false` to disable loading [css-module-hook] + - If this is an object, then it's used as `options` for `cssModuleHook` + - `isomorphicExtendRequire` - Set to `false` to disable loading isomorphic-loader support + +#### optimizeModulesForProduction Options + +The `optimizeModulesForProduction` options supported the following flags: + + - `quiet` - Boolean to turn off console.log messages + - `force` - Boolean to force enable regardless of `NODE_ENV` + +### src/lib Mode + +In order to avoid requiring run time babel transpilation, this archetype supports a src/lib app mode. To use this, you need to put your `client` and `server` into a `src` directory. The archetype's `build` task will transpile those into the `lib` directory. + +> The archetype `build` task will only overwrite `lib/client` and `lib/server`. So you can put other normal code under `lib` if you want. + +> If you are migrating from babel-register mode, then you should move your code to `src` directory. For the most part, there should be very little you need to change except if you have code that refers to those two directories explicitly from outside. + +You will also need to use the [Run time support API](#run-time-support-api) to initialize your server startup. + +#### `src` or `lib`? + +So where is your code being executed from? `src` or `lib`? Actually, it could be either, depending on `NODE_ENV`. + +If you need to refer to your client or server code from outside, you can use [`APP_SRC_DIR` environment variable](#app_src_dir) to automatically refer to the directory where code is being executed from. + +For UI code under `src/client` or `test/client`, webpack config sets `src` as a root so you can refer to code in `src/client` with `"client/foo"`. + +For example: ```js -require("electrode-archetype-react-app")(); +import Hello from "client/components/hello" ``` -###### Add a `.babelrc` to your project -The `.babelrc` needs to extend -[the archetype's babel configuration](config/babel/.babelrc) in order to apply the presents (ES2015, React) and the plugins like i18n. If your project needs additional Babel settings (like using stage 0 features) you can add them to this file. See the [Babel docs](https://babeljs.io/docs/usage/babelrc/) for more information. +For code under `src/server`, you can refer to client with `"../client/foo"`. -```json +##### development + +In dev mode, your code will be executed from the `src` directory. Your client code is transpiled and packed by `webpack-dev-server` on the fly. Your node server is executed by `babel-node`. + +##### production + +In prod mode, your client code is being loaded from the bundle packed by webpack. On server side, SSR will load client code from `lib` and your node server being executed from the `lib/server` directory. ie: `NODE_ENV=production node lib/server/index.js` + +#### src/lib .babelrc + +In the src/lib mode, an independent `.babelrc` file will be automatically generated for you in both `src/client` and `src/server`. The one for client is setup for React and the one for server is setup for the NodeJS version you are using. + +#### APP_SRC_DIR + +An env variable `APP_SRC_DIR` is always set by the [Run time support API](#run-time-support-api) to indicate where your code is running from. If your app config needs to refer to some code there, you can use this. + +**NOTE:** The dir path set in `$APP_SRC_DIR` will include the trailing `/` automatically. ie: `src/` or `lib/` + +For example, to pass your server side React app entry to react-webapp: + +```js { - "extends": "./node_modules/electrode-archetype-react-app/config/babel/.babelrc" + "plugins": { + "electrode-react-webapp": { + "options": { + "pageTitle": "Getting Started", + "paths": { + "/{args*}": { + "view": "index", + "content": { + "module": "./{{env.APP_SRC_DIR}}/server/app" // Note the use of APP_SRC_DIR here + } + } + } + } + } + } } ``` -###### Use `babel-core/register` in your server entry point +#### NPM Prune for production + +Starting with version 9.4.0, the archetypes now fully support your application running without the development archetype. Make sure you install `electrode-archetype-react-app` under your `dependencies` and `electrode-archetype-react-app-dev` under `devDependencies` and you can run `npm prune --production` on your app to clear `devDependencies`. If you are using the electrode-build script in your build job, you can set the build parameter `PRUNE_DEV_DEPENDENCIES` to true. + +> NOTE: if your app runs in production mode but rely on something else you've put under `devDependencies` then doing `npm prune --production` will still break your app. + +#### Define client entry points + +By default, the archetype uses `client/app.js` or `client/app.jsx` as a client entry point. Alternatively, +you can define multiple entry points for your application, so the Webpack will create bundles for each app +entry point. To do that, place `entry.config.js` module into your app's `client` directory: + +Here is an **example** of `entry.config.js`: +```js +module.exports = { + "web": "./app-web.js", + "ios": "./app-ios.js", + "android": "./app-android.js" +}; +``` + + +## NodeJS Server Required Runtime Support + +If you don't use the `support.load` API to get the runtime support needed for some of the features in your NodeJS server, then you need to initialize them manually during your server startup. + + +### `babel-core/register` + +If you are using the babel-register mode, then you need to load the `babel-register` module. If you don't have a build step for your server code, then you must transpile on the fly using `babel-register`. For performance reasons, we recommend @@ -165,7 +225,7 @@ require("babel-core/register")({ }); ``` -###### Use the `cssModuleHook` in your server entry point for isomorphic CSS modules +### `cssModuleHook` for isomorphic CSS modules If you want to enable isomorphic css modules, the server needs to know how to import css files and generate unique class names. @@ -177,24 +237,6 @@ var supports = require("electrode-archetype-react-app/supports"); supports.cssModuleHook(); ``` -###### Define client entry points - -By default, the archetype uses `client/app.js` or `client/app.jsx` as a client entry point. Alternatively, -you can define multiple entry points for your application, so the Webpack will create bundles for each app -entry point. To do that, place `entry.config.js` module into your app's `client` directory: - -Here is an **example** of `entry.config.js`: -```js -module.exports = { - "web": "./app-web.js", - "ios": "./app-ios.js", - "android": "./app-android.js" -}; -``` - -## Tasks - -Run `gulp` to see list of tasks. Built with :heart: by [Team Electrode](https://github.com/orgs/electrode-io/people) @WalmartLabs. @@ -204,3 +246,5 @@ Built with :heart: by [Team Electrode](https://github.com/orgs/electrode-io/peop [daviddm-url]: https://david-dm.org/electrode-io/electrode?path=packages/electrode-archetype-react-app [daviddm-dev-image]:https://david-dm.org/electrode-io/electrode/dev-status.svg?path=packages/electrode-archetype-react-app [daviddm-dev-url]:https://david-dm.org/electrode-io/electrode?path=packages/electrode-archetype-react-app?type-dev +[generator-electrode]: https://www.npmjs.com/package/generator-electrode +[Electrode getting started]: http://www.electrode.io/docs/get_started.html diff --git a/packages/electrode-archetype-react-app/arch-gulpfile.js b/packages/electrode-archetype-react-app/arch-gulpfile.js index a6255c3ea..3742f1cc8 100644 --- a/packages/electrode-archetype-react-app/arch-gulpfile.js +++ b/packages/electrode-archetype-react-app/arch-gulpfile.js @@ -1,25 +1,29 @@ "use strict"; -const Path = require("path"); -const fs = require("fs"); +const Fs = require("fs"); const archetype = require("./config/archetype"); -const gulpHelper = archetype.devRequire("electrode-gulp-helper"); +const assert = require("assert"); + +assert(!archetype.noDev, "dev archetype is missing - development & build tasks not possible"); + +const Path = archetype.Path; +const devRequire = archetype.devRequire; +const gulpHelper = devRequire("electrode-gulp-helper"); +const chalk = devRequire("chalk"); const shell = gulpHelper.shell; -const mkdirp = archetype.devRequire("mkdirp"); const config = archetype.config; +const mkdirp = devRequire("mkdirp"); +const envify = devRequire("gulp-envify"); +const uglify = devRequire("gulp-uglify"); +const filter = devRequire("gulp-filter"); + const penthouse = archetype.devRequire("penthouse"); const CleanCSS = archetype.devRequire('clean-css'); - -const envify = archetype.devRequire("gulp-envify"); -const uglify = archetype.devRequire("gulp-uglify"); -const filter = archetype.devRequire("gulp-filter"); -const assert = require("assert"); - function setupPath() { - const nmBin = "node_modules/.bin"; + const nmBin = Path.join("node_modules", ".bin"); gulpHelper.envPath.addToFront(Path.resolve(nmBin)); - gulpHelper.envPath.addToFront(Path.join(archetype.devPath, nmBin)); + gulpHelper.envPath.addToFront(Path.join(archetype.devDir, nmBin)); gulpHelper.envPath.addToFront(Path.join(__dirname, nmBin)); } @@ -41,6 +45,31 @@ function setOptimizeStats() { const exec = gulpHelper.exec; +const eTmpDir = archetype.eTmpDir; + +function createGitIgnoreDir(dir, comment) { + comment = comment || ""; + const dirFP = Path.resolve(dir); + try { + mkdirp.sync(dirFP); + } catch(e) { + console.log("mkdir", e); + } + + const gitIgnore = Path.join(dirFP, ".gitignore"); + if (!Fs.existsSync(gitIgnore)) { + Fs.writeFileSync(gitIgnore, `# ${comment}\n*\n`); + } +} + +function createElectrodeTmpDir() { + createGitIgnoreDir(Path.resolve(eTmpDir), "Electrode tmp dir"); +} + +function mkCmd(a) { + return Array.isArray(a) ? a.join(" ") : Array.prototype.slice.call(arguments).join(" "); +} + /* * There are multiple eslint config for different groups of code @@ -60,7 +89,7 @@ function lint(options) { const checkCustom = (t) => { const f = ["", ".json", ".yml"].find((e) => { const x = Path.resolve(Path.join(t, `.eslintrc${e}`)); - return fs.existsSync(x); + return Fs.existsSync(x); }); return f !== undefined; }; @@ -90,32 +119,14 @@ function lint(options) { return promise; } -function checkFrontendCov(minimum) { - if (typeof minimum === "string") { - minimum += "."; - } else { - minimum = ""; - } - return exec(`istanbul check-coverage 'coverage/client/*/coverage.json' --config=${config.istanbul}/.istanbul.${minimum}yml`); -} - -function createElectrodeTmpDir() { - const eTmp = Path.resolve(".etmp"); - const eTmpGitIgnore = Path.join(eTmp, ".gitignore"); - if (!fs.existsSync(eTmpGitIgnore)) { - mkdirp.sync(eTmp); - fs.writeFileSync(eTmpGitIgnore, "# Electrode tmp dir\n*\n"); - } -} - /** * [generateServiceWorker gulp task to generate service worker code that will precache specific resources so they work offline.] * */ function generateServiceWorker() { const NODE_ENV = process.env.NODE_ENV; - const serviceWorkerExists = fs.existsSync("./service-worker.js"); - const serviceWorkerConfigExists = fs.existsSync("config/sw-precache-config.json"); + const serviceWorkerExists = Fs.existsSync("./service-worker.js"); + const serviceWorkerConfigExists = Fs.existsSync("config/sw-precache-config.json"); /** * Determines whether the fetch event handler is included in the generated service worker code, @@ -145,7 +156,7 @@ function inlineCriticalCSS(cb) { const PATH = process.env.CRITICAL_PATH || '/'; const url = `http://${HOST}:${PORT}${PATH}`; const statsPath = Path.resolve(process.cwd(), 'dist/server/stats.json'); - const stats = JSON.parse(fs.readFileSync(statsPath)); + const stats = JSON.parse(Fs.readFileSync(statsPath)); const cssAsset = stats.assets.find((asset) => asset.name.endsWith('.css')); const cssAssetPath = Path.resolve(process.cwd(), `dist/js/${cssAsset.name}`); const targetPath = Path.resolve(process.cwd(), 'dist/js/critical.css'); @@ -167,7 +178,7 @@ function inlineCriticalCSS(cb) { throw err; } const minifiedCSS = new CleanCSS().minify(css).styles; - fs.writeFile(targetPath, minifiedCSS, (err) => { + Fs.writeFile(targetPath, minifiedCSS, (err) => { if (err) { throw err; } @@ -176,6 +187,7 @@ function inlineCriticalCSS(cb) { }); }); } + /* * * For information on how to specify a task, see: @@ -185,10 +197,21 @@ function inlineCriticalCSS(cb) { */ function makeTasks(gulp) { + const checkFrontendCov = (minimum) => { + if (typeof minimum === "string") { + minimum += "."; + } else { + minimum = ""; + } + return exec(`istanbul check-coverage 'coverage/client/*/coverage.json'`, + `--config=${config.istanbul}/.istanbul.${minimum}yml`); + }; + const optimizeModuleForProd = (module) => { - const modulePath = Path.resolve(Path.join("node_modules", module)); + const modulePath = Path.resolve("node_modules", module); assert(shell.test("-d", modulePath), `${modulePath} is not a directory`); - const prodPath = Path.join(".prod", module); + createGitIgnoreDir(Path.resolve(archetype.prodModulesDir), "Electrode production modules dir"); + const prodPath = Path.join(archetype.prodModulesDir, module); return new Promise((resolve, reject) => gulp.src(`${modulePath}/**/*.js`) .pipe(filter(["**", "!**/dist/**"])) @@ -215,109 +238,184 @@ function makeTasks(gulp) { }); }; - return { + const makeBabelRc = (destDir, rcFile) => { + const fn = Path.resolve(destDir, ".babelrc"); + if (!Fs.existsSync(fn)) { + console.log(chalk.green(`INFO: generating ${fn} for you - please commit it.`)); + const rc = JSON.stringify({ + extends: `${Path.join(archetype.devPkg.name, "config", "babel", rcFile)}` + }, null, 2); + Fs.writeFileSync(fn, `${rc}\n`); + } + }; + + const AppMode = archetype.AppMode; + + let tasks = { + ".mk-prod-dir": () => createGitIgnoreDir(Path.resolve(archetype.prodDir), "Electrode production dir"), ".production-env": () => setProductionEnv(), - ".static-files-env": () => setStaticFilesEnv(), ".webpack-dev": () => setWebpackDev(), + ".static-files-env": () => setStaticFilesEnv(), ".optimize-stats": () => setOptimizeStats(), "build": { - desc: "Build your app's client bundle for production", - task: ["ss-prod-react", "build-dist"] - }, - "build-analyze": { - dep: [".optimize-stats"], - desc: "Build your app's client bundle for production and run bundle analyzer", - task: ["build-dist", "optimize-stats"] + desc: AppMode.isSrc + ? `Build your app's ${AppMode.src.dir} directory into ${AppMode.lib.dir} for production` + : "Build your app's client bundle", + task: ["build-dist", ".build-lib", "ss-prod-react", ".check.top.level.babelrc"] }, + + // + // browser coverage + // ".build-browser-coverage-1": () => { setProductionEnv(); - return exec(`webpack --config ${config.webpack}/webpack.config.browsercoverage.js --colors`); + return exec(`webpack`, + `--config ${config.webpack}/webpack.config.browsercoverage.js`, + `--colors`); }, "build-browser-coverage": { desc: "Build browser coverage", - task: ["clean-dist", ".build-browser-coverage-1", "build-dist:flatten-l10n", "build-dist:clean-tmp"] + task: [".clean.dist", ".build-browser-coverage-1", "build-dist:flatten-l10n", "build-dist:clean-tmp"] }, + "build-dev-static": { desc: "Build static copy of your app's client bundle for development", - task: ["clean-dist", "build-dist-dev-static"] + task: [".clean.dist", "build-dist-dev-static"] }, - "build-dist": ["clean-dist", "build-dist-min", "build-dist:flatten-l10n", "generate-service-worker", "build-dist:clean-tmp"], + + "build-dist": [".clean.dist", ".clean.dll", "build-dist-dll", "build-dist-min", "build-dist:flatten-l10n", + "build-dist:merge-isomorphic-assets", "copy-dll", "build-dist:clean-tmp"], + "build-dist-dev-static": { desc: false, task: `webpack --config ${config.webpack}/webpack.config.dev.static.js --colors` }, + ".ss-prod-react": () => optimizeModuleForProd("react"), ".ss-prod-react-dom": () => optimizeModuleForProd("react-dom"), - ".ss-clean-prod-react": () => { - shell.rm("-rf", ".prod/react"); - shell.rm("-rf", ".prod/react-dom"); + ".ss-clean.prod-react": () => { + shell.rm("-rf", + Path.join(archetype.prodModulesDir, "react"), + Path.join(archetype.prodModulesDir, "react-dom")); }, "ss-prod-react": { - desc: "Make optimized copy of react&react-dom for server side in directory .prod", - dep: [".ss-clean-prod-react"], + desc: `Make optimized copy of react&react-dom for server side in dir ${archetype.prodModulesDir}`, + dep: [".ss-clean.prod-react", ".mk-prod-dir"], task: [[".ss-prod-react", ".ss-prod-react-dom"]] }, - "electrify": ["clean-dist", "build-webpack-stats-with-fullpath", "build-dist:clean-tmp", "run-electrify-cli"], - "build-webpack-stats-with-fullpath": { - desc: "Build static bundle with stats.json containing fullPaths to inspect the bundle on electrode-electrify", - task: `webpack --config ${config.webpack}/webpack.config.stats.electrify.js --colors` + + "build-dist-dll": () => { + setProductionEnv(); + createGitIgnoreDir(Path.resolve("dll"), "Webpack DLL Output dir"); + return exec(`webpack --config ${config.webpack}/webpack.config.dll.js --colors`) }, + "build-dist-min": { dep: [".production-env"], desc: false, task: `webpack --config ${config.webpack}/webpack.config.js --colors` }, + "build-dist:clean-tmp": { desc: false, task: () => shell.rm("-rf", "./tmp") }, + "build-dist:flatten-l10n": { desc: false, - task: `node ${__dirname}/scripts/l10n/flatten-messages.js` + task: `node ${archetype.devDir}/scripts/l10n/flatten-messages.js` }, - "run-electrify-cli": { + + "build-dist:merge-isomorphic-assets": { desc: false, - task: `electrify dist/server/stats.json -O` + task: `node ${archetype.devDir}/scripts/merge-isomorphic-assets.js` + }, + + ".build-lib": () => undefined, + + ".check.top.level.babelrc": () => { + if (AppMode.isSrc && archetype.checkUserBabelRc() !== false) { + console.log(chalk.yellow(` +INFO: You are using src for client & server, archetype will ignore your top level .babelrc +INFO: Please remove your top level .babelrc file if you have no other use of it. +INFO: Individual .babelrc files were generated for you in src/client and src/server +`)); + } }, + + ".clean.lib:client": () => shell.rm("-rf", AppMode.lib.client), + ".mk.lib.client.dir": () => { + createGitIgnoreDir(Path.resolve(AppMode.lib.client), `Electrode app transpiled code from ${AppMode.src.client}`); + }, + + ".build.client.babelrc": () => makeBabelRc(AppMode.src.client, "babelrc-client"), + + "build-lib:client": { + desc: false, + dep: [".clean.lib:client", ".mk.lib.client.dir", ".build.client.babelrc"], + task: mkCmd(`babel`, + `--source-maps=inline --copy-files --out-dir ${AppMode.lib.client}`, + `${AppMode.src.client}`) + }, + + ".clean.lib:server": () => shell.rm("-rf", AppMode.lib.server), + ".mk.lib.server.dir": () => { + createGitIgnoreDir(Path.resolve(AppMode.lib.server), `Electrode app transpiled code from ${AppMode.src.server}`); + }, + + ".build.server.babelrc": () => makeBabelRc(AppMode.src.server, "babelrc-server"), + + "build-lib:server": { + desc: false, + dep: [".clean.lib:server", ".mk.lib.server.dir", ".build.server.babelrc"], + task: mkCmd(`babel`, + `--source-maps=inline --copy-files --out-dir ${AppMode.lib.server}`, + `${AppMode.src.server}`) + }, + "check": ["lint", "test-cov"], "check-ci": ["lint", "test-ci"], "check-cov": ["lint", "test-cov"], "check-dev": ["lint", "test-dev"], - "clean": ["clean-dist"], - "clean-dist": () => shell.rm("-rf", "dist"), + + "clean": [".clean.dist", ".clean.lib", ".clean.prod", ".clean.etmp", ".clean.dll"], + ".clean.dist": () => shell.rm("-rf", "dist"), + ".clean.lib": () => undefined, // to be updated below for src mode + ".clean.prod": () => shell.rm("-rf", archetype.prodDir), + ".clean.etmp": () => shell.rm("-rf", eTmpDir), + ".clean.dll": () => shell.rm("-rf", "dll"), + + "copy-dll": () => shell.cp("-r", "dll/*", "dist"), + "cov-frontend": () => checkFrontendCov(), "cov-frontend-50": () => checkFrontendCov("50"), "cov-frontend-70": () => checkFrontendCov("70"), "cov-frontend-85": () => checkFrontendCov("85"), "cov-frontend-95": () => checkFrontendCov("95"), + "debug": ["build-dev-static", "server-debug"], "dev": { desc: "Start server with watch in development mode with webpack-dev-server", task: [".webpack-dev", ["server-dev", "server-watch", "generate-service-worker"]] }, + "dev-static": { desc: "Start server in development mode with statically built files", - task: ["build-dev-static", "server"] + task: ["build-dev-static", "app-server"] }, + "hot": { desc: "Start server with watch in hot mode with webpack-dev-server", task: [".webpack-dev", ["server-hot", "server-watch", "generate-service-worker"]] }, - "critical-css": { - desc: "Start server and run penthouse to output critical CSS", - task: inlineCriticalCSS - }, - "generate-service-worker": { - desc: "Generate Service Worker using the options provided in the app/config/sw-precache-config.json file for prod/dev/hot mode", - task: () => generateServiceWorker() - }, + "lint": [["lint-client", "lint-client-test", "lint-server", "lint-server-test"]], "lint-client": { desc: "Run eslint on client code in directories client and templates", task: () => lint({ ext: ".js,.jsx", config: `${config.eslint}/.eslintrc-react`, - targets: ["client", "templates"] + targets: [AppMode.src.client, "templates"] }) }, "lint-client-test": { @@ -342,63 +440,171 @@ function makeTasks(gulp) { targets: ["test/server", "test/func"] }) }, - "optimize-stats": { - desc: "Generate a list of all files that went into production bundle JS (results in .etmp)", - task: `analyze-bundle -b dist/js/bundle.*.js -s dist/server/stats.json` - }, + "npm:test": ["check"], - "npm:release": `node ${__dirname}/scripts/map-isomorphic-cdn.js`, - "pwa": { - desc: "PWA must have dist by running `gulp build` first and then start the app server only.", - task: ["build", "server"] - }, - "server": { + "npm:release": `node ${archetype.devDir}/scripts/map-isomorphic-cdn.js`, + + "server": ["app-server"], // keep old server name for backward compat + + "app-server": { desc: "Start the app server only, Must have dist by running `gulp build` first.", - task: `node server/index.js` + task: () => { + AppMode.setEnv(AppMode.lib.dir); + return exec(`node ${Path.join(AppMode.lib.server, "index.js")}`); + } + }, + + "server-debug": () => { + AppMode.setEnv(AppMode.lib.dir); + return exec(`node debug ${Path.join(AppMode.lib.server, "index.js")}`); }, + "server-prod": { dep: [".production-env", ".static-files-env"], desc: "Start server in production mode with static files routes. Must have dist by running `gulp build`.", - task: `node server/index.js` + task: () => { + AppMode.setEnv(AppMode.lib.dir); + return exec(`node ${Path.join(AppMode.lib.server, "index.js")}`); + } }, - "server-debug": `node debug server/index.js`, - ".init-bundle.valid.log": () => fs.writeFileSync(Path.resolve(".etmp/bundle.valid.log"), `${Date.now()}`), + + ".init-bundle.valid.log": () => Fs.writeFileSync(Path.resolve(eTmpDir, "bundle.valid.log"), `${Date.now()}`), + "server-watch": { dep: [".init-bundle.valid.log"], - task: `nodemon -C --ext js,jsx,json,yaml --watch .etmp/bundle.valid.log --watch server --watch config server/index.js --exec node` + desc: "Start app's node server in watch mode with nodemon", + task: () => { + const watches = [ + Path.join(eTmpDir, "bundle.valid.log"), + AppMode.src.server, + "config" + ].map((n) => `--watch ${n}`).join(" "); + AppMode.setEnv(AppMode.src.dir); + const node = AppMode.isSrc ? `babel-node` : "node"; + const serverIndex = Path.join(AppMode.src.server, "index.js"); + return exec(`nodemon`, + `--delay 1 -C --ext js,jsx,json,yaml ${watches}`, + `--exec ${node} ${serverIndex}`); + } }, + "server-dev": { - desc: "Start server in dev mode with webpack-dev-server", - task: `webpack-dev-server --config ${config.webpack}/webpack.config.dev.js --progress --colors --port ${archetype.webpack.devPort}` + desc: "Start webpack-dev-server in dev mode", + task: mkCmd("webpack-dev-server", + `--config ${config.webpack}/webpack.config.dev.js`, + `--progress --colors`, + `--port ${archetype.webpack.devPort}`) }, + "server-hot": { - desc: "Start server in hot mode with webpack-dev-server", - task: `webpack-dev-server --config ${config.webpack}/webpack.config.hot.js --hot --progress --colors --port ${archetype.webpack.devPort} --inline` + desc: "Start webpack-dev-server in hot mode", + task: mkCmd("webpack-dev-server", + `--config ${config.webpack}/webpack.config.hot.js`, + `--hot --progress --colors --inline`, + `--port ${archetype.webpack.devPort}`) }, + "server-test": { - desc: "Start server in test mode with webpack-dev-server", - task: `webpack-dev-server --config ${config.webpack}/webpack.config.test.js --progress --colors --port ${archetype.webpack.testPort}` + desc: "Start webpack-dev-server in test mode", + task: mkCmd("webpack-dev-server", + `--config ${config.webpack}/webpack.config.test.js`, + `--progress --colors`, + `--port ${archetype.webpack.testPort}`) }, + + "test-server": () => [["lint-server", "lint-server-test"], "test-server-cov"], + "test-watch-all": ["server-test", "test-frontend-dev-watch"], + "test-ci": ["test-frontend-ci"], "test-cov": ["test-frontend-cov", "test-server-cov"], "test-dev": ["test-frontend-dev", "test-server-dev"], + "test-watch": () => exec(`pgrep -fl 'webpack-dev-server.*${archetype.webpack.testPort}`) .then(() => exec(`gulp test-frontend-dev-watch`)) .catch(() => exec(`gulp test-watch-all`)), - "test-watch-all": ["server-test", "test-frontend-dev-watch"], + "test-frontend": `karma start ${config.karma}/karma.conf.js --colors`, - "test-frontend-ci": `karma start --browsers PhantomJS,Firefox ${config.karma}/karma.conf.coverage.js --colors`, - "test-frontend-cov": `karma start ${config.karma}/karma.conf.coverage.js --colors`, + + "test-frontend-ci": mkCmd(`karma`, + `start --browsers PhantomJS,Firefox ${config.karma}/karma.conf.coverage.js`, + `--colors`), + + "test-frontend-cov": mkCmd(`karma`, + `start ${config.karma}/karma.conf.coverage.js`, `--colors`), + "test-frontend-dev": () => exec(`pgrep -fl 'webpack-dev-server.*${archetype.webpack.testPort}'`) .then(() => exec(`karma start ${config.karma}/karma.conf.dev.js --colors`)) .catch(() => exec(`gulp test-frontend`)), - "test-frontend-dev-watch": `karma start ${config.karma}/karma.conf.watch.js --colors --browsers Chrome --no-single-run --auto-watch`, - "test-server": () => [["lint-server", "lint-server-test"], "test-server-cov"], - "test-server-cov": () => shell.test("-d", "test/server") && exec(`istanbul cover _mocha -- -c --opts ${config.mocha}/mocha.opts test/server`), - "test-server-dev": () => shell.test("-d", "test/server") && exec(`mocha -c --opts ${config.mocha}/mocha.opts test/server`) + + "test-frontend-dev-watch": mkCmd(`karma start`, + `${config.karma}/karma.conf.watch.js`, + `--colors --browsers Chrome --no-single-run --auto-watch`), + + "test-server-cov": () => { + if (shell.test("-d", "test/server")) { + AppMode.setEnv(AppMode.src.dir); + return exec(`istanbul cover _mocha`, + `-- -c --opts ${config.mocha}/mocha.opts test/server`); + } + }, + + "test-server-dev": () => { + if (shell.test("-d", "test/server")) { + AppMode.setEnv(AppMode.src.dir); + return exec(`mocha`, + `-c --opts ${config.mocha}/mocha.opts test/server`); + } + }, + + "build-analyze": { + dep: [".optimize-stats"], + desc: "Build your app's client bundle for production and run bundle analyzer", + task: ["build-dist", "optimize-stats"] + }, + "run-electrify-cli": { + desc: false, + task: `electrify dist/server/stats.json -O` + }, + "electrify": ["clean-dist", "build-webpack-stats-with-fullpath", "build-dist:clean-tmp", "run-electrify-cli"], + "build-webpack-stats-with-fullpath": { + desc: "Build static bundle with stats.json containing fullPaths to inspect the bundle on electrode-electrify", + task: `webpack --config ${config.webpack}/webpack.config.stats.electrify.js --colors` + }, + "critical-css": { + desc: "Start server and run penthouse to output critical CSS", + task: inlineCriticalCSS + }, + "generate-service-worker": { + desc: "Generate Service Worker using the options provided in the app/config/sw-precache-config.json file for prod/dev/hot mode", + task: () => generateServiceWorker() + }, + "optimize-stats": { + desc: "Generate a list of all files that went into production bundle JS (results in .etmp)", + task: `analyze-bundle -b dist/js/bundle.*.js -s dist/server/stats.json` + }, + "pwa": { + desc: "PWA must have dist by running `gulp build` first and then start the app server only.", + task: ["build", "server"] + } + }; + + if (AppMode.isSrc) { + tasks = Object.assign(tasks, { + ".clean.lib": () => shell.rm("-rf", AppMode.lib.client, AppMode.lib.server, AppMode.savedFile), + ".build-lib:app-mode": () => Fs.writeFileSync(Path.resolve(AppMode.savedFile), JSON.stringify(AppMode, null, 2)), + ".build-lib": { + desc: false, + dep: [".clean.lib", ".mk-prod-dir"], + task: ["build-lib:client", "build-lib:server", ".build-lib:app-mode"] + } + }); + } + + return tasks; } + module.exports = function (gulp) { setupPath(); createElectrodeTmpDir(); diff --git a/packages/electrode-archetype-react-app/config/archetype.js b/packages/electrode-archetype-react-app/config/archetype.js index 0b9ac68c0..70f2e41f8 100644 --- a/packages/electrode-archetype-react-app/config/archetype.js +++ b/packages/electrode-archetype-react-app/config/archetype.js @@ -1,7 +1,11 @@ "use strict"; var Path = require("path"); -var PlatformPath = Path[process.platform] || Path; +Path = Path[process.platform] || Path; +var Fs = require("fs"); +var pkg = require("../package.json"); + +const prodDir = ".prod"; function getInt(str, def) { if (str) { @@ -14,26 +18,125 @@ function getInt(str, def) { return def; } -module.exports = { - PlatformPath, - clientSrcDir: "client", - devRequire: require("electrode-archetype-react-app-dev/require"), - // A normal `require.resolve` looks at `package.json:main`. We instead want - // just the _directory_ of the module. So use heuristic of finding dir of - // package.json which **must** exist at a predictable location. - devPath: Path.dirname(require.resolve("electrode-archetype-react-app-dev/package.json")), - webpack: { - devHostname: "localhost", - devPort: getInt(process.env.WEBPACK_DEV_PORT, 2992), - testPort: getInt(process.env.WEBPACK_TEST_PORT, 3001), - modulesDirectories: [] - }, - config: { - babel: `${__dirname}/babel`, - eslint: `${__dirname}/eslint`, - istanbul: `${__dirname}/istanbul`, - karma: `${__dirname}/karma`, - mocha: `${__dirname}/mocha`, - webpack: `${__dirname}/webpack` +function makeAppMode() { + const client = "client"; + const server = "server"; + + let srcDir = ""; + let libDir = ""; + let saved = {}; + const savedFile = Path.join(prodDir, ".app-mode.json"); + const savedFileFP = Path.resolve(savedFile); + if (Fs.existsSync(Path.resolve("src", client)) || Fs.existsSync(Path.resolve("src", server))) { + srcDir = "src"; + libDir = "lib"; + } else if (Fs.existsSync(savedFileFP)) { + saved = JSON.parse(Fs.readFileSync(savedFileFP)); + } + + if (!srcDir) { + console.log(`Just FYI: There's a new src/lib mode that doesn't need babel-register.`); + } + + const envKey = "APP_SRC_DIR"; + return Object.assign({ + savedFile, + envKey, + isSrc: !!srcDir, + setEnv: (dir) => { + if (dir) { + if (!dir.endsWith("/")) { + dir += "/"; + } + process.env[envKey] = dir; + } else { + delete process.env[envKey]; + } + }, + getEnv: () => { + return process.env[envKey]; + }, + hasEnv: () => { + return !!process.env[envKey]; + }, + src: { + dir: srcDir, + client: Path.join(srcDir, client), + server: Path.join(srcDir, server) + }, + lib: { + dir: libDir, + client: Path.join(libDir, client), + server: Path.join(libDir, server) + } + }, saved); +} + +function checkUserBabelRc() { + const user = Path.resolve(".babelrc"); + if (Fs.existsSync(user)) { + const userRc = JSON.parse(Fs.readFileSync(user).toString()); + if (Object.keys(userRc).length === 1 && typeof userRc.extends === "string" && + userRc.extends.indexOf(pkg.name) >= 0) { + return "extendsOnly"; + } else { + return "custom"; + } } + + return false; +} + + +module.exports = { + dir: Path.resolve(__dirname, ".."), + pkg, + Path, + AppMode: makeAppMode(), + prodDir, + eTmpDir: ".etmp", + prodModulesDir: Path.join(prodDir, "modules"), + checkUserBabelRc }; + +// +// Try to set dev settings, if the dev archetype is available. +// It may have been removed for production deployment. +// +function loadDev() { + const devPkgFile = "electrode-archetype-react-app-dev/package.json"; + let devPkg; + try { + devPkg = require(devPkgFile); + } catch (e) { // eslint-disable-line + module.exports.noDev = true; + return; + } + + const devDir = Path.dirname(require.resolve(devPkgFile)); + const devRequire = require(`${devPkg.name}/require`); + const configDir = `${devDir}/config`; + + Object.assign(module.exports, { + devDir, + devPkg, + devRequire, + webpack: { + devHostname: "localhost", + devPort: getInt(process.env.WEBPACK_DEV_PORT, 2992), + testPort: getInt(process.env.WEBPACK_TEST_PORT, 3001), + modulesDirectories: [] + }, + config: { + babel: `${configDir}/babel`, + eslint: `${configDir}/eslint`, + istanbul: `${configDir}/istanbul`, + karma: `${configDir}/karma`, + mocha: `${configDir}/mocha`, + webpack: `${configDir}/webpack` + } + }); +} + +loadDev(); + diff --git a/packages/electrode-archetype-react-app/config/mocha/mocha.opts b/packages/electrode-archetype-react-app/config/mocha/mocha.opts deleted file mode 100644 index 7784b999a..000000000 --- a/packages/electrode-archetype-react-app/config/mocha/mocha.opts +++ /dev/null @@ -1,4 +0,0 @@ ---require electrode-archetype-react-app/config/mocha/setup.js ---reporter spec ---recursive ---ui bdd diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/babel.js b/packages/electrode-archetype-react-app/config/webpack/partial/babel.js deleted file mode 100644 index 6fe21bbb4..000000000 --- a/packages/electrode-archetype-react-app/config/webpack/partial/babel.js +++ /dev/null @@ -1,21 +0,0 @@ -"use strict"; - -var archetype = require("../../archetype"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; - -module.exports = function (babel) { - return function (config) { - return mergeWebpackConfig(config, { - module: { - loaders: [{ - name: "babel", - test: /\.jsx?$/, - exclude: /(node_modules|\bclient\/vendor\b)/, - // NOTE: webpack.config.hot.js inserts "react-hot" into loaders array - loader: archetype.devRequire.resolve("babel-loader"), - query: babel - }] - } - }); - }; -}; diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/fail.js b/packages/electrode-archetype-react-app/config/webpack/partial/fail.js deleted file mode 100644 index e4c5e6d92..000000000 --- a/packages/electrode-archetype-react-app/config/webpack/partial/fail.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict"; - -var archDevRequire = require("electrode-archetype-react-app-dev/require"); -var mergeWebpackConfig = archDevRequire("webpack-partial").default; -var FailPlugin = archDevRequire("webpack-fail-plugin"); - -module.exports = function () { - return function (config) { - return mergeWebpackConfig(config, { - plugins: [ - FailPlugin - ] - }); - }; -}; diff --git a/packages/electrode-archetype-react-app/config/webpack/partial/images.js b/packages/electrode-archetype-react-app/config/webpack/partial/images.js deleted file mode 100644 index 9a13e39eb..000000000 --- a/packages/electrode-archetype-react-app/config/webpack/partial/images.js +++ /dev/null @@ -1,21 +0,0 @@ -"use strict"; - -var archetype = require("../../archetype"); -var mergeWebpackConfig = archetype.devRequire("webpack-partial").default; -var fileLoader = archetype.devRequire.resolve("file-loader"); -var isomorphicLoader = archetype.devRequire.resolve("isomorphic-loader"); -var cdnLoader = archetype.devRequire.resolve('electrode-cdn-file-loader'); - -module.exports = function () { - return function (config) { - return mergeWebpackConfig(config, { - module: { - loaders: [{ - name: "images", - test: /\.(jpe?g|png|gif|svg)(\?\S*)?$/i, - loader: cdnLoader + "?limit=10000!" + isomorphicLoader - }] - } - }); - }; -}; diff --git a/packages/electrode-archetype-react-app/package.json b/packages/electrode-archetype-react-app/package.json index cdd8636f8..70ba38eb3 100644 --- a/packages/electrode-archetype-react-app/package.json +++ b/packages/electrode-archetype-react-app/package.json @@ -23,12 +23,15 @@ "babel-plugin-transform-react-constant-elements": "^6.5.0", "babel-plugin-transform-react-inline-elements": "^6.6.5", "babel-plugin-transform-runtime": "^6.8.0", - "babel-polyfill": "6.5.0", + "babel-polyfill": "^6.5.0", "babel-preset-es2015": "^6.3.13", "babel-preset-es2015-loose": "^7.0.0", "babel-preset-react": "^6.3.13", "babel-register": "^6.14.0", - "css-modules-require-hook": "^4.0.2" + "css-modules-require-hook": "^4.0.2", + "isomorphic-loader": "^1.6.1", + "react": "^15.0.0 || ^0.14.0", + "react-dom": "^15.0.0 || ^0.14.0" }, "devDependencies": { "babel-eslint": "^7.1.0", diff --git a/packages/electrode-archetype-react-app/support/index.js b/packages/electrode-archetype-react-app/support/index.js new file mode 100644 index 000000000..4f6abbf4e --- /dev/null +++ b/packages/electrode-archetype-react-app/support/index.js @@ -0,0 +1,124 @@ +"use strict"; + +const optimizeModulesForProduction = require("./optimize-modules-for-production"); +const babelRegister = require("babel-register"); +const isomorphicExtendRequire = require("isomorphic-loader/lib/extend-require"); +const babelPolyfill = require("babel-polyfill"); +const archetype = require("../config/archetype"); +const AppMode = archetype.AppMode; +const Path = require("path"); + +const support = { + cssModuleHook: function (options) { + options = options || {}; + options.generateScopedName = options.generateScopedName || "[hash:base64]"; + options.rootDir = options.rootDir || Path.resolve(process.cwd(), "client"); + + require("css-modules-require-hook")(options); + }, + require: require("../require"), + babelRegister, + isomorphicExtendRequire: () => { + return isomorphicExtendRequire({ + processAssets: (assets) => { + let appSrcDir = (AppMode.getEnv() || AppMode.lib.dir).split("/")[0]; + if (appSrcDir !== AppMode.src.dir && assets.marked) { + const marked = assets.marked; + Object.keys(marked).forEach((k) => { + if (k.startsWith(AppMode.src.client) || k.startsWith(AppMode.src.server)) { + const nk = k.replace(AppMode.src.dir, appSrcDir); + marked[nk] = marked[k]; + } + }); + } + + return assets; + } + }); + }, + babelPolyfill, + optimizeModulesForProduction +}; + +if (AppMode.isSrc) { + if (!AppMode.hasEnv()) { + const guessAppSrcDir = () => { + if (module.parent && module.parent.filename) { + const fn = module.parent.filename; + const dir = fn.substr(process.cwd().length + 1).split("/")[0]; + if (dir === AppMode.src.dir || dir === AppMode.lib.dir) { + return `${dir}/`; + } + } + return "lib/"; + }; + AppMode.setEnv(guessAppSrcDir()); + } + console.log(`Just FYI: ${AppMode.envKey} set to`, AppMode.getEnv()); +} + +support.load = function (options, callback) { + if (typeof options === "function") { + callback = options; + options = {}; + } else { + options = options || {}; + } + + let br = options.babelRegister; + if (br !== false) { + if (!br) { + br = !AppMode.isSrc; // we need babel-register if not in src/lib mode + } else { + br = true; // normalize flag to boolean + } + } + + if (br) { + const regOptions = Object.assign({ + extensions: [".es6", ".es", ".jsx", ".js"] + }, options.babelRegister || {}); + + console.log(`Just FYI: installing babel-register for runtime transpilation files (extensions: ${regOptions.extensions}).`); + console.log(`Just FYI: the transpilation only occurs the first time you load a file.`); + support.babelRegister(regOptions); + } + + if (options.optimizeModulesForProduction !== false) { + const opts = options.optimizeModulesForProduction; + + support.optimizeModulesForProduction(typeof opts === "object" && opts); + } + + /** + * css-modules-require-hook: handle css-modules on node.js server. + * similar to Babel's babel/register it compiles CSS modules in runtime. + * + * generateScopedName - Short alias for the postcss-modules-scope plugin's option. + * Helps you to specify the custom way to build generic names for the class selectors. + * You may also use a string pattern similar to the webpack's css-loader. + * + * https://github.com/css-modules/css-modules-require-hook#generatescopedname-function + * https://github.com/webpack/css-loader#local-scope + * https://github.com/css-modules/postcss-modules-scope + */ + if (options.cssModuleHook !== false) { + const opts = Object.assign({ + generateScopedName: "[name]__[local]___[hash:base64:5]" + }, options.cssModuleHook || {}); + + support.cssModuleHook(opts); + } + + if (options.isomorphicExtendRequire !== false) { + return support.isomorphicExtendRequire(); + } + + if (callback) { + callback(); + } else { + return Promise.resolve(); + } +}; + +module.exports = support; diff --git a/packages/electrode-archetype-react-app/scripts/optimize-modules-for-production.js b/packages/electrode-archetype-react-app/support/optimize-modules-for-production.js similarity index 60% rename from packages/electrode-archetype-react-app/scripts/optimize-modules-for-production.js rename to packages/electrode-archetype-react-app/support/optimize-modules-for-production.js index 640304e78..e368c02d1 100644 --- a/packages/electrode-archetype-react-app/scripts/optimize-modules-for-production.js +++ b/packages/electrode-archetype-react-app/support/optimize-modules-for-production.js @@ -1,21 +1,22 @@ "use strict"; const Module = require("module"); -const Path = require("path"); const assert = require("assert"); const fs = require("fs"); +const archetype = require("../config/archetype"); +const Path = archetype.Path; function optimizeModulesForProduction(options) { const originalResolve = Module._resolveFilename; options = options || {}; - const prodDir = Path.resolve(options.prodDir || ".prod"); + const prodModulesDir = Path.resolve(options.prodModulesDir || archetype.prodModulesDir); const isProd = () => process.env.NODE_ENV === "production"; const readProdDir = () => { try { - return fs.readdirSync(prodDir); + return fs.readdirSync(prodModulesDir); } catch (err) { return []; } @@ -24,10 +25,14 @@ function optimizeModulesForProduction(options) { const verbose = !options.quiet; if (!isProd()) { - if (verbose) { - console.log(`NOTICE: optimizeModulesForProduction - skipping since NODE_ENV !== "production"`); // eslint-disable-line + if (!options.force) { + if (verbose) { + console.log(`Just FYI: optimizeModulesForProduction - skipping since NODE_ENV !== "production"`); // eslint-disable-line + } + return; + } else if (verbose) { + console.log(`Just FYI: optimizeModulesForProduction - force enabled`); // eslint-disable-line } - return; } const modules = readProdDir(); @@ -40,7 +45,7 @@ function optimizeModulesForProduction(options) { const notify = (m) => { if (verbose && !notified[m]) { - console.log(`NOTICE: overriding module ${m} with copy optimized for production`); // eslint-disable-line + console.log(`Just FYI: overriding module ${m} with copy optimized for production`); // eslint-disable-line notified[m] = true; } }; @@ -54,14 +59,14 @@ function optimizeModulesForProduction(options) { if (name && !request.startsWith(`${name}/dist`)) { notify(name); - request = Path.join(prodDir, request); + request = Path.join(prodModulesDir, request); } } return originalResolve.call(this, request, parent); }; } else if (verbose) { - console.log(`NOTICE: optimizeModulesForProduction - no optimized modules found in .prod`); // eslint-disable-line + console.log(`Just FYI: optimizeModulesForProduction - no optimized modules found in ${archetype.prodModulesDir}`); // eslint-disable-line } } diff --git a/packages/electrode-archetype-react-app/supports.js b/packages/electrode-archetype-react-app/supports.js index 6068074af..58f489bdf 100644 --- a/packages/electrode-archetype-react-app/supports.js +++ b/packages/electrode-archetype-react-app/supports.js @@ -1,18 +1,3 @@ "use strict"; -const Path = require("path"); - -module.exports = { - cssModuleHook: function(options) { - options = options || {}; - options.generateScopedName = options.generateScopedName || "[hash:base64]"; - options.rootDir = options.rootDir || Path.resolve(process.cwd(), "client"); - - require("css-modules-require-hook")(options); - }, - babelRegister: require("babel-register"), - isomorphicExtendRequire: require("isomorphic-loader/lib/extend-require"), - require: require("./require"), - babelPolyfill: require("babel-polyfill"), - optimizeModulesForProduction: require("./scripts/optimize-modules-for-production") -}; +module.exports = require("./support"); diff --git a/packages/generator-electrode/generators/app/index.js b/packages/generator-electrode/generators/app/index.js index 36e29f732..194017246 100644 --- a/packages/generator-electrode/generators/app/index.js +++ b/packages/generator-electrode/generators/app/index.js @@ -80,7 +80,7 @@ module.exports = generators.Base.extend({ this.props.authorUrl = this.pkg.author.url; this.props.createDirectory = false; this.props.serverType = this.fs.exists(this.destinationPath('server/express-server.js')) ? ExpressJS : - this.fs.exists(this.destinationPath('server/koa-server.js')) ? koaJS : HapiJS; + this.fs.exists(this.destinationPath('server/koa-server.js')) ? koaJS : HapiJS; this.props.pwa = this.fs.exists(this.destinationPath('client/sw-registration.js')); this.props.autoSsr = this.fs.exists(this.destinationPath('server/plugins/autossr.js')); } else if (_.isString(this.pkg.author)) { @@ -271,14 +271,17 @@ module.exports = generators.Base.extend({ pkg.keywords = _.uniq(this.props.keywords.concat(pkg.keywords)).filter((x) => x); } + const sortDep = (dep) => { + if (typeof pkg[dep] === "object") { + pkg[dep] = _.pick(pkg[dep], Object.keys(pkg[dep]).sort()); + } + }; + + ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"].forEach(sortDep); + // Let's extend package.json so we're not overwriting user previous fields this.fs.writeJSON(this.destinationPath('package.json'), pkg); - this.fs.copy( - this.templatePath('babelrc'), - this.destinationPath('.babelrc') - ); - ['gulpfile.js', 'config', 'test'].forEach((f) => { this.fs.copy( this.templatePath(f), @@ -288,19 +291,21 @@ module.exports = generators.Base.extend({ //special handling for the server file this.fs.copyTpl( - this.templatePath('server'), - this.destinationPath('server'), + this.templatePath('src/server'), + this.destinationPath('src/server'), {isHapi, isExpress}, {}, { - globOptions: {ignore: [isHapi ? '**/server/express-server.js, **/server/koa-server.js' : - isExpress ? '**/server/koa-server.js' : '**/server/express-server.js']} + globOptions: { + ignore: [isHapi ? '**/server/express-server.js, **/server/koa-server.js' : + isExpress ? '**/server/koa-server.js' : '**/server/express-server.js'] + } } ); this.fs.copyTpl( - this.templatePath('client'), - this.destinationPath('client'), + this.templatePath('src/client'), + this.destinationPath('src/client'), {pwa: isPWA}, {}, // template options { // copy options @@ -311,9 +316,13 @@ module.exports = generators.Base.extend({ } ); + ['src/client', 'src/server', 'test/client', 'test/server'].forEach((d) => { + this.fs.move(this.destinationPath(d + '/babelrc'), this.destinationPath(d + '/.babelrc')); + }); + this.fs.copy( - this.templatePath('client/images'), - this.destinationPath('client/images') + this.templatePath('src/client/images'), + this.destinationPath('src/client/images') ); }, diff --git a/packages/generator-electrode/generators/app/templates/_package.json b/packages/generator-electrode/generators/app/templates/_package.json index 1de3c7e1a..48f7e970b 100644 --- a/packages/generator-electrode/generators/app/templates/_package.json +++ b/packages/generator-electrode/generators/app/templates/_package.json @@ -10,7 +10,7 @@ "client", "test" ], - "main": "server/index.js", + "main": "lib/server/index.js", "keywords": [], "repository": {}, "license": "ISC", diff --git a/packages/generator-electrode/generators/app/templates/babelrc b/packages/generator-electrode/generators/app/templates/babelrc deleted file mode 100644 index e989ef94f..000000000 --- a/packages/generator-electrode/generators/app/templates/babelrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "./node_modules/electrode-archetype-react-app/config/babel/.babelrc" -} diff --git a/packages/generator-electrode/generators/app/templates/server/index.js b/packages/generator-electrode/generators/app/templates/server/index.js deleted file mode 100644 index a3def9ff8..000000000 --- a/packages/generator-electrode/generators/app/templates/server/index.js +++ /dev/null @@ -1,60 +0,0 @@ -"use strict"; - -const SSRCaching = require("electrode-react-ssr-caching"); - -process.on("SIGINT", () => { - process.exit(0); -}); - -const config = require("electrode-confippet").config; -<% if (isHapi) { %>const staticPathsDecor = require("electrode-static-paths");<% } %> -const supports = require("electrode-archetype-react-app/supports"); - -require.extensions[".css"] = () => { - return; -}; - -/** - * Use babel register to transpile any JSX code on the fly to run - * in server mode, and also transpile react code to apply process.env.NODE_ENV - * removal to improve performance in production mode. - */ -supports.babelRegister({ - ignore: /node_modules\/(?!react\/)/ -}); - -const cacheConfig = { - components: { - SSRCachingTemplateType: { - strategy: "template", - enable: true - }, - SSRCachingSimpleType: { - strategy: "simple", - enable: true - } - } -}; - -SSRCaching.enableCaching(); -SSRCaching.setCachingConfig(cacheConfig); - -/** - * css-modules-require-hook: handle css-modules on node.js server. - * similar to Babel's babel/register it compiles CSS modules in runtime. - * - * generateScopedName - Short alias for the postcss-modules-scope plugin's option. - * Helps you to specify the custom way to build generic names for the class selectors. - * You may also use a string pattern similar to the webpack's css-loader. - * - * https://github.com/css-modules/css-modules-require-hook#generatescopedname-function - * https://github.com/webpack/css-loader#local-scope - * https://github.com/css-modules/postcss-modules-scope - */ -supports.cssModuleHook({ - generateScopedName: "[name]__[local]___[hash:base64:5]" -}); - -supports.isomorphicExtendRequire().then(() => { - require<% if (isHapi) { %>("electrode-server")(config, [staticPathsDecor()])<% } else if (isExpress){ %>("./express-server")(config)<% } else { %>("./koa-server")(config)<% } %>; // eslint-disable-line -}); diff --git a/packages/generator-electrode/generators/app/templates/client/actions/index.jsx b/packages/generator-electrode/generators/app/templates/src/client/actions/index.jsx similarity index 100% rename from packages/generator-electrode/generators/app/templates/client/actions/index.jsx rename to packages/generator-electrode/generators/app/templates/src/client/actions/index.jsx diff --git a/packages/generator-electrode/generators/app/templates/client/app.jsx b/packages/generator-electrode/generators/app/templates/src/client/app.jsx similarity index 100% rename from packages/generator-electrode/generators/app/templates/client/app.jsx rename to packages/generator-electrode/generators/app/templates/src/client/app.jsx diff --git a/packages/generator-electrode/generators/app/templates/src/client/babelrc b/packages/generator-electrode/generators/app/templates/src/client/babelrc new file mode 100644 index 000000000..e4c602292 --- /dev/null +++ b/packages/generator-electrode/generators/app/templates/src/client/babelrc @@ -0,0 +1,3 @@ +{ + "extends": "electrode-archetype-react-app-dev/config/babel/babelrc-client" +} diff --git a/packages/generator-electrode/generators/app/templates/client/components/home.jsx b/packages/generator-electrode/generators/app/templates/src/client/components/home.jsx similarity index 100% rename from packages/generator-electrode/generators/app/templates/client/components/home.jsx rename to packages/generator-electrode/generators/app/templates/src/client/components/home.jsx diff --git a/packages/generator-electrode/generators/app/templates/client/images/electrode.png b/packages/generator-electrode/generators/app/templates/src/client/images/electrode.png similarity index 100% rename from packages/generator-electrode/generators/app/templates/client/images/electrode.png rename to packages/generator-electrode/generators/app/templates/src/client/images/electrode.png diff --git a/packages/generator-electrode/generators/app/templates/client/reducers/index.jsx b/packages/generator-electrode/generators/app/templates/src/client/reducers/index.jsx similarity index 100% rename from packages/generator-electrode/generators/app/templates/client/reducers/index.jsx rename to packages/generator-electrode/generators/app/templates/src/client/reducers/index.jsx diff --git a/packages/generator-electrode/generators/app/templates/client/routes.jsx b/packages/generator-electrode/generators/app/templates/src/client/routes.jsx similarity index 100% rename from packages/generator-electrode/generators/app/templates/client/routes.jsx rename to packages/generator-electrode/generators/app/templates/src/client/routes.jsx diff --git a/packages/generator-electrode/generators/app/templates/client/styles/base.css b/packages/generator-electrode/generators/app/templates/src/client/styles/base.css similarity index 100% rename from packages/generator-electrode/generators/app/templates/client/styles/base.css rename to packages/generator-electrode/generators/app/templates/src/client/styles/base.css diff --git a/packages/generator-electrode/generators/app/templates/client/sw-registration.js b/packages/generator-electrode/generators/app/templates/src/client/sw-registration.js similarity index 100% rename from packages/generator-electrode/generators/app/templates/client/sw-registration.js rename to packages/generator-electrode/generators/app/templates/src/client/sw-registration.js diff --git a/packages/generator-electrode/generators/app/templates/src/server/babelrc b/packages/generator-electrode/generators/app/templates/src/server/babelrc new file mode 100644 index 000000000..ebb946309 --- /dev/null +++ b/packages/generator-electrode/generators/app/templates/src/server/babelrc @@ -0,0 +1,3 @@ +{ + "extends": "electrode-archetype-react-app-dev/config/babel/babelrc-server" +} diff --git a/packages/generator-electrode/generators/app/templates/server/express-server.js b/packages/generator-electrode/generators/app/templates/src/server/express-server.js similarity index 100% rename from packages/generator-electrode/generators/app/templates/server/express-server.js rename to packages/generator-electrode/generators/app/templates/src/server/express-server.js diff --git a/packages/generator-electrode/generators/app/templates/src/server/index.js b/packages/generator-electrode/generators/app/templates/src/server/index.js new file mode 100644 index 000000000..3fe3fd842 --- /dev/null +++ b/packages/generator-electrode/generators/app/templates/src/server/index.js @@ -0,0 +1,38 @@ +"use strict"; + +const SSRCaching = require("electrode-react-ssr-caching"); + +process.on("SIGINT", () => { + process.exit(0); +}); + +const electrodeConfippet = require("electrode-confippet"); +<% if (isHapi) { %>const staticPathsDecor = require("electrode-static-paths");<% } %> +const support = require("electrode-archetype-react-app/support"); + +require.extensions[".css"] = () => { + return; +}; + +const cacheConfig = { + components: { + SSRCachingTemplateType: { + strategy: "template", + enable: true + }, + SSRCachingSimpleType: { + strategy: "simple", + enable: true + } + } +}; + +support.load() + .then(() => { + const config = electrodeConfippet.config; + + SSRCaching.enableCaching(); + SSRCaching.setCachingConfig(cacheConfig); + + require<% if (isHapi) { %>("electrode-server")(config, [staticPathsDecor()])<% } else if (isExpress){ %>("./express-server")(config)<% } else { %>("./koa-server")(config)<% } %>; // eslint-disable-line + }); diff --git a/packages/generator-electrode/generators/app/templates/server/koa-server.js b/packages/generator-electrode/generators/app/templates/src/server/koa-server.js similarity index 100% rename from packages/generator-electrode/generators/app/templates/server/koa-server.js rename to packages/generator-electrode/generators/app/templates/src/server/koa-server.js diff --git a/packages/generator-electrode/generators/app/templates/test/client/babelrc b/packages/generator-electrode/generators/app/templates/test/client/babelrc new file mode 100644 index 000000000..e4c602292 --- /dev/null +++ b/packages/generator-electrode/generators/app/templates/test/client/babelrc @@ -0,0 +1,3 @@ +{ + "extends": "electrode-archetype-react-app-dev/config/babel/babelrc-client" +} diff --git a/packages/generator-electrode/generators/app/templates/test/client/components/home.spec.jsx b/packages/generator-electrode/generators/app/templates/test/client/components/home.spec.jsx index 020b5912e..e72b18565 100644 --- a/packages/generator-electrode/generators/app/templates/test/client/components/home.spec.jsx +++ b/packages/generator-electrode/generators/app/templates/test/client/components/home.spec.jsx @@ -2,7 +2,7 @@ import React from "react"; import ReactDOM from "react-dom"; import Home from "client/components/home"; import {createStore} from "redux"; -import rootReducer from "../../../client/reducers"; +import rootReducer from "client/reducers"; describe("Home", () => { let component; diff --git a/packages/generator-electrode/generators/app/templates/test/server/babelrc b/packages/generator-electrode/generators/app/templates/test/server/babelrc new file mode 100644 index 000000000..ebb946309 --- /dev/null +++ b/packages/generator-electrode/generators/app/templates/test/server/babelrc @@ -0,0 +1,3 @@ +{ + "extends": "electrode-archetype-react-app-dev/config/babel/babelrc-server" +} diff --git a/packages/generator-electrode/generators/config/templates/default.js b/packages/generator-electrode/generators/config/templates/default.js index 09e743282..10fb64c5e 100644 --- a/packages/generator-electrode/generators/config/templates/default.js +++ b/packages/generator-electrode/generators/config/templates/default.js @@ -19,7 +19,9 @@ module.exports = { "pathPrefix": "dist" } },<%if(pwa){%> - "./server/plugins/pwa": {},<%} if(isAutoSsr){%> + "server/plugins/pwa": { + "module": "./{{env.APP_SRC_DIR}}/server/plugins/pwa" + },<%} if(isAutoSsr){%> "electrode-auto-ssr": {}, <%}%> "webapp": { "module": <%if(serverType==="HapiJS"){%>"electrode-react-webapp/lib/hapi"<%}else if (serverType==="ExpressJS"){%>"electrode-react-webapp/lib/express"<%} else {%>"electrode-react-webapp/lib/koa"<%}%>, @@ -28,7 +30,7 @@ module.exports = { "paths": { "<%= routeValue %>": { "content": { - "module": "./server/views/index-view" + "module": "./{{env.APP_SRC_DIR}}/server/views/index-view" } } } diff --git a/packages/generator-electrode/generators/webapp/index.js b/packages/generator-electrode/generators/webapp/index.js index 35f60a47f..b0ffd1bad 100644 --- a/packages/generator-electrode/generators/webapp/index.js +++ b/packages/generator-electrode/generators/webapp/index.js @@ -23,8 +23,8 @@ module.exports = generators.Base.extend({ const isHapi = this.config.get('serverType') === 'hapijs'; this.fs.copyTpl( - this.templatePath('server'), - this.destinationPath(this.options.generateInto, 'server'), + this.templatePath('src/server'), + this.destinationPath(this.options.generateInto, 'src/server'), { isHapi, pwa: this.options.pwa @@ -42,8 +42,8 @@ module.exports = generators.Base.extend({ if (this.options.pwa) { this.fs.copy( - this.templatePath('server/plugins/pwa.js'), - this.destinationPath(this.options.generateInto, 'server/plugins/pwa.js') + this.templatePath('src/server/plugins/pwa.js'), + this.destinationPath(this.options.generateInto, 'src/server/plugins/pwa.js') ); } } diff --git a/packages/generator-electrode/generators/webapp/templates/server/plugins/pwa.js b/packages/generator-electrode/generators/webapp/templates/src/server/plugins/pwa.js similarity index 100% rename from packages/generator-electrode/generators/webapp/templates/server/plugins/pwa.js rename to packages/generator-electrode/generators/webapp/templates/src/server/plugins/pwa.js diff --git a/packages/generator-electrode/generators/webapp/templates/server/views/index-view.js b/packages/generator-electrode/generators/webapp/templates/src/server/views/index-view.js similarity index 100% rename from packages/generator-electrode/generators/webapp/templates/server/views/index-view.js rename to packages/generator-electrode/generators/webapp/templates/src/server/views/index-view.js diff --git a/samples/universal-react-node/client/app.jsx b/samples/universal-react-node/client/app.jsx index 532c65321..1f96bc770 100644 --- a/samples/universal-react-node/client/app.jsx +++ b/samples/universal-react-node/client/app.jsx @@ -5,9 +5,9 @@ import {Router, browserHistory} from "react-router"; import {createStore, compose, applyMiddleware} from "redux"; import {Provider} from "react-redux"; import {notify} from "react-notify-toast"; -import "styles/base.css"; +import "./styles/base.css"; import rootReducer from "./reducers"; -import DevTools from "../client/devtools"; +import DevTools from "./devtools"; import updateStorage from "./middleware"; require.ensure(["./sw-registration"], (require) => {