Niedzielski has uploaded a new change for review. ( 
https://gerrit.wikimedia.org/r/391467 )

Change subject: WIP: use Webpack for server production builds
......................................................................

WIP: use Webpack for server production builds

Bug: T177235
Change-Id: I04fa8f52dfd19da9b6a9f90a59daa559353a8efe
---
M docs/setting-a-staging-server.md
M package-lock.json
M package.json
M src/server/index.tsx
D src/server/types/ignore-styles.d.ts
M webpack.config.ts
6 files changed, 83 insertions(+), 118 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/marvin refs/changes/67/391467/1

diff --git a/docs/setting-a-staging-server.md b/docs/setting-a-staging-server.md
index dd92f23..6429f14 100644
--- a/docs/setting-a-staging-server.md
+++ b/docs/setting-a-staging-server.md
@@ -165,7 +165,7 @@
 cp -R node_modules/ /home/marvin/dist/node_modules
 
 echo "Running new server version"
-NODE_ENV=production node /home/marvin/dist/server/index.js &
+node /home/marvin/dist &
 ```
 
 And make it executable
@@ -183,7 +183,7 @@
 added 115 packages in 12.848s
 
 > [email protected] build /home/marvin/sources
-> NODE_ENV=production npm-run-all --silent clean --parallel server:build 
'client:build -- -p'
+> NODE_ENV=production webpack
 
 Removing previously started server processes
 Copying new tarball
diff --git a/package-lock.json b/package-lock.json
index 523d7a9..8106353 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -145,6 +145,15 @@
         "@types/uglify-js": "2.6.29"
       }
     },
+    "@types/webpack-node-externals": {
+      "version": "1.6.0",
+      "resolved": 
"https://registry.npmjs.org/@types/webpack-node-externals/-/webpack-node-externals-1.6.0.tgz";,
+      "integrity": 
"sha512-9O3qTR4rDvHIUtKw1Uy95W3r7Ipac7E/obKwdiqdhqTbCuaAyyTpPOA7LFceqxB1TXs2NbFWxorbCkqmb5eFlw==",
+      "dev": true,
+      "requires": {
+        "@types/webpack": "3.0.9"
+      }
+    },
     "abbrev": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz";,
@@ -1214,20 +1223,6 @@
         "mkdirp": "0.5.1",
         "rimraf": "2.6.2",
         "run-queue": "1.0.3"
-      }
-    },
-    "copyfiles": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-1.2.0.tgz";,
-      "integrity": "sha1-qNo6xBqiIgrim9PFi2mEKU8sWTw=",
-      "dev": true,
-      "requires": {
-        "glob": "7.1.2",
-        "ltcdr": "2.2.1",
-        "minimatch": "3.0.4",
-        "mkdirp": "0.5.1",
-        "noms": "0.0.0",
-        "through2": "2.0.3"
       }
     },
     "core-util-is": {
@@ -3035,6 +3030,12 @@
       "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=",
       "dev": true
     },
+    "ignore-loader": {
+      "version": "0.1.2",
+      "resolved": 
"https://registry.npmjs.org/ignore-loader/-/ignore-loader-0.1.2.tgz";,
+      "integrity": "sha1-2B8kA3bQuk8Nd4lyw60lh0EXpGM=",
+      "dev": true
+    },
     "ignore-styles": {
       "version": "5.0.1",
       "resolved": 
"https://registry.npmjs.org/ignore-styles/-/ignore-styles-5.0.1.tgz";,
@@ -3944,12 +3945,6 @@
         "yallist": "2.1.2"
       }
     },
-    "ltcdr": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/ltcdr/-/ltcdr-2.2.1.tgz";,
-      "integrity": "sha1-Wrh60dTB2rjowIu/A37gwZAih88=",
-      "dev": true
-    },
     "macaddress": {
       "version": "0.2.8",
       "resolved": 
"https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz";,
@@ -4479,42 +4474,6 @@
         "touch": "3.1.0",
         "undefsafe": "0.0.3",
         "update-notifier": "2.3.0"
-      }
-    },
-    "noms": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz";,
-      "integrity": "sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=",
-      "dev": true,
-      "requires": {
-        "inherits": "2.0.3",
-        "readable-stream": "1.0.34"
-      },
-      "dependencies": {
-        "isarray": {
-          "version": "0.0.1",
-          "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz";,
-          "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "1.0.34",
-          "resolved": 
"https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz";,
-          "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=",
-          "dev": true,
-          "requires": {
-            "core-util-is": "1.0.2",
-            "inherits": "2.0.3",
-            "isarray": "0.0.1",
-            "string_decoder": "0.10.31"
-          }
-        },
-        "string_decoder": {
-          "version": "0.10.31",
-          "resolved": 
"https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz";,
-          "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
-          "dev": true
-        }
       }
     },
     "nopt": {
@@ -7778,6 +7737,12 @@
         }
       }
     },
+    "webpack-node-externals": {
+      "version": "1.6.0",
+      "resolved": 
"https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-1.6.0.tgz";,
+      "integrity": "sha1-Iyxi7GCSsQBjWj0p2DwXRxKN+b0=",
+      "dev": true
+    },
     "webpack-sources": {
       "version": "1.0.1",
       "resolved": 
"https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.0.1.tgz";,
diff --git a/package.json b/package.json
index f2c786c..1d98047 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
     "--- PRIMARY ---": "# Frequent user scripts.",
     "start": "run-p -s start:\\*",
     "watch": "run-p -s start:\\* test:watch",
-    "build": "NODE_ENV=production run-p -s build:\\*",
+    "build": "NODE_ENV=production webpack",
     "format": "npm run -s lint -- --fix",
     "lint": "eslint --cache --max-warnings 0 
--report-unused-disable-directives --ext ts,tsx,js,json .",
     "test": "run-p -s lint build mocha",
@@ -17,9 +17,7 @@
     "precommit": "npm test -s",
     "--- INTERNAL ---": "# Private scripts.",
     "start:server": "nodemon -i dist -i src/client -e js,json,ts,tsx 
${VERBOSE:--q} -x 'ts-node -P src/server src/server'",
-    "start:client": "webpack-dev-server -dw",
-    "build:server": "tsc -p src/server && copyup 'src/**/*.{css,svg}' dist/",
-    "build:client": "webpack"
+    "start:client": "webpack-dev-server -dw"
   },
   "repository": {
     "type": "git",
@@ -61,9 +59,9 @@
     "@types/mocha": "2.2.44",
     "@types/node": "8.0.47",
     "@types/node-fetch": "1.6.7",
+    "@types/webpack-node-externals": "1.6.0",
     "assets-webpack-plugin": "3.5.1",
     "clean-webpack-plugin": "0.1.17",
-    "copyfiles": "1.2.0",
     "css-loader": "0.28.7",
     "eslint": "4.10.0",
     "eslint-config-node-services": "2.2.3",
@@ -76,6 +74,7 @@
     "extract-text-webpack-plugin": "3.0.2",
     "history": "4.7.2",
     "husky": "0.14.3",
+    "ignore-loader": "0.1.2",
     "ignore-styles": "5.0.1",
     "mocha": "4.0.1",
     "nodemon": "1.12.1",
@@ -91,6 +90,7 @@
     "uglifyjs-webpack-plugin": "1.0.1",
     "webpack": "3.6.0",
     "webpack-dev-server": "2.9.1",
+    "webpack-node-externals": "1.6.0",
     "wikimedia-ui-base": "0.9.2"
   }
 }
diff --git a/src/server/index.tsx b/src/server/index.tsx
index 9352630..feb5c7d 100644
--- a/src/server/index.tsx
+++ b/src/server/index.tsx
@@ -1,21 +1,7 @@
-import * as fs from "fs";
-// Ignore importing style and image files when running on Node.js
-import register from "ignore-styles";
-register(undefined, (module: any, filename: string) => {
-  // Fake that requiring SVG files returns a default export with the string of
-  // the svg, which is what svg-inline-loader does with webpack for the client
-  // code.
-  // TODO: Consider using wepback for node code to avoid this and the CSS hacks
-  if (filename.endsWith(".svg")) {
-    module.exports = { default: fs.readFileSync(filename).toString() };
-  }
-});
-
 import * as express from "express";
 import * as compression from "compression";
 import { h } from "preact";
 import { render as renderToString } from "preact-render-to-string";
-
 import { RouteResponse, newRouter } from "../common/routers/router";
 import { RedirectError } from "../common/http/fetch-with-redirect";
 import { routes } from "../common/routers/api";
@@ -26,18 +12,19 @@
   WEBPACK_DEV_SERVER_URL
 } from "./config";
 import HTMLPage from "./components/html-page";
+declare function __non_webpack_require__(name: string): any; // 
eslint-disable-line camelcase
 
 // The asset manifest built or the webpack-dev-server URL (which has no
 // manifest).
 const manifest = PRODUCTION
-  ? require("../../dist/public/assets-manifest.json")
+  ? __non_webpack_require__("./public/assets-manifest.json")
   : WEBPACK_DEV_SERVER_URL;
 
 const server = express();
 
 server.use(compression());
 
-server.use("/public", express.static("dist/public"));
+server.use("/public", express.static("public"));
 
 const render = ({ chunkName, Component, props }: RouteResponse<any>) => {
   return (
diff --git a/src/server/types/ignore-styles.d.ts 
b/src/server/types/ignore-styles.d.ts
deleted file mode 100644
index 5ac4223..0000000
--- a/src/server/types/ignore-styles.d.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-declare module "ignore-styles" {
-  interface onLoad {
-    (module: any, filename: string): any;
-  }
-  function register(extensions: any, cb: onLoad): void;
-  export default register;
-}
diff --git a/webpack.config.ts b/webpack.config.ts
index a350ec3..6fb2a02 100644
--- a/webpack.config.ts
+++ b/webpack.config.ts
@@ -3,6 +3,7 @@
 import * as ExtractTextPlugin from "extract-text-webpack-plugin";
 import * as webpack from "webpack";
 import * as CleanPlugin from "clean-webpack-plugin";
+import * as nodeExternals from "webpack-node-externals";
 import {
   PRODUCTION,
   VERBOSE,
@@ -15,9 +16,7 @@
 
 const PATHS = {
   // Files used by the client and the server.
-  public: {
-    output: path.resolve("./dist/public/")
-  }
+  public: { output: path.resolve("dist/public/") }
 };
 
 // `chunkhash` is used instead of `hash` to get per-file / chunk hashes instead
@@ -37,7 +36,29 @@
   warnings: true
 };
 
-const config: webpack.Configuration = {
+const DEV_TOOL = PRODUCTION ? "source-map" : "cheap-module-eval-source-map";
+
+// Add `.ts` and `.tsx` as a resolvable extension.
+const EXTENSIONS = [".ts", ".tsx", ".js"];
+
+// Embed values of process.env.NODE_ENV and other variables in the code.
+// This allows to embed information in the source itself at build time, and it
+// is used for example to have uglify remove code at minification time,
+// getting rid of development only code (for exmaple, like preact/debug)
+const definePlugin = new webpack.DefinePlugin({
+  "process.env": {
+    NODE_ENV: JSON.stringify(PRODUCTION ? "production" : "development")
+  },
+  VERSION: JSON.stringify(pkg.version)
+});
+
+const typescriptLoader = {
+  test: /\.tsx?$/,
+  loader: "ts-loader",
+  options: { logLevel: VERBOSE ? "info" : "warn" }
+};
+
+const client: webpack.Configuration = {
   entry: {
     // _The_ browser entry point is needed to load and render pages and perform
     // routing and has full-control over the site's browsing experience
@@ -123,19 +144,12 @@
       preact$: "preact/dist/preact.js"
     },
 
-    // Add `.ts` and `.tsx` as a resolvable extension.
-    extensions: [".ts", ".tsx", ".js"]
+    extensions: EXTENSIONS
   },
 
   module: {
     rules: [
-      {
-        test: /\.tsx?$/,
-        loader: "ts-loader",
-        options: {
-          logLevel: VERBOSE ? "info" : "warn"
-        }
-      },
+      typescriptLoader,
       {
         test: /\.css$/,
         use: ExtractTextPlugin.extract({
@@ -153,14 +167,14 @@
         test: /\.svg$/,
         loader: "svg-inline-loader",
         options: {
-          /* Don't remove SVG attributes, which defaulted to true */
+          // Don't remove SVG attributes, which defaulted to true.
           removeSVGTagAttrs: false
         }
       }
     ]
   },
 
-  devtool: PRODUCTION ? "source-map" : "cheap-module-eval-source-map",
+  devtool: DEV_TOOL,
 
   // For development builds, serve the packaged result over
   // http://localhost:8080/ and live reload the browser when the bundle is
@@ -195,19 +209,10 @@
 
 // See also
 // 
https://medium.com/webpack/predictable-long-term-caching-with-webpack-d3eee1d3fa31.
-config.plugins = [
+client.plugins = [
   new CleanPlugin(["dist"], { verbose: VERBOSE }),
 
-  // Embed values of process.env.NODE_ENV and other variables in the code.
-  // This allows to embed information in the source itself at build time, and 
it
-  // is used for example to have uglify remove code at minification time,
-  // getting rid of development only code (for exmaple, like preact/debug)
-  new webpack.DefinePlugin({
-    "process.env": {
-      NODE_ENV: JSON.stringify(PRODUCTION ? "production" : "development")
-    },
-    VERSION: JSON.stringify(pkg.version)
-  }),
+  definePlugin,
 
   // Reference modules by name instead of by chunk ID so hashes don't change
   // when new files are added. For example,
@@ -252,10 +257,7 @@
   // parent chunks, when used a minimum number of times. 3 times used is
   // considered right now like a good tradeoff.
   // 
https://webpack.js.org/plugins/commons-chunk-plugin/#move-common-modules-into-the-parent-chunk
-  new webpack.optimize.CommonsChunkPlugin({
-    children: true,
-    minChunks: 3
-  }),
+  new webpack.optimize.CommonsChunkPlugin({ children: true, minChunks: 3 }),
 
   // Create a separate chunk for the client's Webpack runtime. When a name with
   // no corresponding entry and no chunks configuration is specified, Webpack's
@@ -315,7 +317,7 @@
   // inlined within dynamic imports like `/* webpackChunkName: "pages/home" 
*/`.
   // The property values are derived from `config.output.publicPath` and either
   // `CHUNK_FILENAME` or, for CSS only, `ExtractPluginOptions.filename`.
-  config.plugins.push(
+  client.plugins.push(
     new AssetsPlugin({
       prettyPrint: VERBOSE,
       filename: "assets-manifest.json",
@@ -335,4 +337,22 @@
   );
 }
 
-export default config;
+const server: webpack.Configuration = {
+  target: "node",
+  entry: "./src/server",
+  stats: STATS,
+  output: { path: path.resolve("dist"), filename: "index.js" },
+
+  devtool: DEV_TOOL,
+
+  resolve: { extensions: EXTENSIONS },
+
+  externals: nodeExternals(),
+  module: {
+    rules: [{ test: /\.(css|svg)$/, loader: "ignore-loader" }, 
typescriptLoader]
+  },
+
+  plugins: [definePlugin]
+};
+
+export default [client, server];

-- 
To view, visit https://gerrit.wikimedia.org/r/391467
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I04fa8f52dfd19da9b6a9f90a59daa559353a8efe
Gerrit-PatchSet: 1
Gerrit-Project: marvin
Gerrit-Branch: master
Gerrit-Owner: Niedzielski <[email protected]>
Gerrit-Reviewer: Sniedzielski <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to