Skip to content
This repository was archived by the owner on May 1, 2020. It is now read-only.

Commit

Permalink
generators - wip
Browse files Browse the repository at this point in the history
  • Loading branch information
danbucholtz committed Feb 18, 2017
1 parent 47dfaf2 commit 333c7d0
Show file tree
Hide file tree
Showing 11 changed files with 470 additions and 9 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"@types/uglify-js": "^2.0.27",
"@types/webpack": "^1.12.35",
"@types/ws": "^0.0.38",
"change-case": "^3.0.1",
"conventional-changelog-cli": "1.2.0",
"github": "0.2.4",
"ionic-cz-conventional-changelog": "1.0.0",
Expand Down
26 changes: 26 additions & 0 deletions src/generators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Logger} from './logger/logger';
import { generateContext } from './util/config';
import * as Constants from './util/constants';
import { BuildContext, GeneratorOption, GeneratorRequest } from './util/interfaces';

export function generateNonTab(request: GeneratorRequest) {
const context = generateContext();
return processNonTabRequest(context, request);
}

function processNonTabRequest(context: BuildContext, request: GeneratorRequest) {

}

export function listOptions() {
const list: GeneratorOption[] = [];
list.push({type: Constants.COMPONENT, multiple: false});
list.push({type: Constants.DIRECTIVE, multiple: false});
list.push({type: Constants.PAGE, multiple: false});
list.push({type: Constants.PIPE, multiple: false});
list.push({type: Constants.PROVIDER, multiple: false});
list.push({type: Constants.TABS, multiple: true});
}



8 changes: 8 additions & 0 deletions src/generators/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export const CLASSNAME_VARIABLE = '$CLASSNAME';
export const FILENAME_VARIABLE = '$FILENAME';

export const KNOWN_FILE_EXTENSION = '.tmpl';

export const SPEC_FILE_EXTENSION = 'spec.ts';
export const NG_MODULE_FILE_EXTENSION = 'module.ts';

233 changes: 233 additions & 0 deletions src/generators/util.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
import { basename, join } from 'path';
import * as fs from 'fs';
import * as Constants from '../util/constants';
import * as helpers from '../util/helpers';
import * as util from './util';
import * as GeneratorConstants from './constants';

