Skip to content

Commit

Permalink
feat: add features in get-dependent-files.ts
Browse files Browse the repository at this point in the history
add logic to filter out spec file associated with the given component unit and all index files while getting all dependent files
add utlity function to get all the files (.html/stylesheets/.spec.ts) associated with the given component unit
change the constructor method of the class ModuleResolver (add 'rootPath' as an additional parameter)
  • Loading branch information
ashoktamang committed Aug 10, 2016
1 parent 539c57d commit 77a7662
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 16 deletions.
32 changes: 30 additions & 2 deletions addon/ng2/utilities/get-dependent-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,37 @@ export function hasIndexFile(dirPath: string): Promise<Boolean> {
});
}

/**
* Function to get all the templates, stylesheets, and spec files of a given component unit
* Assumption: When any component/service/pipe unit is generated, Angular CLI has a blueprint for
* creating associated files with the name of the generated unit. So, there are two
* assumptions made:
* a. the function only returns associated files that have names matching to the given unit.
* b. the function only looks for the associated files in the directory where the given unit
* exists.
*
* @todo read the metadata to look for the associated files of a given file.
*
* @param fileName
*
* @return absolute paths of '.html/.css/.sass/.spec.ts' files associated with the given file.
*
*/
export function getAllAssociatedFiles(fileName: string): Promise<string[]> {
let fileDirName = path.dirname(fileName);
let componentName = path.basename(fileName, '.ts');
const globSearch = denodeify(glob);
return globSearch(path.join(fileDirName, `${componentName}.*`), { nodir: true })
.then((files: string[]) => {
return files.filter((file) => {
return (path.basename(file) !== 'index.ts');
});
});
}

