mirror of https://github.com/abpframework/abp.git
committed by
GitHub
69 changed files with 967 additions and 1212 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.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'; |
|||
@ -1,5 +1,5 @@ |
|||
**DISCLAIMER** |
|||
|
|||
This directory is a direct copy of https://github.com/angular/angular-cli/tree/master/packages/schematics/angular/utility and is used under terms and permissions by the MIT license granted by Google, Inc. |
|||
This directory is a direct copy of https://github.com/angular/angular-cli/tree/d091bb0c7b76a73e1b7d4b5690c532c68452b1d6 and is used under terms and permissions by the MIT license granted by Google, Inc. |
|||
|
|||
All credits go to Angular team for building these utilities. |
|||
|
|||
@ -1,541 +0,0 @@ |
|||
/* eslint-disable @typescript-eslint/ban-types */ |
|||
/** |
|||
* @license |
|||
* Copyright Google Inc. All Rights Reserved. |
|||
* |
|||
* Use of this source code is governed by an MIT-style license that can be |
|||
* found in the LICENSE file at https://angular.io/license
|
|||
*/ |
|||
import { JsonParseMode, parseJson } from '@angular-devkit/core'; |
|||
import { Rule, SchematicContext, SchematicsException, Tree } from '@angular-devkit/schematics'; |
|||
import { ProjectType, WorkspaceProject, WorkspaceSchema } from './workspace-models'; |
|||
|
|||
// The interfaces below are generated from the Angular CLI configuration schema
|
|||
// https://github.com/angular/angular-cli/blob/master/packages/@angular/cli/lib/config/schema.json
|
|||
export interface AppConfig { |
|||
/** |
|||
* Name of the app. |
|||
*/ |
|||
name?: string; |
|||
/** |
|||
* Directory where app files are placed. |
|||
*/ |
|||
appRoot?: string; |
|||
/** |
|||
* The root directory of the app. |
|||
*/ |
|||
root?: string; |
|||
/** |
|||
* The output directory for build results. |
|||
*/ |
|||
outDir?: string; |
|||
/** |
|||
* List of application assets. |
|||
*/ |
|||
assets?: ( |
|||
| string |
|||
| { |
|||
/** |
|||
* The pattern to match. |
|||
*/ |
|||
glob?: string; |
|||
/** |
|||
* The dir to search within. |
|||
*/ |
|||
input?: string; |
|||
/** |
|||
* The output path (relative to the outDir). |
|||
*/ |
|||
output?: string; |
|||
} |
|||
)[]; |
|||
/** |
|||
* URL where files will be deployed. |
|||
*/ |
|||
deployUrl?: string; |
|||
/** |
|||
* Base url for the application being built. |
|||
*/ |
|||
baseHref?: string; |
|||
/** |
|||
* The runtime platform of the app. |
|||
*/ |
|||
platform?: 'browser' | 'server'; |
|||
/** |
|||
* The name of the start HTML file. |
|||
*/ |
|||
index?: string; |
|||
/** |
|||
* The name of the main entry-point file. |
|||
*/ |
|||
main?: string; |
|||
/** |
|||
* The name of the polyfills file. |
|||
*/ |
|||
polyfills?: string; |
|||
/** |
|||
* The name of the test entry-point file. |
|||
*/ |
|||
test?: string; |
|||
/** |
|||
* The name of the TypeScript configuration file. |
|||
*/ |
|||
tsconfig?: string; |
|||
/** |
|||
* The name of the TypeScript configuration file for unit tests. |
|||
*/ |
|||
testTsconfig?: string; |
|||
/** |
|||
* The prefix to apply to generated selectors. |
|||
*/ |
|||
prefix?: string; |
|||
/** |
|||
* Experimental support for a service worker from @angular/service-worker. |
|||
*/ |
|||
serviceWorker?: boolean; |
|||
/** |
|||
* Global styles to be included in the build. |
|||
*/ |
|||
styles?: ( |
|||
| string |
|||
| { |
|||
input?: string; |
|||
[name: string]: any; |
|||
} |
|||
)[]; |
|||
/** |
|||
* Options to pass to style preprocessors |
|||
*/ |
|||
stylePreprocessorOptions?: { |
|||
/** |
|||
* Paths to include. Paths will be resolved to project root. |
|||
*/ |
|||
includePaths?: string[]; |
|||
}; |
|||
/** |
|||
* Global scripts to be included in the build. |
|||
*/ |
|||
scripts?: ( |
|||
| string |
|||
| { |
|||
input: string; |
|||
[name: string]: any; |
|||
} |
|||
)[]; |
|||
/** |
|||
* Source file for environment config. |
|||
*/ |
|||
environmentSource?: string; |
|||
/** |
|||
* Name and corresponding file for environment config. |
|||
*/ |
|||
environments?: { |
|||
[name: string]: any; |
|||
}; |
|||
appShell?: { |
|||
app: string; |
|||
route: string; |
|||
}; |
|||
budgets?: { |
|||
/** |
|||
* The type of budget |
|||
*/ |
|||
type?: 'bundle' | 'initial' | 'allScript' | 'all' | 'anyScript' | 'any' | 'anyComponentStyle'; |
|||
/** |
|||
* The name of the bundle |
|||
*/ |
|||
name?: string; |
|||
/** |
|||
* The baseline size for comparison. |
|||
*/ |
|||
baseline?: string; |
|||
/** |
|||
* The maximum threshold for warning relative to the baseline. |
|||
*/ |
|||
maximumWarning?: string; |
|||
/** |
|||
* The maximum threshold for error relative to the baseline. |
|||
*/ |
|||
maximumError?: string; |
|||
/** |
|||
* The minimum threshold for warning relative to the baseline. |
|||
*/ |
|||
minimumWarning?: string; |
|||
/** |
|||
* The minimum threshold for error relative to the baseline. |
|||
*/ |
|||
minimumError?: string; |
|||
/** |
|||
* The threshold for warning relative to the baseline (min & max). |
|||
*/ |
|||
warning?: string; |
|||
/** |
|||
* The threshold for error relative to the baseline (min & max). |
|||
*/ |
|||
error?: string; |
|||
}[]; |
|||
} |
|||
|
|||
export interface CliConfig { |
|||
$schema?: string; |
|||
/** |
|||
* The global configuration of the project. |
|||
*/ |
|||
project?: { |
|||
/** |
|||
* The name of the project. |
|||
*/ |
|||
name?: string; |
|||
/** |
|||
* Whether or not this project was ejected. |
|||
*/ |
|||
ejected?: boolean; |
|||
}; |
|||
/** |
|||
* Properties of the different applications in this project. |
|||
*/ |
|||
apps?: AppConfig[]; |
|||
/** |
|||
* Configuration for end-to-end tests. |
|||
*/ |
|||
e2e?: { |
|||
protractor?: { |
|||
/** |
|||
* Path to the config file. |
|||
*/ |
|||
config?: string; |
|||
}; |
|||
}; |
|||
/** |
|||
* Properties to be passed to TSLint. |
|||
*/ |
|||
lint?: { |
|||
/** |
|||
* File glob(s) to lint. |
|||
*/ |
|||
files?: string | string[]; |
|||
/** |
|||
* Location of the tsconfig.json project file. |
|||
* Will also use as files to lint if 'files' property not present. |
|||
*/ |
|||
project: string; |
|||
/** |
|||
* Location of the tslint.json configuration. |
|||
*/ |
|||
tslintConfig?: string; |
|||
/** |
|||
* File glob(s) to ignore. |
|||
*/ |
|||
exclude?: string | string[]; |
|||
}[]; |
|||
/** |
|||
* Configuration for unit tests. |
|||
*/ |
|||
test?: { |
|||
karma?: { |
|||
/** |
|||
* Path to the karma config file. |
|||
*/ |
|||
config?: string; |
|||
}; |
|||
codeCoverage?: { |
|||
/** |
|||
* Globs to exclude from code coverage. |
|||
*/ |
|||
exclude?: string[]; |
|||
}; |
|||
}; |
|||
/** |
|||
* Specify the default values for generating. |
|||
*/ |
|||
defaults?: { |
|||
/** |
|||
* The file extension to be used for style files. |
|||
*/ |
|||
styleExt?: string; |
|||
/** |
|||
* How often to check for file updates. |
|||
*/ |
|||
poll?: number; |
|||
/** |
|||
* Use lint to fix files after generation |
|||
*/ |
|||
lintFix?: boolean; |
|||
/** |
|||
* Options for generating a class. |
|||
*/ |
|||
class?: { |
|||
/** |
|||
* Specifies if a spec file is generated. |
|||
*/ |
|||
spec?: boolean; |
|||
}; |
|||
/** |
|||
* Options for generating a component. |
|||
*/ |
|||
component?: { |
|||
/** |
|||
* Flag to indicate if a directory is created. |
|||
*/ |
|||
flat?: boolean; |
|||
/** |
|||
* Specifies if a spec file is generated. |
|||
*/ |
|||
spec?: boolean; |
|||
/** |
|||
* Specifies if the style will be in the ts file. |
|||
*/ |
|||
inlineStyle?: boolean; |
|||
/** |
|||
* Specifies if the template will be in the ts file. |
|||
*/ |
|||
inlineTemplate?: boolean; |
|||
/** |
|||
* Specifies the view encapsulation strategy. |
|||
*/ |
|||
viewEncapsulation?: 'Emulated' | 'Native' | 'None'; |
|||
/** |
|||
* Specifies the change detection strategy. |
|||
*/ |
|||
changeDetection?: 'Default' | 'OnPush'; |
|||
}; |
|||
/** |
|||
* Options for generating a directive. |
|||
*/ |
|||
directive?: { |
|||
/** |
|||
* Flag to indicate if a directory is created. |
|||
*/ |
|||
flat?: boolean; |
|||
/** |
|||
* Specifies if a spec file is generated. |
|||
*/ |
|||
spec?: boolean; |
|||
}; |
|||
/** |
|||
* Options for generating a guard. |
|||
*/ |
|||
guard?: { |
|||
/** |
|||
* Flag to indicate if a directory is created. |
|||
*/ |
|||
flat?: boolean; |
|||
/** |
|||
* Specifies if a spec file is generated. |
|||
*/ |
|||
spec?: boolean; |
|||
}; |
|||
/** |
|||
* Options for generating an interface. |
|||
*/ |
|||
interface?: { |
|||
/** |
|||
* Prefix to apply to interface names. (i.e. I) |
|||
*/ |
|||
prefix?: string; |
|||
}; |
|||
/** |
|||
* Options for generating a module. |
|||
*/ |
|||
module?: { |
|||
/** |
|||
* Flag to indicate if a directory is created. |
|||
*/ |
|||
flat?: boolean; |
|||
/** |
|||
* Specifies if a spec file is generated. |
|||
*/ |
|||
spec?: boolean; |
|||
}; |
|||
/** |
|||
* Options for generating a pipe. |
|||
*/ |
|||
pipe?: { |
|||
/** |
|||
* Flag to indicate if a directory is created. |
|||
*/ |
|||
flat?: boolean; |
|||
/** |
|||
* Specifies if a spec file is generated. |
|||
*/ |
|||
spec?: boolean; |
|||
}; |
|||
/** |
|||
* Options for generating a service. |
|||
*/ |
|||
service?: { |
|||
/** |
|||
* Flag to indicate if a directory is created. |
|||
*/ |
|||
flat?: boolean; |
|||
/** |
|||
* Specifies if a spec file is generated. |
|||
*/ |
|||
spec?: boolean; |
|||
}; |
|||
/** |
|||
* Properties to be passed to the build command. |
|||
*/ |
|||
build?: { |
|||
/** |
|||
* Output sourcemaps. |
|||
*/ |
|||
sourcemaps?: boolean; |
|||
/** |
|||
* Base url for the application being built. |
|||
*/ |
|||
baseHref?: string; |
|||
/** |
|||
* The ssl key used by the server. |
|||
*/ |
|||
progress?: boolean; |
|||
/** |
|||
* Enable and define the file watching poll time period (milliseconds). |
|||
*/ |
|||
poll?: number; |
|||
/** |
|||
* Delete output path before build. |
|||
*/ |
|||
deleteOutputPath?: boolean; |
|||
/** |
|||
* Do not use the real path when resolving modules. |
|||
*/ |
|||
preserveSymlinks?: boolean; |
|||
/** |
|||
* Show circular dependency warnings on builds. |
|||
*/ |
|||
showCircularDependencies?: boolean; |
|||
/** |
|||
* Use a separate bundle containing code used across multiple bundles. |
|||
*/ |
|||
commonChunk?: boolean; |
|||
/** |
|||
* Use file name for lazy loaded chunks. |
|||
*/ |
|||
namedChunks?: boolean; |
|||
}; |
|||
/** |
|||
* Properties to be passed to the serve command. |
|||
*/ |
|||
serve?: { |
|||
/** |
|||
* The port the application will be served on. |
|||
*/ |
|||
port?: number; |
|||
/** |
|||
* The host the application will be served on. |
|||
*/ |
|||
host?: string; |
|||
/** |
|||
* Enables ssl for the application. |
|||
*/ |
|||
ssl?: boolean; |
|||
/** |
|||
* The ssl key used by the server. |
|||
*/ |
|||
sslKey?: string; |
|||
/** |
|||
* The ssl certificate used by the server. |
|||
*/ |
|||
sslCert?: string; |
|||
/** |
|||
* Proxy configuration file. |
|||
*/ |
|||
proxyConfig?: string; |
|||
}; |
|||
/** |
|||
* Properties about schematics. |
|||
*/ |
|||
schematics?: { |
|||
/** |
|||
* The schematics collection to use. |
|||
*/ |
|||
collection?: string; |
|||
/** |
|||
* The new app schematic. |
|||
*/ |
|||
newApp?: string; |
|||
}; |
|||
}; |
|||
/** |
|||
* Specify which package manager tool to use. |
|||
*/ |
|||
packageManager?: 'npm' | 'cnpm' | 'yarn' | 'default'; |
|||
/** |
|||
* Allow people to disable console warnings. |
|||
*/ |
|||
warnings?: { |
|||
versionMismatch?: boolean; |
|||
}; |
|||
} |
|||
|
|||
export function getWorkspacePath(host: Tree): string { |
|||
const possibleFiles = ['/angular.json', '/.angular.json']; |
|||
const path = possibleFiles.filter(path => host.exists(path))[0]; |
|||
|
|||
return path; |
|||
} |
|||
|
|||
export function getWorkspaceSchema(host: Tree): WorkspaceSchema { |
|||
const path = getWorkspacePath(host); |
|||
const configBuffer = host.read(path); |
|||
if (configBuffer === null) { |
|||
throw new SchematicsException(`Could not find (${path})`); |
|||
} |
|||
const content = configBuffer.toString(); |
|||
|
|||
return parseJson(content, JsonParseMode.Loose) as {} as WorkspaceSchema; |
|||
} |
|||
|
|||
export function addProjectToWorkspace<TProjectType extends ProjectType = ProjectType.Application>( |
|||
workspace: WorkspaceSchema, |
|||
name: string, |
|||
project: WorkspaceProject<TProjectType>, |
|||
): Rule { |
|||
return (_host: Tree, _context: SchematicContext) => { |
|||
if (workspace.projects[name]) { |
|||
throw new Error(`Project '${name}' already exists in workspace.`); |
|||
} |
|||
|
|||
// Add project to workspace.
|
|||
workspace.projects[name] = project; |
|||
|
|||
if (!workspace.defaultProject && Object.keys(workspace.projects).length === 1) { |
|||
// Make the new project the default one.
|
|||
workspace.defaultProject = name; |
|||
} |
|||
|
|||
return updateWorkspaceSchema(workspace); |
|||
}; |
|||
} |
|||
|
|||
export function updateWorkspaceSchema(workspace: WorkspaceSchema): Rule { |
|||
return (host: Tree, _context: SchematicContext) => { |
|||
host.overwrite(getWorkspacePath(host), JSON.stringify(workspace, null, 2)); |
|||
}; |
|||
} |
|||
|
|||
export const configPath = '/.angular-cli.json'; |
|||
|
|||
export function getConfig(host: Tree): CliConfig { |
|||
const configBuffer = host.read(configPath); |
|||
if (configBuffer === null) { |
|||
throw new SchematicsException('Could not find .angular-cli.json'); |
|||
} |
|||
|
|||
const config = parseJson(configBuffer.toString(), JsonParseMode.Loose) as {} as CliConfig; |
|||
|
|||
return config; |
|||
} |
|||
|
|||
export function getAppFromConfig(config: CliConfig, appIndexOrName: string): AppConfig | null { |
|||
if (!config.apps) { |
|||
return null; |
|||
} |
|||
|
|||
if (parseInt(appIndexOrName) >= 0) { |
|||
return config.apps[parseInt(appIndexOrName)]; |
|||
} |
|||
|
|||
return config.apps.filter(app => app.name === appIndexOrName)[0]; |
|||
} |
|||
@ -0,0 +1,61 @@ |
|||
/** |
|||
* @license |
|||
* Copyright Google LLC All Rights Reserved. |
|||
* |
|||
* Use of this source code is governed by an MIT-style license that can be |
|||
* found in the LICENSE file at https://angular.io/license
|
|||
*/ |
|||
|
|||
import { strings } from '@angular-devkit/core'; |
|||
import { |
|||
apply, |
|||
applyTemplates, |
|||
chain, |
|||
filter, |
|||
mergeWith, |
|||
move, |
|||
noop, |
|||
Rule, |
|||
Tree, |
|||
url, |
|||
} from '@angular-devkit/schematics'; |
|||
import { parseName } from './parse-name'; |
|||
import { createDefaultPath } from './workspace'; |
|||
|
|||
export interface GenerateFromFilesOptions { |
|||
flat?: boolean; |
|||
name: string; |
|||
path?: string; |
|||
prefix?: string; |
|||
project?: string; |
|||
skipTests?: boolean; |
|||
} |
|||
|
|||
export function generateFromFiles( |
|||
options: GenerateFromFilesOptions, |
|||
extraTemplateValues: Record<string, string | ((v: string) => string)> = {}, |
|||
): Rule { |
|||
return async (host: Tree) => { |
|||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|||
// @ts-ignore
|
|||
options.path ??= await createDefaultPath(host, options.project as string); |
|||
options.prefix ??= ''; |
|||
options.flat ??= true; |
|||
|
|||
const parsedPath = parseName(options.path!, options.name); |
|||
options.name = parsedPath.name; |
|||
options.path = parsedPath.path; |
|||
|
|||
const templateSource = apply(url('./files'), [ |
|||
options.skipTests ? filter(path => !path.endsWith('.spec.ts.template')) : noop(), |
|||
applyTemplates({ |
|||
...strings, |
|||
...options, |
|||
...extraTemplateValues, |
|||
}), |
|||
move(parsedPath.path + (options.flat ? '' : '/' + strings.dasherize(options.name))), |
|||
]); |
|||
|
|||
return chain([mergeWith(templateSource)]); |
|||
}; |
|||
} |
|||
@ -1,17 +1,13 @@ |
|||
export * from './ast-utils'; |
|||
export * from './change'; |
|||
export * from './config'; |
|||
export * from './dependencies'; |
|||
export * from './find-module'; |
|||
export * from './generate-from-files'; |
|||
export * from './json-file'; |
|||
export * from './json-utils'; |
|||
export * from './latest-versions'; |
|||
export * from './lint-fix'; |
|||
export * from './ng-ast-utils'; |
|||
export * from './parse-name'; |
|||
export * from './paths'; |
|||
export * from './project-targets'; |
|||
export * from './tsconfig'; |
|||
export * from './validation'; |
|||
export * from './workspace'; |
|||
export * from './workspace-models'; |
|||
|
|||
@ -1,221 +0,0 @@ |
|||
/** |
|||
* @license |
|||
* Copyright Google Inc. All Rights Reserved. |
|||
* |
|||
* Use of this source code is governed by an MIT-style license that can be |
|||
* found in the LICENSE file at https://angular.io/license
|
|||
*/ |
|||
import { |
|||
JsonAstArray, |
|||
JsonAstKeyValue, |
|||
JsonAstNode, |
|||
JsonAstObject, |
|||
JsonValue, |
|||
} from '@angular-devkit/core'; |
|||
import { UpdateRecorder } from '@angular-devkit/schematics'; |
|||
|
|||
export function appendPropertyInAstObject( |
|||
recorder: UpdateRecorder, |
|||
node: JsonAstObject, |
|||
propertyName: string, |
|||
value: JsonValue, |
|||
indent: number, |
|||
) { |
|||
const indentStr = _buildIndent(indent); |
|||
let index = node.start.offset + 1; |
|||
if (node.properties.length > 0) { |
|||
// Insert comma.
|
|||
const last = node.properties[node.properties.length - 1]; |
|||
const { text, end } = last; |
|||
const commaIndex = text.endsWith('\n') ? end.offset - 1 : end.offset; |
|||
recorder.insertRight(commaIndex, ','); |
|||
index = end.offset; |
|||
} |
|||
const content = _stringifyContent(value, indentStr); |
|||
recorder.insertRight( |
|||
index, |
|||
(node.properties.length === 0 && indent ? '\n' : '') + |
|||
' '.repeat(indent) + |
|||
`"${propertyName}":${indent ? ' ' : ''}${content}` + |
|||
indentStr.slice(0, -indent), |
|||
); |
|||
} |
|||
|
|||
export function insertPropertyInAstObjectInOrder( |
|||
recorder: UpdateRecorder, |
|||
node: JsonAstObject, |
|||
propertyName: string, |
|||
value: JsonValue, |
|||
indent: number, |
|||
) { |
|||
if (node.properties.length === 0) { |
|||
appendPropertyInAstObject(recorder, node, propertyName, value, indent); |
|||
|
|||
return; |
|||
} |
|||
|
|||
// Find insertion info.
|
|||
let insertAfterProp: JsonAstKeyValue | null = null; |
|||
let prev: JsonAstKeyValue | null = null; |
|||
let isLastProp = false; |
|||
const last = node.properties[node.properties.length - 1]; |
|||
for (const prop of node.properties) { |
|||
if (prop.key.value > propertyName) { |
|||
if (prev) { |
|||
insertAfterProp = prev; |
|||
} |
|||
break; |
|||
} |
|||
if (prop === last) { |
|||
isLastProp = true; |
|||
insertAfterProp = last; |
|||
} |
|||
prev = prop; |
|||
} |
|||
|
|||
if (isLastProp) { |
|||
appendPropertyInAstObject(recorder, node, propertyName, value, indent); |
|||
|
|||
return; |
|||
} |
|||
|
|||
const indentStr = _buildIndent(indent); |
|||
const insertIndex = |
|||
insertAfterProp === null ? node.start.offset + 1 : insertAfterProp.end.offset + 1; |
|||
const content = _stringifyContent(value, indentStr); |
|||
recorder.insertRight( |
|||
insertIndex, |
|||
indentStr + `"${propertyName}":${indent ? ' ' : ''}${content}` + ',', |
|||
); |
|||
} |
|||
|
|||
export function removePropertyInAstObject( |
|||
recorder: UpdateRecorder, |
|||
node: JsonAstObject, |
|||
propertyName: string, |
|||
) { |
|||
// Find the property inside the object.
|
|||
const propIdx = node.properties.findIndex(prop => prop.key.value === propertyName); |
|||
|
|||
if (propIdx === -1) { |
|||
// There's nothing to remove.
|
|||
return; |
|||
} |
|||
|
|||
if (node.properties.length === 1) { |
|||
// This is a special case. Everything should be removed, including indentation.
|
|||
recorder.remove(node.start.offset, node.end.offset - node.start.offset); |
|||
recorder.insertRight(node.start.offset, '{}'); |
|||
|
|||
return; |
|||
} |
|||
|
|||
// The AST considers commas and indentation to be part of the preceding property.
|
|||
// To get around messy comma and identation management, we can work over the range between
|
|||
// two properties instead.
|
|||
const previousProp = node.properties[propIdx - 1]; |
|||
const targetProp = node.properties[propIdx]; |
|||
const nextProp = node.properties[propIdx + 1]; |
|||
|
|||
let start, end; |
|||
if (previousProp) { |
|||
// Given the object below, and intending to remove the `m` property:
|
|||
// "{\n \"a\": \"a\",\n \"m\": \"m\",\n \"z\": \"z\"\n}"
|
|||
// ^---------------^
|
|||
// Removing the range above results in:
|
|||
// "{\n \"a\": \"a\",\n \"z\": \"z\"\n}"
|
|||
start = previousProp.end; |
|||
end = targetProp.end; |
|||
} else { |
|||
// If there's no previousProp there is a nextProp, since we've specialcased the 1 length case.
|
|||
// Given the object below, and intending to remove the `a` property:
|
|||
// "{\n \"a\": \"a\",\n \"m\": \"m\",\n \"z\": \"z\"\n}"
|
|||
// ^---------------^
|
|||
// Removing the range above results in:
|
|||
// "{\n \"m\": \"m\",\n \"z\": \"z\"\n}"
|
|||
start = targetProp.start; |
|||
end = nextProp.start; |
|||
} |
|||
|
|||
recorder.remove(start.offset, end.offset - start.offset); |
|||
if (!nextProp) { |
|||
recorder.insertRight(start.offset, '\n'); |
|||
} |
|||
} |
|||
|
|||
export function appendValueInAstArray( |
|||
recorder: UpdateRecorder, |
|||
node: JsonAstArray, |
|||
value: JsonValue, |
|||
indent = 4, |
|||
) { |
|||
let indentStr = _buildIndent(indent); |
|||
let index = node.start.offset + 1; |
|||
let newNodes: any[] | undefined; |
|||
|
|||
if (node.elements.length > 0) { |
|||
// Insert comma.
|
|||
const { end } = node.elements[node.elements.length - 1]; |
|||
const isClosingOnSameLine = node.end.offset - end.offset === 1; |
|||
|
|||
if (isClosingOnSameLine && indent) { |
|||
// Reformat the entire array
|
|||
recorder.remove(node.start.offset, node.end.offset - node.start.offset); |
|||
newNodes = [...node.elements.map(({ value }) => value), value]; |
|||
index = node.start.offset; |
|||
// In case we are generating the entire node we need to reduce the spacing as
|
|||
// otherwise we'd end up having incorrect double spacing
|
|||
indent = indent - 2; |
|||
indentStr = _buildIndent(indent); |
|||
} else { |
|||
recorder.insertRight(end.offset, ','); |
|||
index = end.offset; |
|||
} |
|||
} |
|||
|
|||
recorder.insertRight( |
|||
index, |
|||
(newNodes ? '' : indentStr) + |
|||
_stringifyContent(newNodes || value, indentStr) + |
|||
(node.elements.length === 0 && indent ? indentStr.substr(0, -indent) + '\n' : ''), |
|||
); |
|||
} |
|||
|
|||
export function findPropertyInAstObject( |
|||
node: JsonAstObject, |
|||
propertyName: string, |
|||
): JsonAstNode | null { |
|||
let maybeNode: JsonAstNode | null = null; |
|||
for (const property of node.properties) { |
|||
if (property.key.value == propertyName) { |
|||
maybeNode = property.value; |
|||
} |
|||
} |
|||
|
|||
return maybeNode; |
|||
} |
|||
|
|||
function _buildIndent(count: number): string { |
|||
return count ? '\n' + ' '.repeat(count) : ''; |
|||
} |
|||
|
|||
function _stringifyContent(value: JsonValue, indentStr: string): string { |
|||
// TODO: Add snapshot tests
|
|||
|
|||
// The 'space' value is 2, because we want to add 2 additional
|
|||
// indents from the 'key' node.
|
|||
|
|||
// If we use the indent provided we will have double indents:
|
|||
// "budgets": [
|
|||
// {
|
|||
// "type": "initial",
|
|||
// "maximumWarning": "2mb",
|
|||
// "maximumError": "5mb"
|
|||
// },
|
|||
// {
|
|||
// "type": "anyComponentStyle",
|
|||
// 'maximumWarning": "5kb"
|
|||
// }
|
|||
// ]
|
|||
return JSON.stringify(value, null, 2).replace(/\n/g, indentStr); |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
/** |
|||
* @license |
|||
* Copyright Google Inc. All Rights Reserved. |
|||
* |
|||
* Use of this source code is governed by an MIT-style license that can be |
|||
* found in the LICENSE file at https://angular.io/license
|
|||
*/ |
|||
|
|||
export const latestVersions = { |
|||
// These versions should be kept up to date with latest Angular peer dependencies.
|
|||
Angular: '~10.0.0-rc.0', |
|||
RxJs: '~6.6.0', |
|||
ZoneJs: '~0.10.2', |
|||
TypeScript: '~3.9.5', |
|||
TsLib: '^2.0.0', |
|||
|
|||
// The versions below must be manually updated when making a new devkit release.
|
|||
// For our e2e tests, these versions must match the latest tag present on the branch.
|
|||
// During RC periods they will not match the latest RC until there's a new git tag, and
|
|||
// should not be updated.
|
|||
DevkitBuildAngular: '~0.1000.0-rc.0', |
|||
DevkitBuildNgPackagr: '~0.1000.0-rc.0', |
|||
DevkitBuildWebpack: '~0.1000.0-rc.0', |
|||
|
|||
ngPackagr: '^10.0.0', |
|||
}; |
|||
@ -0,0 +1,20 @@ |
|||
{ |
|||
"description": "Package versions used by schematics in @schematics/angular.", |
|||
"comment": "This file is needed so that depedencies are synced by Renovate.", |
|||
"private": true, |
|||
"dependencies": { |
|||
"@types/jasmine": "~3.10.0", |
|||
"@types/node": "^12.11.1", |
|||
"jasmine-core": "~4.0.0", |
|||
"karma-chrome-launcher": "~3.1.0", |
|||
"karma-coverage": "~2.1.0", |
|||
"karma-jasmine-html-reporter": "~1.7.0", |
|||
"karma-jasmine": "~4.0.0", |
|||
"karma": "~6.3.0", |
|||
"ng-packagr": "^13.0.0", |
|||
"rxjs": "~6.6.7", |
|||
"tslib": "^2.3.0", |
|||
"typescript": "~4.6.2", |
|||
"zone.js": "~0.11.4" |
|||
} |
|||
} |
|||
@ -1,54 +0,0 @@ |
|||
/** |
|||
* @license |
|||
* Copyright Google Inc. All Rights Reserved. |
|||
* |
|||
* Use of this source code is governed by an MIT-style license that can be |
|||
* found in the LICENSE file at https://angular.io/license
|
|||
*/ |
|||
import { |
|||
DirEntry, |
|||
Rule, |
|||
SchematicContext, |
|||
SchematicsException, |
|||
Tree, |
|||
} from '@angular-devkit/schematics'; |
|||
import { TslintFixTask } from '@angular-devkit/schematics/tasks'; |
|||
|
|||
export function applyLintFix(path = '/'): Rule { |
|||
return (tree: Tree, context: SchematicContext) => { |
|||
// Find the closest tslint.json or tslint.yaml
|
|||
let dir: DirEntry | null = tree.getDir(path.substr(0, path.lastIndexOf('/'))); |
|||
|
|||
do { |
|||
if ((dir.subfiles as string[]).some(f => f === 'tslint.json' || f === 'tslint.yaml')) { |
|||
break; |
|||
} |
|||
|
|||
dir = dir.parent; |
|||
} while (dir !== null); |
|||
|
|||
if (dir === null) { |
|||
throw new SchematicsException( |
|||
'Asked to run lint fixes, but could not find a tslint.json or tslint.yaml config file.', |
|||
); |
|||
} |
|||
|
|||
// Only include files that have been touched.
|
|||
const files = tree.actions.reduce((acc: Set<string>, action) => { |
|||
const path = action.path.substr(1); // Remove the starting '/'.
|
|||
if (path.endsWith('.ts') && dir && action.path.startsWith(dir.path)) { |
|||
acc.add(path); |
|||
} |
|||
|
|||
return acc; |
|||
}, new Set<string>()); |
|||
|
|||
context.addTask( |
|||
new TslintFixTask({ |
|||
ignoreErrors: true, |
|||
tsConfigPath: 'tsconfig.json', |
|||
files: [...files], |
|||
}), |
|||
); |
|||
}; |
|||
} |
|||
@ -1,76 +0,0 @@ |
|||
/** |
|||
* @license |
|||
* Copyright Google Inc. All Rights Reserved. |
|||
* |
|||
* Use of this source code is governed by an MIT-style license that can be |
|||
* found in the LICENSE file at https://angular.io/license
|
|||
*/ |
|||
|
|||
import { JsonParseMode, parseJsonAst } from '@angular-devkit/core'; |
|||
import { Rule, SchematicsException, Tree } from '@angular-devkit/schematics'; |
|||
import { appendValueInAstArray, findPropertyInAstObject } from './json-utils'; |
|||
|
|||
const SOLUTION_TSCONFIG_PATH = 'tsconfig.json'; |
|||
|
|||
/** |
|||
* Add project references in "Solution Style" tsconfig. |
|||
*/ |
|||
export function addTsConfigProjectReferences(paths: string[]): Rule { |
|||
return (host, context) => { |
|||
const logger = context.logger; |
|||
|
|||
// We need to read after each write to avoid missing `,` when appending multiple items.
|
|||
for (const path of paths) { |
|||
const source = host.read(SOLUTION_TSCONFIG_PATH); |
|||
if (!source) { |
|||
// Solution tsconfig doesn't exist.
|
|||
logger.warn( |
|||
`Cannot add reference '${path}' in '${SOLUTION_TSCONFIG_PATH}'. File doesn't exists.`, |
|||
); |
|||
|
|||
return; |
|||
} |
|||
|
|||
const jsonAst = parseJsonAst(source.toString(), JsonParseMode.Loose); |
|||
if (jsonAst?.kind !== 'object') { |
|||
// Invalid JSON
|
|||
throw new SchematicsException(`Invalid JSON AST Object '${SOLUTION_TSCONFIG_PATH}'.`); |
|||
} |
|||
|
|||
// Solutions style tsconfig can contain 2 properties:
|
|||
// - 'files' with a value of empty array
|
|||
// - 'references'
|
|||
const filesAst = findPropertyInAstObject(jsonAst, 'files'); |
|||
const referencesAst = findPropertyInAstObject(jsonAst, 'references'); |
|||
if ( |
|||
filesAst?.kind !== 'array' || |
|||
filesAst.elements.length !== 0 || |
|||
referencesAst?.kind !== 'array' |
|||
) { |
|||
logger.warn( |
|||
`Cannot add reference '${path}' in '${SOLUTION_TSCONFIG_PATH}'. It appears to be an invalid solution style tsconfig.`, |
|||
); |
|||
|
|||
return; |
|||
} |
|||
|
|||
// Append new paths
|
|||
const recorder = host.beginUpdate(SOLUTION_TSCONFIG_PATH); |
|||
appendValueInAstArray(recorder, referencesAst, { path: `./${path}` }, 4); |
|||
host.commitUpdate(recorder); |
|||
} |
|||
}; |
|||
} |
|||
|
|||
/** |
|||
* Throws an exception when the base tsconfig doesn't exists. |
|||
*/ |
|||
export function verifyBaseTsConfigExists(host: Tree): void { |
|||
if (host.exists('tsconfig.json')) { |
|||
return; |
|||
} |
|||
|
|||
throw new SchematicsException( |
|||
`Cannot find base TypeScript configuration file 'tsconfig.json'.`, |
|||
); |
|||
} |
|||
@ -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