How to manage AngularJS dependencies within an extension
An approach: composer-downloads-plugin

civicrm-core now uses the https://github.com/civicrm/composer-downloads-plugin for Javascript and CSS libraries. You can use the same technique in an extension. For example, suppose you have an extension civinfinite with a composer.json file which declares:



{
  "name": "example/civinfinite",
  "require": {
    "civicrm/composer-downloads-plugin": "~2.1"
  },
  "extra": {
    "downloads": {
      "ngInfiniteScroll": {
        "url": "https://github.com/sroze/ngInfiniteScroll/archive/1.3.4.zip",
        "path": "extern/ngInfiniteScroll",
        "ignore": ["test"]
      }
    }
  }
}


The zip file will be downloaded and extracted into your extension's folder (under extern/ngInfiniteScroll).

For example, suppose that you install civinfinite on a Civi-D7 site under:

sites/default/civicrm/ext/civinfinite


If you run composer install, then the assets will be at:

sites/default/civicrm/ext/civinfinite/extern/ngInfiniteScroll


At runtime, you can determine the URL as:



Civi::resources()-&gt;getUrl('civinfinite', 'extern/ngInfiniteScroll/src/infinite-scroll.js');


Similarly, if you're registering an Angular module for use in Civi (hook_civirm_angularModules or *.ang.php), then the registration can reference that path:



[
  'ext' =&gt; 'civinfinite',
  'basePages' =&gt; [],
  'js' =&gt; ['extern/ngInfiniteScroll/src/infinite-scroll.js'],
];



Critical analysis

There are many different techniques for managing JS/CSS assets in PHP projects -- D7 modules have libraries; Backdrop and WordPress modules tend to embed assets within modules; composer has various add-ons like installer-paths, asset-packagist, composer-npm-bridge, composer-asset-plugin, etc. Each of these has strengths and weaknesses.

I would submit that the relative strength of this technique is that it is easy for both developers and site-builders to reason about the behavior. (Tangentially, it's also faster than other composer-based techniques because it has no affect on the size of the dependency graph. But the main purpose is to make it easier to reason.)

For example: Consider that there are different ways in which people may deploy an extension:


One may download an extension as a *.zip file with all assets bundled in. (This is what happens when using the in-app extension UI or cv dl or Extension API.) One may not be running composer at all.
One may clone the extension's git repo and run composer install.
One may have a composer.json in a site-root (and some open-ended mix of dependencies, plugins, configuration options), and then you add the extension as a dependency. 


As a developer, it's hard to predict which mix of techniques are being used by downstream site-builders. In the composer-downloads-plugin approach, you can rely on the JS/CSS assets always being in the same place (relative to your code).

As a site-builder, this approach does not require you to add (or change/update) any "repositories" or "plugins" or configuration-options in a composer root-project. You don't even need to have a composer root-project. (You probably should for other reasons... but the extension and its assets aren't going to force you into it.) If a developer changes some small detail of the assets in their package, then it's not your concern.

This approach complies with a simple, pre-existing contract:


The developer decides what code goes inside their extension.
The site-builder decides which extensions to get - and where to put them.


A developer is free to use composer-downloads-plugin without requiring any changes to that basic contract.