[go: nahoru, domu]

Skip to content

Commit

Permalink
feat(@schematics/angular): support controlling addDependency utilit…
Browse files Browse the repository at this point in the history
…y rule install behavior

When using the `addDependency` rule from `@schematics/angular/utility` a new option is now
available that supports controlling the rule's behavior when scheduling the `NodePackageInstallTask`.
Previously, the behavior was automatic and the rule would only schedule the task if it was not already
scheduled by a previous `addDependency` rule usage for the `package.json`. This behavior is still the
default behavior. However, it can now be customized per rule invocation via the `install` rule option
which can be either `InstallBehavior.None`, `InstallBehavior.Auto`, or `InstallBehavior.Always`.
  • Loading branch information
clydin authored and dgp1130 committed May 25, 2022
1 parent c3821ca commit 707911d
Show file tree
Hide file tree
Showing 3 changed files with 174 additions and 5 deletions.
41 changes: 39 additions & 2 deletions packages/schematics/angular/utility/dependency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,30 @@ export enum DependencyType {
Peer = 'peerDependencies',
}

/**
* An enum used to specify the dependency installation behavior for the {@link addDependency}
* schematics rule. The installation behavior affects if and when {@link NodePackageInstallTask}
* will be scheduled when using the rule.
*/
export enum InstallBehavior {
/**
* No installation will occur as a result of the rule when specified.
*
* NOTE: This does not prevent other rules from scheduling a {@link NodePackageInstallTask}
* which may install the dependency.
*/
None,
/**
* Automatically determine the need to schedule a {@link NodePackageInstallTask} based on
* previous usage of the {@link addDependency} within the schematic.
*/
Auto,
/**
* Always schedule a {@link NodePackageInstallTask} when the rule is executed.
*/
Always,
}

