Sharing components between projects


#1

Hello,

Here is our current environment:

  • 1 Visual Studio solution with several projects (Aurelia and .NET Core Web API)
  • Aurelia projects with built-in bundler

We have already 2 “standalone” Aurelia app (A and B) running. Our goal is to create a portal/dashboard that will include the projects A and B.
We tried to load the main component of project A as a route in the portal ©, without success.
It looked like this: { ..., route: 'route/:a?/:b?', moduleId: '../../../ProjectA/src/resources/elements/main-component', ... }.

Is there a way to accomplish this, while keeping those projects separated ?

If possible, the included project/component will use the authentication mechanism of the including project.

Do you have resources to share about sharing code (components, services, configuration) between different projects in the same repo ?

Edit: I would like to add that our ideal structure would look something like this:

  • ProjectWebAPI
  • ProjectPortal
  • ProjectA
  • ProjectB
  • ProjectFramework - will hold shared service (http, auth, …) and components (navbar, sidebar, …)

Thanks for your help !


#2

Yeah that should be doable with path mappings. Take a look at the fresh new cli docs https://aurelia.io/docs/cli/cli-bundler/basics#path-mappings


#3

I recommend to do the proper way, extract all your shared components/css to few aurelia plugins.
Then you can easily load them up in any apps.

If you want an easy way to develop plugin, have a look of https://github.com/aurelia-contrib/aurelia-getting-started/blob/master/guides/how-to-use-cli-to-develop-plugin.md


#4

The path mapping itself is not enough @zewa666, you need to show him/her how you setup your transpile sources to catch all those foreign files.


#5

This is something I’ve architected for several applications already. As CP said, using plugins is the way to go. More specifically:

  • ProjectFramework should be written as a plugin.
  • ProjectA should be written as a plugin that depends on ProjectFramework.
  • ProjectB should be written as a plugin that depends on ProjectFramework.
  • ProjectPortal should be a standalone project.

Framework will need to be your connection between the plugins and the portal. A common use case for this is menus. Define an API in Framework for extending routes and menus. ProjectA and ProjectB will use that API to define their specific routes and menu items. Portal will read that API to register the routes and display the menu.

Something else to consider is the why. If you’re planning to deploy your application with various configurations for various customers, for example, then you’re going to need more than what you’re asking for here, or else you will end up forking Portal for each customer and reconfiguring it every time. If you want to create a straightforward API for quickly plugging new features and new applications, then this is the way to go.


#6

We’ve achieved something like this via private modules in a monorepository. Using Yarn workspaces, our private modules get symlinked in the root node_modules and then get bundled in any top level project that references them. This allows us to have an Electron, browser and server apps which all use shared code, but this could work for any combination of top level targets.

The whole lot gets built using the latest TypeScript build mode which compiles dependencies in the correct order.

What works and what doesn’t?

The good

  • Forces isolation between modules. You’ll get compile errors if you try and import something across module boundaries without using the module syntax.
  • Modules are compiled separately (we’ve used this to migrate our code-base to strict TypeScript one module at a time).
  • Simple in the future to publish a module to public or private npm repository.
  • Go-to-definition in vscode takes you to the correct source file, even in a module dependency.

The bad

  • Modules are compiled separately. This is much slower than a single transpile/build.
  • Wont work with JSPM as resolution is through node_modules.
  • Some node dependencies still don’t work by default in a monorepo with hoisted dependencies. You can selectively disable hoisting where it breaks things.

Is all this complication required?

Shared code between multiple apps is achievable from a single directory of files with a decent bundler config for each top level app/entry point. For example, Webpack should tree shake out all the unused code and you should be left with only the files required by your top level app. The major downside is that you (and team members) are now responsible for ensuring you properly isolate your modules.


#7

Thanks for your answers. Sorry for not coming back earlier, I was busy making some tests.

@huochunpeng First I updated my project to the last CLI version. I followed the guide “how to use cli to develop plugin”.
It seems to work pretty well. But I think we’ll have to set up a private repository solution like Verdaccio. It was already hard to test (had to use npm link).
Plus I was not able to import static resources from plugin to main application (like font-awesome).

@davismj So since this PoC is working, I will follow your advice and change A, B and the framework as plugins. Your definition of the portal with several clients is exactly what we’re doing. Good point !

@timfish I tried with only typescript import without success. But I’ll definitely have a look at Yarn workspace. Perhaps a combination of aurelia plugins + Yarn workspaces is the best/easiest option.

Thanks for sharing your experiences and details !


#8

With cli bundle, you don’t need npm link to use local package, use explicit config.

{
  "name": "private-plugin",
  "path": "../../private-plugin"
}

The above example assumes your app and plugin repos are in sibling folders.


#9

With font-awesome, the plugin’s dependencies should able to bring font-awesome to your local app node_modules folder. You can import it normally.
If it’s not in local node_modules folder, add it to app’s dependencies.


#10

Or add a <require> tag in the html file(s) pointing to the font awesome files saved in the ClientApp directory tree such as <require from="./…/…/…/resources/styling/fontawesome/css/all.css"></require>