diff --git a/demo/.gitignore b/demo/.gitignore index 3f3200bc..90a487ea 100644 --- a/demo/.gitignore +++ b/demo/.gitignore @@ -1,5 +1,8 @@ node_modules platforms +testapp +*-report.* + hooks report diff --git a/demo/AngularApp/app/ninjas/details/ninja.module.ts b/demo/AngularApp/app/ninjas/details/ninja.module.ts index 1104172f..dd67895b 100644 --- a/demo/AngularApp/app/ninjas/details/ninja.module.ts +++ b/demo/AngularApp/app/ninjas/details/ninja.module.ts @@ -1,5 +1,5 @@ +import { NativeScriptCommonModule } from "nativescript-angular/common"; import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core"; -import { NativeScriptModule } from "nativescript-angular/nativescript.module"; import { NativeScriptRouterModule } from "nativescript-angular/router"; import { NinjaComponent } from "./ninja.component"; @@ -8,9 +8,9 @@ import { routes } from "./ninja.routes"; @NgModule({ schemas: [NO_ERRORS_SCHEMA], imports: [ - NativeScriptModule, NativeScriptRouterModule, - NativeScriptRouterModule.forChild(routes) + NativeScriptRouterModule.forChild(routes), + NativeScriptCommonModule, ], declarations: [NinjaComponent] }) diff --git a/demo/AngularApp/app/ninjas/ninjas.module.ts b/demo/AngularApp/app/ninjas/ninjas.module.ts index dabc4856..403b5a8d 100644 --- a/demo/AngularApp/app/ninjas/ninjas.module.ts +++ b/demo/AngularApp/app/ninjas/ninjas.module.ts @@ -1,5 +1,5 @@ +import { NativeScriptCommonModule } from "nativescript-angular/common"; import { NgModule, NO_ERRORS_SCHEMA } from "@angular/core"; -import { NativeScriptModule } from "nativescript-angular/nativescript.module"; import { NativeScriptRouterModule } from "nativescript-angular/router"; import { NinjasComponent } from "./ninjas.component"; @@ -8,9 +8,9 @@ import { routes } from "./ninjas.routes"; @NgModule({ schemas: [NO_ERRORS_SCHEMA], imports: [ - NativeScriptModule, NativeScriptRouterModule, - NativeScriptRouterModule.forChild(routes) + NativeScriptRouterModule.forChild(routes), + NativeScriptCommonModule, ], declarations: [NinjasComponent] }) diff --git a/demo/AngularApp/package.json b/demo/AngularApp/package.json index 9bb7e114..6a565e5b 100644 --- a/demo/AngularApp/package.json +++ b/demo/AngularApp/package.json @@ -68,7 +68,7 @@ "build-ios-bundle": "npm run ns-bundle --ios --build-app", "publish-ios-bundle": "npm run ns-bundle --ios --publish-app", "generate-android-snapshot": "generate-android-snapshot --targetArchs arm,arm64,ia32 --install", - "e2e": "tsc -p e2e && mocha --opts ./e2e/config/mocha.opts", + "e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json", "compile-tests": "tsc -p e2e --watch" } } diff --git a/demo/JavaScriptApp/e2e/config/mocha.opts b/demo/JavaScriptApp/e2e/config/mocha.opts deleted file mode 100644 index 796ec472..00000000 --- a/demo/JavaScriptApp/e2e/config/mocha.opts +++ /dev/null @@ -1,4 +0,0 @@ ---timeout 80000 ---recursive e2e ---reporter mocha-multi ---reporter-options spec=-,mocha-junit-reporter=test-results.xml \ No newline at end of file diff --git a/demo/JavaScriptApp/package.json b/demo/JavaScriptApp/package.json index 25dd1ff0..c60bed1d 100644 --- a/demo/JavaScriptApp/package.json +++ b/demo/JavaScriptApp/package.json @@ -24,6 +24,9 @@ "css-loader": "~0.28.7", "extract-text-webpack-plugin": "~3.0.0", "lazy": "1.0.11", + "mocha": "~3.5.0", + "mocha-junit-reporter": "^1.13.0", + "mocha-multi": "^0.11.0", "nativescript-dev-appium": "next", "nativescript-dev-sass": "^1.3.5", "nativescript-dev-webpack": "file:../..", @@ -32,9 +35,9 @@ "raw-loader": "~0.5.1", "resolve-url-loader": "~2.1.0", "sass-loader": "^6.0.6", - "webpack": "~3.8.1", - "webpack-bundle-analyzer": "^2.8.2", - "webpack-sources": "~1.0.1" + "webpack": "~3.10.0", + "webpack-bundle-analyzer": "^2.9.1", + "webpack-sources": "^1.1.0" }, "scripts": { "ns-bundle": "ns-bundle", @@ -44,6 +47,6 @@ "build-ios-bundle": "npm run ns-bundle --ios --build-app", "publish-ios-bundle": "npm run ns-bundle --ios --publish-app", "generate-android-snapshot": "generate-android-snapshot --targetArchs arm,arm64,ia32 --install", - "e2e": "mocha --opts ./e2e/config/mocha.opts" + "e2e": "mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json" } -} \ No newline at end of file +} diff --git a/demo/TypeScriptApp/e2e/config/mocha.opts b/demo/TypeScriptApp/e2e/config/mocha.opts deleted file mode 100644 index 796ec472..00000000 --- a/demo/TypeScriptApp/e2e/config/mocha.opts +++ /dev/null @@ -1,4 +0,0 @@ ---timeout 80000 ---recursive e2e ---reporter mocha-multi ---reporter-options spec=-,mocha-junit-reporter=test-results.xml \ No newline at end of file diff --git a/demo/TypeScriptApp/package.json b/demo/TypeScriptApp/package.json index e8e12a12..b21ac9c6 100644 --- a/demo/TypeScriptApp/package.json +++ b/demo/TypeScriptApp/package.json @@ -53,7 +53,7 @@ "build-ios-bundle": "npm run ns-bundle --ios --build-app", "publish-ios-bundle": "npm run ns-bundle --ios --publish-app", "generate-android-snapshot": "generate-android-snapshot --targetArchs arm,arm64,ia32 --install", - "e2e": "tsc -p e2e && mocha --opts ./e2e/config/mocha.opts", + "e2e": "tsc -p e2e && mocha --opts ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json", "compile-tests": "tsc -p e2e --watch" } } diff --git a/demo/AngularApp/e2e/config/appium.capabilities.json b/demo/config/appium.capabilities.json similarity index 81% rename from demo/AngularApp/e2e/config/appium.capabilities.json rename to demo/config/appium.capabilities.json index 3cd2bab2..16749592 100644 --- a/demo/AngularApp/e2e/config/appium.capabilities.json +++ b/demo/config/appium.capabilities.json @@ -71,6 +71,18 @@ "fullReset": false, "app": "" }, + "android27": { + "platformName": "Android", + "platformVersion": "27", + "deviceName": "Emulator-Api27-Google", + "avd": "Emulator-Api27-Google", + "lt": 60000, + "appActivity": "com.tns.NativeScriptActivity", + "newCommandTimeout": 720, + "noReset": true, + "fullReset": false, + "app": "" + }, "sim.iPhone7.iOS100": { "platformName": "iOS", "platformVersion": "10.0", @@ -79,6 +91,14 @@ "fullReset": false, "app": "" }, + "sim.iPhone7.iOS110": { + "platformName": "iOS", + "platformVersion": "11.2", + "deviceName": "iPhone 7 110", + "noReset": true, + "fullReset": false, + "app": "" + }, "sim.iPhone8.iOS110": { "platformName": "iOS", "platformVersion": "11.2", @@ -90,7 +110,7 @@ "sim.iPhoneX.iOS110": { "platformName": "iOS", "platformVersion": "11.2", - "deviceName": "iPhone X", + "deviceName": "iPhone X 110", "noReset": true, "fullReset": false, "app": "" @@ -98,9 +118,9 @@ "sim.iPhoneX.iOS111": { "platformName": "iOS", "platformVersion": "11.1", - "deviceName": "iPhone X", + "deviceName": "iPhone X 111", "noReset": true, "fullReset": false, "app": "" } -} +} \ No newline at end of file diff --git a/demo/AngularApp/e2e/config/mocha.opts b/demo/config/mocha.opts similarity index 100% rename from demo/AngularApp/e2e/config/mocha.opts rename to demo/config/mocha.opts diff --git a/index.js b/index.js index 635752e3..31544e28 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,18 @@ const path = require("path"); const { existsSync } = require("fs"); -const { getPackageJson, getProjectDir, isAngular, resolveAndroidAppPath } = require("./projectHelpers"); +const { + APP_DIR, + getPackageJson, + getProjectDir, + isAngular, + isAndroid, + isIos, + resolveAndroidAppPath, +} = require("./projectHelpers"); const PROJECT_DIR = getProjectDir(); -const APP_DIR = path.join(PROJECT_DIR, "app"); +const APP_PATH = path.join(PROJECT_DIR, APP_DIR); Object.assign(exports, require('./plugins')); @@ -12,23 +20,39 @@ if (isAngular({ projectDir: PROJECT_DIR })) { Object.assign(exports, require('./plugins/angular')); } -exports.getEntryModule = function () { - const maybePackageJsonEntry = getPackageJsonEntry(); - if (!maybePackageJsonEntry) { - throw new Error("app/package.json must contain a `main` attribute."); +exports.getAotEntryModule = function (appDirectory = APP_PATH) { + const entry = getPackageJsonEntry(appDirectory); + const aotEntry = `${entry}.aot.ts`; + + const aotEntryPath = path.resolve(appDirectory, aotEntry); + if (!existsSync(aotEntryPath)) { + throw new Error(`For ahead-of-time compilation you need to have an entry module ` + + `at ${aotEntryPath} that bootstraps the app with a static platform instead of dynamic one!`) } - const maybeAotEntry = getAotEntry(maybePackageJsonEntry); - return maybeAotEntry || maybePackageJsonEntry; + return aotEntry; +} + +exports.getEntryModule = function (appDirectory = APP_PATH) { + const entry = getPackageJsonEntry(appDirectory); + + const tsEntryPath = path.resolve(appDirectory, `${entry}.ts`); + const jsEntryPath = path.resolve(appDirectory, `${entry}.js`); + if (!existsSync(tsEntryPath) && !existsSync(jsEntryPath)) { + throw new Error(`The entry module ${entry} specified in ` + + `${appDirectory}/package.json doesn't exist!`) + } + + return entry; }; exports.getAppPath = platform => { - if (/ios/i.test(platform)) { + if (isIos(platform)) { const appName = path.basename(PROJECT_DIR); const sanitizedName = sanitize(appName); return `platforms/ios/${sanitizedName}/app`; - } else if (/android/i.test(platform)) { + } else if (isAndroid(platform)) { return resolveAndroidAppPath(PROJECT_DIR); } else { throw new Error(`Invalid platform: ${platform}`); @@ -40,16 +64,13 @@ const sanitize = name => name .filter(char => /[a-zA-Z0-9]/.test(char)) .join(""); -function getPackageJsonEntry() { - const packageJsonSource = getPackageJson(APP_DIR); +function getPackageJsonEntry(appDirectory) { + const packageJsonSource = getPackageJson(appDirectory); const entry = packageJsonSource.main; - return entry ? entry.replace(/\.js$/i, "") : null; -} - -function getAotEntry(entry) { - const aotEntry = `${entry}.aot.ts`; - const aotEntryPath = path.join(APP_DIR, aotEntry); + if (!entry) { + throw new Error(`${appDirectory}/package.json must contain a 'main' attribute!`); + } - return existsSync(aotEntryPath) ? aotEntry : null; + return entry.replace(/\.js$/i, ""); } diff --git a/installer.js b/installer.js index 94b2b7ed..2ce9d7de 100644 --- a/installer.js +++ b/installer.js @@ -1,28 +1,27 @@ -const path = require("path"); -const fs = require("fs"); - const helpers = require("./projectHelpers"); const projectFilesManager = require("./projectFilesManager"); const dependencyManager = require("./dependencyManager"); -const PROJECT_DIR = helpers.getProjectDir(); -const APP_DIR = path.resolve(PROJECT_DIR, "app"); - function install() { - const packageJson = helpers.getPackageJson(PROJECT_DIR); + const projectDir = helpers.getProjectDir(); + const appPath = helpers.getAppPath(); + const packageJson = helpers.getPackageJson(projectDir); - projectFilesManager.addProjectFiles(PROJECT_DIR, APP_DIR); + projectFilesManager.addProjectFiles(projectDir, appPath); const postinstallOptions = dependencyManager.addProjectDeps(packageJson); packageJson.devDependencies = postinstallOptions.deps; - helpers.writePackageJson(packageJson, PROJECT_DIR); + helpers.writePackageJson(packageJson, projectDir); dependencyManager.showHelperMessages(postinstallOptions); } function uninstall() { - projectFilesManager.removeProjectFiles(PROJECT_DIR, APP_DIR); + const projectDir = helpers.getProjectDir(); + const appPath = helpers.getAppPath(); + projectFilesManager.removeProjectFiles(projectDir, appPath); + console.log("NativeScript Webpack removed!"); } diff --git a/lib/after-prepare.js b/lib/after-prepare.js index 85da7ddc..884527b4 100644 --- a/lib/after-prepare.js +++ b/lib/after-prepare.js @@ -1,7 +1,7 @@ -const snapshotGenerator = require("../snapshot/android/project-snapshot-generator"); -const utils = require("./utils"); +const { installSnapshotArtefacts } = require("../snapshot/android/project-snapshot-generator"); +const { shouldSnapshot } = require("./utils"); -module.exports = function ($mobileHelper, $projectData, hookArgs) { +module.exports = function ($projectData, hookArgs) { const env = hookArgs.env || {}; const shouldSnapshotOptions = { platform: hookArgs.platform, @@ -9,7 +9,7 @@ module.exports = function ($mobileHelper, $projectData, hookArgs) { release: hookArgs.appFilesUpdaterOptions.release }; - if (env.snapshot && utils.shouldSnapshot($mobileHelper, shouldSnapshotOptions)) { - snapshotGenerator.installSnapshotArtefacts($projectData.projectDir); + if (env.snapshot && shouldSnapshot(shouldSnapshotOptions)) { + installSnapshotArtefacts($projectData.projectDir); } } diff --git a/lib/before-cleanApp.js b/lib/before-cleanApp.js index 99d1c5c7..83968a37 100644 --- a/lib/before-cleanApp.js +++ b/lib/before-cleanApp.js @@ -1,6 +1,8 @@ -const ProjectSnapshotGenerator = require("../snapshot/android/project-snapshot-generator"); -module.exports = function ($mobileHelper, hookArgs) { - if ($mobileHelper.isAndroidPlatform(hookArgs.platformInfo.platform)) { - ProjectSnapshotGenerator.cleanSnapshotArtefacts(hookArgs.platformInfo.projectData.projectDir); - } +const { cleanSnapshotArtefacts } = require("../snapshot/android/project-snapshot-generator"); +const { isAndroid } = require("../projectHelpers"); + +module.exports = function (hookArgs) { + if (isAndroid(hookArgs.platformInfo.platform)) { + cleanSnapshotArtefacts(hookArgs.platformInfo.projectData.projectDir); + } } diff --git a/lib/before-prepareJS.js b/lib/before-prepareJS.js index bb2ea12c..056a66d2 100644 --- a/lib/before-prepareJS.js +++ b/lib/before-prepareJS.js @@ -1,6 +1,6 @@ const { runWebpackCompiler } = require("./compiler"); -module.exports = function ($mobileHelper, $projectData, $logger, hookArgs) { +module.exports = function ($projectData, $logger, hookArgs) { const env = hookArgs.config.env || {}; const platform = hookArgs.config.platform; const appFilesUpdaterOptions = hookArgs.config.appFilesUpdaterOptions; @@ -10,6 +10,6 @@ module.exports = function ($mobileHelper, $projectData, $logger, hookArgs) { bundle: appFilesUpdaterOptions.bundle, release: appFilesUpdaterOptions.release, }; - const result = config.bundle && runWebpackCompiler.bind(runWebpackCompiler, config, $mobileHelper, $projectData, $logger, hookArgs); + const result = config.bundle && runWebpackCompiler.bind(runWebpackCompiler, config, $projectData, $logger, hookArgs); return result; } diff --git a/lib/before-watch.js b/lib/before-watch.js index 6e989d4b..c37906c9 100644 --- a/lib/before-watch.js +++ b/lib/before-watch.js @@ -1,6 +1,6 @@ const { runWebpackCompiler } = require("./compiler"); -module.exports = function ($mobileHelper, $projectData, $logger, hookArgs) { +module.exports = function ($projectData, $logger, hookArgs) { if (hookArgs.config) { const appFilesUpdaterOptions = hookArgs.config.appFilesUpdaterOptions; if (appFilesUpdaterOptions.bundle) { @@ -15,7 +15,7 @@ module.exports = function ($mobileHelper, $projectData, $logger, hookArgs) { watch: true }; - return runWebpackCompiler(config, $mobileHelper, $projectData, $logger, hookArgs); + return runWebpackCompiler(config, $projectData, $logger, hookArgs); })); } } diff --git a/lib/before-watchPatterns.js b/lib/before-watchPatterns.js index 0e2703a7..ea25cba6 100644 --- a/lib/before-watchPatterns.js +++ b/lib/before-watchPatterns.js @@ -1,12 +1,17 @@ const { basename } = require("path"); -const { buildEnvData, getCompilationContext } = require("./utils"); +const { + buildEnvData, + getCompilationContext, + setProcessInitDirectory, +} = require("./utils"); -module.exports = function (hookArgs) { +module.exports = function ($projectData, hookArgs) { const { liveSyncData } = hookArgs; if (!liveSyncData || !liveSyncData.bundle) { return; } + setProcessInitDirectory($projectData.projectDir); const { platforms } = hookArgs; const { env } = liveSyncData; return (args, originalMethod) => { @@ -16,7 +21,7 @@ module.exports = function (hookArgs) { } const compilationContexts = platforms.map(platform => - getContext(platform, env)); + getContext($projectData, platform, env)); const ignorePatterns = compilationContexts.map( context => `!${context}` @@ -27,7 +32,7 @@ module.exports = function (hookArgs) { }; } -function getContext(platform, env) { - const fullEnvData = buildEnvData(platform, env); - return getCompilationContext(fullEnvData); +function getContext($projectData, platform, env) { + const fullEnvData = buildEnvData($projectData, platform, env); + return getCompilationContext($projectData.projectDir, fullEnvData); } diff --git a/lib/compiler.js b/lib/compiler.js index 5a5b571d..9f66cd92 100644 --- a/lib/compiler.js +++ b/lib/compiler.js @@ -3,6 +3,7 @@ const { spawn } = require("child_process"); const { join, resolve: pathResolve } = require("path"); const { existsSync } = require("fs"); const readline = require("readline"); + const { messages } = require("../plugins/WatchStateLoggerPlugin"); const { buildEnvData, getCompilationContext } = require("./utils"); @@ -11,17 +12,11 @@ let hasBeenInvoked = false; let webpackProcess = null; let hasLoggedSnapshotWarningMessage = false; -function logdSnapshotWarningMessage($logger) { - if (!hasLoggedSnapshotWarningMessage) { - $logger.warn("Stripping the snapshot flag. Bear in mind that snapshot is only available in release builds and is NOT available on Windows systems."); - } -} - exports.getWebpackProcess = function getWebpackProcess() { return webpackProcess; } -exports.runWebpackCompiler = function runWebpackCompiler(config, $mobileHelper, $projectData, $logger, hookArgs) { +exports.runWebpackCompiler = function runWebpackCompiler(config, $projectData, $logger, hookArgs) { if (config.bundle) { return new Promise(function (resolveBase, rejectBase) { if (webpackProcess) { @@ -42,19 +37,15 @@ exports.runWebpackCompiler = function runWebpackCompiler(config, $mobileHelper, console.log(`Running webpack for ${config.platform}...`); - const envData = buildEnvData(config.platform, config.env); - const envFlagNames = Object.keys(envData); - - const snapshotEnvIndex = envFlagNames.indexOf("snapshot"); - if (snapshotEnvIndex !== -1 && !utils.shouldSnapshot($mobileHelper, config)) { - logdSnapshotWarningMessage($logger); - envFlagNames.splice(snapshotEnvIndex, 1); - } + const projectDir = $projectData.projectDir; + const { platform, env } = config; + const envData = buildEnvData($projectData, platform, env); + const envParams = buildEnvCommandLineParams(config, envData, $logger); // Adding `npm i source-map-support --save-dev` in an app will make source maps work // and stack traces will point to .ts if .ts files and proper source maps exist. let sourceMapSupportArgs = []; - const appSourceMapSupportInstallPath = pathResolve($projectData.projectDir, "node_modules", "source-map-support", "register.js"); + const appSourceMapSupportInstallPath = pathResolve(projectDir, "node_modules", "source-map-support", "register.js"); const devDepSourceMapSupportInstallPath = pathResolve(__dirname, "..", "node_modules", "source-map-support", "register.js"); if (existsSync(appSourceMapSupportInstallPath)) { sourceMapSupportArgs = ["--require", appSourceMapSupportInstallPath]; @@ -65,11 +56,11 @@ exports.runWebpackCompiler = function runWebpackCompiler(config, $mobileHelper, const args = [ "--preserve-symlinks", ...sourceMapSupportArgs, - join($projectData.projectDir, "node_modules", "webpack", "bin", "webpack.js"), - "--config=webpack.config.js", + pathResolve(projectDir, "node_modules", "webpack", "bin", "webpack.js"), + `--config=${pathResolve(projectDir, "webpack.config.js")}`, "--progress", ...(config.watch ? ["--watch"] : []), - ...envFlagNames.map(item => `--env.${item}`), + ...envParams, ].filter(a => !!a); const childProcess = spawn("node", args, { @@ -77,7 +68,7 @@ exports.runWebpackCompiler = function runWebpackCompiler(config, $mobileHelper, // These will notify us for the webpack compilation states. // Enables `childProcess.on("message", msg => ...)` kind of communication. stdio: config.watch ? ["inherit", "inherit", "inherit", "ipc"] : "inherit", - cwd: $projectData.projectDir + cwd: projectDir }); let isFirstWebpackWatchCompilation = true; @@ -94,10 +85,10 @@ exports.runWebpackCompiler = function runWebpackCompiler(config, $mobileHelper, } if (hookArgs.filesToSync && hookArgs.startSyncFilesTimeout) { - const compilationContext = getCompilationContext(envData); + const compilationContext = getCompilationContext(projectDir, envData); hookArgs.filesToSync.push( ...message.emittedFiles.map( - emittedFile => join($projectData.projectDir, compilationContext, emittedFile) + emittedFile => join(projectDir, compilationContext, emittedFile) ) ); hookArgs.startSyncFilesTimeout(); @@ -128,3 +119,25 @@ exports.runWebpackCompiler = function runWebpackCompiler(config, $mobileHelper, }); } } + +function buildEnvCommandLineParams(config, envData, $logger) { + const envFlagNames = Object.keys(envData); + const snapshotEnvIndex = envFlagNames.indexOf("snapshot"); + if (snapshotEnvIndex > -1 && !utils.shouldSnapshot(config)) { + logSnapshotWarningMessage($logger); + envFlagNames.splice(snapshotEnvIndex, 1); + } + + return envFlagNames.map(item => `--env.${item}=${envData[item]}`); +} + +function logSnapshotWarningMessage($logger) { + if (!hasLoggedSnapshotWarningMessage) { + $logger.warn("Stripping the snapshot flag. " + + "Bear in mind that snapshot is only available in release builds and " + + "is NOT available on Windows systems."); + + hasLoggedSnapshotWarningMessage = true; + } +} + diff --git a/lib/utils.js b/lib/utils.js index 3a4329d3..441b12fc 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,17 +1,31 @@ const os = require("os"); const path = require("path"); -const { getProjectDir, getWebpackConfig } = require("../projectHelpers"); +const { + getAppPathFromProjectData, + getAppResourcesPathFromProjectData, + getProjectDir, + getWebpackConfig, + isAndroid, +} = require("../projectHelpers"); -function buildEnvData(platform, env) { - return Object.assign({}, +function buildEnvData($projectData, platform, env) { + const envData = Object.assign({}, env, { [platform.toLowerCase()]: true } ); + + const appPath = getAppPathFromProjectData($projectData); + const appResourcesPath = getAppResourcesPathFromProjectData($projectData); + Object.assign(envData, + appPath && { appPath }, + appResourcesPath && { appResourcesPath }, + ); + + return envData; } -function getCompilationContext(env) { - const projectDir = getProjectDir(); +function getCompilationContext(projectDir, env) { const config = getWebpackConfig(projectDir, env); const { context } = config; @@ -20,15 +34,20 @@ function getCompilationContext(env) { "."; } -function shouldSnapshot($mobileHelper, config) { - const platformSupportsSnapshot = $mobileHelper.isAndroidPlatform(config.platform); +function shouldSnapshot(config) { + const platformSupportsSnapshot = isAndroid(config.platform); const osSupportsSnapshot = os.type() !== "Windows_NT"; return config.bundle && config.release && platformSupportsSnapshot && osSupportsSnapshot; } +function setProcessInitDirectory(dir) { + process.env.INIT_CWD = dir; +} + module.exports = { buildEnvData, getCompilationContext, shouldSnapshot, + setProcessInitDirectory, }; diff --git a/nsCliHelpers.js b/nsCliHelpers.js new file mode 100644 index 00000000..66b2f8e6 --- /dev/null +++ b/nsCliHelpers.js @@ -0,0 +1,42 @@ +const { getPath } = require("global-modules-path"); + +const PROJECT_DATA_GETTERS = { + appPath: "getAppDirectoryRelativePath", + appResourcesPath: "getAppResourcesRelativeDirectoryPath", +}; + +function getProjectData(projectDir) { + const cli = getNsCli(); + const projectDataService = cli.projectDataService; + const projectData = safeGet(cli, "getProjectData", projectDir); + + return projectData; +} + +function getNsCli() { + const cliPath = getPath("nativescript", "tns"); + const cli = require(cliPath); + + return cli; +} + +function safeGet(object, property, args = []) { + if (!object) { + return; + } + + const value = object[property]; + if (!value) { + return; + } + + return typeof value === "function" ? + value.bind(object)(...args) : + value; +} + +module.exports = { + PROJECT_DATA_GETTERS, + getProjectData, + safeGet, +}; diff --git a/package.json b/package.json index 78709a08..b03ae04f 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "generate-android-snapshot": "./bin/generate-android-snapshot" }, "dependencies": { + "global-modules-path": "2.0.0", "minimatch": "3.0.4", "nativescript-hook": "0.2.2", "proxy-lib": "0.4.0", diff --git a/projectHelpers.js b/projectHelpers.js index cabeb51c..b42f4a91 100644 --- a/projectHelpers.js +++ b/projectHelpers.js @@ -3,6 +3,14 @@ const fs = require("fs"); const semver = require("semver"); const { EOL } = require("os"); +const { + PROJECT_DATA_GETTERS, + getProjectData, + safeGet, +} = require("./nsCliHelpers"); + +const APP_DIR = "app"; + const isTypeScript = ({ projectDir, packageJson } = {}) => { packageJson = packageJson || getPackageJson(projectDir); @@ -48,6 +56,7 @@ const getAndroidRuntimeVersion = (projectDir) => { const getWebpackConfig = (projectDir, env, configPath = "webpack.config.js") => { const configAbsolutePath = path.resolve(projectDir, configPath); let config; + try { config = require(configAbsolutePath); } catch (e) { @@ -137,12 +146,39 @@ const resolveAndroidConfigurationsPath = projectDir => { const getPackageJsonPath = projectDir => path.resolve(projectDir, "package.json"); +const isAndroid = platform => /android/i.test(platform); +const isIos = platform => /ios/i.test(platform); + +function getAppPath() { + const projectDir = getProjectDir(); + const projectData = getProjectData(projectDir); + const appDir = getAppPathFromProjectData(projectData) || APP_DIR; + + const appPath = path.resolve(projectDir, appDir); + + return appPath; +} + +function getAppPathFromProjectData(data) { + return safeGet(data, PROJECT_DATA_GETTERS.appPath); +} + +function getAppResourcesPathFromProjectData(data) { + return safeGet(data, PROJECT_DATA_GETTERS.appResourcesPath); +} + module.exports = { + APP_DIR, + getAppPath, + getAppPathFromProjectData, + getAppResourcesPathFromProjectData, getAndroidProjectPath, getAndroidRuntimeVersion, getPackageJson, getProjectDir, getWebpackConfig, + isAndroid, + isIos, isAngular, isSass, isTypeScript, diff --git a/templates/webpack.angular.js b/templates/webpack.angular.js index c0ae7065..d959a8f1 100644 --- a/templates/webpack.angular.js +++ b/templates/webpack.angular.js @@ -4,7 +4,6 @@ const webpack = require("webpack"); const nsWebpack = require("nativescript-dev-webpack"); const nativescriptTarget = require("nativescript-dev-webpack/nativescript-target"); const CopyWebpackPlugin = require("copy-webpack-plugin"); -const ExtractTextPlugin = require("extract-text-webpack-plugin"); const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); const { NativeScriptWorkerPlugin } = require("nativescript-worker-loader/NativeScriptWorkerPlugin"); const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); @@ -15,21 +14,41 @@ module.exports = env => { throw new Error("You need to provide a target platform!"); } const platforms = ["ios", "android"]; - const { snapshot, uglify, report, aot } = env; + const { + // The 'appPath' and 'appResourcesDir' values are fetched from + // the nsconfig.json configuration file + // when bundling with `tns run android|ios --bundle`. + appPath = "app", + appResourcesPath = "app/App_Resources", + + // Aot, snapshot, uglify and report can be enabled by providing + // the `--env.snapshot`, `--env.uglify` or `--env.report` flags + // when running 'tns run android|ios' + aot, + snapshot, + uglify, + report, + } = env; const ngToolsWebpackOptions = { tsConfigPath: join(__dirname, "tsconfig.json") }; + const projectRoot = __dirname; + const appFullPath = resolve(projectRoot, appPath); + const appResourcesFullPath = resolve(projectRoot, appResourcesPath); + const config = { - context: resolve(__dirname, "app"), + context: appFullPath, watchOptions: { ignored: [ - resolve(__dirname, "./app/App_Resources"), + appResourcesFullPath, // Don't watch hidden files "**/.*", ] }, target: nativescriptTarget, entry: { - bundle: aot ? "./main.aot.ts" : "./main.ts", + bundle: aot ? + `./${nsWebpack.getAotEntryModule(appFullPath)}` : + `./${nsWebpack.getEntryModule(appFullPath)}`, vendor: "./vendor", }, output: { @@ -47,7 +66,7 @@ module.exports = env => { "node_modules", ], alias: { - '~': resolve("./app") + '~': appFullPath }, // don't resolve symlinks to symlinked modules symlinks: false @@ -105,7 +124,7 @@ module.exports = env => { }), // Copy assets to out dir. Add your own globs as needed. new CopyWebpackPlugin([ - { from: "App_Resources/**" }, + { from: `${appResourcesFullPath}/**`, context: projectRoot }, { from: "fonts/**" }, { from: "**/*.jpg" }, { from: "**/*.png" }, @@ -140,14 +159,14 @@ module.exports = env => { analyzerMode: "static", openAnalyzer: false, generateStatsFile: true, - reportFilename: join(__dirname, "report", `report.html`), - statsFilename: join(__dirname, "report", `stats.json`), + reportFilename: resolve(projectRoot, "report", `report.html`), + statsFilename: resolve(projectRoot, "report", `stats.json`), })); } if (snapshot) { config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({ chunk: "vendor", - projectRoot: __dirname, + projectRoot, webpackConfig: config, targetArchs: ["arm", "arm64", "ia32"], tnsJavaClassesOptions: { packages: ["tns-core-modules" ] }, diff --git a/templates/webpack.javascript.js b/templates/webpack.javascript.js index 9a54b4e9..def08781 100644 --- a/templates/webpack.javascript.js +++ b/templates/webpack.javascript.js @@ -14,20 +14,37 @@ module.exports = env => { throw new Error("You need to provide a target platform!"); } const platforms = ["ios", "android"]; - const { snapshot, uglify, report } = env; + const { + // The 'appPath' and 'appResourcesPath' values are fetched from + // the nsconfig.json configuration file + // when bundling with `tns run android|ios --bundle`. + appPath = "app", + appResourcesPath = "app/App_Resources", + + // Snapshot, uglify and report can be enabled by providing + // the `--env.snapshot`, `--env.uglify` or `--env.report` flags + // when running 'tns run android|ios' + snapshot, + uglify, + report, + } = env; + + const projectRoot = __dirname; + const appFullPath = resolve(projectRoot, appPath); + const appResourcesFullPath = resolve(projectRoot, appResourcesPath); const config = { - context: resolve(__dirname, "app"), + context: appFullPath, watchOptions: { ignored: [ - resolve(__dirname, "./app/App_Resources"), + appResourcesFullPath, // Don't watch hidden files "**/.*", ] }, target: nativescriptTarget, entry: { - bundle: `./${nsWebpack.getEntryModule()}`, + bundle: `./${nsWebpack.getEntryModule(appFullPath)}`, vendor: "./vendor" }, output: { @@ -45,7 +62,7 @@ module.exports = env => { "node_modules", ], alias: { - '~': resolve("./app") + '~': appFullPath }, // don't resolve symlinks to symlinked modules symlinks: false @@ -90,7 +107,7 @@ module.exports = env => { }), // Copy assets to out dir. Add your own globs as needed. new CopyWebpackPlugin([ - { from: "App_Resources/**" }, + { from: `${appResourcesFullPath}/**`, context: projectRoot }, { from: "fonts/**" }, { from: "**/*.jpg" }, { from: "**/*.png" }, @@ -106,7 +123,6 @@ module.exports = env => { new nsWebpack.PlatformFSPlugin({ platform, platforms, - // ignore: ["App_Resources"] }), // Does IPC communication with the {N} CLI to notify events when running in watch mode. new nsWebpack.WatchStateLoggerPlugin(), @@ -118,14 +134,14 @@ module.exports = env => { analyzerMode: "static", openAnalyzer: false, generateStatsFile: true, - reportFilename: join(__dirname, "report", `report.html`), - statsFilename: join(__dirname, "report", `stats.json`), + reportFilename: resolve(projectRoot, "report", `report.html`), + statsFilename: resolve(projectRoot, "report", `stats.json`), })); } if (snapshot) { config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({ chunk: "vendor", - projectRoot: __dirname, + projectRoot, webpackConfig: config, targetArchs: ["arm", "arm64", "ia32"], tnsJavaClassesOptions: { packages: ["tns-core-modules" ] }, diff --git a/templates/webpack.typescript.js b/templates/webpack.typescript.js index 99c1f2b6..cbb13ea9 100644 --- a/templates/webpack.typescript.js +++ b/templates/webpack.typescript.js @@ -14,20 +14,37 @@ module.exports = env => { throw new Error("You need to provide a target platform!"); } const platforms = ["ios", "android"]; - const { snapshot, uglify, report } = env; + const { + // The 'appPath' and 'appResourcesDir' values are fetched from + // the nsconfig.json configuration file + // when bundling with `tns run android|ios --bundle`. + appPath = "app", + appResourcesPath = "app/App_Resources", + + // Snapshot, uglify and report can be enabled by providing + // the `--env.snapshot`, `--env.uglify` or `--env.report` flags + // when running 'tns run android|ios' + snapshot, + uglify, + report, + } = env; + + const projectRoot = __dirname; + const appFullPath = resolve(projectRoot, appPath); + const appResourcesFullPath = resolve(projectRoot, appResourcesPath); const config = { - context: resolve(__dirname, "app"), + context: appFullPath, watchOptions: { ignored: [ - resolve(__dirname, "./app/App_Resources"), + appResourcesFullPath, // Don't watch hidden files "**/.*", ] }, target: nativescriptTarget, entry: { - bundle: `./${nsWebpack.getEntryModule()}`, + bundle: `./${nsWebpack.getEntryModule(appFullPath)}`, vendor: "./vendor" }, output: { @@ -92,7 +109,7 @@ module.exports = env => { }), // Copy assets to out dir. Add your own globs as needed. new CopyWebpackPlugin([ - { from: "App_Resources/**" }, + { from: `${appResourcesFullPath}/**`, context: projectRoot }, { from: "fonts/**" }, { from: "**/*.jpg" }, { from: "**/*.png" }, @@ -108,7 +125,6 @@ module.exports = env => { new nsWebpack.PlatformFSPlugin({ platform, platforms, - // ignore: ["App_Resources"] }), // Does IPC communication with the {N} CLI to notify events when running in watch mode. new nsWebpack.WatchStateLoggerPlugin(), @@ -120,14 +136,14 @@ module.exports = env => { analyzerMode: "static", openAnalyzer: false, generateStatsFile: true, - reportFilename: join(__dirname, "report", `report.html`), - statsFilename: join(__dirname, "report", `stats.json`), + reportFilename: resolve(projectRoot, "report", `report.html`), + statsFilename: resolve(projectRoot, "report", `stats.json`), })); } if (snapshot) { config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({ chunk: "vendor", - projectRoot: __dirname, + projectRoot, webpackConfig: config, targetArchs: ["arm", "arm64", "ia32"], tnsJavaClassesOptions: { packages: ["tns-core-modules" ] }, diff --git a/verify/update.js b/verify/update.js index a5eac1b7..fa37c172 100644 --- a/verify/update.js +++ b/verify/update.js @@ -1,7 +1,12 @@ const { spawn } = require("child_process"); const { resolve: pathResolve } = require("path"); -const { getPackageJson, getProjectDir, writePackageJson } = require("../projectHelpers"); +const { + getAppPath, + getPackageJson, + getProjectDir, + writePackageJson, +} = require("../projectHelpers"); const { forceUpdateProjectFiles } = require("../projectFilesManager"); const { forceUpdateProjectDeps } = require("../dependencyManager"); @@ -44,7 +49,7 @@ function updateDeps(projectDir) { function updateConfigs(projectDir) { console.info("Updating configuration files..."); - const appDir = pathResolve(projectDir, "app"); + const appDir = getAppPath(); forceUpdateProjectFiles(projectDir, appDir); }