angular / angular/angular-cli

`externalSchematics` should install packages locally when they are no present in the `node_modules`

Open
#19,301 13 comments 5 reactions 0 assignees View on GitHub
area: @angular-devkit/schematics feature feature: insufficient votes
Dominant language
TypeScript
Stars
27k
Forks
11.8k
Avg merge
14h 23m
Merged PRs (30d)
162

Description

# 🚀 Feature request

### Command (mark with an `x`)

- [ ] new
- [ ] build
- [ ] serve
- [ ] test
- [ ] e2e
- [ ] generate
- [ ] add
- [ ] update
- [ ] lint
- [ ] extract-i18n
- [ ] run
- [ ] config
- [ ] help
- [ ] version
- [ ] doc
- [x] other

### Description

when creating own schematics, they can make use of other schematics by calling the `externalSchematic` function provided by the `@angular-devkit/schematics` collection.

To call an external schematic, the function can be used like this:

```ts
options = {};
externalSchematic('my-schematic-package', 'my-schematic-name', options);
```

But to execute an external schematic package it has to be part of the `node_modules`. Internally the CLI uses the function for example [for calling the PWA schematic](https://github.com/angular/angular-cli/blob/b2174947924a781f7db41377be989750e042a968/packages/angular/pwa/pwa/index.ts#L159). This won't be problematic as is already installed as part of the `@angular/schematics` package and in fact it's part of the `node_modules`.

Once you'll try to install an external schematic which isn't already available in the `node_modules` the schematic fails.

A workaround is to manually integrate an installation procedure first and then call the function like this:

```ts
export const installNpmPackage = (
context: SchematicContext,
packageName: string,
): Promise => {
return new Promise((resolve) => {
context.logger.info(
`📦 Installing package '${packageName}' for external schematic setup...`,
);
const spawnOptions: SpawnOptions = { stdio: 'inherit' };
spawn('npm', ['install', packageName], spawnOptions).on(
'close',
(code: number) => {
if (code === 0) {
context.logger.info(
`✅ '${packageName}' package installed successfully`,
);
resolve();
} else {
const errorMessage = `❌ installation of '${packageName}' package failed`;
context.logger.error(errorMessage);
throw new Error();
}
},
);
});
};

const packageName = '@briebug/cypress-schematic';
await installNpmPackage(context, packageName);
return externalSchematic(packageName, 'ng-add', {
removeProtractor: true,
addCypressTestScripts: true,
});
```

### Describe the solution you'd like

It would be great to integrate the installation process for a package that's not present in the `node_modules` in the `externalSchematic` function.

### Describe alternatives you've considered

Contributor guide

Open the contributing guide

Research direction

Start with the externalSchematic function in @angular-devkit/schematics and the referenced packages/angular/pwa/pwa/index.ts call. Trace how an external package is resolved when it is absent from node_modules, then determine how installation should integrate with that flow. Done means an unavailable external schematic package is installed and the schematic executes successfully.

Written by the indexing model from the issue text.

Assessment

Tech stack
angular, typescript
Domain
cli
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.