Skip to content
This repository was archived by the owner on Aug 7, 2021. It is now read-only.

uglify option prevent the generation of application java class. #564

Closed
9 tasks done
RadouaneRoufid opened this issue Jun 11, 2018 · 11 comments
Closed
9 tasks done
Assignees

Comments

@RadouaneRoufid
Copy link

RadouaneRoufid commented Jun 11, 2018

Issue Checklist

Tell us about the problem

The issue 559 results in a problem in using --env.uglify option when using a custom application. In fact, the custom application Java Class is not generated causing an error on app start: ClassNotFoundException

with a -- env.uglify option, the build succeed without console errors. But when starting the app on device or emulator, the classNotFoundExcetion occurs. I tried to understand why it does not work and I realized the java class of the @JavaProxy is not generated as you can see below

image

while without --env.uglify option, a MainActivity.java exists under org.bricool as below :

image

Local environment

  • Platform Linux
  • Node version 8.11.2
  • NativeScript CLI version 4.1.0

Project data

  • Platform you are building the app for Android
  • Node dependencies:
{
  "name": "project",
  "version": "0.0.0",
  "repository": "<fill-your-repository-here>",
  "nativescript": {
    "id": "org.bricool.project",
    "tns-ios": {
      "version": "3.1.0"
    },
    "tns-android": {
      "version": "4.1.1"
    }
  },
  "scripts": {
  
  },
  "dependencies": {
    "@angular/animations": "~5.2.1",
    "@angular/common": "^5.2.1",
    "@angular/compiler": "^5.2.1",
    "@angular/core": "^5.2.1",
    "@angular/forms": "^5.2.1",
    "@angular/http": "^5.2.1",
    "@angular/platform-browser": "^5.2.1",
    "@angular/platform-browser-dynamic": "^5.2.1",
    "@angular/router": "^5.2.1",
    "@ngrx/effects": "^4.1.1",
    "@ngrx/router-store": "^4.1.1",
    "@ngrx/store": "^4.1.1",
    "@ngrx/store-devtools": "^4.1.1",
    "@ngx-progressbar/core": "^3.0.1",
    "@ngx-progressbar/http-client": "^3.0.1",
    "@ngx-translate/core": "^9.1.1",
    "@ngx-translate/http-loader": "^2.0.1",
    "arrive": "^2.3.1",
    "bootstrap": "4.0.0-alpha.6",
    "bootstrap-datepicker": "1.6.4",
    "bootstrap-social": "^5.1.1",
    "core-js": "^2.5.2",
    "font-awesome": "4.7.0",
    "fs": "0.0.1-security",
    "hammerjs": "^2.0.8",
    "i": "^0.3.6",
    "lodash": "^4.17.4",
    "materialize-css": "0.100.2",
    "nativescript-angular": "~5.2.0",
    "nativescript-background-http": "^3.2.7",
    "nativescript-bottom-navigation": "^1.1.2",
    "nativescript-bottombar": "^3.0.8",
    "nativescript-cardview": "^2.0.5",
    "nativescript-checkbox": "^3.0.3",
    "nativescript-floatingactionbutton": "^4.1.3",
    "nativescript-fresco": "^3.0.2",
    "nativescript-imagepicker": "^5.0.0",
    "nativescript-localstorage": "^1.1.5",
    "nativescript-mediafilepicker": "^1.0.5",
    "nativescript-modal-datetimepicker": "^1.1.0",
    "nativescript-ng-shadow": "^2.1.0",
    "nativescript-oauth": "^2.1.2",
    "nativescript-phone": "^1.3.1",
    "nativescript-photoviewer": "^1.4.0",
    "nativescript-snackbar": "^2.0.0",
    "nativescript-theme-core": "~1.0.4",
    "nativescript-ui-listview": "^3.5.7",
    "nativescript-ui-sidedrawer": "^4.1.1",
    "ng2-validation": "^4.2.0",
    "ngrx-store-freeze": "^0.1.9",
    "ngx-pagination": "^3.0.3",
    "node-sass": "^4.9.0",
    "npm": "^4.2.0",
    "osenv": "^0.1.4",
    "path": "^0.12.7",
    "reflect-metadata": "^0.1.12",
    "rxjs": "^5.5.5",
    "tether": "^1.4.3",
    "tns-core-modules": "~4.1.0",
    "uglify-es": "^3.3.9",
    "url": "^0.11.0",
    "velocity-animate": "^1.5.1",
    "zone.js": "0.8.5"
  },
  "devDependencies": {
    "@angular-devkit/core": "~0.6.3",
    "@angular/compiler-cli": "^5.2.1",
    "@ngtools/webpack": "~6.0.3",
    "@types/googlemaps": "^3.30.8",
    "@types/jasmine": "^2.8.5",
    "babel-traverse": "6.26.0",
    "babel-types": "6.26.0",
    "babylon": "6.18.0",
    "clean-webpack-plugin": "~0.1.19",
    "copy-webpack-plugin": "~4.5.1",
    "css-loader": "~0.28.11",
    "del": "^2.2.2",
    "extract-text-webpack-plugin": "~3.0.2",
    "fs-walk": "0.0.2",
    "gulp": "^4.0.0",
    "gulp-debug": "^3.1.0",
    "gulp-exec": "^3.0.1",
    "gulp-if": "^2.0.2",
    "gulp-rename": "^1.2.2",
    "gulp-spawn": "^0.4.0",
    "gulp-string-replace": "^0.4.0",
    "lazy": "1.0.11",
    "mkdirp": "^0.5.1",
    "nativescript-css-loader": "~0.26.1",
    "nativescript-dev-sass": "^1.6.0",
    "nativescript-dev-typescript": "~0.7.0",
    "nativescript-dev-webpack": "^0.12.0",
    "nativescript-worker-loader": "~0.9.0",
    "raw-loader": "~0.5.1",
    "resolve-url-loader": "~2.3.0",
    "run-sequence": "^2.2.1",
    "sass-loader": "~7.0.1",
    "tns-platform-declarations": "^4.1.0",
    "typescript": "~2.7.2",
    "typescript-register": "^1.1.0",
    "uglifyjs-webpack-plugin": "~1.2.5",
    "webpack": "~4.6.0",
    "webpack-bundle-analyzer": "~2.13.0",
    "webpack-sources": "~1.1.0",
    "webpack-cli": "~2.1.3"
  },
  "description": "YouFix"
}

  • Webpack configuration:
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/MainActivity.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;
    nsWebpack.loadAdditionalPlugins({ projectDir: projectRoot });

    // Default destination inside platforms/<platform>/...
    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,
            MainActivity: "./MainActivity"
        },
        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: true
        },
        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: {
                                registerPages: false,
                                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: "assets/i18n/**" },
                { 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(),
            // AngularCompilerPlugin with augmented NativeScript filesystem to handle platform specific resource resolution.
            new nsWebpack.NativeScriptAngularCompilerPlugin({
                entryModule: resolve(appPath, "app.module#AppModule"),
                tsConfigPath: join(__dirname, "tsconfig.esm.json"),
                skipCodeGeneration: !aot,
                platformOptions: {
                    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: [
                "reflect-metadata",
                "@angular/platform-browser",
                "@angular/core",
                "@angular/common",
                "@angular/router",
                "nativescript-angular/platform-static",
                "nativescript-angular/router",
            ],
            projectRoot,
            webpackConfig: config,
        }));
    }

    return config;
};

