diff --git a/android-app-components-loader.js b/android-app-components-loader.js index 5e2ecfd3..9321e401 100644 --- a/android-app-components-loader.js +++ b/android-app-components-loader.js @@ -1,7 +1,10 @@ -module.exports = function(source) { +const { convertSlashesInPath } = require("./projectHelpers"); + +module.exports = function (source) { this.cacheable(); const { modules } = this.query; - const imports = modules.map(m => `require("${m}");`).join("\n"); + const imports = modules.map(convertSlashesInPath) + .map(m => `require("${m}");`).join("\n"); const augmentedSource = ` if (!global["__snapshot"]) { ${imports} diff --git a/demo/.gitignore b/demo/.gitignore index 829954a9..dcdcde1a 100644 --- a/demo/.gitignore +++ b/demo/.gitignore @@ -16,4 +16,4 @@ tsconfig.aot.json vendor.js vendor.ts -webpack.config.js +tsconfig.esm.json \ No newline at end of file diff --git a/demo/AngularApp/app/App_Resources/Android/AndroidManifest.xml b/demo/AngularApp/app/App_Resources/Android/AndroidManifest.xml index 9db83215..0bb603e9 100644 --- a/demo/AngularApp/app/App_Resources/Android/AndroidManifest.xml +++ b/demo/AngularApp/app/App_Resources/Android/AndroidManifest.xml @@ -19,14 +19,14 @@ diff --git a/demo/AngularApp/app/activity.android.ts b/demo/AngularApp/app/activity.android.ts new file mode 100644 index 00000000..f04e39bd --- /dev/null +++ b/demo/AngularApp/app/activity.android.ts @@ -0,0 +1,42 @@ +import {setActivityCallbacks, AndroidActivityCallbacks} from "ui/frame"; + +@JavaProxy("org.myApp.MainActivity") +class Activity extends android.app.Activity { + private _callbacks: AndroidActivityCallbacks; + + protected onCreate(savedInstanceState: any): void { // android.os.Bundle + if (!this._callbacks) { + setActivityCallbacks(this); + } + + this._callbacks.onCreate(this, savedInstanceState, super.onCreate); + } + + protected onSaveInstanceState(outState: any): void { // android.os.Bundle + this._callbacks.onSaveInstanceState(this, outState, super.onSaveInstanceState); + } + + protected onStart(): void { + this._callbacks.onStart(this, super.onStart); + } + + protected onStop(): void { + this._callbacks.onStop(this, super.onStop); + } + + protected onDestroy(): void { + this._callbacks.onDestroy(this, super.onDestroy); + } + + public onBackPressed(): void { + this._callbacks.onBackPressed(this, super.onBackPressed); + } + + public onRequestPermissionsResult(requestCode: number, permissions: Array, grantResults: Array): void { + this._callbacks.onRequestPermissionsResult(this, requestCode, permissions, grantResults, undefined /*TODO: Enable if needed*/); + } + + protected onActivityResult(requestCode: number, resultCode: number, data: any): void { // android.content.Intent + this._callbacks.onActivityResult(this, requestCode, resultCode, data, super.onActivityResult); + } +} diff --git a/demo/AngularApp/app/application.android.ts b/demo/AngularApp/app/application.android.ts new file mode 100644 index 00000000..4c23696f --- /dev/null +++ b/demo/AngularApp/app/application.android.ts @@ -0,0 +1,10 @@ +@JavaProxy("org.myApp.Application") +class Application extends android.app.Application { + onCreate(): void { + super.onCreate(); + } + + protected attachBaseContext(baseContext: any) { // android.content.Context + super.attachBaseContext(baseContext); + } +} diff --git a/demo/AngularApp/app/application.d.ts b/demo/AngularApp/app/application.d.ts new file mode 100644 index 00000000..3cb28cfd --- /dev/null +++ b/demo/AngularApp/app/application.d.ts @@ -0,0 +1 @@ +declare const android: any; diff --git a/demo/AngularApp/package.json b/demo/AngularApp/package.json index 27f97bbe..79551755 100644 --- a/demo/AngularApp/package.json +++ b/demo/AngularApp/package.json @@ -30,7 +30,7 @@ }, "devDependencies": { "@angular-devkit/build-angular": "~0.7.0-beta.1", - "@angular/compiler-cli": "~6.0.0", + "@angular/compiler-cli": "~6.1.0-beta.1", "@types/chai": "^4.0.2", "@types/mocha": "^2.2.41", "@types/node": "^7.0.5", @@ -50,13 +50,6 @@ "typescript": "~2.7.2" }, "scripts": { - "ns-bundle": "ns-bundle", - "start-android-bundle": "npm run ns-bundle --android --run-app", - "start-ios-bundle": "npm run ns-bundle --ios --run-app", - "build-android-bundle": "npm run ns-bundle --android --build-app", - "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 ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json", "compile-tests": "tsc -p e2e --watch" } diff --git a/demo/AngularApp/webpack.config.js b/demo/AngularApp/webpack.config.js new file mode 100644 index 00000000..04731f1a --- /dev/null +++ b/demo/AngularApp/webpack.config.js @@ -0,0 +1,270 @@ +const { join, relative, resolve, sep } = require("path"); + +const webpack = require("webpack"); +const nsWebpack = require("nativescript-dev-webpack"); +const nativescriptTarget = require("nativescript-dev-webpack/nativescript-target"); +const { PlatformReplacementHost } = require("nativescript-dev-webpack/host/platform"); +const CleanWebpackPlugin = require("clean-webpack-plugin"); +const CopyWebpackPlugin = require("copy-webpack-plugin"); +const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); +const { NativeScriptWorkerPlugin } = require("nativescript-worker-loader/NativeScriptWorkerPlugin"); +const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); +const { AngularCompilerPlugin } = require("@ngtools/webpack"); + +module.exports = env => { + // Add your custom Activities, Services and other Android app components here. + const appComponents = [ + "tns-core-modules/ui/frame", + "tns-core-modules/ui/frame/activity", + resolve(__dirname, "app/activity.android.ts"), + ]; + + const platform = env && (env.android && "android" || env.ios && "ios"); + if (!platform) { + throw new Error("You need to provide a target platform!"); + } + + const extensions = ["tns", platform]; + const platformHost = new PlatformReplacementHost(extensions); + + const projectRoot = __dirname; + + // Default destination inside platforms//... + const dist = resolve(projectRoot, nsWebpack.getAppPath(platform, projectRoot)); + const appResourcesPlatformDir = platform === "android" ? "Android" : "iOS"; + + 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", + + // You can provide the following flags when running 'tns run android|ios' + aot, // --env.aot + snapshot, // --env.snapshot + uglify, // --env.uglify + report, // --env.report + } = env; + + const appFullPath = resolve(projectRoot, appPath); + const appResourcesFullPath = resolve(projectRoot, appResourcesPath); + + const entryModule = aot ? + nsWebpack.getAotEntryModule(appFullPath) : + `${nsWebpack.getEntryModule(appFullPath)}.ts`; + const entryPath = `.${sep}${entryModule}`; + + const config = { + mode: uglify ? "production" : "development", + context: appFullPath, + watchOptions: { + ignored: [ + appResourcesFullPath, + // Don't watch hidden files + "**/.*", + ] + }, + target: nativescriptTarget, + entry: { + bundle: entryPath, + application: "./application.android", + }, + output: { + pathinfo: false, + path: dist, + libraryTarget: "commonjs2", + filename: "[name].js", + globalObject: "global", + }, + resolve: { + extensions: [".ts", ".js", ".scss", ".css"], + // Resolve {N} system modules from tns-core-modules + modules: [ + resolve(__dirname, "node_modules/tns-core-modules"), + resolve(__dirname, "node_modules"), + "node_modules/tns-core-modules", + "node_modules", + ], + alias: { + '~': appFullPath + }, + symlinks: true + }, + resolveLoader: { + symlinks: false + }, + node: { + // Disable node shims that conflict with NativeScript + "http": false, + "timers": false, + "setImmediate": false, + "fs": "empty", + "__dirname": false, + }, + devtool: "none", + optimization: { + splitChunks: { + cacheGroups: { + vendor: { + name: "vendor", + chunks: "all", + test: (module, chunks) => { + const moduleName = module.nameForCondition ? module.nameForCondition() : ''; + return /[\\/]node_modules[\\/]/.test(moduleName) || + appComponents.some(comp => comp === moduleName); + }, + enforce: true, + }, + } + }, + minimize: !!uglify, + minimizer: [ + new UglifyJsPlugin({ + uglifyOptions: { + parallel: true, + cache: true, + output: { + comments: false, + }, + compress: { + // The Android SBG has problems parsing the output + // when these options are enabled + 'collapse_vars': platform !== "android", + sequences: platform !== "android", + } + } + }) + ], + }, + module: { + rules: [ + { + test: new RegExp(entryPath), + use: [ + // Require all Android app components + platform === "android" && { + loader: "nativescript-dev-webpack/android-app-components-loader", + options: { modules: appComponents } + }, + + { + loader: "nativescript-dev-webpack/bundle-config-loader", + options: { + angular: true, + loadCss: !snapshot, // load the application css if in debug mode + } + }, + ].filter(loader => !!loader) + }, + + { test: /\.html$|\.xml$/, use: "raw-loader" }, + + // tns-core-modules reads the app.css and its imports using css-loader + { + test: /[\/|\\]app\.css$/, + use: { + loader: "css-loader", + options: { minimize: false, url: false }, + } + }, + { + test: /[\/|\\]app\.scss$/, + use: [ + { loader: "css-loader", options: { minimize: false, url: false } }, + "sass-loader" + ] + }, + + // Angular components reference css files and their imports using raw-loader + { test: /\.css$/, exclude: /[\/|\\]app\.css$/, use: "raw-loader" }, + { test: /\.scss$/, exclude: /[\/|\\]app\.scss$/, use: ["raw-loader", "resolve-url-loader", "sass-loader"] }, + + // Compile TypeScript files with ahead-of-time compiler. + { + test: /.ts$/, use: [ + "nativescript-dev-webpack/moduleid-compat-loader", + "@ngtools/webpack", + ] + }, + + // Mark files inside `@angular/core` as using SystemJS style dynamic imports. + // Removing this will cause deprecation warnings to appear. + { + test: /[\/\\]@angular[\/\\]core[\/\\].+\.js$/, + parser: { system: true }, + }, + ], + }, + plugins: [ + // Define useful constants like TNS_WEBPACK + new webpack.DefinePlugin({ + "global.TNS_WEBPACK": "true", + }), + // Remove all files from the out dir. + new CleanWebpackPlugin([ `${dist}/**/*` ]), + // Copy native app resources to out dir. + new CopyWebpackPlugin([ + { + from: `${appResourcesFullPath}/${appResourcesPlatformDir}`, + to: `${dist}/App_Resources/${appResourcesPlatformDir}`, + context: projectRoot + }, + ]), + // Copy assets to out dir. Add your own globs as needed. + new CopyWebpackPlugin([ + { from: "fonts/**" }, + { from: "**/*.jpg" }, + { from: "**/*.png" }, + ], { ignore: [`${relative(appPath, appResourcesFullPath)}/**`] }), + // Generate a bundle starter script and activate it in package.json + new nsWebpack.GenerateBundleStarterPlugin([ + "./vendor", + "./bundle", + ]), + // For instructions on how to set up workers with webpack + // check out https://github.com/nativescript/worker-loader + new NativeScriptWorkerPlugin(), + + new AngularCompilerPlugin({ + host: platformHost, + entryModule: resolve(appPath, "app.module#AppModule"), + tsConfigPath: join(__dirname, "tsconfig.esm.json"), + skipCodeGeneration: !aot, + }), + // Does IPC communication with the {N} CLI to notify events when running in watch mode. + new nsWebpack.WatchStateLoggerPlugin(), + ], + }; + + if (report) { + // Generate report files for bundles content + config.plugins.push(new BundleAnalyzerPlugin({ + analyzerMode: "static", + openAnalyzer: false, + generateStatsFile: true, + reportFilename: resolve(projectRoot, "report", `report.html`), + statsFilename: resolve(projectRoot, "report", `stats.json`), + })); + } + + if (snapshot) { + config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({ + chunk: "vendor", + angular: true, + requireModules: [ + "reflect-metadata", + "@angular/platform-browser", + "@angular/core", + "@angular/common", + "@angular/router", + "nativescript-angular/platform-static", + "nativescript-angular/router", + ], + projectRoot, + webpackConfig: config, + })); + } + + return config; +}; diff --git a/demo/JavaScriptApp/app/App_Resources/Android/AndroidManifest.xml b/demo/JavaScriptApp/app/App_Resources/Android/AndroidManifest.xml index 9db83215..0bb603e9 100644 --- a/demo/JavaScriptApp/app/App_Resources/Android/AndroidManifest.xml +++ b/demo/JavaScriptApp/app/App_Resources/Android/AndroidManifest.xml @@ -19,14 +19,14 @@ diff --git a/demo/JavaScriptApp/app/activity.android.js b/demo/JavaScriptApp/app/activity.android.js new file mode 100644 index 00000000..c5013fd5 --- /dev/null +++ b/demo/JavaScriptApp/app/activity.android.js @@ -0,0 +1,32 @@ +const frame = require("ui/frame"); + +const superProto = android.app.Activity.prototype; +android.app.Activity.extend("org.myApp.MainActivity", { + onCreate: function(savedInstanceState) { + if(!this._callbacks) { + frame.setActivityCallbacks(this); + } + this._callbacks.onCreate(this, savedInstanceState, superProto.onCreate); + }, + onSaveInstanceState: function(outState) { + this._callbacks.onSaveInstanceState(this, outState, superProto.onSaveInstanceState); + }, + onStart: function() { + this._callbacks.onStart(this, superProto.onStart); + }, + onStop: function() { + this._callbacks.onStop(this, superProto.onStop); + }, + onDestroy: function() { + this._callbacks.onDestroy(this, superProto.onDestroy); + }, + onBackPressed: function() { + this._callbacks.onBackPressed(this, superProto.onBackPressed); + }, + onRequestPermissionsResult: function (requestCode, permissions, grantResults) { + this._callbacks.onRequestPermissionsResult(this, requestCode, permissions, grantResults, undefined); + }, + onActivityResult: function (requestCode, resultCode, data) { + this._callbacks.onActivityResult(this, requestCode, resultCode, data, _super.prototype.onActivityResult); + } +}); diff --git a/demo/JavaScriptApp/app/application.android.js b/demo/JavaScriptApp/app/application.android.js new file mode 100644 index 00000000..858b33b6 --- /dev/null +++ b/demo/JavaScriptApp/app/application.android.js @@ -0,0 +1,9 @@ +const superProto = android.app.Application.prototype; +android.app.Application.extend("org.myApp.Application", { + onCreate: function() { + superProto.onCreate.call(this); + }, + attachBaseContext: function(base) { + superProto.attachBaseContext.call(this, base); + } +}); diff --git a/demo/JavaScriptApp/package.json b/demo/JavaScriptApp/package.json index a492ee07..32543aa0 100644 --- a/demo/JavaScriptApp/package.json +++ b/demo/JavaScriptApp/package.json @@ -33,13 +33,6 @@ "node-sass": "^4.7.1" }, "scripts": { - "ns-bundle": "ns-bundle", - "start-android-bundle": "npm run ns-bundle --android --run-app", - "start-ios-bundle": "npm run ns-bundle --ios --run-app", - "build-android-bundle": "npm run ns-bundle --android --build-app", - "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 ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json" } } diff --git a/demo/JavaScriptApp/webpack.config.js b/demo/JavaScriptApp/webpack.config.js new file mode 100644 index 00000000..64ea43c1 --- /dev/null +++ b/demo/JavaScriptApp/webpack.config.js @@ -0,0 +1,231 @@ +const { join, relative, resolve, sep } = require("path"); + +const webpack = require("webpack"); +const nsWebpack = require("nativescript-dev-webpack"); +const nativescriptTarget = require("nativescript-dev-webpack/nativescript-target"); +const CleanWebpackPlugin = require("clean-webpack-plugin"); +const CopyWebpackPlugin = require("copy-webpack-plugin"); +const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); +const { NativeScriptWorkerPlugin } = require("nativescript-worker-loader/NativeScriptWorkerPlugin"); +const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); + +module.exports = env => { + // Add your custom Activities, Services and other android app components here. + const appComponents = [ + "tns-core-modules/ui/frame", + "tns-core-modules/ui/frame/activity", + resolve(__dirname, "app/activity.android.js"), + ]; + + const platform = env && (env.android && "android" || env.ios && "ios"); + if (!platform) { + throw new Error("You need to provide a target platform!"); + } + + const platforms = ["ios", "android"]; + const projectRoot = __dirname; + + // Default destination inside platforms//... + const dist = resolve(projectRoot, nsWebpack.getAppPath(platform, projectRoot)); + const appResourcesPlatformDir = platform === "android" ? "Android" : "iOS"; + + 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", + + // You can provide the following flags when running 'tns run android|ios' + snapshot, // --env.snapshot + uglify, // --env.uglify + report, // --env.report + } = env; + + const appFullPath = resolve(projectRoot, appPath); + const appResourcesFullPath = resolve(projectRoot, appResourcesPath); + + const entryModule = nsWebpack.getEntryModule(appFullPath); + const entryPath = `.${sep}${entryModule}.js`; + + const config = { + mode: uglify ? "production" : "development", + context: appFullPath, + watchOptions: { + ignored: [ + appResourcesFullPath, + // Don't watch hidden files + "**/.*", + ] + }, + target: nativescriptTarget, + entry: { + bundle: entryPath, + application: "./application.android", + }, + output: { + pathinfo: false, + path: dist, + libraryTarget: "commonjs2", + filename: "[name].js", + globalObject: "global", + }, + resolve: { + extensions: [".js", ".scss", ".css"], + // Resolve {N} system modules from tns-core-modules + modules: [ + "node_modules/tns-core-modules", + "node_modules", + ], + alias: { + '~': appFullPath + }, + // don't resolve symlinks to symlinked modules + symlinks: false + }, + resolveLoader: { + // don't resolve symlinks to symlinked loaders + symlinks: false + }, + node: { + // Disable node shims that conflict with NativeScript + "http": false, + "timers": false, + "setImmediate": false, + "fs": "empty", + "__dirname": false, + }, + devtool: "none", + optimization: { + splitChunks: { + cacheGroups: { + vendor: { + name: "vendor", + chunks: "all", + test: (module, chunks) => { + const moduleName = module.nameForCondition ? module.nameForCondition() : ''; + return /[\\/]node_modules[\\/]/.test(moduleName) || + appComponents.some(comp => comp === moduleName); + + }, + enforce: true, + }, + } + }, + minimize: !!uglify, + minimizer: [ + new UglifyJsPlugin({ + uglifyOptions: { + parallel: true, + cache: true, + output: { + comments: false, + }, + compress: { + // The Android SBG has problems parsing the output + // when these options are enabled + 'collapse_vars': platform !== "android", + sequences: platform !== "android", + } + } + }) + ], + }, + module: { + rules: [ + { + test: new RegExp(entryPath), + use: [ + // Require all Android app components + platform === "android" && { + loader: "nativescript-dev-webpack/android-app-components-loader", + options: { modules: appComponents } + }, + + { + loader: "nativescript-dev-webpack/bundle-config-loader", + options: { + loadCss: !snapshot, // load the application css if in debug mode + } + }, + ].filter(loader => !!loader) + }, + + { test: /\.(html|xml)$/, use: "nativescript-dev-webpack/xml-namespace-loader"}, + + { + test: /\.css$/, + use: { loader: "css-loader", options: { minimize: false, url: false } } + }, + + { + test: /\.scss$/, + use: [ + { loader: "css-loader", options: { minimize: false, url: false } }, + "sass-loader" + ] + } + ] + }, + plugins: [ + // Define useful constants like TNS_WEBPACK + new webpack.DefinePlugin({ + "global.TNS_WEBPACK": "true", + }), + // Remove all files from the out dir. + new CleanWebpackPlugin([ `${dist}/**/*` ]), + // Copy native app resources to out dir. + new CopyWebpackPlugin([ + { + from: `${appResourcesFullPath}/${appResourcesPlatformDir}`, + to: `${dist}/App_Resources/${appResourcesPlatformDir}`, + context: projectRoot + }, + ]), + // Copy assets to out dir. Add your own globs as needed. + new CopyWebpackPlugin([ + { from: "fonts/**" }, + { from: "**/*.jpg" }, + { from: "**/*.png" }, + ], { ignore: [`${relative(appPath, appResourcesFullPath)}/**`] }), + // Generate a bundle starter script and activate it in package.json + new nsWebpack.GenerateBundleStarterPlugin([ + "./vendor", + "./bundle", + ]), + // For instructions on how to set up workers with webpack + // check out https://github.com/nativescript/worker-loader + new NativeScriptWorkerPlugin(), + new nsWebpack.PlatformFSPlugin({ + platform, + platforms, + }), + // Does IPC communication with the {N} CLI to notify events when running in watch mode. + new nsWebpack.WatchStateLoggerPlugin(), + ], + }; + + if (report) { + // Generate report files for bundles content + config.plugins.push(new BundleAnalyzerPlugin({ + analyzerMode: "static", + openAnalyzer: false, + generateStatsFile: true, + reportFilename: resolve(projectRoot, "report", `report.html`), + statsFilename: resolve(projectRoot, "report", `stats.json`), + })); + } + + if (snapshot) { + config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({ + chunk: "vendor", + requireModules: [ + "tns-core-modules/bundle-entry-points", + ], + projectRoot, + webpackConfig: config, + })); + } + + return config; +}; diff --git a/demo/TypeScriptApp/app/App_Resources/Android/AndroidManifest.xml b/demo/TypeScriptApp/app/App_Resources/Android/AndroidManifest.xml index 9db83215..0bb603e9 100644 --- a/demo/TypeScriptApp/app/App_Resources/Android/AndroidManifest.xml +++ b/demo/TypeScriptApp/app/App_Resources/Android/AndroidManifest.xml @@ -19,14 +19,14 @@ diff --git a/demo/TypeScriptApp/app/activity.android.ts b/demo/TypeScriptApp/app/activity.android.ts new file mode 100644 index 00000000..f04e39bd --- /dev/null +++ b/demo/TypeScriptApp/app/activity.android.ts @@ -0,0 +1,42 @@ +import {setActivityCallbacks, AndroidActivityCallbacks} from "ui/frame"; + +@JavaProxy("org.myApp.MainActivity") +class Activity extends android.app.Activity { + private _callbacks: AndroidActivityCallbacks; + + protected onCreate(savedInstanceState: any): void { // android.os.Bundle + if (!this._callbacks) { + setActivityCallbacks(this); + } + + this._callbacks.onCreate(this, savedInstanceState, super.onCreate); + } + + protected onSaveInstanceState(outState: any): void { // android.os.Bundle + this._callbacks.onSaveInstanceState(this, outState, super.onSaveInstanceState); + } + + protected onStart(): void { + this._callbacks.onStart(this, super.onStart); + } + + protected onStop(): void { + this._callbacks.onStop(this, super.onStop); + } + + protected onDestroy(): void { + this._callbacks.onDestroy(this, super.onDestroy); + } + + public onBackPressed(): void { + this._callbacks.onBackPressed(this, super.onBackPressed); + } + + public onRequestPermissionsResult(requestCode: number, permissions: Array, grantResults: Array): void { + this._callbacks.onRequestPermissionsResult(this, requestCode, permissions, grantResults, undefined /*TODO: Enable if needed*/); + } + + protected onActivityResult(requestCode: number, resultCode: number, data: any): void { // android.content.Intent + this._callbacks.onActivityResult(this, requestCode, resultCode, data, super.onActivityResult); + } +} diff --git a/demo/TypeScriptApp/app/app.android.css b/demo/TypeScriptApp/app/app.android.css new file mode 100644 index 00000000..c55b85fe --- /dev/null +++ b/demo/TypeScriptApp/app/app.android.css @@ -0,0 +1 @@ +@import url(/Users/vchimev/Work/git/nativescript-dev-webpack/demo/TypeScriptApp/node_modules/nativescript-theme-core/css/core.light.css);ActionBar{background-color:#7F9}.app-class{background-color:#7F9} diff --git a/demo/TypeScriptApp/app/app.ios.css b/demo/TypeScriptApp/app/app.ios.css new file mode 100644 index 00000000..2a1f4e39 --- /dev/null +++ b/demo/TypeScriptApp/app/app.ios.css @@ -0,0 +1 @@ +@import url(/Users/vchimev/Work/git/nativescript-dev-webpack/demo/TypeScriptApp/node_modules/nativescript-theme-core/css/core.light.css);ActionBar{background-color:#999}.app-class{background-color:#999} diff --git a/demo/TypeScriptApp/app/application.android.ts b/demo/TypeScriptApp/app/application.android.ts new file mode 100644 index 00000000..4c23696f --- /dev/null +++ b/demo/TypeScriptApp/app/application.android.ts @@ -0,0 +1,10 @@ +@JavaProxy("org.myApp.Application") +class Application extends android.app.Application { + onCreate(): void { + super.onCreate(); + } + + protected attachBaseContext(baseContext: any) { // android.content.Context + super.attachBaseContext(baseContext); + } +} diff --git a/demo/TypeScriptApp/app/application.d.ts b/demo/TypeScriptApp/app/application.d.ts new file mode 100644 index 00000000..3cb28cfd --- /dev/null +++ b/demo/TypeScriptApp/app/application.d.ts @@ -0,0 +1 @@ +declare const android: any; diff --git a/demo/TypeScriptApp/package.json b/demo/TypeScriptApp/package.json index 3cef17fa..c4ecf688 100644 --- a/demo/TypeScriptApp/package.json +++ b/demo/TypeScriptApp/package.json @@ -34,13 +34,6 @@ "typescript": "~2.7.2" }, "scripts": { - "ns-bundle": "ns-bundle", - "start-android-bundle": "npm run ns-bundle --android --run-app", - "start-ios-bundle": "npm run ns-bundle --ios --run-app", - "build-android-bundle": "npm run ns-bundle --android --build-app", - "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 ../config/mocha.opts --recursive e2e --appiumCapsLocation ../config/appium.capabilities.json", "compile-tests": "tsc -p e2e --watch" } diff --git a/demo/TypeScriptApp/webpack.config.js b/demo/TypeScriptApp/webpack.config.js new file mode 100644 index 00000000..3c2bd1e1 --- /dev/null +++ b/demo/TypeScriptApp/webpack.config.js @@ -0,0 +1,241 @@ +const { join, relative, resolve, sep } = require("path"); + +const webpack = require("webpack"); +const nsWebpack = require("nativescript-dev-webpack"); +const nativescriptTarget = require("nativescript-dev-webpack/nativescript-target"); +const CleanWebpackPlugin = require("clean-webpack-plugin"); +const CopyWebpackPlugin = require("copy-webpack-plugin"); +const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); +const { NativeScriptWorkerPlugin } = require("nativescript-worker-loader/NativeScriptWorkerPlugin"); +const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); + +module.exports = env => { + // Add your custom Activities, Services and other Android app components here. + const appComponents = [ + "tns-core-modules/ui/frame", + "tns-core-modules/ui/frame/activity", + resolve(__dirname, "app/activity.android.ts"), + ]; + + const platform = env && (env.android && "android" || env.ios && "ios"); + if (!platform) { + throw new Error("You need to provide a target platform!"); + } + + const platforms = ["ios", "android"]; + const projectRoot = __dirname; + + // Default destination inside platforms//... + const dist = resolve(projectRoot, nsWebpack.getAppPath(platform, projectRoot)); + const appResourcesPlatformDir = platform === "android" ? "Android" : "iOS"; + + 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", + + // You can provide the following flags when running 'tns run android|ios' + snapshot, // --env.snapshot + uglify, // --env.uglify + report, // --env.report + } = env; + + const appFullPath = resolve(projectRoot, appPath); + const appResourcesFullPath = resolve(projectRoot, appResourcesPath); + + const entryModule = nsWebpack.getEntryModule(appFullPath); + const entryPath = `.${sep}${entryModule}.ts`; + + const config = { + mode: uglify ? "production" : "development", + context: appFullPath, + watchOptions: { + ignored: [ + appResourcesFullPath, + // Don't watch hidden files + "**/.*", + ] + }, + target: nativescriptTarget, + entry: { + bundle: entryPath, + application: "./application.android", + }, + output: { + pathinfo: false, + path: dist, + libraryTarget: "commonjs2", + filename: "[name].js", + globalObject: "global", + }, + resolve: { + extensions: [".ts", ".js", ".scss", ".css"], + // Resolve {N} system modules from tns-core-modules + modules: [ + resolve(__dirname, "node_modules/tns-core-modules"), + resolve(__dirname, "node_modules"), + "node_modules/tns-core-modules", + "node_modules", + ], + alias: { + '~': appFullPath + }, + // don't resolve symlinks to symlinked modules + symlinks: false + }, + resolveLoader: { + // don't resolve symlinks to symlinked loaders + symlinks: false + }, + node: { + // Disable node shims that conflict with NativeScript + "http": false, + "timers": false, + "setImmediate": false, + "fs": "empty", + "__dirname": false, + }, + devtool: "none", + optimization: { + splitChunks: { + cacheGroups: { + vendor: { + name: "vendor", + chunks: "all", + test: (module, chunks) => { + const moduleName = module.nameForCondition ? module.nameForCondition() : ''; + return /[\\/]node_modules[\\/]/.test(moduleName) || + appComponents.some(comp => comp === moduleName); + + }, + enforce: true, + }, + } + }, + minimize: !!uglify, + minimizer: [ + new UglifyJsPlugin({ + uglifyOptions: { + parallel: true, + cache: true, + output: { + comments: false, + }, + compress: { + // The Android SBG has problems parsing the output + // when these options are enabled + 'collapse_vars': platform !== "android", + sequences: platform !== "android", + } + } + }) + ], + }, + module: { + rules: [ + { + test: new RegExp(entryPath), + use: [ + // Require all Android app components + platform === "android" && { + loader: "nativescript-dev-webpack/android-app-components-loader", + options: { modules: appComponents } + }, + + { + loader: "nativescript-dev-webpack/bundle-config-loader", + options: { + loadCss: !snapshot, // load the application css if in debug mode + } + }, + ].filter(loader => !!loader) + }, + + { test: /\.(html|xml)$/, use: "nativescript-dev-webpack/xml-namespace-loader"}, + + { + test: /\.css$/, + use: { loader: "css-loader", options: { minimize: false, url: false } } + }, + + { + test: /\.scss$/, + use: [ + { loader: "css-loader", options: { minimize: false, url: false } }, + "sass-loader" + ] + }, + + { + test: /\.ts$/, + use: { + loader: "awesome-typescript-loader", + options: { configFileName: "tsconfig.esm.json" }, + } + }, + ] + }, + plugins: [ + // Define useful constants like TNS_WEBPACK + new webpack.DefinePlugin({ + "global.TNS_WEBPACK": "true", + }), + // Remove all files from the out dir. + new CleanWebpackPlugin([ `${dist}/**/*` ]), + // Copy native app resources to out dir. + new CopyWebpackPlugin([ + { + from: `${appResourcesFullPath}/${appResourcesPlatformDir}`, + to: `${dist}/App_Resources/${appResourcesPlatformDir}`, + context: projectRoot + }, + ]), + // Copy assets to out dir. Add your own globs as needed. + new CopyWebpackPlugin([ + { from: "fonts/**" }, + { from: "**/*.jpg" }, + { from: "**/*.png" }, + ], { ignore: [`${relative(appPath, appResourcesFullPath)}/**`] }), + // Generate a bundle starter script and activate it in package.json + new nsWebpack.GenerateBundleStarterPlugin([ + "./vendor", + "./bundle", + ]), + // For instructions on how to set up workers with webpack + // check out https://github.com/nativescript/worker-loader + new NativeScriptWorkerPlugin(), + new nsWebpack.PlatformFSPlugin({ + platform, + platforms, + }), + // Does IPC communication with the {N} CLI to notify events when running in watch mode. + new nsWebpack.WatchStateLoggerPlugin(), + ], + }; + + if (report) { + // Generate report files for bundles content + config.plugins.push(new BundleAnalyzerPlugin({ + analyzerMode: "static", + openAnalyzer: false, + generateStatsFile: true, + reportFilename: resolve(projectRoot, "report", `report.html`), + statsFilename: resolve(projectRoot, "report", `stats.json`), + })); + } + + if (snapshot) { + config.plugins.push(new nsWebpack.NativeScriptSnapshotPlugin({ + chunk: "vendor", + requireModules: [ + "tns-core-modules/bundle-entry-points", + ], + projectRoot, + webpackConfig: config, + })); + } + + return config; +}; diff --git a/demo/config/appium.capabilities.json b/demo/config/appium.capabilities.json index 16749592..4bdd8c65 100644 --- a/demo/config/appium.capabilities.json +++ b/demo/config/appium.capabilities.json @@ -5,7 +5,6 @@ "deviceName": "Emulator-Api19-Default", "avd": "Emulator-Api19-Default", "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", "newCommandTimeout": 720, "noReset": true, "fullReset": false, @@ -17,7 +16,6 @@ "deviceName": "Emulator-Api21-Default", "avd": "Emulator-Api21-Default", "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", "newCommandTimeout": 720, "noReset": true, "fullReset": false, @@ -29,7 +27,6 @@ "deviceName": "Emulator-Api23-Default", "avd": "Emulator-Api23-Default", "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", "newCommandTimeout": 720, "noReset": true, "fullReset": false, @@ -41,7 +38,6 @@ "deviceName": "Emulator-Api24-Default", "avd": "Emulator-Api24-Default", "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", "newCommandTimeout": 720, "noReset": true, "fullReset": false, @@ -53,7 +49,6 @@ "deviceName": "Emulator-Api25-Google", "avd": "Emulator-Api25-Google", "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", "newCommandTimeout": 720, "noReset": true, "fullReset": false, @@ -65,7 +60,6 @@ "deviceName": "Emulator-Api26-Google", "avd": "Emulator-Api26-Google", "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", "newCommandTimeout": 720, "noReset": true, "fullReset": false, @@ -77,7 +71,6 @@ "deviceName": "Emulator-Api27-Google", "avd": "Emulator-Api27-Google", "lt": 60000, - "appActivity": "com.tns.NativeScriptActivity", "newCommandTimeout": 720, "noReset": true, "fullReset": false, diff --git a/projectHelpers.js b/projectHelpers.js index 1e9464f5..bc66ac04 100644 --- a/projectHelpers.js +++ b/projectHelpers.js @@ -66,6 +66,16 @@ function safeGet(object, property, ...args) { value; } +// Convert paths from C:\some\path to C:/some/path in order to be required +function convertSlashesInPath(modulePath) { + if (isWindows) { + modulePath = modulePath.replace(/\\/g, "/"); + } + return modulePath; +} + +const isWindows = process.platform.startsWith("win32"); + module.exports = { getAppPathFromProjectData, getAppResourcesPathFromProjectData, @@ -76,4 +86,5 @@ module.exports = { isAngular, isTypeScript, writePackageJson, + convertSlashesInPath }; diff --git a/xml-namespace-loader.js b/xml-namespace-loader.js index a0c0d664..f640b30d 100644 --- a/xml-namespace-loader.js +++ b/xml-namespace-loader.js @@ -1,6 +1,7 @@ const { parse, relative, join, basename, extname } = require("path"); +const { convertSlashesInPath } = require("./projectHelpers"); -module.exports = function(source) { +module.exports = function (source) { this.value = source; const { XmlParser } = require("tns-core-modules/xml"); @@ -47,7 +48,7 @@ module.exports = function(source) { namespaces.push({ name: namespace, path: resolvedPath }); namespaces.push({ name: moduleName, path: resolvedPath }); - const { dir, name } = parse(resolvedPath); + const { dir, name } = parse(resolvedPath); const noExtFilename = join(dir, name); const xml = tryResolve(`${noExtFilename}.xml`); @@ -67,6 +68,7 @@ module.exports = function(source) { parser.parse(source); const moduleRegisters = namespaces + .map(convertPath) .map(n => `global.registerModule("${n.name}", function() { return require("${n.path}"); });` ) @@ -83,12 +85,16 @@ module.exports = function(source) { this.callback(null, wrapped); } +function convertPath(obj) { + obj.path = convertSlashesInPath(obj.path); + return obj; +} + function tryResolve(path) { try { return require.resolve(path); - } catch(e) { + } catch (e) { // The path couldn't be resolved return; } } -