From 0161d11621b5e1181a9960ff19eb83bff44c7d80 Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 3 Dec 2020 15:05:58 +1100 Subject: [PATCH 1/2] feat(lib): Cross-platform compatability --- package.json | 2 ++ src/NodejsFunction.ts | 26 ++++++++++++++------------ yarn.lock | 25 ++++++++++++++++--------- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index e6a68a6..61f122c 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "@babel/preset-env": "7.10.4", "babel-loader": "8.1.0", "babel-plugin-source-map-support": "2.1.2", + "cross-spawn": "7.0.3", "find-up": "5.0.0", "noop2": "2.0.0", "source-map-support": "0.5.19", @@ -58,6 +59,7 @@ "devDependencies": { "@aws-cdk/aws-lambda": "1.54.0", "@aws-cdk/core": "1.54.0", + "@types/cross-spawn": "6.0.2", "@typescript-eslint/eslint-plugin": "^3.7.1", "@typescript-eslint/parser": "^3.7.1", "eslint-plugin-import": "2.22.0", diff --git a/src/NodejsFunction.ts b/src/NodejsFunction.ts index 9b6b24f..9db51ae 100644 --- a/src/NodejsFunction.ts +++ b/src/NodejsFunction.ts @@ -2,7 +2,7 @@ import * as fs from "fs"; import * as path from "path"; import * as os from "os"; import * as process from "process"; -import { spawnSync } from "child_process"; +import * as spawn from "cross-spawn"; import findUp from "find-up"; import * as lambda from "@aws-cdk/aws-lambda"; @@ -123,15 +123,17 @@ export class NodejsFunction extends lambda.Function { }; }, {}); + const nodeifyPath = (path: string) => path.replace(/\\/g, '\\\\'); + const webpackConfiguration = ` const { builtinModules } = require("module"); const { NormalModuleReplacementPlugin } = require("${ - pluginsPaths["webpack"] + nodeifyPath(pluginsPaths["webpack"]) }"); module.exports = { mode: "none", - entry: "${entryFullPath}", + entry: "${nodeifyPath(entryFullPath)}", target: "node", resolve: { modules: ["node_modules", "."], @@ -144,12 +146,12 @@ export class NodejsFunction extends lambda.Function { test: /\\.js$/, exclude: /node_modules/, use: { - loader: "${pluginsPaths["babel-loader"]}", + loader: "${nodeifyPath(pluginsPaths["babel-loader"])}", options: { cacheDirectory: true, presets: [ [ - "${pluginsPaths["@babel/preset-env"]}", + "${nodeifyPath(pluginsPaths["@babel/preset-env"])}", { "targets": { "node": "${ @@ -162,8 +164,8 @@ export class NodejsFunction extends lambda.Function { ] ], plugins: [ - "${pluginsPaths["@babel/plugin-transform-runtime"]}", - "${pluginsPaths["babel-plugin-source-map-support"]}" + "${nodeifyPath(pluginsPaths["@babel/plugin-transform-runtime"])}", + "${nodeifyPath(pluginsPaths["babel-plugin-source-map-support"])}" ] } } @@ -178,15 +180,15 @@ export class NodejsFunction extends lambda.Function { externals: [...builtinModules, "aws-sdk"], output: { filename: "[name].js", - path: "${outputDir}", + path: "${nodeifyPath(outputDir)}", libraryTarget: "commonjs2", }, ${(props.modulesToIgnore && ` plugins: [ new NormalModuleReplacementPlugin( - /${props.modulesToIgnore.join("|")}/, - "${pluginsPaths["noop2"]}", + /${nodeifyPath(props.modulesToIgnore.join("|"))}/, + "${nodeifyPath(pluginsPaths["noop2"])}", ), ] `) || @@ -196,12 +198,12 @@ export class NodejsFunction extends lambda.Function { fs.writeFileSync(webpackConfigPath, webpackConfiguration); // to implement cache, create a script that uses webpack API, store cache in a file with JSON.stringify, based on entry path key then reuse it - const webpack = spawnSync(webpackBinPath, ["--config", webpackConfigPath], { + const webpack = spawn.sync(webpackBinPath, ["--config", webpackConfigPath], { cwd: process.cwd(), }); if (webpack.status !== 0) { - console.error("webpack had an error when bundling."); + console.error(`webpack had an error when bundling. Return status was ${webpack.status}`); console.error( webpack?.output?.map(out => { return out?.toString(); diff --git a/yarn.lock b/yarn.lock index 5fdb987..58aed81 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1470,6 +1470,13 @@ resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/cross-spawn@6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/cross-spawn/-/cross-spawn-6.0.2.tgz#168309de311cd30a2b8ae720de6475c2fbf33ac7" + integrity sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw== + dependencies: + "@types/node" "*" + "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" @@ -3869,6 +3876,15 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +cross-spawn@7.0.3, cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + cross-spawn@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -3889,15 +3905,6 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" From 8151d293a933e1e3aa0b2dc2ba0a0616ee67904c Mon Sep 17 00:00:00 2001 From: Ben Beattie-Hood Date: Wed, 9 Dec 2020 11:06:01 +1100 Subject: [PATCH 2/2] Added comment to explain `escapePathForNodeJs` --- src/NodejsFunction.ts | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/NodejsFunction.ts b/src/NodejsFunction.ts index 9db51ae..86bbea5 100644 --- a/src/NodejsFunction.ts +++ b/src/NodejsFunction.ts @@ -123,17 +123,20 @@ export class NodejsFunction extends lambda.Function { }; }, {}); - const nodeifyPath = (path: string) => path.replace(/\\/g, '\\\\'); + // NodeJs reserves '\' as an escape char; but pluginsPaths etc are inlined directly in the + // TemplateString below, so will contain this escape character on paths computed when running + // the Construct on a Windows machine, and so we need to escape these chars before writing them + const escapePathForNodeJs = (path: string) => path.replace(/\\/g, '\\\\'); const webpackConfiguration = ` const { builtinModules } = require("module"); const { NormalModuleReplacementPlugin } = require("${ - nodeifyPath(pluginsPaths["webpack"]) + escapePathForNodeJs(pluginsPaths["webpack"]) }"); module.exports = { mode: "none", - entry: "${nodeifyPath(entryFullPath)}", + entry: "${escapePathForNodeJs(entryFullPath)}", target: "node", resolve: { modules: ["node_modules", "."], @@ -146,12 +149,12 @@ export class NodejsFunction extends lambda.Function { test: /\\.js$/, exclude: /node_modules/, use: { - loader: "${nodeifyPath(pluginsPaths["babel-loader"])}", + loader: "${escapePathForNodeJs(pluginsPaths["babel-loader"])}", options: { cacheDirectory: true, presets: [ [ - "${nodeifyPath(pluginsPaths["@babel/preset-env"])}", + "${escapePathForNodeJs(pluginsPaths["@babel/preset-env"])}", { "targets": { "node": "${ @@ -164,8 +167,8 @@ export class NodejsFunction extends lambda.Function { ] ], plugins: [ - "${nodeifyPath(pluginsPaths["@babel/plugin-transform-runtime"])}", - "${nodeifyPath(pluginsPaths["babel-plugin-source-map-support"])}" + "${escapePathForNodeJs(pluginsPaths["@babel/plugin-transform-runtime"])}", + "${escapePathForNodeJs(pluginsPaths["babel-plugin-source-map-support"])}" ] } } @@ -180,15 +183,15 @@ export class NodejsFunction extends lambda.Function { externals: [...builtinModules, "aws-sdk"], output: { filename: "[name].js", - path: "${nodeifyPath(outputDir)}", + path: "${escapePathForNodeJs(outputDir)}", libraryTarget: "commonjs2", }, ${(props.modulesToIgnore && ` plugins: [ new NormalModuleReplacementPlugin( - /${nodeifyPath(props.modulesToIgnore.join("|"))}/, - "${nodeifyPath(pluginsPaths["noop2"])}", + /${escapePathForNodeJs(props.modulesToIgnore.join("|"))}/, + "${escapePathForNodeJs(pluginsPaths["noop2"])}", ), ] `) ||