/**
* Adds a package as a dependency to a `package.json`. By default the `package.json` located
* at the schematic's root will be used. The `manifestPath` option can be used to explicitly specify
Expand Down Expand Up @@ -58,9 +82,19 @@ export function addDependency(
* Defaults to `/package.json`.
*/
packageJsonPath?: string;
/**
* The dependency installation behavior to use to determine whether a
* {@link NodePackageInstallTask} should be scheduled after adding the dependency.
* Defaults to {@link InstallBehavior.Auto}.
*/
install?: InstallBehavior;
} = {},
): Rule {
const { type = DependencyType.Default, packageJsonPath = '/package.json' } = options;
const {
type = DependencyType.Default,
packageJsonPath = '/package.json',
install = InstallBehavior.Auto,
} = options;

return (tree, context) => {
const manifest = tree.readJson(packageJsonPath) as MinimalPackageManifest;
Expand All @@ -86,7 +120,10 @@ export function addDependency(
tree.overwrite(packageJsonPath, JSON.stringify(manifest, null, 2));

const installPaths = installTasks.get(context) ?? new Set<string>();
if (!installPaths.has(packageJsonPath)) {
if (
install === InstallBehavior.Always ||
(install === InstallBehavior.Auto && !installPaths.has(packageJsonPath))
) {
context.addTask(
new NodePackageInstallTask({ workingDirectory: path.dirname(packageJsonPath) }),
);
Expand Down
136 changes: 134 additions & 2 deletions packages/schematics/angular/utility/dependency_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
callRule,
chain,
} from '@angular-devkit/schematics';
import { DependencyType, addDependency } from './dependency';
import { DependencyType, InstallBehavior, addDependency } from './dependency';

async function testRule(rule: Rule, tree: Tree): Promise<TaskConfigurationGenerator[]> {
const tasks: TaskConfigurationGenerator[] = [];
Expand Down Expand Up @@ -192,6 +192,58 @@ describe('addDependency', () => {
]);
});

it('schedules a package install task when install behavior is auto', async () => {
const tree = new EmptyTree();
tree.create('/abc/package.json', JSON.stringify({}));

const rule = addDependency('@angular/core', '^14.0.0', {
packageJsonPath: '/abc/package.json',
install: InstallBehavior.Auto,
});

const tasks = await testRule(rule, tree);

expect(tasks.map((task) => task.toConfiguration())).toEqual([
{
name: 'node-package',
options: jasmine.objectContaining({ command: 'install', workingDirectory: '/abc' }),
},
]);
});

it('schedules a package install task when install behavior is always', async () => {
const tree = new EmptyTree();
tree.create('/abc/package.json', JSON.stringify({}));

const rule = addDependency('@angular/core', '^14.0.0', {
packageJsonPath: '/abc/package.json',
install: InstallBehavior.Always,
});

const tasks = await testRule(rule, tree);

expect(tasks.map((task) => task.toConfiguration())).toEqual([
{
name: 'node-package',
options: jasmine.objectContaining({ command: 'install', workingDirectory: '/abc' }),
},
]);
});

it('does not schedule a package install task when install behavior is none', async () => {
const tree = new EmptyTree();
tree.create('/abc/package.json', JSON.stringify({}));

const rule = addDependency('@angular/core', '^14.0.0', {
packageJsonPath: '/abc/package.json',
install: InstallBehavior.None,
});

const tasks = await testRule(rule, tree);

expect(tasks).toEqual([]);
});

it('does not schedule a package install task if version is the same', async () => {
const tree = new EmptyTree();
tree.create(
Expand All @@ -208,7 +260,7 @@ describe('addDependency', () => {
expect(tasks).toEqual([]);
});

it('only schedules one package install task for the same manifest path', async () => {
it('only schedules one package install task for the same manifest path by default', async () => {
const tree = new EmptyTree();
tree.create('/package.json', JSON.stringify({}));

Expand All @@ -227,6 +279,86 @@ describe('addDependency', () => {
]);
});

it('only schedules one package install task for the same manifest path with auto install behavior', async () => {
const tree = new EmptyTree();
tree.create('/package.json', JSON.stringify({}));

const rule = chain([
addDependency('@angular/core', '^14.0.0', { install: InstallBehavior.Auto }),
addDependency('@angular/common', '^14.0.0', { install: InstallBehavior.Auto }),
]);

const tasks = await testRule(rule, tree);

expect(tasks.map((task) => task.toConfiguration())).toEqual([
{
name: 'node-package',
options: jasmine.objectContaining({ command: 'install', workingDirectory: '/' }),
},
]);
});

it('only schedules one package install task for the same manifest path with mixed auto/none install behavior', async () => {
const tree = new EmptyTree();
tree.create('/package.json', JSON.stringify({}));

const rule = chain([
addDependency('@angular/core', '^14.0.0', { install: InstallBehavior.Auto }),
addDependency('@angular/common', '^14.0.0', { install: InstallBehavior.None }),
]);

const tasks = await testRule(rule, tree);

expect(tasks.map((task) => task.toConfiguration())).toEqual([
{
name: 'node-package',
options: jasmine.objectContaining({ command: 'install', workingDirectory: '/' }),
},
]);
});

it('only schedules one package install task for the same manifest path with mixed always then auto install behavior', async () => {
const tree = new EmptyTree();
tree.create('/package.json', JSON.stringify({}));

const rule = chain([
addDependency('@angular/core', '^14.0.0', { install: InstallBehavior.Always }),
addDependency('@angular/common', '^14.0.0', { install: InstallBehavior.Auto }),
]);

const tasks = await testRule(rule, tree);

expect(tasks.map((task) => task.toConfiguration())).toEqual([
{
name: 'node-package',
options: jasmine.objectContaining({ command: 'install', workingDirectory: '/' }),
},
]);
});

it('schedules multiple package install tasks for the same manifest path with mixed auto then always install behavior', async () => {
const tree = new EmptyTree();
tree.create('/package.json', JSON.stringify({}));

const rule = chain([
addDependency('@angular/core', '^14.0.0', { install: InstallBehavior.Auto }),
addDependency('@angular/common', '^14.0.0', { install: InstallBehavior.Always }),
]);

const tasks = await testRule(rule, tree);

expect(tasks.map((task) => task.toConfiguration())).toEqual([
{
name: 'node-package',
options: jasmine.objectContaining({ command: 'install', workingDirectory: '/' }),
},
{
name: 'node-package',
options: jasmine.objectContaining({ command: 'install', workingDirectory: '/' }),
},
]);
});

it('schedules a package install task for each manifest path present', async () => {
const tree = new EmptyTree();
tree.create('/package.json', JSON.stringify({}));
Expand Down
2 changes: 1 addition & 1 deletion packages/schematics/angular/utility/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ export {
export { Builders as AngularBuilder } from './workspace-models';

// Package dependency related rules and types
export { DependencyType, addDependency } from './dependency';
export { DependencyType, InstallBehavior, addDependency } from './dependency';

0 comments on commit 707911d

Please sign in to comment.