-
-
Notifications
You must be signed in to change notification settings - Fork 870
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(schematics): support ng add schematics
Closes #888
- Loading branch information
1 parent
00deb58
commit 2dc2f47
Showing
15 changed files
with
601 additions
and
18 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
100 changes: 93 additions & 7 deletions
100
projects/angular-calendar/schematics/ng-add/index.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,103 @@ | ||
import { Tree } from '@angular-devkit/schematics'; | ||
import { SchematicTestRunner } from '@angular-devkit/schematics/testing'; | ||
import { | ||
SchematicTestRunner, | ||
UnitTestTree | ||
} from '@angular-devkit/schematics/testing'; | ||
import { getWorkspace } from '@schematics/angular/utility/config'; | ||
|
||
import * as path from 'path'; | ||
import { expect } from 'chai'; | ||
|
||
import { createTestApp } from '../testing/workspace'; | ||
import { Schema } from './schema'; | ||
import { angularCalendarVersion, momentVersion } from './version-names'; | ||
import { getProjectFromWorkspace, getProjectTargetOptions } from '../utils'; | ||
|
||
const collectionPath = path.join(__dirname, '../collection.json'); | ||
|
||
export interface PackageJson { | ||
dependencies: DependencyMap; | ||
devDependencies: DependencyMap; | ||
} | ||
|
||
export interface DependencyMap { | ||
[dependencyName: string]: string; | ||
} | ||
|
||
const defaultAngularCalendarStylePath = | ||
'node_modules/angular-calendar/css/angular-calendar.css'; | ||
|
||
describe('angular-calendar schematics', () => { | ||
it('works', async () => { | ||
const runner = new SchematicTestRunner('schematics', collectionPath); | ||
const tree = await runner | ||
.runSchematicAsync('ng-add', {}, Tree.empty()) | ||
const projectName = 'angular-calendar-app'; | ||
const defaultOptions = {} as Schema; | ||
let tree: UnitTestTree; | ||
let appTree: UnitTestTree; | ||
let runner: SchematicTestRunner; | ||
let packageJsonPath: string; | ||
let packageJson: PackageJson; | ||
|
||
beforeEach(async () => { | ||
appTree = await createTestApp({ name: projectName }); | ||
runner = new SchematicTestRunner( | ||
'angular-calendar-schematics', | ||
collectionPath | ||
); | ||
packageJsonPath = '/package.json'; | ||
}); | ||
|
||
it('should add angular-calendar to dependencies', async () => { | ||
const { name, version } = { | ||
name: 'angular-calendar', | ||
version: angularCalendarVersion | ||
}; | ||
tree = await runner | ||
.runSchematicAsync('ng-add', defaultOptions, appTree) | ||
.toPromise(); | ||
packageJson = JSON.parse(tree.readContent(packageJsonPath)); | ||
expect(packageJson.dependencies[name]).equal(version); | ||
}); | ||
|
||
it('should add date adapter to dependencies based on option selected ', async () => { | ||
const { name, version } = { name: 'moment', version: momentVersion }; | ||
defaultOptions.dateAdapter = 'moment'; | ||
tree = await runner | ||
.runSchematicAsync('ng-add', defaultOptions, appTree) | ||
.toPromise(); | ||
packageJson = JSON.parse(tree.readContent(packageJsonPath)); | ||
expect(packageJson.dependencies[name]).equal(version); | ||
}); | ||
|
||
it('should schedule install dependencies task', async () => { | ||
await runner | ||
.runSchematicAsync('ng-add', defaultOptions, appTree) | ||
.toPromise(); | ||
const tasks = runner.tasks; | ||
expect(tasks.length).to.equal(1); | ||
}); | ||
|
||
it('should import angular-calendar module to root module', async () => { | ||
const rootModulePath = `/projects/${projectName}/src/app/app.module.ts`; | ||
tree = await runner | ||
.runSchematicAsync('ng-add', defaultOptions, appTree) | ||
.toPromise(); | ||
expect(tree.files).contain(rootModulePath); | ||
|
||
const rootModule = tree.readContent(rootModulePath); | ||
|
||
const calendarModuleImport = `import { CalendarModule } from 'angular-calendar'`; | ||
expect(rootModule).contain(calendarModuleImport); | ||
}); | ||
|
||
it('should add angular-calendar css to architect builder', async () => { | ||
tree = await runner | ||
.runSchematicAsync('ng-add', defaultOptions, appTree) | ||
.toPromise(); | ||
|
||
const workspace = getWorkspace(tree); | ||
const project = getProjectFromWorkspace(workspace); | ||
const styles = getProjectTargetOptions(project, 'build').styles; | ||
const stylesTest = getProjectTargetOptions(project, 'test').styles; | ||
|
||
expect(tree.files).to.deep.equal([]); | ||
expect(styles[0]).to.contains(defaultAngularCalendarStylePath); | ||
expect(stylesTest[0]).to.contains(defaultAngularCalendarStylePath); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,150 @@ | ||
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; | ||
import * as ts from '@schematics/angular/third_party/github.com/Microsoft/TypeScript/lib/typescript'; | ||
import { | ||
Rule, | ||
SchematicContext, | ||
Tree, | ||
chain | ||
} from '@angular-devkit/schematics'; | ||
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; | ||
import { getWorkspace } from '@schematics/angular/utility/config'; | ||
import { getAppModulePath } from '@schematics/angular/utility/ng-ast-utils'; | ||
import { insertImport } from '@schematics/angular/utility/ast-utils'; | ||
import { InsertChange } from '@schematics/angular/utility/change'; | ||
import { | ||
addPackageJsonDependency, | ||
NodeDependency, | ||
NodeDependencyType | ||
} from '@schematics/angular/utility/dependencies'; | ||
|
||
import { | ||
addModuleImportToRootModule, | ||
addStyle, | ||
getSourceFile, | ||
getProjectMainFile, | ||
getProjectFromWorkspace | ||
} from '../utils'; | ||
|
||
import { Schema } from './schema'; | ||
import { | ||
dateFnsVersion, | ||
momentVersion, | ||
angularCalendarVersion | ||
} from './version-names'; | ||
|
||
export default function(options: Schema): Rule { | ||
return (tree: Tree, _context: SchematicContext) => { | ||
return tree; | ||
return chain([ | ||
addPackageJsonDependencies(options), | ||
installPackageJsonDependencies(), | ||
addModuleToImports(options), | ||
addAngularCalendarStyle(options) | ||
]); | ||
} | ||
|
||
function installPackageJsonDependencies(): Rule { | ||
return (host: Tree, context: SchematicContext) => { | ||
context.addTask(new NodePackageInstallTask()); | ||
context.logger.log('info', `Installing angular calendar dependencies...`); | ||
|
||
return host; | ||
}; | ||
} | ||
|
||
function addPackageJsonDependencies(options: Schema): Rule { | ||
return (host: Tree, context: SchematicContext) => { | ||
const dateAdapters: { [key: string]: string } = { | ||
moment: momentVersion, | ||
'date-fns': dateFnsVersion | ||
}; | ||
|
||
const angularCalendarDependency: NodeDependency = nodeDependencyFactory( | ||
'angular-calendar', | ||
angularCalendarVersion | ||
); | ||
const dateAdapterLibrary = options.dateAdapter; | ||
const dateAdapterLibraryDependency: NodeDependency = nodeDependencyFactory( | ||
dateAdapterLibrary, | ||
dateAdapters[dateAdapterLibrary] | ||
); | ||
|
||
addPackageJsonDependency(host, angularCalendarDependency); | ||
context.logger.log( | ||
'info', | ||
`Added "${angularCalendarDependency.name}" into ${angularCalendarDependency.type}` | ||
); | ||
|
||
addPackageJsonDependency(host, dateAdapterLibraryDependency); | ||
context.logger.log( | ||
'info', | ||
`Added "${dateAdapterLibraryDependency.name}" into ${dateAdapterLibraryDependency.type}` | ||
); | ||
|
||
return host; | ||
}; | ||
} | ||
|
||
function nodeDependencyFactory( | ||
packageName: string, | ||
version: string | ||
): NodeDependency { | ||
return { | ||
type: NodeDependencyType.Default, | ||
name: packageName, | ||
version, | ||
overwrite: true | ||
}; | ||
} | ||
|
||
function addModuleToImports(options: Schema): Rule { | ||
return (host: Tree, context: SchematicContext) => { | ||
context.logger.log('info', `Add modules imports options...`); | ||
|
||
const workspace = getWorkspace(host); | ||
const project = getProjectFromWorkspace( | ||
workspace, | ||
options.projectName | ||
? options.projectName | ||
: Object.keys(workspace['projects'])[0] | ||
); | ||
const mainPath = getProjectMainFile(project); | ||
const appModulePath = options.module | ||
? options.module | ||
: getAppModulePath(host, mainPath); | ||
const moduleSource = getSourceFile(host, appModulePath); | ||
const moduleName = `CalendarModule.forRoot({ provide: DateAdapter, useFactory: adapterFactory })`; | ||
const moduleCalendarSrc = 'angular-calendar'; | ||
const PEER_DEPENDENCIES = ['DateAdapter', 'adapterFactory']; | ||
|
||
addModuleImportToRootModule(host, moduleName, moduleCalendarSrc, project); | ||
|
||
const peerDependencyChange1 = insertImport( | ||
moduleSource as ts.SourceFile, | ||
appModulePath, | ||
PEER_DEPENDENCIES[0], | ||
moduleCalendarSrc | ||
) as InsertChange; | ||
|
||
const peerDependencyChange2 = insertImport( | ||
moduleSource as ts.SourceFile, | ||
appModulePath, | ||
PEER_DEPENDENCIES[1], | ||
`${moduleCalendarSrc}/date-adapters/${options.dateAdapter}` | ||
) as InsertChange; | ||
|
||
const recorder = host.beginUpdate(appModulePath); | ||
|
||
recorder.insertLeft(peerDependencyChange1.pos, peerDependencyChange1.toAdd); | ||
recorder.insertLeft(peerDependencyChange2.pos, peerDependencyChange2.toAdd); | ||
host.commitUpdate(recorder); | ||
|
||
return host; | ||
}; | ||
} | ||
|
||
function addAngularCalendarStyle(options: Schema): Rule { | ||
return (host: Tree) => { | ||
const libStylePath = | ||
'node_modules/angular-calendar/css/angular-calendar.css'; | ||
addStyle(host, libStylePath, options.projectName); | ||
return host; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.