describe('util', () => {
describe('hydrateRequest', () => {
it('should take a request and return a hydrated request', () => {
// arrange
const componentsDir = '/Users/dan/project/src/components';
const context = {
componentsDir: componentsDir
};
const request = {
type: Constants.COMPONENT,
name: 'settings view',
includeSpec: true,
includeNgModule: true
};

const templateDir = '/Users/dan/project/node_modules/ionic-angular/templates';
spyOn(helpers, helpers.getPropertyValue.name).and.returnValue(templateDir);

// act
const hydratedRequest = util.hydrateRequest(context, request);

// assert
expect(hydratedRequest.type).toEqual(Constants.COMPONENT);
expect(hydratedRequest.name).toEqual(request.name);
expect(hydratedRequest.includeNgModule).toBeTruthy();
expect(hydratedRequest.includeSpec).toBeTruthy();
expect(hydratedRequest.className).toEqual('SettingsView');
expect(hydratedRequest.fileName).toEqual('settings-view');
expect(hydratedRequest.dirToRead).toEqual(join(templateDir, Constants.COMPONENT));
expect(hydratedRequest.dirToWrite).toEqual(join(componentsDir, hydratedRequest.fileName));
});
});

describe('readTemplates', () => {
it('should get a map of templates and their content back', () => {
// arrange
const templateDir = '/Users/dan/project/node_modules/ionic-angular/templates/component';
const knownValues = ['html.tmpl', 'scss.tmpl', 'spec.ts.tmpl', 'ts.tmpl', 'module.tmpl'];
const fileContent = 'SomeContent';
spyOn(fs, 'readdirSync').and.returnValue(knownValues);
spyOn(helpers, helpers.readFileAsync.name).and.returnValue(Promise.resolve(fileContent));

// act
const promise = util.readTemplates(templateDir);

// assert
return promise.then((map: Map<string, string>) => {
expect(map.get(join(templateDir, knownValues[0]))).toEqual(fileContent);
expect(map.get(join(templateDir, knownValues[1]))).toEqual(fileContent);
expect(map.get(join(templateDir, knownValues[2]))).toEqual(fileContent);
expect(map.get(join(templateDir, knownValues[3]))).toEqual(fileContent);
expect(map.get(join(templateDir, knownValues[4]))).toEqual(fileContent);
});
});
});

describe('filterOutTemplates', () => {
it('should preserve all templates', () => {
const map = new Map<string, string>();
const templateDir = '/Users/dan/project/node_modules/ionic-angular/templates/component';
const fileContent = 'SomeContent';
const knownValues = ['html.tmpl', 'scss.tmpl', 'spec.ts.tmpl', 'ts.tmpl', 'module.tmpl'];
map.set(join(templateDir, knownValues[0]), fileContent);
map.set(join(templateDir, knownValues[1]), fileContent);
map.set(join(templateDir, knownValues[2]), fileContent);
map.set(join(templateDir, knownValues[3]), fileContent);
map.set(join(templateDir, knownValues[4]), fileContent);

const newMap = util.filterOutTemplates({includeNgModule: true, includeSpec: true}, map);
expect(newMap.size).toEqual(knownValues.length);
});

it('should remove spec', () => {
const map = new Map<string, string>();
const templateDir = '/Users/dan/project/node_modules/ionic-angular/templates/component';
const fileContent = 'SomeContent';
const knownValues = ['html.tmpl', 'scss.tmpl', 'spec.ts.tmpl', 'ts.tmpl', 'module.tmpl'];
map.set(join(templateDir, knownValues[0]), fileContent);
map.set(join(templateDir, knownValues[1]), fileContent);
map.set(join(templateDir, knownValues[2]), fileContent);
map.set(join(templateDir, knownValues[3]), fileContent);
map.set(join(templateDir, knownValues[4]), fileContent);

const newMap = util.filterOutTemplates({includeNgModule: true, includeSpec: false}, map);
expect(newMap.size).toEqual(4);
expect(newMap.get(join(templateDir, knownValues[0]))).toBeTruthy();
expect(newMap.get(join(templateDir, knownValues[1]))).toBeTruthy();
expect(newMap.get(join(templateDir, knownValues[2]))).toBeFalsy();
expect(newMap.get(join(templateDir, knownValues[3]))).toBeTruthy();
expect(newMap.get(join(templateDir, knownValues[4]))).toBeTruthy();
});

it('should remove spec and module', () => {
const map = new Map<string, string>();
const templateDir = '/Users/dan/project/node_modules/ionic-angular/templates/component';
const fileContent = 'SomeContent';
const knownValues = ['html.tmpl', 'scss.tmpl', 'spec.ts.tmpl', 'ts.tmpl', 'module.ts.tmpl'];
map.set(join(templateDir, knownValues[0]), fileContent);
map.set(join(templateDir, knownValues[1]), fileContent);
map.set(join(templateDir, knownValues[2]), fileContent);
map.set(join(templateDir, knownValues[3]), fileContent);
map.set(join(templateDir, knownValues[4]), fileContent);

const newMap = util.filterOutTemplates({includeNgModule: false, includeSpec: false}, map);
expect(newMap.size).toEqual(3);
expect(newMap.get(join(templateDir, knownValues[0]))).toBeTruthy();
expect(newMap.get(join(templateDir, knownValues[1]))).toBeTruthy();
expect(newMap.get(join(templateDir, knownValues[2]))).toBeFalsy();
expect(newMap.get(join(templateDir, knownValues[3]))).toBeTruthy();
expect(newMap.get(join(templateDir, knownValues[4]))).toBeFalsy();
});
});

describe('applyTemplates', () => {
it('should replace the template content', () => {
const fileOne = '/Users/dan/fileOne';

const fileOneContent = `
<!--
Generated template for the $CLASSNAME component.
See https://angular.io/docs/ts/latest/api/core/index/ComponentMetadata-class.html
for more info on Angular 2 Components.
-->
{{text}}
`;

const fileTwo = '/Users/dan/fileTwo';
const fileTwoContent = `
$FILENAME {
}
`;

const fileThree = '/Users/dan/fileThree';
const fileThreeContent = `
describe('$CLASSNAME', () => {
it('should do something', () => {
expect(true).toEqual(true);
});
});
`;

const fileFour = '/Users/dan/fileFour';
const fileFourContent = `
import { Component } from '@angular/core';
/*
Generated class for the $CLASSNAME component.
See https://angular.io/docs/ts/latest/api/core/index/ComponentMetadata-class.html
for more info on Angular 2 Components.
*/
@Component({
selector: '$FILENAME',
templateUrl: '$FILENAME.html'
})
export class $CLASSNAMEComponent {
text: string;
constructor() {
console.log('Hello $CLASSNAME Component');
this.text = 'Hello World';
}
}
`;

const map = new Map<string, string>();
map.set(fileOne, fileOneContent);
map.set(fileTwo, fileTwoContent);
map.set(fileThree, fileThreeContent);
map.set(fileFour, fileFourContent);

const className = 'SettingsView';
const fileName = 'settings-view';

const results = util.applyTemplates({ className: className, fileName: fileName}, map);
const modifiedContentOne = results.get(fileOne);
const modifiedContentTwo = results.get(fileTwo);
const modifiedContentThree = results.get(fileThree);
const modifiedContentFour = results.get(fileFour);
expect(modifiedContentOne.indexOf(GeneratorConstants.CLASSNAME_VARIABLE)).toEqual(-1);
expect(modifiedContentOne.indexOf(GeneratorConstants.FILENAME_VARIABLE)).toEqual(-1);
expect(modifiedContentTwo.indexOf(GeneratorConstants.CLASSNAME_VARIABLE)).toEqual(-1);
expect(modifiedContentTwo.indexOf(GeneratorConstants.FILENAME_VARIABLE)).toEqual(-1);
expect(modifiedContentThree.indexOf(GeneratorConstants.CLASSNAME_VARIABLE)).toEqual(-1);
expect(modifiedContentThree.indexOf(GeneratorConstants.FILENAME_VARIABLE)).toEqual(-1);
expect(modifiedContentFour.indexOf(GeneratorConstants.CLASSNAME_VARIABLE)).toEqual(-1);
expect(modifiedContentFour.indexOf(GeneratorConstants.FILENAME_VARIABLE)).toEqual(-1);
});
});

describe('writeGeneratedFiles', () => {
it('should return the list of files generated', () => {
const map = new Map<string, string>();
const templateDir = '/Users/dan/project/node_modules/ionic-angular/templates/component';
const fileContent = 'SomeContent';
const knownValues = ['html.tmpl', 'scss.tmpl', 'spec.ts.tmpl', 'ts.tmpl', 'module.tmpl'];
const fileName = 'settings-view';
const dirToWrite = join('/Users/dan/project/src/components', fileName);
map.set(join(templateDir, knownValues[0]), fileContent);
map.set(join(templateDir, knownValues[1]), fileContent);
map.set(join(templateDir, knownValues[2]), fileContent);
map.set(join(templateDir, knownValues[3]), fileContent);
map.set(join(templateDir, knownValues[4]), fileContent);

spyOn(helpers, helpers.writeFileAsync.name).and.returnValue(Promise.resolve());

const promise = util.writeGeneratedFiles({ dirToWrite: dirToWrite, fileName: fileName }, map);

return promise.then((filesCreated: string[]) => {
const fileExtensions = knownValues.map(knownValue => basename(knownValue, GeneratorConstants.KNOWN_FILE_EXTENSION));
expect(filesCreated[0]).toEqual(join(dirToWrite, `${fileName}.${fileExtensions[0]}`));
expect(filesCreated[1]).toEqual(join(dirToWrite, `${fileName}.${fileExtensions[1]}`));
expect(filesCreated[2]).toEqual(join(dirToWrite, `${fileName}.${fileExtensions[2]}`));
expect(filesCreated[3]).toEqual(join(dirToWrite, `${fileName}.${fileExtensions[3]}`));
expect(filesCreated[4]).toEqual(join(dirToWrite, `${fileName}.${fileExtensions[4]}`));
});
});
});
});
91 changes: 91 additions & 0 deletions src/generators/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { basename, join } from 'path';
import { readdirSync} from 'fs';

