-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add ng-add schematics support (#19)
- Loading branch information
1 parent
a8b7747
commit c9aa42a
Showing
8 changed files
with
238 additions
and
5 deletions.
There are no files selected for viewing
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
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
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 |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"$schema": "./node_modules/@angular-devkit/schematics/collection-schema.json", | ||
"schematics": { | ||
"ng-add": { | ||
"description": "Adds ngx-apexcharts to the application", | ||
"factory": "./ng-add/index", | ||
"schema": "./ng-add/schema.json", | ||
"aliases": ["ngx-apexcharts-shell", "install"] | ||
} | ||
} | ||
} |
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 |
---|---|---|
@@ -0,0 +1,128 @@ | ||
import { Rule, SchematicContext, Tree } from '@angular-devkit/schematics'; | ||
import { NodePackageInstallTask } from '@angular-devkit/schematics/tasks'; | ||
import { getAppModulePath } from '@schematics/angular/utility/ng-ast-utils'; | ||
import { ProjectDefinition, WorkspaceDefinition, getWorkspace } from '@schematics/angular/utility/workspace'; | ||
import { SchematicsException } from '@angular-devkit/schematics'; | ||
import { addSymbolToNgModuleMetadata, insertImport } from '@schematics/angular/utility/ast-utils'; | ||
import { ProjectType } from '@schematics/angular/utility/workspace-models'; | ||
import { InsertChange } from '@schematics/angular/utility/change'; | ||
|
||
import * as ts from '@schematics/angular/third_party/github.com/Microsoft/TypeScript/lib/typescript'; | ||
|
||
export default function(options: NgxApexchartNgAddSchema): Rule { | ||
return async (_host: Tree, _context: SchematicContext) => { | ||
const workspace = await getWorkspace(_host); | ||
const project = getProjectFromWorkspace(workspace, options.project); | ||
|
||
if (!project) { | ||
throw new Error(`can not find ${options.project} angular project`); | ||
} | ||
if (project.extensions.projectType === ProjectType.Application) { | ||
addNgxPendoModule(project as ProjectDefinition, _host); | ||
} | ||
addPackageToPackageJson(_host, 'ngx-apexcharts', '~0.2.0'); | ||
addPackageToPackageJson(_host, 'apexcharts', '~3.36.3'); | ||
_context.logger.log('info', '✅️ Added "ngx-apexcharts"'); | ||
_context.addTask(new NodePackageInstallTask()); | ||
}; | ||
} | ||
|
||
function addNgxPendoModule(project: ProjectDefinition, _host: Tree): void { | ||
if (!project) { | ||
return; | ||
} | ||
const appModulePath = getAppModulePath(_host, getProjectMainFile(project)); | ||
const sourceFile = readIntoSourceFile(_host, appModulePath); | ||
const importPath = 'ngx-apexcharts'; | ||
const recorder = _host.beginUpdate(appModulePath); | ||
const moduleName = 'NgxApexchartsModule'; | ||
const importChange = insertImport(sourceFile, appModulePath, moduleName, importPath); | ||
if (importChange instanceof InsertChange) { | ||
recorder.insertLeft(importChange.pos, importChange.toAdd); | ||
} | ||
const ngModuleName = 'NgxApexchartsModule'; | ||
const ngModuleChanges = addSymbolToNgModuleMetadata(sourceFile, appModulePath, 'imports', ngModuleName, null); | ||
for (const change of ngModuleChanges) { | ||
if (change instanceof InsertChange) { | ||
recorder.insertLeft(change.pos, change.toAdd); | ||
} | ||
} | ||
_host.commitUpdate(recorder); | ||
} | ||
|
||
|
||
function readIntoSourceFile(host: Tree, modulePath: string): ts.SourceFile { | ||
const text = host.read(modulePath); | ||
if (text === null) { | ||
throw new SchematicsException(`File ${modulePath} does noot exist`); | ||
} | ||
|
||
const sourceText = text.toString('utf-8'); | ||
return ts.createSourceFile(modulePath, sourceText, ts.ScriptTarget.Latest, true); | ||
} | ||
|
||
function addPackageToPackageJson(host: Tree, pkg: string, version: string): Tree { | ||
if (host.exists('package.json')) { | ||
const sourceText = host.read('package.json')!.toString('utf-8'); | ||
|
||
|
||
const json = JSON.parse(sourceText); | ||
|
||
if (!json.dependencies) { | ||
json.dependencies = {}; | ||
} | ||
|
||
if (!json.dependencies[pkg]) { | ||
json.dependencies[pkg] = version; | ||
json.dependencies = sortObjectByKeys(json.dependencies); | ||
} | ||
|
||
host.overwrite('package.json', JSON.stringify(json, null, 2)); | ||
} | ||
|
||
return host; | ||
} | ||
|
||
function sortObjectByKeys(obj: any): any { | ||
return Object.keys(obj) | ||
.sort() | ||
.reduce((result: any, key: any) => (result[key] = obj[key]) && result, {}); | ||
} | ||
|
||
// eslint-disable-next-line | ||
function getProjectTargetOptions(project: ProjectDefinition, buildTarget: string) { | ||
if (project?.targets?.get(buildTarget)?.options) { | ||
return project!.targets!.get(buildTarget)!.options; | ||
} | ||
|
||
throw new Error(`Cannot determine project target configuration for: ${buildTarget}.`); | ||
} | ||
|
||
function getProjectMainFile(project: ProjectDefinition): string { | ||
const buildOptions = getProjectTargetOptions(project, 'build'); | ||
if (!buildOptions || !buildOptions.main) { | ||
throw new SchematicsException(`Could not find the project main file inside of the ` + | ||
`workspace config (${project.sourceRoot})`); | ||
} | ||
|
||
return buildOptions.main.toString(); | ||
} | ||
|
||
export function getProjectFromWorkspace( | ||
workspace: WorkspaceDefinition, | ||
projectName: string | undefined, | ||
): ProjectDefinition { | ||
if (!projectName) { | ||
// TODO(crisbeto): some schematics APIs have the project name as optional so for now it's | ||
// simpler to allow undefined and checking it at runtime. Eventually we should clean this up. | ||
throw new SchematicsException('Project name is required.'); | ||
} | ||
|
||
const project = workspace.projects.get(projectName); | ||
|
||
if (!project) { | ||
throw new SchematicsException(`Could not find project in workspace: ${projectName}`); | ||
} | ||
|
||
return project; | ||
} |
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 |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/** | ||
* Ngx apexchart ng-add schematic | ||
* Generate a file of JavaScript | ||
*/ | ||
declare interface NgxApexchartNgAddSchema { | ||
/** | ||
* Name of the project. | ||
*/ | ||
project?: string; | ||
} |
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 |
---|---|---|
@@ -0,0 +1,16 @@ | ||
{ | ||
"$schema": "http://json-schema.org/schema", | ||
"$id": "ngx-apexcharts-ng-add-schema", | ||
"title": "Ngx Apexcharts ng-add schematic", | ||
"description": "Generate a file of JavaScript", | ||
"type": "object", | ||
"properties": { | ||
"project": { | ||
"type": "string", | ||
"description": "Name of the project.", | ||
"$default": { | ||
"$source": "projectName" | ||
} | ||
} | ||
} | ||
} |
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 |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"compilerOptions": { | ||
"baseUrl": "tsconfig", | ||
"lib": [ | ||
"es2018", | ||
"dom" | ||
], | ||
"declaration": true, | ||
"module": "commonjs", | ||
"moduleResolution": "node", | ||
"noEmitOnError": true, | ||
"noFallthroughCasesInSwitch": true, | ||
"noImplicitAny": true, | ||
"noImplicitThis": true, | ||
"noUnusedParameters": true, | ||
"noUnusedLocals": false, | ||
"rootDir": "./", | ||
"outDir": "dist", | ||
"skipDefaultLibCheck": true, | ||
"skipLibCheck": true, | ||
"sourceMap": false, | ||
"strictNullChecks": true, | ||
"target": "es6", | ||
"types": [ | ||
"jasmine", | ||
"node" | ||
] | ||
}, | ||
"include": [ | ||
"./**/*", | ||
"**.json" | ||
] | ||
} |
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