From f03ff807b440bde880aff117ec816c07e12f6b06 Mon Sep 17 00:00:00 2001 From: Alan Date: Thu, 18 Apr 2019 07:44:48 +0200 Subject: [PATCH] refactor(@schematics/angular): create `relativePathToWorkspaceRoot` helper and replace usages --- .../schematics/angular/application/index.ts | 9 ++--- packages/schematics/angular/e2e/index.ts | 14 ++----- packages/schematics/angular/library/index.ts | 4 +- .../angular/service-worker/index.ts | 15 +++++--- packages/schematics/angular/utility/paths.ts | 19 ++++++++++ .../schematics/angular/utility/paths_spec.ts | 38 +++++++++++++++++++ .../schematics/angular/web-worker/index.ts | 14 +++---- 7 files changed, 81 insertions(+), 32 deletions(-) create mode 100644 packages/schematics/angular/utility/paths.ts create mode 100644 packages/schematics/angular/utility/paths_spec.ts diff --git a/packages/schematics/angular/application/index.ts b/packages/schematics/angular/application/index.ts index e8b877b110..6de80e6d77 100644 --- a/packages/schematics/angular/application/index.ts +++ b/packages/schematics/angular/application/index.ts @@ -37,6 +37,7 @@ import { NodeDependencyType, addPackageJsonDependency } from '../utility/depende import { findPropertyInAstObject, insertPropertyInAstObjectInOrder } from '../utility/json-utils'; import { latestVersions } from '../utility/latest-versions'; import { applyLintFix } from '../utility/lint-fix'; +import { relativePathToWorkspaceRoot } from '../utility/paths'; import { validateProjectName } from '../utility/validation'; import { getWorkspace, updateWorkspace } from '../utility/workspace'; import { Builders, ProjectType } from '../utility/workspace-models'; @@ -147,7 +148,8 @@ function addAppToWorkspaceFile(options: ApplicationOptions, newProjectRoot: stri ? options.projectRoot : `${newProjectRoot}/${options.name}`; - if (projectRoot !== '' && !projectRoot.endsWith('/')) { + projectRoot = normalize(projectRoot); + if (projectRoot) { projectRoot += '/'; } @@ -324,9 +326,6 @@ export default function (options: ApplicationOptions): Rule { const appDir = isRootApp ? options.projectRoot as string : `${newProjectRoot}/${options.name}`; - const relativePathToWorkspaceRoot = appDir - ? appDir.split('/').map(() => '..').join('/') - : '.'; const sourceDir = `${appDir}/src/app`; const e2eOptions: E2eOptions = { @@ -342,7 +341,7 @@ export default function (options: ApplicationOptions): Rule { applyTemplates({ utils: strings, ...options, - relativePathToWorkspaceRoot, + relativePathToWorkspaceRoot: relativePathToWorkspaceRoot(appDir), appName: options.name, isRootApp, }), diff --git a/packages/schematics/angular/e2e/index.ts b/packages/schematics/angular/e2e/index.ts index 128f3d0e59..e26e20cfe5 100644 --- a/packages/schematics/angular/e2e/index.ts +++ b/packages/schematics/angular/e2e/index.ts @@ -5,7 +5,7 @@ * 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 { normalize, strings } from '@angular-devkit/core'; +import { join, normalize, strings } from '@angular-devkit/core'; import { Rule, SchematicsException, @@ -17,16 +17,11 @@ import { move, url, } from '@angular-devkit/schematics'; +import { relativePathToWorkspaceRoot } from '../utility/paths'; import { getWorkspace, updateWorkspace } from '../utility/workspace'; import { Builders } from '../utility/workspace-models'; import { Schema as E2eOptions } from './schema'; -function getE2eRoot(projectRoot: string): string { - const root = normalize(projectRoot); - - return root ? root + '/e2e' : 'e2e'; -} - export default function (options: E2eOptions): Rule { return async (host: Tree) => { const appProject = options.relatedAppName; @@ -36,8 +31,7 @@ export default function (options: E2eOptions): Rule { throw new SchematicsException(`Project name "${appProject}" doesn't not exist.`); } - const root = getE2eRoot(project.root); - const relativePathToWorkspaceRoot = root.split('/').map(() => '..').join('/'); + const root = join(normalize(project.root), 'e2e'); project.targets.add({ name: 'e2e', @@ -66,7 +60,7 @@ export default function (options: E2eOptions): Rule { applyTemplates({ utils: strings, ...options, - relativePathToWorkspaceRoot, + relativePathToWorkspaceRoot: relativePathToWorkspaceRoot(root), }), move(root), ])), diff --git a/packages/schematics/angular/library/index.ts b/packages/schematics/angular/library/index.ts index 2f3c928187..8378e961c5 100644 --- a/packages/schematics/angular/library/index.ts +++ b/packages/schematics/angular/library/index.ts @@ -24,6 +24,7 @@ import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; import { NodeDependencyType, addPackageJsonDependency } from '../utility/dependencies'; import { latestVersions } from '../utility/latest-versions'; import { applyLintFix } from '../utility/lint-fix'; +import { relativePathToWorkspaceRoot } from '../utility/paths'; import { validateProjectName } from '../utility/validation'; import { getWorkspace, updateWorkspace } from '../utility/workspace'; import { Builders, ProjectType } from '../utility/workspace-models'; @@ -195,7 +196,6 @@ export default function (options: LibraryOptions): Rule { const distRoot = `dist/${folderName}`; const sourceDir = `${projectRoot}/src/lib`; - const relativePathToWorkspaceRoot = projectRoot.split('/').map(x => '..').join('/'); const templateSource = apply(url('./files'), [ applyTemplates({ @@ -204,7 +204,7 @@ export default function (options: LibraryOptions): Rule { packageName, projectRoot, distRoot, - relativePathToWorkspaceRoot, + relativePathToWorkspaceRoot: relativePathToWorkspaceRoot(projectRoot), prefix, angularLatestVersion: latestVersions.Angular.replace('~', '').replace('^', ''), folderName, diff --git a/packages/schematics/angular/service-worker/index.ts b/packages/schematics/angular/service-worker/index.ts index 33247c50bb..fa01bff3a4 100644 --- a/packages/schematics/angular/service-worker/index.ts +++ b/packages/schematics/angular/service-worker/index.ts @@ -5,6 +5,7 @@ * 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 { join, normalize } from '@angular-devkit/core'; import { Rule, SchematicContext, @@ -23,6 +24,7 @@ import { addSymbolToNgModuleMetadata, insertImport, isImported } from '../utilit import { InsertChange } from '../utility/change'; import { addPackageJsonDependency, getPackageJsonDependency } from '../utility/dependencies'; import { getAppModulePath } from '../utility/ng-ast-utils'; +import { relativePathToWorkspaceRoot } from '../utility/paths'; import { targetBuildNotFoundError } from '../utility/project-targets'; import { getWorkspace, updateWorkspace } from '../utility/workspace'; import { BrowserBuilderOptions } from '../utility/workspace-models'; @@ -136,18 +138,19 @@ export default function (options: ServiceWorkerOptions): Rule { const root = project.root; config.serviceWorker = true; - config.ngswConfigPath = `${root && !root.endsWith('/') ? root + '/' : root}ngsw-config.json`; - - const relativePathToWorkspaceRoot = project.root ? - project.root.split('/').filter(x => x !== '').map(x => '..').join('/') : '.'; + config.ngswConfigPath = join(normalize(root), 'ngsw-config.json'); let { resourcesOutputPath = '' } = config; if (resourcesOutputPath) { - resourcesOutputPath = '/' + resourcesOutputPath.split('/').filter(x => !!x).join('/'); + resourcesOutputPath = normalize(`/${resourcesOutputPath}`); } const templateSource = apply(url('./files'), [ - applyTemplates({ ...options, resourcesOutputPath, relativePathToWorkspaceRoot }), + applyTemplates({ + ...options, + resourcesOutputPath, + relativePathToWorkspaceRoot: relativePathToWorkspaceRoot(project.root), + }), move(project.root), ]); diff --git a/packages/schematics/angular/utility/paths.ts b/packages/schematics/angular/utility/paths.ts new file mode 100644 index 0000000000..35729a6417 --- /dev/null +++ b/packages/schematics/angular/utility/paths.ts @@ -0,0 +1,19 @@ +/** + * @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 { normalize, split } from '@angular-devkit/core'; + +export function relativePathToWorkspaceRoot(projectRoot: string | undefined): string { + const normalizedPath = split(normalize(projectRoot || '')); + + if (normalizedPath.length === 0 || !normalizedPath[0]) { + return '.'; + } else { + return normalizedPath.map(() => '..').join('/'); + } +} diff --git a/packages/schematics/angular/utility/paths_spec.ts b/packages/schematics/angular/utility/paths_spec.ts new file mode 100644 index 0000000000..1cdc3e0316 --- /dev/null +++ b/packages/schematics/angular/utility/paths_spec.ts @@ -0,0 +1,38 @@ +/** + * @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 { relativePathToWorkspaceRoot } from './paths'; + +describe('paths', () => { + describe('relativePathToWorkspaceRoot', () => { + it(`should handle root '/'`, () => { + const result = relativePathToWorkspaceRoot('/'); + expect(result).toBe('.'); + }); + + it(`should handle an empty path`, () => { + const result = relativePathToWorkspaceRoot(''); + expect(result).toBe('.'); + }); + + it(`should handle an undefined path`, () => { + const result = relativePathToWorkspaceRoot(undefined); + expect(result).toBe('.'); + }); + + it(`should handle path with trailing '/'`, () => { + const result = relativePathToWorkspaceRoot('foo/bar/'); + expect(result).toBe('../..'); + }); + + it(`should handle path without trailing '/'`, () => { + const result = relativePathToWorkspaceRoot('foo/bar'); + expect(result).toBe('../..'); + }); + }); +}); diff --git a/packages/schematics/angular/web-worker/index.ts b/packages/schematics/angular/web-worker/index.ts index 92e6df7ff4..717a3741bf 100644 --- a/packages/schematics/angular/web-worker/index.ts +++ b/packages/schematics/angular/web-worker/index.ts @@ -12,6 +12,7 @@ import { } from '@angular-devkit/schematics'; import { appendValueInAstArray, findPropertyInAstObject } from '../utility/json-utils'; import { parseName } from '../utility/parse-name'; +import { relativePathToWorkspaceRoot } from '../utility/paths'; import { buildDefaultPath, getWorkspace, updateWorkspace } from '../utility/workspace'; import { BrowserBuilderOptions, LintBuilderOptions } from '../utility/workspace-models'; import { Schema as WebWorkerOptions } from './schema'; @@ -21,14 +22,6 @@ function addConfig(options: WebWorkerOptions, root: string, tsConfigPath: string return (host: Tree, context: SchematicContext) => { context.logger.debug('updating project configuration.'); - // todo: replace with the new helper method in a seperate PR - // https://github.com/angular/angular-cli/pull/14207 - const rootNormalized = root.endsWith('/') ? root.slice(0, -1) : root; - const relativePathToWorkspaceRoot = - rootNormalized - ? rootNormalized.split('/').map(x => '..').join('/') - : '.'; - // Add worker glob exclusion to tsconfig.app.json. const workerGlob = 'src/**/*.worker.ts'; const buffer = host.read(tsConfigPath); @@ -51,7 +44,10 @@ function addConfig(options: WebWorkerOptions, root: string, tsConfigPath: string return mergeWith( apply(url('./files/worker-tsconfig'), [ - applyTemplates({ ...options, relativePathToWorkspaceRoot }), + applyTemplates({ + ...options, + relativePathToWorkspaceRoot: relativePathToWorkspaceRoot(root), + }), move(root), ]), );