import { paramCase, pascalCase } from 'change-case';

import * as Constants from '../util/constants';
import * as GeneratorConstants from './constants';
import { getPropertyValue, readFileAsync, replaceAll, writeFileAsync } from '../util/helpers';
import { BuildContext, GeneratorRequest, HydratedGeneratorRequest } from '../util/interfaces';

export function hydrateRequest(context: BuildContext, request: GeneratorRequest) {
const hydrated = Object.assign({}, request) as HydratedGeneratorRequest;
hydrated.className = pascalCase(request.name);
hydrated.fileName = paramCase(request.name);

hydrated.dirToRead = join(getPropertyValue(Constants.ENV_VAR_IONIC_ANGULAR_TEMPLATE_DIR), request.type);

const baseDir = getDirToWriteToByType(context, request.type);
hydrated.dirToWrite = join(baseDir, hydrated.fileName);

return hydrated;
}

export function readTemplates(pathToRead: string): Promise<Map<string, string>> {
const fileNames = readdirSync(pathToRead);
const absolutePaths = fileNames.map(fileName => {
return join(pathToRead, fileName);
});
const filePathToContent = new Map<string, string>();
const promises = absolutePaths.map(absolutePath => {
const promise = readFileAsync(absolutePath);
promise.then((fileContent: string) => {
filePathToContent.set(absolutePath, fileContent);
});
});
return Promise.all(promises).then(() => {
return filePathToContent;
});
}

