mirror of
https://github.com/angular/angular-cli.git
synced 2025-05-15 18:13:38 +08:00
refactor: Consolidate adding dependencies
This commit is contained in:
parent
04893ca343
commit
d3b49a5590
@ -29,6 +29,7 @@ import {
|
|||||||
addProjectToWorkspace,
|
addProjectToWorkspace,
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
} from '../utility/config';
|
} from '../utility/config';
|
||||||
|
import { NodeDependencyType, addPackageJsonDependency } from '../utility/dependencies';
|
||||||
import { latestVersions } from '../utility/latest-versions';
|
import { latestVersions } from '../utility/latest-versions';
|
||||||
import { validateProjectName } from '../utility/validation';
|
import { validateProjectName } from '../utility/validation';
|
||||||
import { Schema as ApplicationOptions } from './schema';
|
import { Schema as ApplicationOptions } from './schema';
|
||||||
@ -60,29 +61,23 @@ import { Schema as ApplicationOptions } from './schema';
|
|||||||
|
|
||||||
function addDependenciesToPackageJson() {
|
function addDependenciesToPackageJson() {
|
||||||
return (host: Tree) => {
|
return (host: Tree) => {
|
||||||
const packageJsonPath = 'package.json';
|
[
|
||||||
|
{
|
||||||
if (!host.exists('package.json')) { return host; }
|
type: NodeDependencyType.Dev,
|
||||||
|
name: '@angular/compiler-cli',
|
||||||
const source = host.read('package.json');
|
version: latestVersions.Angular,
|
||||||
if (!source) { return host; }
|
},
|
||||||
|
{
|
||||||
const sourceText = source.toString('utf-8');
|
type: NodeDependencyType.Dev,
|
||||||
const json = JSON.parse(sourceText);
|
name: '@angular-devkit/build-angular',
|
||||||
|
version: latestVersions.DevkitBuildAngular,
|
||||||
if (!json['devDependencies']) {
|
},
|
||||||
json['devDependencies'] = {};
|
{
|
||||||
}
|
type: NodeDependencyType.Dev,
|
||||||
|
name: 'typescript',
|
||||||
json.devDependencies = {
|
version: latestVersions.TypeScript,
|
||||||
'@angular/compiler-cli': latestVersions.Angular,
|
},
|
||||||
'@angular-devkit/build-angular': latestVersions.DevkitBuildAngular,
|
].forEach(dependency => addPackageJsonDependency(host, dependency));
|
||||||
'typescript': latestVersions.TypeScript,
|
|
||||||
// De-structure last keeps existing user dependencies.
|
|
||||||
...json.devDependencies,
|
|
||||||
};
|
|
||||||
|
|
||||||
host.overwrite(packageJsonPath, JSON.stringify(json, null, 2));
|
|
||||||
|
|
||||||
return host;
|
return host;
|
||||||
};
|
};
|
||||||
|
@ -27,23 +27,15 @@ import {
|
|||||||
addProjectToWorkspace,
|
addProjectToWorkspace,
|
||||||
getWorkspace,
|
getWorkspace,
|
||||||
} from '../utility/config';
|
} from '../utility/config';
|
||||||
|
import {
|
||||||
|
NodeDependencyType,
|
||||||
|
addPackageJsonDependency,
|
||||||
|
} from '../utility/dependencies';
|
||||||
import { latestVersions } from '../utility/latest-versions';
|
import { latestVersions } from '../utility/latest-versions';
|
||||||
import { validateProjectName } from '../utility/validation';
|
import { validateProjectName } from '../utility/validation';
|
||||||
import { Schema as LibraryOptions } from './schema';
|
import { Schema as LibraryOptions } from './schema';
|
||||||
|
|
||||||
|
|
||||||
type PackageJsonPartialType = {
|
|
||||||
scripts: {
|
|
||||||
[key: string]: string;
|
|
||||||
},
|
|
||||||
dependencies: {
|
|
||||||
[key: string]: string;
|
|
||||||
},
|
|
||||||
devDependencies: {
|
|
||||||
[key: string]: string;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
interface UpdateJsonFn<T> {
|
interface UpdateJsonFn<T> {
|
||||||
(obj: T): T | void;
|
(obj: T): T | void;
|
||||||
}
|
}
|
||||||
@ -96,39 +88,45 @@ function updateTsConfig(packageName: string, distRoot: string) {
|
|||||||
function addDependenciesToPackageJson() {
|
function addDependenciesToPackageJson() {
|
||||||
|
|
||||||
return (host: Tree) => {
|
return (host: Tree) => {
|
||||||
if (!host.exists('package.json')) { return host; }
|
[
|
||||||
|
{
|
||||||
|
type: NodeDependencyType.Dev,
|
||||||
|
name: '@angular/compiler-cli',
|
||||||
|
version: latestVersions.Angular,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NodeDependencyType.Dev,
|
||||||
|
name: '@angular-devkit/build-ng-packagr',
|
||||||
|
version: latestVersions.DevkitBuildNgPackagr,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NodeDependencyType.Dev,
|
||||||
|
name: '@angular-devkit/build-angular',
|
||||||
|
version: latestVersions.DevkitBuildNgPackagr,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NodeDependencyType.Dev,
|
||||||
|
name: 'ng-packagr',
|
||||||
|
version: '^3.0.0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NodeDependencyType.Dev,
|
||||||
|
name: 'tsickle',
|
||||||
|
version: '>=0.29.0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NodeDependencyType.Dev,
|
||||||
|
name: 'tslib',
|
||||||
|
version: '^1.9.0',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: NodeDependencyType.Dev,
|
||||||
|
name: 'typescript',
|
||||||
|
version: latestVersions.TypeScript,
|
||||||
|
},
|
||||||
|
].forEach(dependency => addPackageJsonDependency(host, dependency));
|
||||||
|
|
||||||
return updateJsonFile(host, 'package.json', (json: PackageJsonPartialType) => {
|
return host;
|
||||||
|
|
||||||
|
|
||||||
if (!json['dependencies']) {
|
|
||||||
json['dependencies'] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
json.dependencies = {
|
|
||||||
'@angular/common': latestVersions.Angular,
|
|
||||||
'@angular/core': latestVersions.Angular,
|
|
||||||
'@angular/compiler': latestVersions.Angular,
|
|
||||||
// De-structure last keeps existing user dependencies.
|
|
||||||
...json.dependencies,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!json['devDependencies']) {
|
|
||||||
json['devDependencies'] = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
json.devDependencies = {
|
|
||||||
'@angular/compiler-cli': latestVersions.Angular,
|
|
||||||
'@angular-devkit/build-ng-packagr': latestVersions.DevkitBuildNgPackagr,
|
|
||||||
'@angular-devkit/build-angular': latestVersions.DevkitBuildNgPackagr,
|
|
||||||
'ng-packagr': '^3.0.0',
|
|
||||||
'tsickle': '>=0.29.0',
|
|
||||||
'tslib': '^1.9.0',
|
|
||||||
'typescript': latestVersions.TypeScript,
|
|
||||||
// De-structure last keeps existing user dependencies.
|
|
||||||
...json.devDependencies,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,12 +26,16 @@ import {
|
|||||||
} from '@angular-devkit/schematics';
|
} from '@angular-devkit/schematics';
|
||||||
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
|
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks';
|
||||||
import { AppConfig, CliConfig } from '../../utility/config';
|
import { AppConfig, CliConfig } from '../../utility/config';
|
||||||
import { latestVersions } from '../../utility/latest-versions';
|
|
||||||
import {
|
import {
|
||||||
appendPropertyInAstObject,
|
NodeDependency,
|
||||||
|
NodeDependencyType,
|
||||||
|
addPackageJsonDependency,
|
||||||
|
} from '../../utility/dependencies';
|
||||||
|
import {
|
||||||
appendValueInAstArray,
|
appendValueInAstArray,
|
||||||
findPropertyInAstObject,
|
findPropertyInAstObject,
|
||||||
} from './json-utils';
|
} from '../../utility/json-utils';
|
||||||
|
import { latestVersions } from '../../utility/latest-versions';
|
||||||
|
|
||||||
const defaults = {
|
const defaults = {
|
||||||
appRoot: 'src',
|
appRoot: 'src',
|
||||||
@ -649,49 +653,13 @@ function updateSpecTsConfig(config: CliConfig): Rule {
|
|||||||
|
|
||||||
function updatePackageJson(config: CliConfig) {
|
function updatePackageJson(config: CliConfig) {
|
||||||
return (host: Tree, context: SchematicContext) => {
|
return (host: Tree, context: SchematicContext) => {
|
||||||
const pkgPath = '/package.json';
|
const dependency: NodeDependency = {
|
||||||
const buffer = host.read(pkgPath);
|
type: NodeDependencyType.Dev,
|
||||||
if (buffer == null) {
|
name: '@angular-devkit/build-angular',
|
||||||
throw new SchematicsException('Could not read package.json');
|
version: latestVersions.DevkitBuildAngular,
|
||||||
}
|
overwrite: true,
|
||||||
const pkgAst = parseJsonAst(buffer.toString(), JsonParseMode.Strict);
|
};
|
||||||
|
addPackageJsonDependency(host, dependency);
|
||||||
if (pkgAst.kind != 'object') {
|
|
||||||
throw new SchematicsException('Error reading package.json');
|
|
||||||
}
|
|
||||||
|
|
||||||
const devDependenciesNode = findPropertyInAstObject(pkgAst, 'devDependencies');
|
|
||||||
if (devDependenciesNode && devDependenciesNode.kind != 'object') {
|
|
||||||
throw new SchematicsException('Error reading package.json; devDependency is not an object.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const recorder = host.beginUpdate(pkgPath);
|
|
||||||
const depName = '@angular-devkit/build-angular';
|
|
||||||
if (!devDependenciesNode) {
|
|
||||||
// Haven't found the devDependencies key, add it to the root of the package.json.
|
|
||||||
appendPropertyInAstObject(recorder, pkgAst, 'devDependencies', {
|
|
||||||
[depName]: latestVersions.DevkitBuildAngular,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Check if there's a build-angular key.
|
|
||||||
const buildAngularNode = findPropertyInAstObject(devDependenciesNode, depName);
|
|
||||||
|
|
||||||
if (!buildAngularNode) {
|
|
||||||
// No build-angular package, add it.
|
|
||||||
appendPropertyInAstObject(
|
|
||||||
recorder,
|
|
||||||
devDependenciesNode,
|
|
||||||
depName,
|
|
||||||
latestVersions.DevkitBuildAngular,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const { end, start } = buildAngularNode;
|
|
||||||
recorder.remove(start.offset, end.offset - start.offset);
|
|
||||||
recorder.insertRight(start.offset, JSON.stringify(latestVersions.DevkitBuildAngular));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
host.commitUpdate(recorder);
|
|
||||||
|
|
||||||
context.addTask(new NodePackageInstallTask({
|
context.addTask(new NodePackageInstallTask({
|
||||||
packageManager: config.packageManager === 'default' ? undefined : config.packageManager,
|
packageManager: config.packageManager === 'default' ? undefined : config.packageManager,
|
||||||
|
@ -26,11 +26,10 @@ import {
|
|||||||
getWorkspace,
|
getWorkspace,
|
||||||
getWorkspacePath,
|
getWorkspacePath,
|
||||||
} from '../utility/config';
|
} from '../utility/config';
|
||||||
|
import { addPackageJsonDependency, getPackageJsonDependency } from '../utility/dependencies';
|
||||||
import { getAppModulePath } from '../utility/ng-ast-utils';
|
import { getAppModulePath } from '../utility/ng-ast-utils';
|
||||||
import { Schema as ServiceWorkerOptions } from './schema';
|
import { Schema as ServiceWorkerOptions } from './schema';
|
||||||
|
|
||||||
const packageJsonPath = '/package.json';
|
|
||||||
|
|
||||||
function updateConfigFile(options: ServiceWorkerOptions): Rule {
|
function updateConfigFile(options: ServiceWorkerOptions): Rule {
|
||||||
return (host: Tree, context: SchematicContext) => {
|
return (host: Tree, context: SchematicContext) => {
|
||||||
context.logger.debug('updating config file.');
|
context.logger.debug('updating config file.');
|
||||||
@ -72,17 +71,15 @@ function addDependencies(): Rule {
|
|||||||
return (host: Tree, context: SchematicContext) => {
|
return (host: Tree, context: SchematicContext) => {
|
||||||
const packageName = '@angular/service-worker';
|
const packageName = '@angular/service-worker';
|
||||||
context.logger.debug(`adding dependency (${packageName})`);
|
context.logger.debug(`adding dependency (${packageName})`);
|
||||||
const buffer = host.read(packageJsonPath);
|
const coreDep = getPackageJsonDependency(host, '@angular/core');
|
||||||
if (buffer === null) {
|
if (coreDep === null) {
|
||||||
throw new SchematicsException('Could not find package.json');
|
throw new SchematicsException('Could not find version.');
|
||||||
}
|
}
|
||||||
|
const platformServerDep = {
|
||||||
const packageObject = JSON.parse(buffer.toString());
|
...coreDep,
|
||||||
|
name: packageName,
|
||||||
const ngCoreVersion = packageObject.dependencies['@angular/core'];
|
};
|
||||||
packageObject.dependencies[packageName] = ngCoreVersion;
|
addPackageJsonDependency(host, platformServerDep);
|
||||||
|
|
||||||
host.overwrite(packageJsonPath, JSON.stringify(packageObject, null, 2));
|
|
||||||
|
|
||||||
return host;
|
return host;
|
||||||
};
|
};
|
||||||
|
@ -34,6 +34,7 @@ import * as ts from 'typescript';
|
|||||||
import { findNode, getDecoratorMetadata } from '../utility/ast-utils';
|
import { findNode, getDecoratorMetadata } from '../utility/ast-utils';
|
||||||
import { InsertChange } from '../utility/change';
|
import { InsertChange } from '../utility/change';
|
||||||
import { getWorkspace } from '../utility/config';
|
import { getWorkspace } from '../utility/config';
|
||||||
|
import { addPackageJsonDependency, getPackageJsonDependency } from '../utility/dependencies';
|
||||||
import { findBootstrapModuleCall, findBootstrapModulePath } from '../utility/ng-ast-utils';
|
import { findBootstrapModuleCall, findBootstrapModulePath } from '../utility/ng-ast-utils';
|
||||||
import { Schema as UniversalOptions } from './schema';
|
import { Schema as UniversalOptions } from './schema';
|
||||||
|
|
||||||
@ -172,18 +173,15 @@ function addServerTransition(options: UniversalOptions): Rule {
|
|||||||
|
|
||||||
function addDependencies(): Rule {
|
function addDependencies(): Rule {
|
||||||
return (host: Tree) => {
|
return (host: Tree) => {
|
||||||
const pkgPath = '/package.json';
|
const coreDep = getPackageJsonDependency(host, '@angular/core');
|
||||||
const buffer = host.read(pkgPath);
|
if (coreDep === null) {
|
||||||
if (buffer === null) {
|
throw new SchematicsException('Could not find version.');
|
||||||
throw new SchematicsException('Could not find package.json');
|
|
||||||
}
|
}
|
||||||
|
const platformServerDep = {
|
||||||
const pkg = JSON.parse(buffer.toString());
|
...coreDep,
|
||||||
|
name: '@angular/platform-server',
|
||||||
const ngCoreVersion = pkg.dependencies['@angular/core'];
|
};
|
||||||
pkg.dependencies['@angular/platform-server'] = ngCoreVersion;
|
addPackageJsonDependency(host, platformServerDep);
|
||||||
|
|
||||||
host.overwrite(pkgPath, JSON.stringify(pkg, null, 2));
|
|
||||||
|
|
||||||
return host;
|
return host;
|
||||||
};
|
};
|
||||||
|
107
packages/schematics/angular/utility/dependencies.ts
Normal file
107
packages/schematics/angular/utility/dependencies.ts
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
import { JsonAstObject, JsonParseMode, parseJsonAst } from '@angular-devkit/core';
|
||||||
|
import { SchematicsException, Tree } from '@angular-devkit/schematics';
|
||||||
|
import {
|
||||||
|
appendPropertyInAstObject,
|
||||||
|
findPropertyInAstObject,
|
||||||
|
insertPropertyInAstObjectInOrder,
|
||||||
|
} from './json-utils';
|
||||||
|
|
||||||
|
|
||||||
|
const pkgJsonPath = '/package.json';
|
||||||
|
export enum NodeDependencyType {
|
||||||
|
Default = 'dependencies',
|
||||||
|
Dev = 'devDependencies',
|
||||||
|
Peer = 'peerDependencies',
|
||||||
|
Optional = 'optionalDependencies',
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NodeDependency {
|
||||||
|
type: NodeDependencyType;
|
||||||
|
name: string;
|
||||||
|
version: string;
|
||||||
|
overwrite?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addPackageJsonDependency(tree: Tree, dependency: NodeDependency): void {
|
||||||
|
const packageJsonAst = _readPackageJson(tree);
|
||||||
|
const depsNode = findPropertyInAstObject(packageJsonAst, dependency.type);
|
||||||
|
const recorder = tree.beginUpdate(pkgJsonPath);
|
||||||
|
if (!depsNode) {
|
||||||
|
// Haven't found the dependencies key, add it to the root of the package.json.
|
||||||
|
appendPropertyInAstObject(recorder, packageJsonAst, dependency.type, {
|
||||||
|
[dependency.name]: dependency.version,
|
||||||
|
}, 4);
|
||||||
|
} else if (depsNode.kind === 'object') {
|
||||||
|
// check if package already added
|
||||||
|
const depNode = findPropertyInAstObject(depsNode, dependency.name);
|
||||||
|
|
||||||
|
if (!depNode) {
|
||||||
|
// Package not found, add it.
|
||||||
|
insertPropertyInAstObjectInOrder(
|
||||||
|
recorder,
|
||||||
|
depsNode,
|
||||||
|
dependency.name,
|
||||||
|
dependency.version,
|
||||||
|
4,
|
||||||
|
);
|
||||||
|
} else if (dependency.overwrite) {
|
||||||
|
// Package found, update version if overwrite.
|
||||||
|
const { end, start } = depNode;
|
||||||
|
recorder.remove(start.offset, end.offset - start.offset);
|
||||||
|
recorder.insertRight(start.offset, JSON.stringify(dependency.version));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tree.commitUpdate(recorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPackageJsonDependency(tree: Tree, name: string): NodeDependency | null {
|
||||||
|
const packageJson = _readPackageJson(tree);
|
||||||
|
let dep: NodeDependency | null = null;
|
||||||
|
[
|
||||||
|
NodeDependencyType.Default,
|
||||||
|
NodeDependencyType.Dev,
|
||||||
|
NodeDependencyType.Optional,
|
||||||
|
NodeDependencyType.Peer,
|
||||||
|
].forEach(depType => {
|
||||||
|
if (dep !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const depsNode = findPropertyInAstObject(packageJson, depType);
|
||||||
|
if (depsNode !== null && depsNode.kind === 'object') {
|
||||||
|
const depNode = findPropertyInAstObject(depsNode, name);
|
||||||
|
if (depNode !== null && depNode.kind === 'string') {
|
||||||
|
const version = depNode.value;
|
||||||
|
dep = {
|
||||||
|
type: depType,
|
||||||
|
name: name,
|
||||||
|
version: version,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return dep;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _readPackageJson(tree: Tree): JsonAstObject {
|
||||||
|
const buffer = tree.read(pkgJsonPath);
|
||||||
|
if (buffer === null) {
|
||||||
|
throw new SchematicsException('Could not read package.json.');
|
||||||
|
}
|
||||||
|
const content = buffer.toString();
|
||||||
|
|
||||||
|
const packageJson = parseJsonAst(content, JsonParseMode.Strict);
|
||||||
|
if (packageJson.kind != 'object') {
|
||||||
|
throw new SchematicsException('Invalid package.json. Was expecting an object');
|
||||||
|
}
|
||||||
|
|
||||||
|
return packageJson;
|
||||||
|
}
|
91
packages/schematics/angular/utility/dependencies_spec.ts
Normal file
91
packages/schematics/angular/utility/dependencies_spec.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
import { EmptyTree } from '@angular-devkit/schematics';
|
||||||
|
import { UnitTestTree } from '@angular-devkit/schematics/testing';
|
||||||
|
import {
|
||||||
|
NodeDependency,
|
||||||
|
NodeDependencyType,
|
||||||
|
addPackageJsonDependency,
|
||||||
|
getPackageJsonDependency,
|
||||||
|
} from './dependencies';
|
||||||
|
|
||||||
|
|
||||||
|
describe('dependencies', () => {
|
||||||
|
describe('addDependency', () => {
|
||||||
|
let tree: UnitTestTree;
|
||||||
|
const pkgJsonPath = '/package.json';
|
||||||
|
let dependency: NodeDependency;
|
||||||
|
beforeEach(() => {
|
||||||
|
tree = new UnitTestTree(new EmptyTree());
|
||||||
|
tree.create(pkgJsonPath, '{}');
|
||||||
|
|
||||||
|
dependency = {
|
||||||
|
type: NodeDependencyType.Default,
|
||||||
|
name: 'my-pkg',
|
||||||
|
version: '1.2.3',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
[
|
||||||
|
{ type: NodeDependencyType.Default, key: 'dependencies' },
|
||||||
|
{ type: NodeDependencyType.Dev, key: 'devDependencies' },
|
||||||
|
{ type: NodeDependencyType.Optional, key: 'optionalDependencies' },
|
||||||
|
{ type: NodeDependencyType.Peer, key: 'peerDependencies' },
|
||||||
|
].forEach(type => {
|
||||||
|
describe(`Type: ${type.toString()}`, () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
dependency.type = type.type;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add a dependency', () => {
|
||||||
|
addPackageJsonDependency(tree, dependency);
|
||||||
|
const pkgJson = JSON.parse(tree.readContent(pkgJsonPath));
|
||||||
|
expect(pkgJson[type.key][dependency.name]).toEqual(dependency.version);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle an existing dependency (update version)', () => {
|
||||||
|
addPackageJsonDependency(tree, {...dependency, version: '0.0.0'});
|
||||||
|
addPackageJsonDependency(tree, {...dependency, overwrite: true});
|
||||||
|
const pkgJson = JSON.parse(tree.readContent(pkgJsonPath));
|
||||||
|
expect(pkgJson[type.key][dependency.name]).toEqual(dependency.version);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw when missing package.json', () => {
|
||||||
|
expect((() => addPackageJsonDependency(new EmptyTree(), dependency))).toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getDependency', () => {
|
||||||
|
let tree: UnitTestTree;
|
||||||
|
beforeEach(() => {
|
||||||
|
const pkgJsonPath = '/package.json';
|
||||||
|
const pkgJsonContent = JSON.stringify({
|
||||||
|
dependencies: {
|
||||||
|
'my-pkg': '1.2.3',
|
||||||
|
},
|
||||||
|
}, null, 2);
|
||||||
|
tree = new UnitTestTree(new EmptyTree());
|
||||||
|
tree.create(pkgJsonPath, pkgJsonContent);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get a dependency', () => {
|
||||||
|
const dep = getPackageJsonDependency(tree, 'my-pkg') as NodeDependency;
|
||||||
|
expect(dep.type).toEqual(NodeDependencyType.Default);
|
||||||
|
expect(dep.name).toEqual('my-pkg');
|
||||||
|
expect(dep.version).toEqual('1.2.3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return null if dependency does not exist', () => {
|
||||||
|
const dep = getPackageJsonDependency(tree, 'missing-pkg') as NodeDependency;
|
||||||
|
expect(dep).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -5,7 +5,13 @@
|
|||||||
* Use of this source code is governed by an MIT-style license that can be
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import { JsonAstArray, JsonAstNode, JsonAstObject, JsonValue } from '@angular-devkit/core';
|
import {
|
||||||
|
JsonAstArray,
|
||||||
|
JsonAstKeyValue,
|
||||||
|
JsonAstNode,
|
||||||
|
JsonAstObject,
|
||||||
|
JsonValue,
|
||||||
|
} from '@angular-devkit/core';
|
||||||
import { UpdateRecorder } from '@angular-devkit/schematics';
|
import { UpdateRecorder } from '@angular-devkit/schematics';
|
||||||
|
|
||||||
export function appendPropertyInAstObject(
|
export function appendPropertyInAstObject(
|
||||||
@ -13,9 +19,9 @@ export function appendPropertyInAstObject(
|
|||||||
node: JsonAstObject,
|
node: JsonAstObject,
|
||||||
propertyName: string,
|
propertyName: string,
|
||||||
value: JsonValue,
|
value: JsonValue,
|
||||||
indent = 4,
|
indent: number,
|
||||||
) {
|
) {
|
||||||
const indentStr = '\n' + new Array(indent + 1).join(' ');
|
const indentStr = _buildIndent(indent);
|
||||||
|
|
||||||
if (node.properties.length > 0) {
|
if (node.properties.length > 0) {
|
||||||
// Insert comma.
|
// Insert comma.
|
||||||
@ -31,6 +37,59 @@ export function appendPropertyInAstObject(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function insertPropertyInAstObjectInOrder(
|
||||||
|
recorder: UpdateRecorder,
|
||||||
|
node: JsonAstObject,
|
||||||
|
propertyName: string,
|
||||||
|
value: JsonValue,
|
||||||
|
indent: number,
|
||||||
|
) {
|
||||||
|
|
||||||
|
if (node.properties.length === 0) {
|
||||||
|
appendPropertyInAstObject(recorder, node, propertyName, value, indent);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find insertion info.
|
||||||
|
let insertAfterProp: JsonAstKeyValue | null = null;
|
||||||
|
let prev: JsonAstKeyValue | null = null;
|
||||||
|
let isLastProp = false;
|
||||||
|
const last = node.properties[node.properties.length - 1];
|
||||||
|
for (const prop of node.properties) {
|
||||||
|
if (prop.key.value > propertyName) {
|
||||||
|
if (prev) {
|
||||||
|
insertAfterProp = prev;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (prop === last) {
|
||||||
|
isLastProp = true;
|
||||||
|
insertAfterProp = last;
|
||||||
|
}
|
||||||
|
prev = prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLastProp) {
|
||||||
|
appendPropertyInAstObject(recorder, node, propertyName, value, indent);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const indentStr = _buildIndent(indent);
|
||||||
|
|
||||||
|
const insertIndex = insertAfterProp === null
|
||||||
|
? node.start.offset + 1
|
||||||
|
: insertAfterProp.end.offset + 1;
|
||||||
|
|
||||||
|
recorder.insertRight(
|
||||||
|
insertIndex,
|
||||||
|
`${indentStr}`
|
||||||
|
+ `"${propertyName}": ${JSON.stringify(value, null, 2).replace(/\n/g, indentStr)}`
|
||||||
|
+ ',',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export function appendValueInAstArray(
|
export function appendValueInAstArray(
|
||||||
recorder: UpdateRecorder,
|
recorder: UpdateRecorder,
|
||||||
@ -38,7 +97,7 @@ export function appendValueInAstArray(
|
|||||||
value: JsonValue,
|
value: JsonValue,
|
||||||
indent = 4,
|
indent = 4,
|
||||||
) {
|
) {
|
||||||
const indentStr = '\n' + new Array(indent + 1).join(' ');
|
const indentStr = _buildIndent(indent);
|
||||||
|
|
||||||
if (node.elements.length > 0) {
|
if (node.elements.length > 0) {
|
||||||
// Insert comma.
|
// Insert comma.
|
||||||
@ -68,3 +127,7 @@ export function findPropertyInAstObject(
|
|||||||
|
|
||||||
return maybeNode;
|
return maybeNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _buildIndent(count: number): string {
|
||||||
|
return '\n' + new Array(count + 1).join(' ');
|
||||||
|
}
|
81
packages/schematics/angular/utility/json-utils_spec.ts
Normal file
81
packages/schematics/angular/utility/json-utils_spec.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
import { parseJsonAst } from '@angular-devkit/core';
|
||||||
|
import { HostTree } from '@angular-devkit/schematics';
|
||||||
|
import { UnitTestTree } from '@angular-devkit/schematics/testing';
|
||||||
|
import { insertPropertyInAstObjectInOrder } from './json-utils';
|
||||||
|
|
||||||
|
type Pojso = {
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('json-utils', () => {
|
||||||
|
const filePath = '/temp';
|
||||||
|
let tree: UnitTestTree;
|
||||||
|
beforeEach(() => {
|
||||||
|
tree = new UnitTestTree(new HostTree());
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('insertPropertyInAstObjectInOrder', () => {
|
||||||
|
function runTest(obj: Pojso, prop: string, val: string): Pojso {
|
||||||
|
const content = JSON.stringify(obj, null, 2);
|
||||||
|
tree.create(filePath, content);
|
||||||
|
const ast = parseJsonAst(content);
|
||||||
|
const rec = tree.beginUpdate(filePath);
|
||||||
|
if (ast.kind === 'object') {
|
||||||
|
insertPropertyInAstObjectInOrder(rec, ast, prop, val, 2);
|
||||||
|
}
|
||||||
|
tree.commitUpdate(rec);
|
||||||
|
|
||||||
|
const result = JSON.parse(tree.readContent(filePath));
|
||||||
|
// Clean up the tree by deleting the file.
|
||||||
|
tree.delete(filePath);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should insert a first prop', () => {
|
||||||
|
const obj = {
|
||||||
|
m: 'm',
|
||||||
|
z: 'z',
|
||||||
|
};
|
||||||
|
const result = runTest(obj, 'a', 'val');
|
||||||
|
expect(Object.keys(result)).toEqual(['a', 'm', 'z']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should insert a middle prop', () => {
|
||||||
|
const obj = {
|
||||||
|
a: 'a',
|
||||||
|
z: 'z',
|
||||||
|
};
|
||||||
|
const result = runTest(obj, 'm', 'val');
|
||||||
|
expect(Object.keys(result)).toEqual(['a', 'm', 'z']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should insert a last prop', () => {
|
||||||
|
const obj = {
|
||||||
|
a: 'a',
|
||||||
|
m: 'm',
|
||||||
|
};
|
||||||
|
const result = runTest(obj, 'z', 'val');
|
||||||
|
expect(Object.keys(result)).toEqual(['a', 'm', 'z']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should insert multiple props', () => {
|
||||||
|
let obj = {};
|
||||||
|
obj = runTest(obj, 'z', 'val');
|
||||||
|
expect(Object.keys(obj)).toEqual(['z']);
|
||||||
|
obj = runTest(obj, 'm', 'val');
|
||||||
|
expect(Object.keys(obj)).toEqual(['m', 'z']);
|
||||||
|
obj = runTest(obj, 'a', 'val');
|
||||||
|
expect(Object.keys(obj)).toEqual(['a', 'm', 'z']);
|
||||||
|
obj = runTest(obj, 'b', 'val');
|
||||||
|
expect(Object.keys(obj)).toEqual(['a', 'b', 'm', 'z']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user