diff --git a/plugins/NativeScriptSnapshotPlugin/index.js b/plugins/NativeScriptSnapshotPlugin/index.js index 97cebd7e..fe32ce1b 100644 --- a/plugins/NativeScriptSnapshotPlugin/index.js +++ b/plugins/NativeScriptSnapshotPlugin/index.js @@ -97,6 +97,11 @@ exports.NativeScriptSnapshotPlugin = (function () { NativeScriptSnapshotPlugin.prototype.generate = function (webpackChunks) { const options = this.options; + if (options.skipSnapshotTools) { + console.log(`Skipping snapshot tools.`); + return Promise.resolve(); + } + const inputFiles = webpackChunks.map(chunk => join(options.webpackConfig.output.path, chunk.files[0])); const preprocessedInputFile = join( this.options.projectRoot, @@ -113,6 +118,8 @@ exports.NativeScriptSnapshotPlugin = (function () { useLibs: options.useLibs, androidNdkPath: options.androidNdkPath, v8Version: options.v8Version, + snapshotInDocker: options.snapshotInDocker, + skipSnapshotTools: options.skipSnapshotTools }).then(() => { // Make the original files empty inputFiles.forEach(inputFile => diff --git a/plugins/NativeScriptSnapshotPlugin/options.json b/plugins/NativeScriptSnapshotPlugin/options.json index d20b74ac..22e08d60 100644 --- a/plugins/NativeScriptSnapshotPlugin/options.json +++ b/plugins/NativeScriptSnapshotPlugin/options.json @@ -5,8 +5,8 @@ "type": "string" }, "angular": { - "type": "boolean", - "default": false + "type": "boolean", + "default": false }, "chunk": { "type": "string" @@ -39,6 +39,13 @@ "type": "boolean", "default": false }, + "snapshotInDocker": { + "type": "boolean" + }, + "skipSnapshotTools": { + "type": "boolean", + "default": false + }, "v8Version": { "type": "string" }, @@ -56,4 +63,4 @@ "webpackConfig" ], "additionalProperties": false -} +} \ No newline at end of file diff --git a/snapshot/android/project-snapshot-generator-cli-ags-parser.js b/snapshot/android/project-snapshot-generator-cli-ags-parser.js index 76af75b1..6d3b9b61 100644 --- a/snapshot/android/project-snapshot-generator-cli-ags-parser.js +++ b/snapshot/android/project-snapshot-generator-cli-ags-parser.js @@ -9,6 +9,14 @@ module.exports = function parseProjectSnapshotGeneratorArgs() { result.useLibs = parseBool(result.useLibs); } + if (result.snapshotInDocker !== undefined) { + result.snapshotInDocker = parseBool(result.snapshotInDocker); + } + + if (result.skipSnapshotTools !== undefined) { + result.skipSnapshotTools = parseBool(result.skipSnapshotTools); + } + if (result.install !== undefined) { result.install = parseBool(result.install); } @@ -22,7 +30,7 @@ function parseJsonFromProcessArgs() { var currentKey = ""; var currentValue = ""; - args.forEach(function(value, index, array) { + args.forEach(function (value, index, array) { if (value.startsWith("--")) { // if is key addKeyAndValueToResult(currentKey, currentValue, result); currentKey = value.slice(2); diff --git a/snapshot/android/project-snapshot-generator.js b/snapshot/android/project-snapshot-generator.js index a208024f..b8a778f8 100644 --- a/snapshot/android/project-snapshot-generator.js +++ b/snapshot/android/project-snapshot-generator.js @@ -10,7 +10,6 @@ const { createDirectory, getJsonFile, } = require("./utils"); -const { getPackageJson } = require("../../projectHelpers"); const { ANDROID_PROJECT_DIR, ANDROID_APP_PATH, @@ -114,7 +113,9 @@ ProjectSnapshotGenerator.installSnapshotArtefacts = function (projectRoot) { shelljs.cp("-R", blobsSrcPath + "/", resolve(appPath, "../snapshots")); /* - Rename TNSSnapshot.blob files to snapshot.blob files. The xxd tool uses the file name for the name of the static array. This is why the *.blob files are initially named TNSSnapshot.blob. After the xxd step, they must be renamed to snapshot.blob, because this is the filename that the Android runtime is looking for. + Rename TNSSnapshot.blob files to snapshot.blob files. The xxd tool uses the file name for the name of the static array. + This is why the *.blob files are initially named TNSSnapshot.blob. + After the xxd step, they must be renamed to snapshot.blob, because this is the filename that the Android runtime is looking for. */ shelljs.exec("find " + blobsDestinationPath + " -name '*.blob' -execdir mv {} snapshot.blob ';'"); @@ -170,7 +171,7 @@ ProjectSnapshotGenerator.prototype.getV8Version = function (generationOptions) { // try to get the V8 Version from the settings.json file in android runtime folder const runtimeV8Version = getAndroidV8Version(this.options.projectRoot); - if(runtimeV8Version) { + if (runtimeV8Version) { return resolve(runtimeV8Version); } @@ -184,7 +185,7 @@ ProjectSnapshotGenerator.prototype.getV8Version = function (generationOptions) { const version = findV8Version(runtimeVersion, latestVersionsMap) return resolve(version); }) - .catch(reject); + .catch(reject); } else { return resolve(v8Version); } @@ -209,6 +210,11 @@ ProjectSnapshotGenerator.prototype.validateAndroidRuntimeVersion = function () { } ProjectSnapshotGenerator.prototype.generate = function (generationOptions) { + if (generationOptions.skipSnapshotTools) { + console.log("Skipping snapshot tools."); + return Promise.resolve(); + } + generationOptions = generationOptions || {}; console.log("Running snapshot generation with the following arguments: "); @@ -254,7 +260,8 @@ ProjectSnapshotGenerator.prototype.generate = function (generationOptions) { useLibs: generationOptions.useLibs || false, inputFiles: generationOptions.inputFiles || [join(this.options.projectRoot, "__snapshot.js")], androidNdkPath, - mksnapshotParams: mksnapshotParams + mksnapshotParams: mksnapshotParams, + snapshotInDocker: generationOptions.snapshotInDocker }; return generator.generate(options).then(() => { diff --git a/snapshot/android/snapshot-generator.js b/snapshot/android/snapshot-generator.js index 4de65fa6..270524dc 100644 --- a/snapshot/android/snapshot-generator.js +++ b/snapshot/android/snapshot-generator.js @@ -1,12 +1,14 @@ const fs = require("fs"); -const { dirname, join, EOL } = require("path"); -const os = require("os"); +const { dirname, relative, join, EOL } = require("path"); const child_process = require("child_process"); +const { convertToUnixPath } = require("../../lib/utils"); const shelljs = require("shelljs"); -const { createDirectory, downloadFile } = require("./utils"); +const { createDirectory, downloadFile, getHostOS, getHostOSArch, CONSTANTS, has32BitArch, isMacOSCatalinaOrHigher, isSubPath } = require("./utils"); +const SNAPSHOTS_DOCKER_IMAGE = "nativescript/v8-snapshot:latest"; +const SNAPSHOT_TOOLS_DIR_NAME = "mksnapshot-tools"; const NDK_BUILD_SEED_PATH = join(__dirname, "snapshot-generator-tools/ndk-build"); const BUNDLE_PREAMBLE_PATH = join(__dirname, "snapshot-generator-tools/bundle-preamble.js"); const BUNDLE_ENDING_PATH = join(__dirname, "snapshot-generator-tools/bundle-ending.js"); @@ -14,6 +16,8 @@ const INCLUDE_GRADLE_PATH = join(__dirname, "snapshot-generator-tools/include.gr const MKSNAPSHOT_TOOLS_DOWNLOAD_ROOT_URL = "https://raw.githubusercontent.com/NativeScript/mksnapshot-tools/production/"; const MKSNAPSHOT_TOOLS_DOWNLOAD_TIMEOUT = 60000; const SNAPSHOT_BLOB_NAME = "TNSSnapshot"; +const DOCKER_IMAGE_OS = "linux"; +const DOCKER_IMAGE_ARCH = "x64"; function shellJsExecuteInDir(dir, action) { const currentDir = shelljs.pwd(); @@ -25,17 +29,6 @@ function shellJsExecuteInDir(dir, action) { } } -function getHostOS() { - const hostOS = os.type().toLowerCase(); - if (hostOS.startsWith("darwin")) - return "darwin"; - if (hostOS.startsWith("linux")) - return "linux"; - if (hostOS.startsWith("win")) - return "win"; - return hostOS; -} - function SnapshotGenerator(options) { this.buildPath = options.buildPath || join(__dirname, "build"); } @@ -43,6 +36,20 @@ module.exports = SnapshotGenerator; SnapshotGenerator.SNAPSHOT_PACKAGE_NANE = "nativescript-android-snapshot"; +SnapshotGenerator.prototype.shouldSnapshotInDocker = function (hostOS, targetArchs) { + let shouldSnapshotInDocker = false; + const generateInDockerMessage = "The snapshots will be generated in a docker container."; + if (hostOS == CONSTANTS.WIN_OS_NAME) { + console.log(`The V8 snapshot tools are not supported on Windows. ${generateInDockerMessage}`); + shouldSnapshotInDocker = true; + } else if (isMacOSCatalinaOrHigher() && has32BitArch(targetArchs)) { + console.log(`Starting from macOS Catalina, the 32-bit processes are no longer supported. ${generateInDockerMessage}`); + shouldSnapshotInDocker = true; + } + + return shouldSnapshotInDocker; +} + SnapshotGenerator.prototype.preprocessInputFiles = function (inputFiles, outputFile) { // Make some modifcations on the original bundle and save it on the specified path const bundlePreambleContent = fs.readFileSync(BUNDLE_PREAMBLE_PATH, "utf8"); @@ -67,9 +74,26 @@ SnapshotGenerator.prototype.preprocessInputFiles = function (inputFiles, outputF const snapshotToolsDownloads = {}; -SnapshotGenerator.prototype.downloadMksnapshotTool = function (snapshotToolsPath, v8Version, targetArch) { - const hostOS = getHostOS(); - const mksnapshotToolRelativePath = join("mksnapshot-tools", "v8-v" + v8Version, hostOS + "-" + os.arch(), "mksnapshot-" + targetArch); +SnapshotGenerator.prototype.downloadMksnapshotTools = function (snapshotToolsPath, v8Version, targetArchs, snapshotInDocker) { + var toolsOS = ""; + var toolsArch = ""; + if (snapshotInDocker) { + toolsOS = DOCKER_IMAGE_OS; + toolsArch = DOCKER_IMAGE_ARCH; + } else { + toolsOS = getHostOS(); + toolsArch = getHostOSArch(); + } + + return Promise.all(targetArchs.map((arch) => { + return this.downloadMksnapshotTool(snapshotToolsPath, v8Version, arch, toolsOS, toolsArch).then(path => { + return { path, arch }; + }); + })); +} + +SnapshotGenerator.prototype.downloadMksnapshotTool = function (snapshotToolsPath, v8Version, targetArch, hostOS, hostArch) { + const mksnapshotToolRelativePath = join(SNAPSHOT_TOOLS_DIR_NAME, "v8-v" + v8Version, hostOS + "-" + hostArch, "mksnapshot-" + targetArch); const mksnapshotToolPath = join(snapshotToolsPath, mksnapshotToolRelativePath); if (fs.existsSync(mksnapshotToolPath)) return Promise.resolve(mksnapshotToolPath); @@ -106,70 +130,68 @@ SnapshotGenerator.prototype.convertToAndroidArchName = function (archName) { } } -SnapshotGenerator.prototype.runMksnapshotTool = function (snapshotToolsPath, inputFile, v8Version, targetArchs, buildCSource, mksnapshotParams) { +SnapshotGenerator.prototype.generateSnapshots = function (snapshotToolsPath, inputFile, v8Version, targetArchs, buildCSource, mksnapshotParams, snapshotInDocker) { // Cleans the snapshot build folder shelljs.rm("-rf", join(this.buildPath, "snapshots")); + return this.downloadMksnapshotTools(snapshotToolsPath, v8Version, targetArchs, snapshotInDocker).then((localTools) => { + var shouldDownloadDockerTools = false; + if (!snapshotInDocker) { + snapshotInDocker = localTools.some(tool => !this.canUseSnapshotTool(tool.path)); + shouldDownloadDockerTools = snapshotInDocker; + } - const mksnapshotStdErrPath = join(this.buildPath, "mksnapshot-stderr.txt"); + if (shouldDownloadDockerTools) { + return this.downloadMksnapshotTools(snapshotToolsPath, v8Version, targetArchs, snapshotInDocker).then((dockerTools) => { + console.log(`Generating snapshots in a docker container.`); + return this.runMksnapshotTools(snapshotToolsPath, dockerTools, inputFile, mksnapshotParams, buildCSource, snapshotInDocker); + }); + } - return Promise.all(targetArchs.map((arch) => { - return this.downloadMksnapshotTool(snapshotToolsPath, v8Version, arch).then((currentArchMksnapshotToolPath) => { - if (!fs.existsSync(currentArchMksnapshotToolPath)) { - throw new Error("Can't find mksnapshot tool for " + arch + " at path " + currentArchMksnapshotToolPath); - } + return this.runMksnapshotTools(snapshotToolsPath, localTools, inputFile, mksnapshotParams, buildCSource, snapshotInDocker); + }); +} - const androidArch = this.convertToAndroidArchName(arch); - console.log("***** Generating snapshot for " + androidArch + " *****"); - // Generate .blob file - const currentArchBlobOutputPath = join(this.buildPath, "snapshots/blobs", androidArch); - shelljs.mkdir("-p", currentArchBlobOutputPath); - var params = "--profile_deserialization"; - if (mksnapshotParams) { - // Starting from android runtime 5.3.0, the parameters passed to mksnapshot are read from the settings.json file - params = mksnapshotParams; - } - const command = `${currentArchMksnapshotToolPath} ${inputFile} --startup_blob ${join(currentArchBlobOutputPath, `${SNAPSHOT_BLOB_NAME}.blob`)} ${params}`; - - return new Promise((resolve, reject) => { - const child = child_process.exec(command, { encoding: "utf8" }, (error, stdout, stderr) => { - const errorHeader = `Target architecture: ${androidArch}\n`; - let errorFooter = ``; - if (stderr.length || error) { - try { - require(inputFile); - } catch (e) { - errorFooter = `\nJavaScript execution error: ${e.stack}$`; - } - } - - if (stderr.length) { - const message = `${errorHeader}${stderr}${errorFooter}`; - reject(new Error(message)); - } else if (error) { - error.message = `${errorHeader}${error.message}${errorFooter}`; - reject(error); - } else { - console.log(stdout); - resolve(); - } - }) - }).then(() => { - // Generate .c file - if (buildCSource) { - const currentArchSrcOutputPath = join(this.buildPath, "snapshots/src", androidArch); - shelljs.mkdir("-p", currentArchSrcOutputPath); - shellJsExecuteInDir(currentArchBlobOutputPath, function () { - shelljs.exec(`xxd -i ${SNAPSHOT_BLOB_NAME}.blob > ${join(currentArchSrcOutputPath, `${SNAPSHOT_BLOB_NAME}.c`)}`); - }); - } +SnapshotGenerator.prototype.runMksnapshotTools = function (snapshotToolsBasePath, snapshotTools, inputFile, mksnapshotParams, buildCSource, snapshotInDocker) { + let currentSnapshotOperation = Promise.resolve(); + const canRunInParallel = !snapshotInDocker; + return Promise.all(snapshotTools.map((tool) => { + if (canRunInParallel) { + return this.runMksnapshotTool(tool, mksnapshotParams, inputFile, snapshotInDocker, snapshotToolsBasePath, buildCSource); + } else { + currentSnapshotOperation = currentSnapshotOperation.then(() => { + return this.runMksnapshotTool(tool, mksnapshotParams, inputFile, snapshotInDocker, snapshotToolsBasePath, buildCSource); }); - }); + + return currentSnapshotOperation; + } })).then(() => { console.log("***** Finished generating snapshots. *****"); }); } +SnapshotGenerator.prototype.canUseSnapshotTool = function (snapshotToolPath) { + try { + child_process.execSync(`${snapshotToolPath} --help`); + return true; + } + catch (error) { + console.log(`Unable to execute '${snapshotToolPath}' locally.Error message: '${error.message}'`); + return false; + } +} + +SnapshotGenerator.prototype.setupDocker = function () { + try { + child_process.execSync(`docker --version`); + } + catch (error) { + throw new Error(`Docker installation cannot be found. Install Docker and add it to your PATH in order to build snapshots.`); + } + + child_process.execSync(`docker pull ${SNAPSHOTS_DOCKER_IMAGE}`); +} + SnapshotGenerator.prototype.buildSnapshotLibs = function (androidNdkBuildPath, targetArchs) { // Compile *.c files to produce *.so libraries with ndk-build tool const ndkBuildPath = join(this.buildPath, "ndk-build"); @@ -199,15 +221,18 @@ SnapshotGenerator.prototype.generate = function (options) { console.log("***** Starting snapshot generation using V8 version: ", options.v8Version); this.preprocessInputFiles(options.inputFiles, preprocessedInputFile); + const hostOS = getHostOS(); + const snapshotInDocker = options.snapshotInDocker || this.shouldSnapshotInDocker(hostOS, options.targetArchs); // generates the actual .blob and .c files - return this.runMksnapshotTool( + return this.generateSnapshots( options.snapshotToolsPath, preprocessedInputFile, options.v8Version, options.targetArchs, options.useLibs, - options.mksnapshotParams + options.mksnapshotParams, + snapshotInDocker ).then(() => { this.buildIncludeGradle(); if (options.useLibs) { @@ -217,3 +242,134 @@ SnapshotGenerator.prototype.generate = function (options) { return this.buildPath; }); } + +SnapshotGenerator.prototype.getSnapshotToolCommand = function (snapshotToolPath, inputFilePath, outputPath, toolParams) { + return `${snapshotToolPath} ${inputFilePath} --startup_blob ${join(outputPath, `${SNAPSHOT_BLOB_NAME}.blob`)} ${toolParams}`; +} + +SnapshotGenerator.prototype.getXxdCommand = function (srcOutputDir) { + // https://github.com/NativeScript/docker-images/tree/master/v8-snapshot/bin + return `/bin/xxd -i ${SNAPSHOT_BLOB_NAME}.blob > ${join(srcOutputDir, `${SNAPSHOT_BLOB_NAME}.c`)}`; +} + +SnapshotGenerator.prototype.getPathInDocker = function (mappedLocalDir, mappedDockerDir, targetPath) { + if (!isSubPath(mappedLocalDir, targetPath)) { + throw new Error(`Cannot determine a docker path. '${targetPath}' should be inside '${mappedLocalDir}'`) + } + + const pathInDocker = join(mappedDockerDir, relative(mappedLocalDir, targetPath)); + + return convertToUnixPath(pathInDocker); +} + +SnapshotGenerator.prototype.handleSnapshotToolResult = function (error, stdout, stderr, inputFile, androidArch) { + let toolError = null; + const errorHeader = `Target architecture: ${androidArch}\n`; + let errorFooter = ``; + if ((stderr && stderr.length) || error) { + try { + require(inputFile); + } + catch (e) { + errorFooter = `\nJavaScript execution error: ${e.stack}$`; + } + } + + if (stderr && stderr.length) { + const message = `${errorHeader}${stderr}${errorFooter}`; + toolError = new Error(message); + } + else if (error) { + error.message = `${errorHeader}${error.message}${errorFooter}`; + toolError = error; + } else { + console.log(stdout); + } + + return toolError; +} + +SnapshotGenerator.prototype.copySnapshotTool = function (allToolsDir, targetTool, destinationDir) { + // we cannot mount the source tools folder as its not shared by default: + // docker: Error response from daemon: Mounts denied: + // The path /var/folders/h2/1yck52fx2mg7c790vhcw90s8087sk8/T/snapshot-tools/mksnapshot-tools + // is not shared from OS X and is not known to Docker. + const toolPathRelativeToAllToolsDir = relative(allToolsDir, targetTool); + const toolDestinationPath = join(destinationDir, toolPathRelativeToAllToolsDir) + createDirectory(dirname(toolDestinationPath)); + shelljs.cp(targetTool, toolDestinationPath); + + return toolDestinationPath; +} + +SnapshotGenerator.prototype.buildCSource = function (androidArch, blobInputDir, snapshotInDocker) { + const srcOutputDir = join(this.buildPath, "snapshots/src", androidArch); + createDirectory(srcOutputDir); + let command = ""; + if (snapshotInDocker) { + const blobsInputInDocker = `/blobs/${androidArch}` + const srcOutputDirInDocker = `/dist/src/${androidArch}`; + const buildCSourceCommand = this.getXxdCommand(srcOutputDirInDocker); + command = `docker run --rm -v "${blobInputDir}:${blobsInputInDocker}" -v "${srcOutputDir}:${srcOutputDirInDocker}" ${SNAPSHOTS_DOCKER_IMAGE} /bin/sh -c "cd ${blobsInputInDocker} && ${buildCSourceCommand}"`; + } + else { + command = this.getXxdCommand(srcOutputDir); + } + shellJsExecuteInDir(blobInputDir, function () { + shelljs.exec(command); + }); +} + +SnapshotGenerator.prototype.runMksnapshotTool = function (tool, mksnapshotParams, inputFile, snapshotInDocker, snapshotToolsPath, buildCSource) { + const toolPath = tool.path; + const androidArch = this.convertToAndroidArchName(tool.arch); + if (!fs.existsSync(toolPath)) { + throw new Error(`Can't find mksnapshot tool for ${androidArch} at path ${toolPath}`); + } + + const tempFolders = []; + return new Promise((resolve, reject) => { + console.log("***** Generating snapshot for " + androidArch + " *****"); + const inputFileDir = dirname(inputFile); + const blobOutputDir = join(this.buildPath, "snapshots/blobs", androidArch); + createDirectory(blobOutputDir); + const toolParams = mksnapshotParams || "--profile_deserialization"; + + let command = ""; + if (snapshotInDocker) { + this.setupDocker(); + const appDirInDocker = "/app"; + const blobOutputDirInDocker = `/dist/blobs/${androidArch}`; + const toolsTempFolder = join(inputFileDir, "tmp"); + tempFolders.push(toolsTempFolder); + const toolPathInAppDir = this.copySnapshotTool(snapshotToolsPath, toolPath, toolsTempFolder); + const toolPathInDocker = this.getPathInDocker(inputFileDir, appDirInDocker, toolPathInAppDir); + const inputFilePathInDocker = this.getPathInDocker(inputFileDir, appDirInDocker, inputFile); + const outputPathInDocker = this.getPathInDocker(blobOutputDir, blobOutputDirInDocker, blobOutputDir); + const toolCommandInDocker = this.getSnapshotToolCommand(toolPathInDocker, inputFilePathInDocker, outputPathInDocker, toolParams); + command = `docker run --rm -v "${inputFileDir}:${appDirInDocker}" -v "${blobOutputDir}:${blobOutputDirInDocker}" ${SNAPSHOTS_DOCKER_IMAGE} /bin/sh -c "${toolCommandInDocker}"`; + } else { + command = this.getSnapshotToolCommand(toolPath, inputFile, blobOutputDir, toolParams); + } + + // Generate .blob file + child_process.exec(command, { encoding: "utf8" }, (error, stdout, stderr) => { + tempFolders.forEach(tempFolder => { + shelljs.rm("-rf", tempFolder); + }); + + const snapshotError = this.handleSnapshotToolResult(error, stdout, stderr, inputFile, androidArch); + if (snapshotError) { + return reject(snapshotError); + } + + return resolve(blobOutputDir); + }); + }).then((blobOutputDir) => { + // Generate .c file + if (buildCSource) { + this.buildCSource(androidArch, blobOutputDir, snapshotInDocker) + } + }); +} + diff --git a/snapshot/android/utils.js b/snapshot/android/utils.js index 2b63ae28..aef7157b 100644 --- a/snapshot/android/utils.js +++ b/snapshot/android/utils.js @@ -1,17 +1,65 @@ -const { chmodSync, createWriteStream, existsSync } = require("fs"); +const { chmodSync, createWriteStream } = require("fs"); const { tmpdir, EOL } = require("os"); -const { dirname, join } = require("path"); +const { join, relative, isAbsolute } = require("path"); +const os = require("os"); const { mkdir } = require("shelljs"); const { get } = require("request"); const { getProxySettings } = require("proxy-lib"); +const semver = require("semver"); const CONSTANTS = { SNAPSHOT_TMP_DIR: join(tmpdir(), "snapshot-tools"), + MAC_OS_NAME: "darwin", + WIN_OS_NAME: "win", + LINUX_OS_NAME: "linux" }; const createDirectory = dir => mkdir('-p', dir); +function getHostOS() { + const hostOS = os.type().toLowerCase(); + if (hostOS.startsWith(CONSTANTS.MAC_OS_NAME)) + return CONSTANTS.MAC_OS_NAME; + if (hostOS.startsWith(CONSTANTS.LINUX_OS_NAME)) + return CONSTANTS.LINUX_OS_NAME; + if (hostOS.startsWith(CONSTANTS.WIN_OS_NAME)) + return CONSTANTS.WIN_OS_NAME; + return hostOS; +} + +function getHostOSVersion() { + return os.release(); +} + +function getHostOSArch() { + return os.arch(); +} + +function has32BitArch(targetArchs) { + return (Array.isArray(targetArchs) && targetArchs.some(arch => arch === "arm" || arch === "ia32")) || + (targetArchs === "arm" || targetArchs === "ia32"); +} + +function isSubPath(parentPath, childPath) { + const relativePath = relative(parentPath, childPath); + + return relativePath === "" || + (relativePath && !relativePath.startsWith('..') && !isAbsolute(relativePath)); +} + +function isMacOSCatalinaOrHigher() { + let isCatalinaOrHigher = false; + const catalinaVersion = "19.0.0"; + const hostOS = getHostOS(); + if (hostOS === CONSTANTS.MAC_OS_NAME) { + const hostOSVersion = getHostOSVersion(); + isCatalinaOrHigher = semver.gte(hostOSVersion, catalinaVersion); + } + + return isCatalinaOrHigher; +} + const downloadFile = (url, destinationFilePath, timeout) => new Promise((resolve, reject) => { getRequestOptions(url, timeout) @@ -64,6 +112,12 @@ const getRequestOptions = (url, timeout) => module.exports = { CONSTANTS, createDirectory, + has32BitArch, + getHostOS, + getHostOSVersion, + getHostOSArch, + isMacOSCatalinaOrHigher, downloadFile, getJsonFile, + isSubPath }; diff --git a/templates/webpack.angular.js b/templates/webpack.angular.js index e6110ff0..43c3d923 100644 --- a/templates/webpack.angular.js +++ b/templates/webpack.angular.js @@ -50,6 +50,8 @@ module.exports = env => { hmr, // --env.hmr, unitTesting, // --env.unitTesting verbose, // --env.verbose + snapshotInDocker, // --env.snapshotInDocker + skipSnapshotTools // --env.skipSnapshotTools } = env; const isAnySourceMapEnabled = !!sourceMap || !!hiddenSourceMap; @@ -308,6 +310,8 @@ module.exports = env => { ], projectRoot, webpackConfig: config, + snapshotInDocker, + skipSnapshotTools })); } diff --git a/templates/webpack.javascript.js b/templates/webpack.javascript.js index 7460dd2a..da824e28 100644 --- a/templates/webpack.javascript.js +++ b/templates/webpack.javascript.js @@ -44,6 +44,8 @@ module.exports = env => { hmr, // --env.hmr, unitTesting, // --env.unitTesting, verbose, // --env.verbose + snapshotInDocker, // --env.snapshotInDocker + skipSnapshotTools // --env.skipSnapshotTools } = env; const isAnySourceMapEnabled = !!sourceMap || !!hiddenSourceMap; @@ -249,6 +251,8 @@ module.exports = env => { ], projectRoot, webpackConfig: config, + snapshotInDocker, + skipSnapshotTools })); } diff --git a/templates/webpack.typescript.js b/templates/webpack.typescript.js index 44a60a10..b50e4d93 100644 --- a/templates/webpack.typescript.js +++ b/templates/webpack.typescript.js @@ -45,6 +45,8 @@ module.exports = env => { hmr, // --env.hmr, unitTesting, // --env.unitTesting, verbose, // --env.verbose + snapshotInDocker, // --env.snapshotInDocker + skipSnapshotTools // --env.skipSnapshotTools } = env; const isAnySourceMapEnabled = !!sourceMap || !!hiddenSourceMap; const externals = nsWebpack.getConvertedExternals(env.externals); @@ -183,7 +185,7 @@ module.exports = env => { }, ].filter(loader => !!loader) }, - + { test: /\.(ts|css|scss|html|xml)$/, use: "nativescript-dev-webpack/hmr/hot-loader" @@ -277,6 +279,8 @@ module.exports = env => { ], projectRoot, webpackConfig: config, + snapshotInDocker, + skipSnapshotTools })); } diff --git a/templates/webpack.vue.js b/templates/webpack.vue.js index 28bbfe9f..5e4427f0 100644 --- a/templates/webpack.vue.js +++ b/templates/webpack.vue.js @@ -47,6 +47,8 @@ module.exports = env => { hiddenSourceMap, // --env.hiddenSourceMap unitTesting, // --env.unitTesting verbose, // --env.verbose + snapshotInDocker, // --env.snapshotInDocker + skipSnapshotTools // --env.skipSnapshotTools } = env; const isAnySourceMapEnabled = !!sourceMap || !!hiddenSourceMap; @@ -296,6 +298,8 @@ module.exports = env => { ], projectRoot, webpackConfig: config, + snapshotInDocker, + skipSnapshotTools })); }