export function filterOutTemplates(request: HydratedGeneratorRequest, templates: Map<string, string>) {
const templatesToUseMap = new Map<string, string>();
templates.forEach((fileContent: string, filePath: string) => {
const newFileExtension = basename(filePath, GeneratorConstants.KNOWN_FILE_EXTENSION);
const shouldSkip = (!request.includeNgModule && newFileExtension === GeneratorConstants.NG_MODULE_FILE_EXTENSION) || (!request.includeSpec && newFileExtension === GeneratorConstants.SPEC_FILE_EXTENSION);
if (!shouldSkip) {
templatesToUseMap.set(filePath, fileContent);
}
});
return templatesToUseMap;
}

export function applyTemplates(request: HydratedGeneratorRequest, templates: Map<string, string>) {
const appliedTemplateMap = new Map<string, string>();
templates.forEach((fileContent: string, filePath: string) => {
const classnameRemovedContent = replaceAll(fileContent, GeneratorConstants.CLASSNAME_VARIABLE, request.className);
const fileNameRemovedContent = replaceAll(classnameRemovedContent, GeneratorConstants.FILENAME_VARIABLE, request.fileName);
appliedTemplateMap.set(filePath, fileNameRemovedContent);
});
return appliedTemplateMap;
}

export function writeGeneratedFiles(request: HydratedGeneratorRequest, processedTemplates: Map<string, string>): Promise<string[]> {
const promises: Promise<any>[] = [];
const createdFileList: string[] = [];
processedTemplates.forEach((fileContent: string, filePath: string) => {
const newFileExtension = basename(filePath, GeneratorConstants.KNOWN_FILE_EXTENSION);
const newFileName = `${request.fileName}.${newFileExtension}`;
const fileToWrite = join(request.dirToWrite, newFileName);
createdFileList.push(fileToWrite);
promises.push(writeFileAsync(fileToWrite, fileContent));
});
return Promise.all(promises).then(() => {
return createdFileList;
});
}

export function getDirToWriteToByType(context: BuildContext, type: string) {
if (type === Constants.COMPONENT) {
return context.componentsDir;
} else if ( type === Constants.DIRECTIVE) {
return context.directivesDir;
} else if (type === Constants.PAGE) {
return context.pagesDir;
} else if ( type === Constants.PIPE) {
return context.pipesDir;
} else if (type === Constants.PROVIDER) {
return context.providersDir;
}
throw new Error(`Unknown Generator Type: ${type}`);
}
6 changes: 6 additions & 0 deletions src/util/config.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,14 @@ describe('config', () => {
expect(context.wwwDir).toEqual(join(process.cwd(), Constants.WWW_DIR));
expect(context.wwwIndex).toEqual('index.html');
expect(context.buildDir).toEqual(join(process.cwd(), Constants.WWW_DIR, Constants.BUILD_DIR));
expect(context.pagesDir).toEqual(join(context.srcDir, 'pages'));
expect(context.componentsDir).toEqual(join(context.srcDir, 'components'));
expect(context.directivesDir).toEqual(join(context.srcDir, 'components'));
expect(context.pipesDir).toEqual(join(context.srcDir, 'pipes'));
expect(context.providersDir).toEqual(join(context.srcDir, 'providers'));
expect(context.nodeModulesDir).toEqual(join(process.cwd(), Constants.NODE_MODULES));
expect(context.ionicAngularDir).toEqual(join(process.cwd(), Constants.NODE_MODULES, Constants.IONIC_ANGULAR));
expect(fakeConfig[Constants.ENV_VAR_IONIC_ANGULAR_TEMPLATE_DIR]).toEqual(join(context.ionicAngularDir, 'templates'));
expect(context.platform).toEqual(null);
expect(context.target).toEqual(null);
expect(fakeConfig[Constants.ENV_VAR_IONIC_ANGULAR_ENTRY_POINT]).toEqual(join(context.ionicAngularDir, 'index.js'));
Expand Down
Loading

0 comments on commit 333c7d0

Please sign in to comment.