@RoyiNamir
Copy link

RoyiNamir commented Jun 11, 2018

There are few differences between your code VS the demo's code . Just saying .

Are you sure you use the .ts version ?

image

@RadouaneRoufid
Copy link
Author

For resolve(__dirname, "app/MainActivity.android.js"), I tried with both .ts and .js. Got the error with the two.

I had to prefix MainActivity with ./ otherwise webpack didn't find the file.

For symlink, I tried to play with the option. I reverted it. It does not solve the problem.

@jibon57
Copy link

jibon57 commented Jun 21, 2018

@vchimev any work around so far regarding application extend?

@vchimev
Copy link
Contributor

vchimev commented Jun 23, 2018

Hey Guys,

Let me first refer to this pull request addressing the issue where an absolute path in appComponents can't be resolved on Windows and pay attention that adding a custom android app component to the array on the top of the webpack.config.js file should be resolved to an absolute path, i.e.
resolve(__dirname, "app/activity.android.ts") in order to be included in the common vendor.js chunk. Adding it by a relative path is only a workaround for the issue above.

I updated all the demos to extend android activity and application in the same branch, and managed to execute them successfully with:

tns run android --bundle
tns run android --bundle --env.aot --env.uglify --env.snapshot --release ...

Extend Android Activity

In regard to extend android activity, it should be added only to the appComponents array by an absolute path as mentioned above.

