mirror of https://github.com/abpframework/abp.git
37 changed files with 627 additions and 4 deletions
@ -0,0 +1,44 @@ |
|||
{ |
|||
"extends": "../../.eslintrc.json", |
|||
"ignorePatterns": [ |
|||
"!**/*" |
|||
], |
|||
"overrides": [ |
|||
{ |
|||
"files": [ |
|||
"*.ts" |
|||
], |
|||
"parserOptions": { |
|||
"project": [ |
|||
"projects/<%= kebab(libraryName) %>/tsconfig.lib.json", |
|||
"projects/<%= kebab(libraryName) %>/tsconfig.spec.json" |
|||
], |
|||
"createDefaultProgram": true |
|||
}, |
|||
"rules": { |
|||
"@angular-eslint/directive-selector": [ |
|||
"error", |
|||
{ |
|||
"type": "attribute", |
|||
"prefix": "lib", |
|||
"style": "camelCase" |
|||
} |
|||
], |
|||
"@angular-eslint/component-selector": [ |
|||
"error", |
|||
{ |
|||
"type": "element", |
|||
"prefix": "lib", |
|||
"style": "kebab-case" |
|||
} |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"files": [ |
|||
"*.html" |
|||
], |
|||
"rules": {} |
|||
} |
|||
] |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", |
|||
"dest": "../../dist/<%= kebab(libraryName) %>/config", |
|||
"lib": { |
|||
"entryFile": "src/public-api.ts" |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
import { ModuleWithProviders, NgModule } from '@angular/core'; |
|||
import { <%= macro(libraryName) %>_ROUTE_PROVIDERS } from './providers/route.provider'; |
|||
|
|||
@NgModule() |
|||
export class <%= pascal(libraryName) %>ConfigModule { |
|||
static forRoot(): ModuleWithProviders<<%= pascal(libraryName) %>ConfigModule> { |
|||
return { |
|||
ngModule: <%= pascal(libraryName) %>ConfigModule, |
|||
providers: [<%= macro(libraryName) %>_ROUTE_PROVIDERS], |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './route-names'; |
|||
@ -0,0 +1,3 @@ |
|||
export const enum e<%= pascal(libraryName) %>RouteNames { |
|||
<%= pascal(libraryName) %> = '<%= pascal(libraryName) %>', |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './route.provider'; |
|||
@ -0,0 +1,26 @@ |
|||
import { eLayoutType, RoutesService } from '@abp/ng.core'; |
|||
import { APP_INITIALIZER } from '@angular/core'; |
|||
import { e<%= pascal(libraryName) %>RouteNames } from '../enums/route-names'; |
|||
|
|||
export const <%= macro(libraryName) %>_ROUTE_PROVIDERS = [ |
|||
{ |
|||
provide: APP_INITIALIZER, |
|||
useFactory: configureRoutes, |
|||
deps: [RoutesService], |
|||
multi: true, |
|||
}, |
|||
]; |
|||
|
|||
export function configureRoutes(routesService: RoutesService) { |
|||
return () => { |
|||
routesService.add([ |
|||
{ |
|||
path: '/<%= kebab(libraryName) %>', |
|||
name: e<%= pascal(libraryName) %>RouteNames.<%= pascal(libraryName) %>, |
|||
iconClass: 'fas fa-book', |
|||
layout: eLayoutType.application, |
|||
order: 3, |
|||
}, |
|||
]); |
|||
}; |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
export * from './enums'; |
|||
export * from './<%= kebab(libraryName) %>-config.module'; |
|||
export * from './providers'; |
|||
@ -0,0 +1,44 @@ |
|||
// Karma configuration file, see link for more information |
|||
// https://karma-runner.github.io/1.0/config/configuration-file.html |
|||
|
|||
module.exports = function (config) { |
|||
config.set({ |
|||
basePath: '', |
|||
frameworks: ['jasmine', '@angular-devkit/build-angular'], |
|||
plugins: [ |
|||
require('karma-jasmine'), |
|||
require('karma-chrome-launcher'), |
|||
require('karma-jasmine-html-reporter'), |
|||
require('karma-coverage'), |
|||
require('@angular-devkit/build-angular/plugins/karma') |
|||
], |
|||
client: { |
|||
jasmine: { |
|||
// you can add configuration options for Jasmine here |
|||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html |
|||
// for example, you can disable the random execution with `random: false` |
|||
// or set a specific seed with `seed: 4321` |
|||
}, |
|||
clearContext: false // leave Jasmine Spec Runner output visible in browser |
|||
}, |
|||
jasmineHtmlReporter: { |
|||
suppressAll: true // removes the duplicated traces |
|||
}, |
|||
coverageReporter: { |
|||
dir: require('path').join(__dirname, '../../coverage/my-project-name'), |
|||
subdir: '.', |
|||
reporters: [ |
|||
{ type: 'html' }, |
|||
{ type: 'text-summary' } |
|||
] |
|||
}, |
|||
reporters: ['progress', 'kjhtml'], |
|||
port: 9876, |
|||
colors: true, |
|||
logLevel: config.LOG_INFO, |
|||
autoWatch: true, |
|||
browsers: ['Chrome'], |
|||
singleRun: false, |
|||
restartOnFileChange: true |
|||
}); |
|||
}; |
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json", |
|||
"dest": "../../dist/<%= kebab(libraryName) %>", |
|||
"lib": { |
|||
"entryFile": "src/public-api.ts" |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
{ |
|||
"name": "@<%= kebab(libraryName) %>/<%= kebab(libraryName) %>", |
|||
"version": "0.0.1", |
|||
"peerDependencies": { |
|||
"@abp/ng.core": "<%= abpVersion %>", |
|||
"@abp/ng.theme.shared": "<%= abpVersion %>" |
|||
}, |
|||
"dependencies": { |
|||
"tslib": "^2.1.0" |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
import { NgModule } from '@angular/core'; |
|||
import { DynamicLayoutComponent } from '@abp/ng.core'; |
|||
import { Routes, RouterModule } from '@angular/router'; |
|||
|
|||
const routes: Routes = [ |
|||
{ |
|||
path: '', |
|||
pathMatch: 'full', |
|||
component: DynamicLayoutComponent, |
|||
children: [], |
|||
}, |
|||
]; |
|||
|
|||
@NgModule({ |
|||
imports: [RouterModule.forChild(routes)], |
|||
exports: [RouterModule], |
|||
}) |
|||
export class <%= pascal(libraryName) %>RoutingModule {} |
|||
@ -0,0 +1,22 @@ |
|||
import { NgModule, NgModuleFactory, ModuleWithProviders } from '@angular/core'; |
|||
import { CoreModule, LazyModuleFactory } from '@abp/ng.core'; |
|||
import { ThemeSharedModule } from '@abp/ng.theme.shared'; |
|||
import { <%= pascal(libraryName) %>RoutingModule } from './<%= kebab(libraryName) %>-routing.module'; |
|||
|
|||
@NgModule({ |
|||
declarations: [], |
|||
imports: [CoreModule, ThemeSharedModule, <%= pascal(libraryName) %>RoutingModule], |
|||
exports: [], |
|||
}) |
|||
export class <%= pascal(libraryName) %>Module { |
|||
static forChild(): ModuleWithProviders<<%= pascal(libraryName) %>Module> { |
|||
return { |
|||
ngModule: <%= pascal(libraryName) %>Module, |
|||
providers: [], |
|||
}; |
|||
} |
|||
|
|||
static forLazy(): NgModuleFactory<<%= pascal(libraryName) %>Module> { |
|||
return new LazyModuleFactory(<%= pascal(libraryName) %>Module.forChild()); |
|||
} |
|||
} |
|||
@ -0,0 +1,4 @@ |
|||
/* |
|||
* Public API Surface of my-project-name |
|||
*/ |
|||
export * from './lib/<%= kebab(libraryName) %>.module'; |
|||
@ -0,0 +1,26 @@ |
|||
// This file is required by karma.conf.js and loads recursively all the .spec and framework files |
|||
|
|||
import 'zone.js'; |
|||
import 'zone.js/testing'; |
|||
import { getTestBed } from '@angular/core/testing'; |
|||
import { |
|||
BrowserDynamicTestingModule, |
|||
platformBrowserDynamicTesting |
|||
} from '@angular/platform-browser-dynamic/testing'; |
|||
|
|||
declare const require: { |
|||
context(path: string, deep?: boolean, filter?: RegExp): { |
|||
keys(): string[]; |
|||
<T>(id: string): T; |
|||
}; |
|||
}; |
|||
|
|||
// First, initialize the Angular testing environment. |
|||
getTestBed().initTestEnvironment( |
|||
BrowserDynamicTestingModule, |
|||
platformBrowserDynamicTesting() |
|||
); |
|||
// Then we find all the tests. |
|||
const context = require.context('./', true, /\.spec\.ts$/); |
|||
// And load the modules. |
|||
context.keys().map(context); |
|||
@ -0,0 +1,20 @@ |
|||
/* To learn more about this file see: https://angular.io/config/tsconfig. */ |
|||
{ |
|||
"extends": "../../tsconfig.prod.json", |
|||
"compilerOptions": { |
|||
"outDir": "../../out-tsc/lib", |
|||
"target": "es2015", |
|||
"declaration": true, |
|||
"declarationMap": true, |
|||
"inlineSources": true, |
|||
"types": [], |
|||
"lib": [ |
|||
"dom", |
|||
"es2018" |
|||
] |
|||
}, |
|||
"exclude": [ |
|||
"src/test.ts", |
|||
"**/*.spec.ts" |
|||
] |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
/* To learn more about this file see: https://angular.io/config/tsconfig. */ |
|||
{ |
|||
"extends": "./tsconfig.lib.json", |
|||
"compilerOptions": { |
|||
"declarationMap": false |
|||
}, |
|||
"angularCompilerOptions": { |
|||
"compilationMode": "partial" |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
/* To learn more about this file see: https://angular.io/config/tsconfig. */ |
|||
{ |
|||
"extends": "../../tsconfig.json", |
|||
"compilerOptions": { |
|||
"outDir": "../../out-tsc/spec", |
|||
"types": [ |
|||
"jasmine" |
|||
] |
|||
}, |
|||
"files": [ |
|||
"src/test.ts" |
|||
], |
|||
"include": [ |
|||
"**/*.spec.ts", |
|||
"**/*.d.ts" |
|||
] |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
{ |
|||
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", |
|||
"dest": "../../dist/<%= kebab(target) %>/<%= kebab(libraryName) %>", |
|||
"lib": { |
|||
"entryFile": "src/public-api.ts" |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
import { ModuleWithProviders, NgModule } from '@angular/core'; |
|||
|
|||
@NgModule() |
|||
export class <%= pascal(target) %><%= pascal(libraryName) %>Module { |
|||
static forRoot(): ModuleWithProviders<<%= pascal(target) %><%= pascal(libraryName) %>Module> { |
|||
return { |
|||
ngModule:<%= pascal(target) %><%= pascal(libraryName) %>Module, |
|||
providers: [] |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './<%= kebab(target) %>-<%= kebab(libraryName) %>.module'; |
|||
@ -0,0 +1 @@ |
|||
export * from './lib/index'; |
|||
@ -0,0 +1,159 @@ |
|||
import { |
|||
applyTemplates, |
|||
chain, |
|||
move, |
|||
noop, |
|||
Rule, |
|||
SchematicContext, |
|||
SchematicsException, |
|||
Tree, |
|||
url, |
|||
} from '@angular-devkit/schematics'; |
|||
import { GenerateLibSchema } from './models/generate-lib-schema'; |
|||
import { |
|||
applyWithOverwrite, |
|||
getWorkspace, |
|||
interpolate, |
|||
isLibrary, |
|||
JSONFile, |
|||
kebab, |
|||
resolveProject, |
|||
updateWorkspace, |
|||
} from '../../utils'; |
|||
import * as cases from '../../utils/text'; |
|||
import { Exception } from '../../enums'; |
|||
import { join, normalize } from '@angular-devkit/core'; |
|||
import { |
|||
ProjectDefinition, |
|||
WorkspaceDefinition, |
|||
} from '@angular-devkit/core/src/workspace/definitions'; |
|||
import { addLibToWorkspaceFile } from '../../utils/angular-schematic/generate-lib'; |
|||
|
|||
export default function (schema: GenerateLibSchema) { |
|||
return async (tree: Tree) => { |
|||
if (schema.override || !(await checkLibExist(schema, tree))) { |
|||
return chain([createLibrary(schema)]); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
async function checkLibExist(options: GenerateLibSchema, tree: Tree) { |
|||
const packageName = kebab(options.packageName); |
|||
if (options.isSecondaryEntrypoint) { |
|||
const lib = await resolveProject(tree, options.target); |
|||
const ngPackagePath = `${lib?.definition.root}/${packageName}/ng-package.json`; |
|||
const packageInfo = tree.read(ngPackagePath); |
|||
if (packageInfo) { |
|||
throw new SchematicsException( |
|||
interpolate(Exception.LibraryAlreadyExists, `${lib.name}/${packageName}`), |
|||
); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
const target = await resolveProject(tree, options.packageName, null); |
|||
if (target) { |
|||
throw new SchematicsException(interpolate(Exception.LibraryAlreadyExists, packageName)); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
function createLibrary(options: GenerateLibSchema): Rule { |
|||
return async (tree: Tree, _context: SchematicContext) => { |
|||
const target = await resolveProject(tree, options.packageName, null); |
|||
if (!target || options.override) { |
|||
if (options.isModuleTemplate) { |
|||
return createLibFromModuleTemplate(tree, options); |
|||
} |
|||
if (options.isSecondaryEntrypoint) { |
|||
return createLibSecondaryEntry(tree, options); |
|||
} |
|||
} else { |
|||
throw new SchematicsException( |
|||
interpolate(Exception.LibraryAlreadyExists, options.packageName), |
|||
); |
|||
} |
|||
}; |
|||
} |
|||
async function resolvePackagesDirFromAngularJson(host: Tree) { |
|||
const workspace = await getWorkspace(host); |
|||
const projectFolder = readFirstLibInAngularJson(workspace); |
|||
return projectFolder?.root?.split('/')?.[0] || 'projects'; |
|||
} |
|||
|
|||
function readFirstLibInAngularJson(workspace: WorkspaceDefinition): ProjectDefinition | undefined { |
|||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|||
const [_, props] = |
|||
Array.from(workspace.projects.entries()).find(([_, value]) => isLibrary(value)) || []; |
|||
return props; |
|||
} |
|||
|
|||
async function createLibFromModuleTemplate(tree: Tree, options: GenerateLibSchema) { |
|||
const packagesDir = await resolvePackagesDirFromAngularJson(tree); |
|||
const packageJson = JSON.parse(tree.read('./package.json')!.toString()); |
|||
const abpVersion = packageJson.dependencies['@abp/ng.core']; |
|||
|
|||
return chain([ |
|||
applyWithOverwrite(url('./files-package'), [ |
|||
applyTemplates({ |
|||
...cases, |
|||
libraryName: options.packageName, |
|||
abpVersion, |
|||
}), |
|||
move(normalize(packagesDir)), |
|||
]), |
|||
addLibToWorkspaceIfNotExist(options.packageName, packagesDir), |
|||
]); |
|||
} |
|||
|
|||
export function addLibToWorkspaceIfNotExist(name: string, packagesDir: string): Rule { |
|||
return async (tree: Tree, _: SchematicContext) => { |
|||
const workspace = await getWorkspace(tree); |
|||
const packageName = kebab(name); |
|||
const isProjectExist = workspace.projects.has(packageName); |
|||
|
|||
const projectRoot = join(normalize(packagesDir), packageName); |
|||
const pathImportLib = `${packagesDir}/${packageName}`; |
|||
|
|||
return chain([ |
|||
isProjectExist |
|||
? updateWorkspace(w => { |
|||
w.projects.delete(packageName); |
|||
}) |
|||
: noop(), |
|||
addLibToWorkspaceFile(projectRoot, packageName), |
|||
updateTsConfig(packageName, pathImportLib), |
|||
]); |
|||
}; |
|||
} |
|||
|
|||
export function updateTsConfig(packageName: string, path: string) { |
|||
return (host: Tree) => { |
|||
const files = ['tsconfig.json', 'tsconfig.app.json', 'tsconfig.base.json']; |
|||
const tsConfig = files.find(f => host.exists(f)); |
|||
if (!tsConfig) { |
|||
return host; |
|||
} |
|||
|
|||
const file = new JSONFile(host, tsConfig); |
|||
const jsonPath = ['compilerOptions', 'paths', packageName]; |
|||
file.modify(jsonPath, [`${path}/src/public-api.ts`]); |
|||
}; |
|||
} |
|||
|
|||
export async function createLibSecondaryEntry(tree: Tree, options: GenerateLibSchema) { |
|||
const targetLib = await resolveProject(tree, options.target); |
|||
const packageName = `${kebab(targetLib.name)}/${kebab(options.packageName)}`; |
|||
const importPath = `${targetLib.definition.root}/${kebab(options.packageName)}`; |
|||
return chain([ |
|||
applyWithOverwrite(url('./files-secondary-entrypoint'), [ |
|||
applyTemplates({ |
|||
...cases, |
|||
libraryName: options.packageName, |
|||
target: targetLib.name, |
|||
}), |
|||
move(normalize(targetLib.definition.root)), |
|||
updateTsConfig(packageName, importPath), |
|||
]), |
|||
]); |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
export interface GenerateLibSchema { |
|||
/** |
|||
* Angular package name will create |
|||
*/ |
|||
packageName: string; |
|||
|
|||
/** |
|||
* İs the package a library or a library module |
|||
*/ |
|||
isSecondaryEntrypoint: boolean; |
|||
|
|||
isModuleTemplate: boolean; |
|||
|
|||
override: boolean; |
|||
|
|||
target: string; |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './generate-lib-schema'; |
|||
@ -0,0 +1,36 @@ |
|||
{ |
|||
"$schema": "http://json-schema.org/schema", |
|||
"$id": "SchematicsABPModuleTemplateCreator", |
|||
"title": "ABP Module Template Generator API Schema", |
|||
"type": "object", |
|||
"properties": { |
|||
"packageName": { |
|||
"description": "The name of the package will create", |
|||
"type": "string", |
|||
"$default": { |
|||
"$source": "argv", |
|||
"index": 0 |
|||
}, |
|||
"x-prompt": "Please enter the package name will create" |
|||
}, |
|||
"isSecondaryEntrypoint": { |
|||
"description": "Is secondary entrypoint", |
|||
"type": "boolean", |
|||
"$default": false, |
|||
"x-prompt": "Is secondary entrypoint?" |
|||
}, |
|||
"isModuleTemplate": { |
|||
"description": "Is module template", |
|||
"type": "boolean", |
|||
"$default": true, |
|||
"x-prompt": "Is module template?" |
|||
}, |
|||
"override": { |
|||
"description": "Override existing files", |
|||
"type": "boolean", |
|||
"$default": false, |
|||
"x-prompt": "Override existing files?" |
|||
} |
|||
}, |
|||
"required": [] |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export const NOT_FOUND_VALUE = Symbol('NOT_FOUND_VALUE'); |
|||
@ -0,0 +1,52 @@ |
|||
import { Rule, Tree } from '@angular-devkit/schematics'; |
|||
import { Builders, JSONFile, ProjectType, updateWorkspace } from '../angular'; |
|||
|
|||
export function updateTsConfig(packageName: string, ...paths: string[]) { |
|||
return (host: Tree) => { |
|||
if (!host.exists('tsconfig.json')) { |
|||
return host; |
|||
} |
|||
|
|||
const file = new JSONFile(host, 'tsconfig.json'); |
|||
const jsonPath = ['compilerOptions', 'paths', packageName]; |
|||
const value = file.get(jsonPath); |
|||
file.modify(jsonPath, Array.isArray(value) ? [...value, ...paths] : paths); |
|||
}; |
|||
} |
|||
|
|||
export function addLibToWorkspaceFile(projectRoot: string, projectName: string): Rule { |
|||
return updateWorkspace(workspace => { |
|||
workspace.projects.add({ |
|||
name: projectName, |
|||
root: projectRoot, |
|||
sourceRoot: `${projectRoot}/src`, |
|||
projectType: ProjectType.Library, |
|||
prefix: 'lib', |
|||
targets: { |
|||
build: { |
|||
builder: Builders.NgPackagr, |
|||
defaultConfiguration: 'production', |
|||
options: { |
|||
project: `${projectRoot}/ng-package.json`, |
|||
}, |
|||
configurations: { |
|||
production: { |
|||
tsConfig: `${projectRoot}/tsconfig.lib.prod.json`, |
|||
}, |
|||
development: { |
|||
tsConfig: `${projectRoot}/tsconfig.lib.json`, |
|||
}, |
|||
}, |
|||
}, |
|||
test: { |
|||
builder: Builders.Karma, |
|||
options: { |
|||
main: `${projectRoot}/src/test.ts`, |
|||
tsConfig: `${projectRoot}/tsconfig.spec.json`, |
|||
karmaConfig: `${projectRoot}/karma.conf.js`, |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
}); |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from './generate-lib'; |
|||
@ -0,0 +1,9 @@ |
|||
import { Tree } from '@angular-devkit/schematics'; |
|||
import { resolveProject } from "./workspace"; |
|||
export async function createTargetLibIfNotExist(tree: Tree, target: string) { |
|||
const lib = await resolveProject(tree, target, null); |
|||
|
|||
if (!lib) { |
|||
|
|||
} |
|||
} |
|||
Loading…
Reference in new issue