/**
* Returns a map of all dependent file/s' path with their moduleSpecifier object
* (specifierText, pos, end)
* (specifierText, pos, end).
*
* @param fileName file upon which other files depend
* @param rootPath root of the project
Expand All @@ -86,7 +114,7 @@ export function hasIndexFile(dirPath: string): Promise<Boolean> {
*/
export function getDependentFiles(fileName: string, rootPath: string): Promise<ModuleMap> {
const globSearch = denodeify(glob);
return globSearch(path.join(rootPath, '**/*.*.ts'), { nodir: true })
return globSearch(path.join(rootPath, '**/*.ts'), { nodir: true })
.then((files: string[]) => Promise.all(files.map(file => createTsSourceFile(file)))
.then((tsFiles: ts.SourceFile[]) => tsFiles.map(file => getImportClauses(file)))
.then((moduleSpecifiers: ModuleImport[][]) => {
Expand Down
18 changes: 12 additions & 6 deletions addon/ng2/utilities/module-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,13 @@ import * as dependentFilesUtils from './get-dependent-files';
import { Promise } from 'es6-promise';
import { Change, ReplaceChange } from './change';

// The root directory of Angular Project.
const ROOT_PATH = path.resolve('src/app');

/**
* Rewrites import module of dependent files when the file is moved.
* Also, rewrites export module of related index file of the given file.
*/
export class ModuleResolver {

constructor(public oldFilePath: string, public newFilePath: string) {}
constructor(public oldFilePath: string, public newFilePath: string, public rootPath: string) {}

/**
* Changes are applied from the bottom of a file to the top.
Expand Down Expand Up @@ -54,10 +51,19 @@ export class ModuleResolver {
* @return {Promise<Change[]>}
*/
resolveDependentFiles(): Promise<Change[]> {
return dependentFilesUtils.getDependentFiles(this.oldFilePath, ROOT_PATH)
return dependentFilesUtils.getDependentFiles(this.oldFilePath, this.rootPath)
.then((files: dependentFilesUtils.ModuleMap) => {
let changes: Change[] = [];
Object.keys(files).forEach(file => {
let fileBaseName = path.basename(this.oldFilePath, '.ts');
// Filter out the spec file associated with to-be-promoted component unit.
let relavantFiles = Object.keys(files).filter((file) => {
if (path.extname(path.basename(file, '.ts')) === '.spec') {
return path.basename(path.basename(file, '.ts'), '.spec') !== fileBaseName;
} else {
return true;
}
});
relavantFiles.forEach(file => {
let tempChanges: ReplaceChange[] = files[file]
.map(specifier => {
let componentName = path.basename(this.oldFilePath, '.ts');
Expand Down
69 changes: 66 additions & 3 deletions tests/acceptance/get-dependent-files.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ describe('Get Dependent Files: ', () => {
'foo': {
'foo.component.ts': `import * from '../bar/baz/baz.component'
import * from '../bar/bar.component'`,
'foo.component.html': '',
'foo.component.css': '',
'foo.component.spec.ts': '',
'foo.ts': '',
'index.ts': `export * from './foo.component'`
},
'bar': {
Expand All @@ -24,12 +28,23 @@ describe('Get Dependent Files: ', () => {
'baz.html': '<h1> Hello </h1>'
},
'bar.component.ts': `import * from './baz/baz.component'
import * from '../foo'`
import * from '../foo'`,
'bar.component.spec.ts': ''
},
'foo-baz': {
'no-module.component.ts': ''
'no-module.component.ts': '',
'no-module.component.spec.ts': 'import * from "../bar/bar.component";'
},
'empty-dir': {}
'quux': {
'quux.ts': '',
'quux.html': '',
'quux.css': '',
'quux.spec.ts': ''
},
'noAngular.tag.ts': '',
'noAngular.tag.html': '',
'noAngular.tag.sass': '',
'noAngular.tag.spec.ts': '',
}
};
mockFs(mockDrive);
Expand Down Expand Up @@ -102,13 +117,56 @@ describe('Get Dependent Files: ', () => {
});
});

describe('returns an array of all the associated files of a given component unit.', () => {
it('when the component name has a special Angular tag(component/pipe/service)', () => {
let sourceFile = path.join(rootPath, 'foo/foo.component.ts');
return dependentFilesUtils.getAllAssociatedFiles(sourceFile)
.then((files: string[]) => {
let expectedContents = [
'src/app/foo/foo.component.css',
'src/app/foo/foo.component.html',
'src/app/foo/foo.component.spec.ts',
'src/app/foo/foo.component.ts'
];
assert.deepEqual(files, expectedContents);
});
});
it('when the component name has non-Angular tag', () => {
let sourceFile = path.join(rootPath, 'noAngular.tag.ts');
return dependentFilesUtils.getAllAssociatedFiles(sourceFile)
.then((files: string[]) => {
let expectedContents = [
'src/app/noAngular.tag.html',
'src/app/noAngular.tag.sass',
'src/app/noAngular.tag.spec.ts',
'src/app/noAngular.tag.ts'
];
assert.deepEqual(files, expectedContents);
});
});
it('when the component name has no tag after the unique file name', () => {
let sourceFile = path.join(rootPath, 'quux/quux.ts');
return dependentFilesUtils.getAllAssociatedFiles(sourceFile)
.then((files: string[]) => {
let expectedContents = [
'src/app/quux/quux.css',
'src/app/quux/quux.html',
'src/app/quux/quux.spec.ts',
'src/app/quux/quux.ts'
];
assert.deepEqual(files, expectedContents);
});
});
});

describe('returns a map of all files which depend on a given file ', () => {
it('when the given component unit has no index file', () => {
let sourceFile = path.join(rootPath, 'bar/bar.component.ts');
return dependentFilesUtils.getDependentFiles(sourceFile, rootPath)
.then((contents: dependentFilesUtils.ModuleMap) => {
let bazFile = path.join(rootPath, 'bar/baz/baz.component.ts');
let fooFile = path.join(rootPath, 'foo/foo.component.ts');
let noModuleSpecFile = path.join(rootPath, 'foo-baz/no-module.component.spec.ts');
let expectedContents: dependentFilesUtils.ModuleMap = {};
expectedContents[bazFile] = [{
specifierText: '../bar.component',
Expand All @@ -120,6 +178,11 @@ describe('Get Dependent Files: ', () => {
pos: 85,
end: 108
}];
expectedContents[noModuleSpecFile] = [{
specifierText: '../bar/bar.component',
pos: 13,
end: 36
}];
assert.deepEqual(contents, expectedContents);
});
});
Expand Down
11 changes: 6 additions & 5 deletions tests/acceptance/module-resolver.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ describe('ModuleResolver', () => {
'baz': {
'baz.component.ts': `import * from "../bar.component"
import * from '../../foo-baz/qux/quux/foobar/foobar.component'
`
`,
'baz.component.spec.ts': 'import * from "./baz.component";'
},
'bar.component.ts': `import * from './baz/baz.component'
import * from '../foo/foo.component'`,
Expand Down Expand Up @@ -64,7 +65,7 @@ describe('ModuleResolver', () => {
it('when there is no index.ts in oldPath', () => {
let oldFilePath = path.join(rootPath, 'bar/baz/baz.component.ts');
let newFilePath = path.join(rootPath, 'foo');
let resolver = new ModuleResolver(oldFilePath, newFilePath);
let resolver = new ModuleResolver(oldFilePath, newFilePath, rootPath);
return resolver.resolveDependentFiles()
.then((changes) => resolver.applySortedChangePromise(changes))
.then(() => dependentFilesUtils.createTsSourceFile(barFile))
Expand Down Expand Up @@ -93,7 +94,7 @@ describe('ModuleResolver', () => {
it('when no files are importing the given file', () => {
let oldFilePath = path.join(rootPath, 'foo-baz/foo-baz.component.ts');
let newFilePath = path.join(rootPath, 'bar');
let resolver = new ModuleResolver(oldFilePath, newFilePath);
let resolver = new ModuleResolver(oldFilePath, newFilePath, rootPath);
return resolver.resolveDependentFiles()
.then((changes) => resolver.applySortedChangePromise(changes))
.then(() => resolver.resolveOwnImports())
Expand All @@ -108,7 +109,7 @@ describe('ModuleResolver', () => {
it('when oldPath and newPath both do not have index.ts', () => {
let oldFilePath = path.join(rootPath, 'bar/baz/baz.component.ts');
let newFilePath = path.join(rootPath, 'foo-baz');
let resolver = new ModuleResolver(oldFilePath, newFilePath);
let resolver = new ModuleResolver(oldFilePath, newFilePath, rootPath);
return resolver.resolveDependentFiles()
.then((changes) => resolver.applySortedChangePromise(changes))
.then(() => dependentFilesUtils.createTsSourceFile(barFile))
Expand Down Expand Up @@ -137,7 +138,7 @@ describe('ModuleResolver', () => {
it('when there are multiple spaces between symbols and specifier', () => {
let oldFilePath = path.join(rootPath, 'foo-baz/qux/quux/foobar/foobar.component.ts');
let newFilePath = path.join(rootPath, 'foo');
let resolver = new ModuleResolver(oldFilePath, newFilePath);
let resolver = new ModuleResolver(oldFilePath, newFilePath, rootPath);
return resolver.resolveDependentFiles()
.then((changes) => resolver.applySortedChangePromise(changes))
.then(() => dependentFilesUtils.createTsSourceFile(fooQuxFile))
Expand Down

0 comments on commit 77a7662

Please sign in to comment.