const appComponents = [
    "tns-core-modules/ui/frame",
    "tns-core-modules/ui/frame/activity",
    resolve(__dirname, "app/activity.android.ts"),
];

In this way and with the default config, these components get in the common vendor.js chunk and are required in the bundle by android-app-comopnents-loader.

Extend Android Application

In regard to extend android application, it should be added only as an entry.

entry: {
    bundle: entryPath,
    application: "./application.android",
},

In this way, the source code of application.android.js|ts is bundled separately as application.js file which is loaded from the native Application.java class on launch.

The application.js bundle file is independent of the bundle.js and vendor.js files and the reason for it is that bundle.js and vendor.js are not loaded so early in the application launch.
That's why the logic in application.android.js|ts is needed to be bundled separately in order to be loaded by the native Application.java as early as needed on launch.

@RoyiNamir
Copy link

RoyiNamir commented Jul 1, 2018

@vchimev @jibon57 @RadouaneRoufid Hello. Can you please explain why Vasil used in here :

const appComponents = [
    "tns-core-modules/ui/frame",
    "tns-core-modules/ui/frame/activity",
    resolve(__dirname, "app/activity.android.ts"),
];
  1. .ts in "app/activity.android.ts" --------> isn't it suppose to be JS ? I mean it looks like as if the TS file will be included and not js included. ( I know that TS is transpiles to JS) - but still - why ts ?

  2. .android in "app/activity.android.ts" --------> isn't it suppose to be without .android ? as it is removed.

Can you please shed some light about the steps that occur here ?

@RadouaneRoufid
Copy link
Author

It may help #568 (comment)

@vchimev
Copy link
Contributor

vchimev commented Jul 24, 2018

Hey @RoyiNamir,

  1. This is because of the loaders and plugins in use to handle .ts files in TypeScript and Angular projects.
  2. Files with .android.|.ios. are prepared for the specific platform on building without bundling. Then these platform specific files go to the specific native projects and the corresponding extension is removed. For bundling, they are handled through the loaders/plugins mentioned in 1.

@vchimev
Copy link
Contributor

vchimev commented Jul 24, 2018

Hey @RadouaneRoufid and @jibon57,

I'm closing this as demos and docs were updated. Please, let me know in case of any other issues.

@vchimev vchimev closed this as completed Jul 24, 2018
@RoyiNamir
Copy link

RoyiNamir commented Jul 24, 2018

Hey @vchimev . Docs here you mean ?

found it .
Thanks NativeScript/docs@14224dd#diff-bb3b3b05f6b7838c7b1041431d9b9f93

and this :
https://docs.nativescript.org/core-concepts/android-runtime/advanced-topics/extend-application-activity

@RadouaneRoufid
Copy link
Author

Any news on this ? Problem still present in tns 6.1.2

@RadouaneRoufid
Copy link
Author

I found the solution. "noEmitHelpers": true, must be present in compilerOptions in tsconfig.json while build with env.uglify with custom activity.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants