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
