diff --git a/packages/@angular/cli/utilities/find-parent-module.ts b/packages/@angular/cli/utilities/find-parent-module.ts index 7a2273e19068..92ce70970129 100644 --- a/packages/@angular/cli/utilities/find-parent-module.ts +++ b/packages/@angular/cli/utilities/find-parent-module.ts @@ -2,25 +2,38 @@ import * as fs from 'fs'; import * as path from 'path'; const SilentError = require('silent-error'); +/** + * Find the path to the first module, moving up through the directory tree from + * currentDir. + * + * @export + * @param {*} project - the project definition. + * @param {string} currentDir - a relative path from the project root. + * @returns {string} the relative path to the first module above currentDir from the project root. + */ export default function findParentModule(project: any, currentDir: string): string { - const sourceRoot = path.join(project.root, project.ngConfig.apps[0].root, 'app'); // trim currentDir - currentDir = currentDir.replace(path.join(project.ngConfig.apps[0].root, 'app'), ''); + currentDir = currentDir.replace(project.root, ''); - let pathToCheck = path.join(sourceRoot, currentDir); + // normalize to project.root + let pathToCheck = path.join(project.root, currentDir); - while (pathToCheck.length >= sourceRoot.length) { + function isModule(fileName: string) { // TODO: refactor to not be based upon file name - const files = fs.readdirSync(pathToCheck) - .filter(fileName => !fileName.endsWith('routing.module.ts')) - .filter(fileName => fileName.endsWith('.module.ts')) - .filter(fileName => fs.statSync(path.join(pathToCheck, fileName)).isFile()); + return !fileName.endsWith('routing.module.ts') && + fileName.endsWith('.module.ts') && + fs.statSync(path.join(pathToCheck, fileName)).isFile(); + } + + while (pathToCheck.length >= project.root.length) { + const files = fs.readdirSync(pathToCheck).filter(isModule); if (files.length === 1) { return path.join(pathToCheck, files[0]); } else if (files.length > 1) { - throw new SilentError(`Multiple module files found: ${pathToCheck.replace(sourceRoot, '')}`); + throw new SilentError(`Multiple module files found: + ${pathToCheck.replace(project.root, '')}`); } // move to parent directory diff --git a/tests/acceptance/find-parent-module.spec.ts b/tests/acceptance/find-parent-module.spec.ts new file mode 100644 index 000000000000..bf097358146f --- /dev/null +++ b/tests/acceptance/find-parent-module.spec.ts @@ -0,0 +1,126 @@ +'use strict'; + +const mockFs = require('mock-fs'); + +import * as ts from 'typescript'; +import * as path from 'path'; +import * as dependentFilesUtils from '@angular/cli/utilities/get-dependent-files'; + +import { expect } from 'chai'; +import findParentModule from '@angular/cli/utilities/find-parent-module'; + +describe('findParentModule', () => { + const project = { + root: 'project', + ngConfig: { + apps: [ + { root: 'src' }, + ], + }, + }; + + const mockDrive = { + 'project': { + 'src': { + 'app': { + 'app.module.ts': ``, + 'app.routing.module.ts': ``, + 'component': {}, + 'child-module': { + 'child-module.module.ts': ``, + 'child-module.routing.module.ts': ``, + 'child-module-component': {}, + }, + 'router-module': { + 'router-module.routing.module.ts': `` + } + }, + 'myapp': { + 'myapp.module.ts': ``, + 'myapp.routing.module.ts': ``, + 'component': {}, + 'child-module': { + 'child-module.module.ts': ``, + 'child-module.routing.module.ts': ``, + 'child-module-component': {}, + }, + 'router-module': { + 'router-module.routing.module.ts': `` + } + } + } + } + } + + const apps = [ + { root: 'src', subfolder: 'app' }, + { root: 'src', subfolder: 'myapp' } + ]; + + beforeEach(() => { + mockFs(mockDrive); + }); + + afterEach(() => { + mockFs.restore(); + }); + + describe('Finds parent module', () => { + it('throws and error if not in an application folder', () => { + let currentDir = ''; + expect(() => { + let parentModule = findParentModule(project, currentDir); + // if for some reason it doesn't throw.. expect undefined + // in hopes of logging something useful. + expect(parentModule).to.equal(undefined); + }).to.throw(); + }); + + apps.forEach((app) => { testApp(app)}); + }); + + function testApp(app: Object) { + describe('module in folder '+ app.subfolder, () => { + it('works for root folder', () => { + let currentDir = path.join(app.root, app.subfolder); + expect(() => { + let parentModule = findParentModule(project, currentDir); + expect(parentModule).to.equal(path.join(project.root, app.root, app.subfolder, app.subfolder +'.module.ts')); + }).to.not.throw(); + }); + + it('works from an app component folder', () => { + let currentDir = path.join(app.root, app.subfolder, 'component'); + expect(() => { + let parentModule = findParentModule(project, currentDir); + expect(parentModule).to.equal(path.join(project.root, app.root, app.subfolder, app.subfolder +'.module.ts')); + }).to.not.throw(); + }); + + it('ignores router modules', () => { + let currentDir = path.join(app.root, app.subfolder, 'router-module'); + expect(() => { + let parentModule = findParentModule(project, currentDir); + expect(parentModule).to.equal(path.join(project.root, app.root, app.subfolder, app.subfolder + '.module.ts')); + }).to.not.throw(); + }); + + it('works from a child module', () => { + let currentDir = path.join(app.root, app.subfolder, 'child-module'); + expect(() => { + let parentModule = findParentModule(project, currentDir); + expect(parentModule).to.equal(path.join(project.root, app.root, app.subfolder, 'child-module', 'child-module.module.ts')); + }).to.not.throw(); + }); + + it('works from a component of a child module', () => { + let currentDir = path.join(app.root, app.subfolder, 'child-module', 'child-module-component'); + expect(() => { + let parentModule = findParentModule(project, currentDir); + expect(parentModule).to.equal(path.join(project.root, app.root, app.subfolder, 'child-module', 'child-module.module.ts')); + }).to.not.throw(); + }); + }); + } + +});