Modules not found in shared code with Nativescript and Angular Schematics

Mark Bell picture Mark Bell · Oct 21, 2018 · Viewed 7.6k times · Source

I'm using Angular with Nativescript Schematics. I have installed the nativescript-ui-sidedrawer. When i run

tns run ios --bundle

The native version is compiled and deployed to the device but when I run

ng serve

The web version fails with several errors, all very similar. The first one being

ERROR in ./node_modules/tns-core-modules/ui/builder/component-builder/component-builder.js
Module not found: Error: Can't resolve '../../../platform' in '/Users/markdev/Documents/Code/mydrawer/node_modules/tns-core-modules/ui/builder/component-builder'

All of the errors are path related.

If I browse to the error given above and edit "component-builder.js", and change the path to

~/node_modules/tns-core-modules/platform

from

../../../platform

then when running "ng serve", that particular error is not displayed in the list of errors in the output.

But... when I run

tns run ios --bundle

I get

ERROR in ../node_modules/tns-core-modules/ui/builder/component-builder/component-builder.js
Module not found: Error: Can't resolve '/Users/markdev/Documents/Code/mydrawer/src/node_modules/tns-core-modules/platform' in '/Users/markdev/Documents/Code/mydrawer/node_modules/tns-core-modules/ui/builder/component-builder'
 @ ../node_modules/tns-core-modules/ui/builder/component-builder/component-builder.js 7:15-66
 @ ../node_modules/tns-core-modules/ui/builder/builder.js
 @ ../node_modules/tns-core-modules/ui/repeater/repeater.js
 @ ../node_modules/tns-core-modules/bundle-entry-points.js
 @ ./main.ns.ts

And if I open the app on the device, I get

*** Terminating app due to uncaught exception 'NativeScript encountered a fatal error: Error: Cannot find module '~/node_modules/tns-core-modules/platform'

tsconfig.json

{
    "compileOnSave": false,
    "compilerOptions": {
        "outDir": "./dist/out-tsc",
        "declaration": false,
        "moduleResolution": "node",
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "target": "es5",
        "typeRoots": [
            "node_modules/@types"
        ],
        "lib": [
            "es2017",
            "dom",
            "es6",
            "es2015.iterable"
        ],
        "baseUrl": ".",
        "paths": {
            "~/*": [
                "src/*"
            ],
            "*": [
                "./node_modules/tns-core-modules/*",
                "./node_modules/*"
            ]
        }
    },
    "exclude": [
        "**/*.tns.ts",
        "**/*.android.ts",
        "**/*.ios.ts",
        "**/*.spec.ts"
    ]
}

tsconfig.tns.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "module": "es2015",
    "moduleResolution": "node",
    "baseUrl": "./",
    "paths": {
      "~/*": [
          "src/*"
      ],
      "*": [
          "./node_modules/tns-core-modules/*",
          "./node_modules/*"
      ]
    }
  }
}

webpack.config.js

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 { nsReplaceBootstrap } = require("nativescript-dev-webpack/transformers/ns-replace-bootstrap");
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",
    ];

    const platform = env && (env.android && "android" || env.ios && "ios");
    if (!platform) {
        throw new Error("You need to provide a target platform!");
    }

    const projectRoot = __dirname;

    // 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
        sourceMap, // --env.sourceMap
    } = env;

    const appFullPath = resolve(projectRoot, appPath);
    const appResourcesFullPath = resolve(projectRoot, appResourcesPath);

    const entryModule = `${nsWebpack.getEntryModule(appFullPath)}.ts`;
    const entryPath = `.${sep}${entryModule}`;

    const ngCompilerPlugin = new AngularCompilerPlugin({
        hostReplacementPaths: nsWebpack.getResolver([platform, "tns"]),
        platformTransformers: aot ? [nsReplaceBootstrap(() => ngCompilerPlugin)] : null,
        mainPath: resolve(appPath, entryModule),
        tsConfigPath: join(__dirname, "tsconfig.tns.json"),
        skipCodeGeneration: !aot,
        sourceMap: !!sourceMap,
    });

    const config = {
        mode: uglify ? "production" : "development",
        context: appFullPath,
        watchOptions: {
            ignored: [
                appResourcesFullPath,
                // Don't watch hidden files
                "**/.*",
            ]
        },
        target: nativescriptTarget,
        entry: {
            bundle: entryPath,
        },
        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: sourceMap ? "inline-source-map" : "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"] },

                {
                    test: /(?:\.ngfactory\.js|\.ngstyle\.js|\.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",
                "process": undefined,
            }),
            // 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(),
            ngCompilerPlugin,
            // 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;
};

package.json

{
  "name": "mydrawer",
  "nativescript": {
    "id": "org.nativescript.mydrawer",
    "tns-ios": {
      "version": "4.2.0"
    }
  },
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "android": "tns run android --bundle",
    "ios": "tns run ios --bundle"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "~6.1.0",
    "@angular/common": "~6.1.0",
    "@angular/compiler": "~6.1.0",
    "@angular/core": "~6.1.0",
    "@angular/forms": "~6.1.0",
    "@angular/http": "~6.1.0",
    "@angular/platform-browser": "~6.1.0",
    "@angular/platform-browser-dynamic": "~6.1.0",
    "@angular/router": "~6.1.0",
    "core-js": "^2.5.4",
    "nativescript-angular": "~6.1.0",
    "nativescript-theme-core": "~1.0.4",
    "nativescript-ui-sidedrawer": "^4.3.1",
    "reflect-metadata": "~0.1.8",
    "rxjs": "^6.0.0",
    "tns-core-modules": "~4.2.0",
    "zone.js": "^0.8.26"
  },
  "devDependencies": {
    "@angular/cli": "^6.2.0",
    "@angular/compiler-cli": "~6.0.3",
    "@angular-devkit/build-angular": "^0.8.0",
    "@nativescript/schematics": "~0.3.0",
    "@types/jasmine": "~2.8.6",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "~8.9.4",
    "codelyzer": "~4.2.1",
    "jasmine-core": "~2.99.1",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~1.7.1",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "~2.0.0",
    "karma-jasmine": "~1.1.1",
    "karma-jasmine-html-reporter": "^0.2.2",
    "nativescript-dev-typescript": "~0.7.0",
    "nativescript-dev-webpack": "^0.16.0",
    "protractor": "~5.3.0",
    "ts-node": "~5.0.1",
    "tslint": "~5.9.1",
    "typescript": "~2.7.2",
    "nativescript-dev-sass": "~1.6.0"
  }
}

Can anyone throw any light as to why the different devices require a different path?

TIA

Answer

Manoj picture Manoj · Oct 21, 2018

Looking at your app component, it's a valid error. Page, ActionItem, RadSideDrawerComponent etc., are available only to your mobile app. So when you use a common app component which imports tns specific components while compiling for web, it should throw error.

If you have common code, move that to a base class and inherit platform specific components for Web and Mobile. If there is no common code, then simply create tns version of app component and web shall have it's own version.

Between, you might have to update your import statements. You don't have to use relative path when importing packages from node modules.