Share functionality with a web app?


#1

Hi guys, do you have experience in sharing functionality between a NativeScript app and an Angular CLI generated one?
I am working in a large A5 project done entirely with Angular CLI. There, we have developed our own library of components, services, directives and etc and scoped them via tsconfig paths like @shared/services, @shared/directives. The whole project is split into different apps, using the CLI’s multi app functionality and we use a mono repo. Basically, our @shared library is just a folder and not an NPM package.
So, currently I am in the process of researching if we can integrate Nativescript and create 2 native applications, using the shared code we already have.
I already CAN confirm that this will work and it will work in a beautiful way! (Thanks NS team). However, I can’t seem to be able to cope with one issue and that is - re-using the library without modifying it.
Currently, I can’t just import @shared/services from my NS app code since those are not in the same workspaces. I dont want to copy the library code everytime, so I created a symlink from the shared folder to the NS app folder. Also, I copied all the tsconfig paths from the web app project to the NS one and the imports seem valid in the IDE. However, once I build I get:
CONSOLE ERROR file:///app/tns_modules/@angular/core/bundles/core.umd.js:1486:24: ERROR Error: Uncaught (in promise): Error: Could not find module '@shared/services'. Computed path '/Users/anikolov/Library/Developer/CoreSimulator/Devices/3008C016-9186-4B5A-B635-ED892E565C18/data/Containers/Bundle/Application/235975BD-EEF2-40F8-A5B9-9F423929D847/testnative.app/app/tns_modules/@shared/services'.
I stumbled upon this issue https://github.com/NativeScript/nativescript-cli/issues/2853 and it looks pretty similar…
It looks like Nativescript will only package what’s in the app and node_modules folder but won’t respect the tsconfig paths.
TsConfig File:

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es5",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "noEmitHelpers": true,
        "noEmitOnError": true,
        "lib": [
            "es6",
            "dom",
            "es2015.iterable"
        ],
        "baseUrl": ".",
        "paths": {
            "*": [
                "./node_modules/tns-core-modules/*",
                "./node_modules/*"
            ],
            "@shared/shared": ["./app/ui-library"],
            "@shared/models": ["./app/ui-library/models"],
            "@shared/interfaces": ["./app/ui-library/interfaces"],
            "@shared/inputs": ["./app/ui-library/inputs"],
            "@shared/enums": ["./app/ui-library/enums"],
            "@shared/services": ["./app/ui-library/services"],
            "@shared/mock-services": ["./app/ui-library/services/mocks"],
            "@shared/guards": ["./app/ui-library/guards"],
            "@shared/resolves": ["./app/ui-library/resolves"],
            "@shared/preloading-strategies": ["./app/ui-library/preloading-strategies"],
            "@shared/config": ["./app/ui-library/config"],
            "@shared/opaque-tokens": ["./app/ui-library/opaque-tokens"],
            "@shared/overrides": ["./app/ui-library/overrides"],
            "@shared/components": ["./app/ui-library/components"],
            "@shared/components/*": ["./app/ui-library/components/*"],
            "@shared/directives": ["./app/ui-library/directives"],
            "@shared/directives/*": ["./app/ui-library/directives/*"],
            "@shared/pipes": ["./app/ui-library/pipes"],
            "@shared/pipes/*": ["./app/ui-library/pipes/*"],
            "@shared/interceptors": ["./app/ui-library/interceptors"],
            "@shared/mock-library": ["./app/mock-library"],
            "@shared/mock-helpers": ["./app/mock-library/helpers"],
            "@shared/fakes/*": ["./app/mock-library/fakes/*"],
            "@shared/fakes": ["./app/mock-library/fakes"],
            "@shared/animations": ["./app/ui-library/animations"],
            "@shared/decorators": ["./app/ui-library/decorators"]
        }
    },
    "exclude": [
        "node_modules",
        "platforms"
    ]
}

Folder Structure:

                apps
app-1 app-2 app-3 shared nativescript-app

TL;DR; My question is how to reuse a shared library of code that doesnt live in the Nativescript project.


#2

Did you try to point the tsconfig paths straight to the shared folder, something like:
"@shared/guards": ["…/…/shared/guards"],

You approach is very similar to nrwl/nx workspace structure.
We are working on some ideas around nx and similar projects for {N}.

Is there any reason why you don’t want the @shared library to be a local npm module?

I have a similar type project, with a package.json at the root

{
  "name" : "my-package"
   ...
}

Then I have a libs folder (the equivalent of your shared folder), which contains other folders like core.

The libs/core folder has a package.json

{
  "name": "@my-package/core",
  "version": "1.0.0",
  "main": "index",
  "typings": "index.d.ts"
}

and it also contains all the shared code, which is exported from index.ts.

Then, in my root folder my tsconfig contains:

{
  "compilerOptions": {
    "paths": {
      "@my-package/*": [
        "libs/*"
      ]
    }
...
}

Then finally, in each project I have:
package.json with dependency:

"@my-package/core": "file:../../libs/core",

which then let’s me do something like:

import {
  PlatformWindowService,
} from '@my-package/core';

Is this helpful at all?


#3

Also, if you are interested in setting up your project using NX and NativeScript, here is a nice step by step guide by @wwwalkerrun :

Code sharing in Nx with web and NativeScript mobile apps


#4

Thanks for answering so quickly!!
I tried to point the tsconfig paths to the direct source folders (one level up), but it failed the same way like the symlinked ones.
I know that it will be possible to do this with distributed npm packages, however initially we didnt want to go into NPM, since that would have introduced some overhead in how we distribute and build our application. Also, since we are actively developing several applications at once we are touching that library quite a bit (maybe every second PR has changes on it). Maybe it makes sense thinking about splitting the shared libraries in a couple of packages so we dont change them all the time, but still I was hoping there’s an easier way to just reference modules in arbitrary folders.
Even as I am typing this I understand that this is not a NS problem, since you cant really reference a typescript module outside of the rootDir so maybe that’s the reason… Anyway, I will iterate on the “packageless” idea a bit more and eventually will circle back to the npm packages again :slight_smile:
Thanks again!!
P.S Yeah, I looked at Nx but that’s also something we dont want to introduce as a new dev experience to the team.