From a60344d9e879f38429eae314d3d70b9383e8fd01 Mon Sep 17 00:00:00 2001 From: muhammedaltug Date: Wed, 17 Aug 2022 08:40:33 +0300 Subject: [PATCH 1/3] update angular utils and angular version --- npm/ng-packs/packages/schematics/package.json | 6 +- .../schematics/src/utils/angular/README.md | 2 +- .../schematics/src/utils/angular/ast-utils.ts | 193 ++----- .../schematics/src/utils/angular/change.ts | 32 +- .../schematics/src/utils/angular/config.ts | 541 ------------------ .../src/utils/angular/dependencies.ts | 12 +- .../src/utils/angular/find-module.ts | 25 +- .../src/utils/angular/generate-from-files.ts | 61 ++ .../schematics/src/utils/angular/index.ts | 6 +- .../schematics/src/utils/angular/json-file.ts | 43 +- .../src/utils/angular/json-utils.ts | 221 ------- .../src/utils/angular/latest-versions.ts | 26 - .../angular/latest-versions/package.json | 20 + .../schematics/src/utils/angular/lint-fix.ts | 54 -- .../src/utils/angular/ng-ast-utils.ts | 9 +- .../src/utils/angular/parse-name.ts | 5 +- .../schematics/src/utils/angular/paths.ts | 2 +- .../src/utils/angular/project-targets.ts | 2 +- .../schematics/src/utils/angular/tsconfig.ts | 76 --- .../src/utils/angular/validation.ts | 60 +- .../src/utils/angular/workspace-models.ts | 23 +- .../schematics/src/utils/angular/workspace.ts | 51 +- 22 files changed, 273 insertions(+), 1197 deletions(-) delete mode 100644 npm/ng-packs/packages/schematics/src/utils/angular/config.ts create mode 100644 npm/ng-packs/packages/schematics/src/utils/angular/generate-from-files.ts delete mode 100644 npm/ng-packs/packages/schematics/src/utils/angular/json-utils.ts delete mode 100644 npm/ng-packs/packages/schematics/src/utils/angular/latest-versions.ts create mode 100644 npm/ng-packs/packages/schematics/src/utils/angular/latest-versions/package.json delete mode 100644 npm/ng-packs/packages/schematics/src/utils/angular/lint-fix.ts delete mode 100644 npm/ng-packs/packages/schematics/src/utils/angular/tsconfig.ts diff --git a/npm/ng-packs/packages/schematics/package.json b/npm/ng-packs/packages/schematics/package.json index b1e06b9db0..ad8168b033 100644 --- a/npm/ng-packs/packages/schematics/package.json +++ b/npm/ng-packs/packages/schematics/package.json @@ -9,12 +9,12 @@ "license": "MIT", "schematics": "./collection.json", "dependencies": { - "@angular-devkit/core": "~11.0.2", - "@angular-devkit/schematics": "~11.0.2", + "@angular-devkit/core": "~13.3.9", + "@angular-devkit/schematics": "~13.3.9", "got": "^11.5.2", "jsonc-parser": "^2.3.0", "should-quote": "^1.0.0", - "typescript": "~3.9.2" + "typescript": "~4.6.4" }, "devDependencies": { "@schematics/angular": "~11.0.2", diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/README.md b/npm/ng-packs/packages/schematics/src/utils/angular/README.md index 8d98f50e2d..2ee7484e2a 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/README.md +++ b/npm/ng-packs/packages/schematics/src/utils/angular/README.md @@ -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. diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/ast-utils.ts b/npm/ng-packs/packages/schematics/src/utils/angular/ast-utils.ts index 66c35a8113..86801032f6 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/ast-utils.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/ast-utils.ts @@ -1,10 +1,12 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * 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 { tags } from '@angular-devkit/core'; import * as ts from 'typescript'; import { Change, InsertChange, NoopChange } from './change'; @@ -146,7 +148,7 @@ export function findNodes( } if (max > 0 && (recursive || !test(node))) { for (const child of node.getChildren()) { - findNodes(child, test, max).forEach(node => { + findNodes(child, test, max, recursive).forEach(node => { if (max > 0) { arr.push(node); } @@ -187,7 +189,6 @@ export function getSourceNodes(sourceFile: ts.SourceFile): ts.Node[] { export function findNode(node: ts.Node, kind: ts.SyntaxKind, text: string): ts.Node | null { if (node.kind === kind && node.getText() === text) { - // throw new Error(node.getText()); return node; } @@ -244,20 +245,7 @@ export function insertAfterLastOccurrence( return new InsertChange(file, lastItemPosition, toInsert); } -export function getContentOfKeyLiteral(_source: ts.SourceFile, node: ts.Node): string | null { - if (node.kind == ts.SyntaxKind.Identifier) { - return (node as ts.Identifier).text; - } else if (node.kind == ts.SyntaxKind.StringLiteral) { - return (node as ts.StringLiteral).text; - } else { - return null; - } -} - -function _angularImportsFromNode( - node: ts.ImportDeclaration, - _sourceFile: ts.SourceFile, -): { [name: string]: string } { +function _angularImportsFromNode(node: ts.ImportDeclaration): { [name: string]: string } { const ms = node.moduleSpecifier; let modulePath: string; switch (ms.kind) { @@ -281,11 +269,11 @@ function _angularImportsFromNode( if (nb.kind == ts.SyntaxKind.NamespaceImport) { // This is of the form `import * as name from 'path'`. Return `name.`. return { - [(nb as ts.NamespaceImport).name.text + '.']: modulePath, + [nb.name.text + '.']: modulePath, }; } else { // This is of the form `import {a,b,c} from 'path'` - const namedImports = nb as ts.NamedImports; + const namedImports = nb; return namedImports.elements .map((is: ts.ImportSpecifier) => (is.propertyName ? is.propertyName.text : is.name.text)) @@ -310,7 +298,7 @@ export function getDecoratorMetadata( module: string, ): ts.Node[] { const angularImports = findNodes(source, ts.isImportDeclaration) - .map(node => _angularImportsFromNode(node, source)) + .map(node => _angularImportsFromNode(node)) .reduce((acc, current) => { for (const key of Object.keys(current)) { acc[key] = current[key]; @@ -354,38 +342,6 @@ export function getDecoratorMetadata( .map(expr => expr.arguments[0] as ts.ObjectLiteralExpression); } -function findClassDeclarationParent(node: ts.Node): ts.ClassDeclaration | undefined { - if (ts.isClassDeclaration(node)) { - return node; - } - - return node.parent && findClassDeclarationParent(node.parent); -} - -/** - * Given a source file with @NgModule class(es), find the name of the first @NgModule class. - * - * @param source source file containing one or more @NgModule - * @returns the name of the first @NgModule, or `undefined` if none is found - */ -export function getFirstNgModuleName(source: ts.SourceFile): string | undefined { - // First, find the @NgModule decorators. - const ngModulesMetadata = getDecoratorMetadata(source, 'NgModule', '@angular/core'); - if (ngModulesMetadata.length === 0) { - return undefined; - } - - // Then walk parent pointers up the AST, looking for the ClassDeclaration parent of the NgModule - // metadata. - const moduleClass = findClassDeclarationParent(ngModulesMetadata[0]); - if (!moduleClass || !moduleClass.name) { - return undefined; - } - - // Get the class name of the module ClassDeclaration. - return moduleClass.name.text; -} - export function getMetadataField( node: ts.ObjectLiteralExpression, metadataField: string, @@ -396,9 +352,7 @@ export function getMetadataField( // Filter out every fields that's not "metadataField". Also handles string literals // (but not expressions). .filter(({ name }) => { - return ( - (ts.isIdentifier(name) || ts.isStringLiteral(name)) && name.getText() === metadataField - ); + return (ts.isIdentifier(name) || ts.isStringLiteral(name)) && name.text === metadataField; }) ); } @@ -411,36 +365,33 @@ export function addSymbolToNgModuleMetadata( importPath: string | null = null, ): Change[] { const nodes = getDecoratorMetadata(source, 'NgModule', '@angular/core'); - let node: any = nodes[0]; + const node = nodes[0]; // Find the decorator declaration. - if (!node) { + if (!node || !ts.isObjectLiteralExpression(node)) { return []; } // Get all the children property assignment of object literals. - const matchingProperties = getMetadataField(node as ts.ObjectLiteralExpression, metadataField); + const matchingProperties = getMetadataField(node, metadataField); - // Get the last node of the array literal. - if (!matchingProperties) { - return []; - } if (matchingProperties.length == 0) { // We haven't found the field in the metadata declaration. Insert a new field. - const expr = node as ts.ObjectLiteralExpression; let position: number; let toInsert: string; - if (expr.properties.length == 0) { - position = expr.getEnd() - 1; - toInsert = ` ${metadataField}: [${symbolName}]\n`; + if (node.properties.length == 0) { + position = node.getEnd() - 1; + toInsert = `\n ${metadataField}: [\n${tags.indentBy(4)`${symbolName}`}\n ]\n`; } else { - node = expr.properties[expr.properties.length - 1]; - position = node.getEnd(); + const childNode = node.properties[node.properties.length - 1]; + position = childNode.getEnd(); // Get the indentation of the last element, if any. - const text = node.getFullText(source); - const matches = text.match(/^\r?\n\s*/); - if (matches && matches.length > 0) { - toInsert = `,${matches[0]}${metadataField}: [${symbolName}]`; + const text = childNode.getFullText(source); + const matches = text.match(/^(\r?\n)(\s*)/); + if (matches) { + toInsert = + `,${matches[0]}${metadataField}: [${matches[1]}` + + `${tags.indentBy(matches[2].length + 2)`${symbolName}`}${matches[0]}]`; } else { toInsert = `, ${metadataField}: [${symbolName}]`; } @@ -454,69 +405,48 @@ export function addSymbolToNgModuleMetadata( return [new InsertChange(ngModulePath, position, toInsert)]; } } - const assignment = matchingProperties[0] as ts.PropertyAssignment; + const assignment = matchingProperties[0]; // If it's not an array, nothing we can do really. - if (assignment.initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression) { + if ( + !ts.isPropertyAssignment(assignment) || + !ts.isArrayLiteralExpression(assignment.initializer) + ) { return []; } - const arrLiteral = assignment.initializer as ts.ArrayLiteralExpression; - if (arrLiteral.elements.length == 0) { - // Forward the property. - node = arrLiteral; - } else { - node = arrLiteral.elements; - } - - if (!node) { - console.error('No app module found. Please add your new class to your component.'); + let expresssion: ts.Expression | ts.ArrayLiteralExpression; + const assignmentInit = assignment.initializer; + const elements = assignmentInit.elements; - return []; - } - - if (Array.isArray(node)) { - // eslint-disable-next-line @typescript-eslint/ban-types - const nodeArray = node as {} as Array; - const symbolsArray = nodeArray.map(node => node.getText()); - if (symbolsArray.includes(symbolName)) { + if (elements.length) { + const symbolsArray = elements.map(node => tags.oneLine`${node.getText()}`); + if (symbolsArray.includes(tags.oneLine`${symbolName}`)) { return []; } - node = node[node.length - 1]; + expresssion = elements[elements.length - 1]; + } else { + expresssion = assignmentInit; } let toInsert: string; - let position = node.getEnd(); - if (node.kind == ts.SyntaxKind.ObjectLiteralExpression) { - // We haven't found the field in the metadata declaration. Insert a new - // field. - const expr = node as ts.ObjectLiteralExpression; - if (expr.properties.length == 0) { - position = expr.getEnd() - 1; - toInsert = ` ${symbolName}\n`; - } else { - // Get the indentation of the last element, if any. - const text = node.getFullText(source); - if (text.match(/^\r?\r?\n/)) { - toInsert = `,${text.match(/^\r?\n\s*/)[0]}${symbolName}`; - } else { - toInsert = `, ${symbolName}`; - } - } - } else if (node.kind == ts.SyntaxKind.ArrayLiteralExpression) { + let position = expresssion.getEnd(); + if (ts.isArrayLiteralExpression(expresssion)) { // We found the field but it's empty. Insert it just before the `]`. position--; - toInsert = `${symbolName}`; + toInsert = `\n${tags.indentBy(4)`${symbolName}`}\n `; } else { // Get the indentation of the last element, if any. - const text = node.getFullText(source); - if (text.match(/^\r?\n/)) { - toInsert = `,${text.match(/^\r?\n(\r?)\s*/)[0]}${symbolName}`; + const text = expresssion.getFullText(source); + const matches = text.match(/^(\r?\n)(\s*)/); + if (matches) { + toInsert = `,${matches[1]}${tags.indentBy(matches[2].length)`${symbolName}`}`; } else { toInsert = `, ${symbolName}`; } } + if (importPath !== null) { return [ new InsertChange(ngModulePath, position, toInsert), @@ -594,25 +524,6 @@ export function addBootstrapToModule( return addSymbolToNgModuleMetadata(source, modulePath, 'bootstrap', classifiedName, importPath); } -/** - * Custom function to insert an entryComponent into NgModule. It also imports it. - * @deprecated - Since version 9.0.0 with Ivy, entryComponents is no longer necessary. - */ -export function addEntryComponentToModule( - source: ts.SourceFile, - modulePath: string, - classifiedName: string, - importPath: string, -): Change[] { - return addSymbolToNgModuleMetadata( - source, - modulePath, - 'entryComponents', - classifiedName, - importPath, - ); -} - /** * Determine if an import already exists. */ @@ -690,10 +601,13 @@ export function getEnvironmentExportName(source: ts.SourceFile): string | null { * Returns the RouterModule declaration from NgModule metadata, if any. */ export function getRouterModuleDeclaration(source: ts.SourceFile): ts.Expression | undefined { - const result = getDecoratorMetadata(source, 'NgModule', '@angular/core') as ts.Node[]; - const node = result[0] as ts.ObjectLiteralExpression; - const matchingProperties = getMetadataField(node, 'imports'); + const result = getDecoratorMetadata(source, 'NgModule', '@angular/core'); + const node = result[0]; + if (!node || !ts.isObjectLiteralExpression(node)) { + return undefined; + } + const matchingProperties = getMetadataField(node, 'imports'); if (!matchingProperties) { return; } @@ -721,7 +635,10 @@ export function addRouteDeclarationToModule( ): Change { const routerModuleExpr = getRouterModuleDeclaration(source); if (!routerModuleExpr) { - throw new Error(`Couldn't find a route declaration in ${fileToAdd}.`); + throw new Error( + `Couldn't find a route declaration in ${fileToAdd}.\n` + + `Use the '--module' option to specify a different routing module.`, + ); } const scopeConfigMethodArgs = (routerModuleExpr as ts.CallExpression).arguments; if (!scopeConfigMethodArgs.length) { diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/change.ts b/npm/ng-packs/packages/schematics/src/utils/angular/change.ts index 543e41427b..08df56a6dd 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/change.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/change.ts @@ -1,10 +1,13 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * 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 { UpdateRecorder } from '@angular-devkit/schematics'; + export interface Host { write(path: string, content: string): Promise; read(path: string): Promise; @@ -56,7 +59,7 @@ export class InsertChange implements Change { * This method does not insert spaces if there is none in the original string. */ apply(host: Host) { - return host.read(this.path).then(content => { + return host.read(this.path).then((content) => { const prefix = content.substring(0, this.pos); const suffix = content.substring(this.pos); @@ -72,7 +75,7 @@ export class RemoveChange implements Change { order: number; description: string; - constructor(public path: string, private pos: number, private toRemove: string) { + constructor(public path: string, private pos: number, public toRemove: string) { if (pos < 0) { throw new Error('Negative positions are invalid'); } @@ -81,7 +84,7 @@ export class RemoveChange implements Change { } apply(host: Host): Promise { - return host.read(this.path).then(content => { + return host.read(this.path).then((content) => { const prefix = content.substring(0, this.pos); const suffix = content.substring(this.pos + this.toRemove.length); @@ -101,8 +104,8 @@ export class ReplaceChange implements Change { constructor( public path: string, private pos: number, - private oldText: string, - private newText: string, + public oldText: string, + public newText: string, ) { if (pos < 0) { throw new Error('Negative positions are invalid'); @@ -112,7 +115,7 @@ export class ReplaceChange implements Change { } apply(host: Host): Promise { - return host.read(this.path).then(content => { + return host.read(this.path).then((content) => { const prefix = content.substring(0, this.pos); const suffix = content.substring(this.pos + this.oldText.length); const text = content.substring(this.pos, this.pos + this.oldText.length); @@ -126,3 +129,18 @@ export class ReplaceChange implements Change { }); } } + +export function applyToUpdateRecorder(recorder: UpdateRecorder, changes: Change[]): void { + for (const change of changes) { + if (change instanceof InsertChange) { + recorder.insertLeft(change.pos, change.toAdd); + } else if (change instanceof RemoveChange) { + recorder.remove(change.order, change.toRemove.length); + } else if (change instanceof ReplaceChange) { + recorder.remove(change.order, change.oldText.length); + recorder.insertLeft(change.order, change.newText); + } else if (!(change instanceof NoopChange)) { + throw new Error('Unknown Change type encountered when updating a recorder.'); + } + } +} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/config.ts b/npm/ng-packs/packages/schematics/src/utils/angular/config.ts deleted file mode 100644 index 86e26d8a08..0000000000 --- a/npm/ng-packs/packages/schematics/src/utils/angular/config.ts +++ /dev/null @@ -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( - workspace: WorkspaceSchema, - name: string, - project: WorkspaceProject, -): 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]; -} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/dependencies.ts b/npm/ng-packs/packages/schematics/src/utils/angular/dependencies.ts index 9298dba3b8..c9aa617191 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/dependencies.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/dependencies.ts @@ -1,10 +1,11 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * 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 { Tree } from '@angular-devkit/schematics'; import { JSONFile } from './json-file'; @@ -36,9 +37,6 @@ export function addPackageJsonDependency( pkgJsonPath = PKG_JSON_PATH, ): void { const json = new JSONFile(tree, pkgJsonPath); - if (json.error) { - throw json.error; - } const { overwrite, type, name, version } = dependency; const path = [type, name]; @@ -53,9 +51,6 @@ export function removePackageJsonDependency( pkgJsonPath = PKG_JSON_PATH, ): void { const json = new JSONFile(tree, pkgJsonPath); - if (json.error) { - throw json.error; - } for (const depType of ALL_DEPENDENCY_TYPE) { json.remove([depType, name]); @@ -68,9 +63,6 @@ export function getPackageJsonDependency( pkgJsonPath = PKG_JSON_PATH, ): NodeDependency | null { const json = new JSONFile(tree, pkgJsonPath); - if (json.error) { - throw json.error; - } for (const depType of ALL_DEPENDENCY_TYPE) { const version = json.get([depType, name]); diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/find-module.ts b/npm/ng-packs/packages/schematics/src/utils/angular/find-module.ts index d8602244ce..a420f07eb0 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/find-module.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/find-module.ts @@ -1,21 +1,19 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * 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 { dirname, join, normalize, NormalizedRoot, Path, relative } from '@angular-devkit/core'; + +import { NormalizedRoot, Path, dirname, join, normalize, relative } from '@angular-devkit/core'; import { DirEntry, Tree } from '@angular-devkit/schematics'; export interface ModuleOptions { - project?: string; // added this module?: string; name: string; flat?: boolean; path?: string; - route?: string; // added this - selector?: string; // added this skipImport?: boolean; moduleExt?: string; routingModuleExt?: string; @@ -28,7 +26,8 @@ export const ROUTING_MODULE_EXT = '-routing.module.ts'; * Find the module referred by a set of options passed to the schematics. */ export function findModuleFromOptions(host: Tree, options: ModuleOptions): Path | undefined { - if (Object.prototype.hasOwnProperty.call(options, 'skipImport') && options.skipImport) { + // eslint-disable-next-line no-prototype-builtins + if (options.hasOwnProperty('skipImport') && options.skipImport) { return undefined; } @@ -55,8 +54,8 @@ export function findModuleFromOptions(host: Tree, options: ModuleOptions): Path const candidatesDirs = [...candidateSet].sort((a, b) => b.length - a.length); for (const c of candidatesDirs) { - const candidateFiles = ['', `${moduleBaseName}.ts`, `${moduleBaseName}${moduleExt}`].map(x => - join(c, x), + const candidateFiles = ['', `${moduleBaseName}.ts`, `${moduleBaseName}${moduleExt}`].map( + (x) => join(c, x), ); for (const sc of candidateFiles) { @@ -86,8 +85,8 @@ export function findModule( let foundRoutingModule = false; while (dir) { - const allMatches = dir.subfiles.filter(p => p.endsWith(moduleExt)); - const filteredMatches = allMatches.filter(p => !p.endsWith(routingModuleExt)); + const allMatches = dir.subfiles.filter((p) => p.endsWith(moduleExt)); + const filteredMatches = allMatches.filter((p) => !p.endsWith(routingModuleExt)); foundRoutingModule = foundRoutingModule || allMatches.length !== filteredMatches.length; @@ -95,7 +94,7 @@ export function findModule( return join(dir.path, filteredMatches[0]); } else if (filteredMatches.length > 1) { throw new Error( - 'More than one module matches. Use the skip-import option to skip importing ' + + `More than one module matches. Use the '--skip-import' option to skip importing ` + 'the component into the closest module or use the module option to specify a module.', ); } @@ -106,8 +105,8 @@ export function findModule( const errorMsg = foundRoutingModule ? 'Could not find a non Routing NgModule.' + `\nModules with suffix '${routingModuleExt}' are strictly reserved for routing.` + - '\nUse the skip-import option to skip importing in NgModule.' - : 'Could not find an NgModule. Use the skip-import option to skip importing in NgModule.'; + `\nUse the '--skip-import' option to skip importing in NgModule.` + : `Could not find an NgModule. Use the '--skip-import' option to skip importing in NgModule.`; throw new Error(errorMsg); } diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/generate-from-files.ts b/npm/ng-packs/packages/schematics/src/utils/angular/generate-from-files.ts new file mode 100644 index 0000000000..2ce391b900 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/utils/angular/generate-from-files.ts @@ -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)> = {}, +): 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)]); + }; +} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/index.ts b/npm/ng-packs/packages/schematics/src/utils/angular/index.ts index fec78af448..abf2992d90 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/index.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/index.ts @@ -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'; diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/json-file.ts b/npm/ng-packs/packages/schematics/src/utils/angular/json-file.ts index 25df010315..647199ef84 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/json-file.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/json-file.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * 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 @@ -10,46 +10,62 @@ import { JsonValue } from '@angular-devkit/core'; import { Tree } from '@angular-devkit/schematics'; import { Node, + ParseError, applyEdits, findNodeAtLocation, getNodeValue, modify, parseTree, + printParseErrorCode, } from 'jsonc-parser'; +export type InsertionIndex = (properties: string[]) => number; export type JSONPath = (string | number)[]; /** @internal */ export class JSONFile { - private content: string; - error: undefined | Error; + content: string; constructor(private readonly host: Tree, private readonly path: string) { const buffer = this.host.read(this.path); if (buffer) { this.content = buffer.toString(); } else { - this.error = new Error(`Could not read ${path}.`); + throw new Error(`Could not read '${path}'.`); } } private _jsonAst: Node | undefined; - private get JsonAst(): Node { + private get JsonAst(): Node | undefined { if (this._jsonAst) { return this._jsonAst; } - this._jsonAst = parseTree(this.content); + const errors: ParseError[] = []; + this._jsonAst = parseTree(this.content, errors, { allowTrailingComma: true }); + if (errors.length) { + const { error, offset } = errors[0]; + throw new Error( + `Failed to parse "${this.path}" as JSON AST Object. ${printParseErrorCode( + error, + )} at location: ${offset}.`, + ); + } return this._jsonAst; } get(jsonPath: JSONPath): unknown { + const jsonAstNode = this.JsonAst; + if (!jsonAstNode) { + return undefined; + } + if (jsonPath.length === 0) { - return getNodeValue(this.JsonAst); + return getNodeValue(jsonAstNode); } - const node = findNodeAtLocation(this.JsonAst, jsonPath); + const node = findNodeAtLocation(jsonAstNode, jsonPath); return node === undefined ? undefined : getNodeValue(node); } @@ -57,12 +73,15 @@ export class JSONFile { modify( jsonPath: JSONPath, value: JsonValue | undefined, - getInsertionIndex?: (properties: string[]) => number, + insertInOrder?: InsertionIndex | false, ): void { - if (!getInsertionIndex) { + let getInsertionIndex: InsertionIndex | undefined; + if (insertInOrder === undefined) { const property = jsonPath.slice(-1)[0]; - getInsertionIndex = properties => - [...properties, property].sort().findIndex(p => p === property); + getInsertionIndex = (properties) => + [...properties, property].sort().findIndex((p) => p === property); + } else if (insertInOrder !== false) { + getInsertionIndex = insertInOrder; } const edits = modify(this.content, jsonPath, value, { diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/json-utils.ts b/npm/ng-packs/packages/schematics/src/utils/angular/json-utils.ts deleted file mode 100644 index 0e71dd7545..0000000000 --- a/npm/ng-packs/packages/schematics/src/utils/angular/json-utils.ts +++ /dev/null @@ -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); -} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/latest-versions.ts b/npm/ng-packs/packages/schematics/src/utils/angular/latest-versions.ts deleted file mode 100644 index bead0e6282..0000000000 --- a/npm/ng-packs/packages/schematics/src/utils/angular/latest-versions.ts +++ /dev/null @@ -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', -}; diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/latest-versions/package.json b/npm/ng-packs/packages/schematics/src/utils/angular/latest-versions/package.json new file mode 100644 index 0000000000..119d4156b8 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/utils/angular/latest-versions/package.json @@ -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" + } +} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/lint-fix.ts b/npm/ng-packs/packages/schematics/src/utils/angular/lint-fix.ts deleted file mode 100644 index d0998db7ab..0000000000 --- a/npm/ng-packs/packages/schematics/src/utils/angular/lint-fix.ts +++ /dev/null @@ -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, 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()); - - context.addTask( - new TslintFixTask({ - ignoreErrors: true, - tsConfigPath: 'tsconfig.json', - files: [...files], - }), - ); - }; -} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/ng-ast-utils.ts b/npm/ng-packs/packages/schematics/src/utils/angular/ng-ast-utils.ts index 2c48c9b8bd..6638f832ac 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/ng-ast-utils.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/ng-ast-utils.ts @@ -1,10 +1,11 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * 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 { normalize } from '@angular-devkit/core'; import { SchematicsException, Tree } from '@angular-devkit/schematics'; import { dirname } from 'path'; @@ -59,17 +60,17 @@ export function findBootstrapModulePath(host: Tree, mainPath: string): string { const mainBuffer = host.read(mainPath); if (!mainBuffer) { - throw new SchematicsException(`Client app main file (${mainPath}) not found`); + throw new SchematicsException(`Client application main file (${mainPath}) not found`); } const mainText = mainBuffer.toString('utf-8'); const source = ts.createSourceFile(mainPath, mainText, ts.ScriptTarget.Latest, true); const allNodes = getSourceNodes(source); const bootstrapModuleRelativePath = allNodes - .filter(node => node.kind === ts.SyntaxKind.ImportDeclaration) + .filter(ts.isImportDeclaration) .filter(imp => { return findNode(imp, ts.SyntaxKind.Identifier, bootstrapModule.getText()); }) - .map((imp: ts.ImportDeclaration) => { + .map(imp => { const modulePathStringLiteral = imp.moduleSpecifier as ts.StringLiteral; return modulePathStringLiteral.text; diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/parse-name.ts b/npm/ng-packs/packages/schematics/src/utils/angular/parse-name.ts index 65a45c21ee..231c99341a 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/parse-name.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/parse-name.ts @@ -1,10 +1,11 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * 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 { relative, Path } from "../../../angular_devkit/core/src/virtual-fs"; import { Path, basename, dirname, join, normalize } from '@angular-devkit/core'; @@ -15,7 +16,7 @@ export interface Location { export function parseName(path: string, name: string): Location { const nameWithoutPath = basename(normalize(name)); - const namePath = dirname(join(normalize(path), name) as Path); + const namePath = dirname(join(normalize(path), name)); return { name: nameWithoutPath, diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/paths.ts b/npm/ng-packs/packages/schematics/src/utils/angular/paths.ts index 35729a6417..dffa9e841d 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/paths.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/paths.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * 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 diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/project-targets.ts b/npm/ng-packs/packages/schematics/src/utils/angular/project-targets.ts index e99293f10e..7f4b7ba8c6 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/project-targets.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/project-targets.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * 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 diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/tsconfig.ts b/npm/ng-packs/packages/schematics/src/utils/angular/tsconfig.ts deleted file mode 100644 index ae9075d94b..0000000000 --- a/npm/ng-packs/packages/schematics/src/utils/angular/tsconfig.ts +++ /dev/null @@ -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'.`, - ); -} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/validation.ts b/npm/ng-packs/packages/schematics/src/utils/angular/validation.ts index 4fa3182a10..8a51bd9541 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/validation.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/validation.ts @@ -1,20 +1,14 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * 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 { tags } from '@angular-devkit/core'; import { SchematicsException } from '@angular-devkit/schematics'; -export function validateName(name: string): void { - if (name && /^\d/.test(name)) { - throw new SchematicsException(tags.oneLine`name (${name}) - can not start with a digit.`); - } -} - // Must start with a letter, and must contain only alphanumeric characters or dashes. // When adding a dash the segment after the dash must also start with a letter. export const htmlSelectorRe = /^[a-zA-Z][.0-9a-zA-Z]*(:?-[a-zA-Z][.0-9a-zA-Z]*)*$/; @@ -25,53 +19,3 @@ export function validateHtmlSelector(selector: string): void { is invalid.`); } } - -export function validateProjectName(projectName: string) { - const errorIndex = getRegExpFailPosition(projectName); - const unsupportedProjectNames: string[] = []; - const packageNameRegex = /^(?:@[a-zA-Z0-9_-]+\/)?[a-zA-Z0-9_-]+$/; - if (errorIndex !== null) { - const firstMessage = tags.oneLine` - Project name "${projectName}" is not valid. New project names must - start with a letter, and must contain only alphanumeric characters or dashes. - When adding a dash the segment after the dash must also start with a letter. - `; - const msg = tags.stripIndent` - ${firstMessage} - ${projectName} - ${Array(errorIndex + 1).join(' ') + '^'} - `; - throw new SchematicsException(msg); - } else if (unsupportedProjectNames.indexOf(projectName) !== -1) { - throw new SchematicsException( - `Project name ${JSON.stringify(projectName)} is not a supported name.`, - ); - } else if (!packageNameRegex.test(projectName)) { - throw new SchematicsException(`Project name ${JSON.stringify(projectName)} is invalid.`); - } -} - -function getRegExpFailPosition(str: string): number | null { - const isScope = /^@.*\/.*/.test(str); - if (isScope) { - // Remove starting @ - str = str.replace(/^@/, ''); - // Change / to - for validation - str = str.replace(/\//g, '-'); - } - - const parts = str.indexOf('-') >= 0 ? str.split('-') : [str]; - const matched: string[] = []; - - const projectNameRegexp = /^[a-zA-Z][.0-9a-zA-Z]*(-[.0-9a-zA-Z]*)*$/; - - parts.forEach(part => { - if (part.match(projectNameRegexp)) { - matched.push(part); - } - }); - - const compare = matched.join('-'); - - return str !== compare ? compare.length : null; -} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/workspace-models.ts b/npm/ng-packs/packages/schematics/src/utils/angular/workspace-models.ts index 0a9c972995..5bfa9c0878 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/workspace-models.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/workspace-models.ts @@ -1,11 +1,11 @@ -/* eslint-disable @typescript-eslint/ban-types */ /** * @license - * Copyright Google Inc. All Rights Reserved. + * 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 */ + export enum ProjectType { Application = 'application', Library = 'library', @@ -49,7 +49,6 @@ export interface BrowserBuilderOptions extends BrowserBuilderBaseOptions { optimization?: boolean; outputHashing?: OutputHashing; resourcesOutputPath?: string; - extractCss?: boolean; namedChunks?: boolean; aot?: boolean; extractLicenses?: boolean; @@ -77,10 +76,12 @@ export interface ServerBuilderOptions { tsConfig: string; main: string; fileReplacements?: FileReplacements[]; - optimization?: { - scripts?: boolean; - styles?: boolean; - }; + optimization?: + | boolean + | { + scripts?: boolean; + styles?: boolean; + }; sourceMap?: | boolean | { @@ -101,11 +102,6 @@ export interface TestBuilderOptions extends Partial { karmaConfig: string; } -export interface LintBuilderOptions { - tsConfig: string[] | string; - exclude?: string[]; -} - export interface ExtractI18nOptions { browserTarget: string; } @@ -128,7 +124,6 @@ export type LibraryBuilderTarget = BuilderTarget; export type ServerBuilderTarget = BuilderTarget; export type AppShellBuilderTarget = BuilderTarget; -export type LintBuilderTarget = BuilderTarget; export type TestBuilderTarget = BuilderTarget; export type ServeBuilderTarget = BuilderTarget; export type ExtractI18nBuilderTarget = BuilderTarget; @@ -168,12 +163,12 @@ export interface WorkspaceProject { build?: TProjectType extends ProjectType.Library ? LibraryBuilderTarget : BrowserBuilderTarget; server?: ServerBuilderTarget; - lint?: LintBuilderTarget; test?: TestBuilderTarget; serve?: ServeBuilderTarget; e2e?: E2EBuilderTarget; 'app-shell'?: AppShellBuilderTarget; 'extract-i18n'?: ExtractI18nBuilderTarget; // TODO(hans): change this any to unknown when google3 supports TypeScript 3.0. + // eslint-disable-next-line @typescript-eslint/no-explicit-any [key: string]: any; } diff --git a/npm/ng-packs/packages/schematics/src/utils/angular/workspace.ts b/npm/ng-packs/packages/schematics/src/utils/angular/workspace.ts index f2c3403ed9..5ac2ad855f 100644 --- a/npm/ng-packs/packages/schematics/src/utils/angular/workspace.ts +++ b/npm/ng-packs/packages/schematics/src/utils/angular/workspace.ts @@ -1,12 +1,13 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * 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 { virtualFs, workspaces } from '@angular-devkit/core'; -import { Rule, Tree } from '@angular-devkit/schematics'; + +import { json, virtualFs, workspaces } from '@angular-devkit/core'; +import { noop, Rule, Tree } from '@angular-devkit/schematics'; import { ProjectType } from './workspace-models'; function createHost(tree: Tree): workspaces.WorkspaceHost { @@ -33,13 +34,13 @@ function createHost(tree: Tree): workspaces.WorkspaceHost { } export function updateWorkspace( - updater: (workspace: workspaces.WorkspaceDefinition) => void | PromiseLike, + updater: (workspace: workspaces.WorkspaceDefinition) => void | Rule | PromiseLike, ): Rule; export function updateWorkspace(workspace: workspaces.WorkspaceDefinition): Rule; export function updateWorkspace( updaterOrWorkspace: | workspaces.WorkspaceDefinition - | ((workspace: workspaces.WorkspaceDefinition) => void | PromiseLike), + | ((workspace: workspaces.WorkspaceDefinition) => void | Rule | PromiseLike), ): Rule { return async (tree: Tree) => { const host = createHost(tree); @@ -47,14 +48,15 @@ export function updateWorkspace( if (typeof updaterOrWorkspace === 'function') { const { workspace } = await workspaces.readWorkspace('/', host); - const result = updaterOrWorkspace(workspace); - if (result !== undefined) { - await result; - } + const result = await updaterOrWorkspace(workspace); await workspaces.writeWorkspace(workspace, host); + + return result || noop; } else { await workspaces.writeWorkspace(updaterOrWorkspace, host); + + return noop; } }; } @@ -83,8 +85,37 @@ export async function createDefaultPath(tree: Tree, projectName: string): Promis const workspace = await getWorkspace(tree); const project = workspace.projects.get(projectName); if (!project) { - throw new Error('Specified project does not exist.'); + throw new Error(`Project "${projectName}" does not exist.`); } return buildDefaultPath(project); } + +export function* allWorkspaceTargets( + workspace: workspaces.WorkspaceDefinition, +): Iterable<[string, workspaces.TargetDefinition, string, workspaces.ProjectDefinition]> { + for (const [projectName, project] of workspace.projects) { + for (const [targetName, target] of project.targets) { + yield [targetName, target, projectName, project]; + } + } +} + +export function* allTargetOptions( + target: workspaces.TargetDefinition, + skipBaseOptions = false, +): Iterable<[string | undefined, Record]> { + if (!skipBaseOptions && target.options) { + yield [undefined, target.options]; + } + + if (!target.configurations) { + return; + } + + for (const [name, options] of Object.entries(target.configurations)) { + if (options !== undefined) { + yield [name, options]; + } + } +} From ce94aeabb9c4a9b4ef557669b95d41eabe4e0932 Mon Sep 17 00:00:00 2001 From: muhammedaltug Date: Wed, 17 Aug 2022 08:44:57 +0300 Subject: [PATCH 2/3] add create lib command --- .../packages/schematics/src/collection.json | 5 + .../.eslintrc.json.template | 44 +++++ .../config/ng-package.json.template | 7 + ...raryName@kebab__-config.module.ts.template | 12 ++ .../config/src/enums/index.ts.template | 1 + .../config/src/enums/route-names.ts.template | 3 + .../config/src/providers/index.ts.template | 1 + .../src/providers/route.provider.ts.template | 26 +++ .../config/src/public-api.ts.template | 3 + .../karma.conf.js.template | 44 +++++ .../ng-package.json.template | 7 + .../package.json.template | 11 ++ ...aryName@kebab__-routing.module.ts.template | 18 ++ .../__libraryName@kebab__.module.ts.template | 22 +++ .../src/public-api.ts.template | 4 + .../src/test.ts.template | 26 +++ .../tsconfig.lib.json.template | 20 +++ .../tsconfig.lib.prod.json.template | 10 ++ .../tsconfig.spec.json.template | 17 ++ .../ng-package.json.template | 7 + ..._-__libraryName@kebab__.module.ts.template | 11 ++ .../src/lib/index.ts.template | 1 + .../src/public-api.ts.template | 1 + .../src/commands/create-lib/index.ts | 159 ++++++++++++++++++ .../create-lib/models/generate-lib-schema.ts | 17 ++ .../src/commands/create-lib/models/index.ts | 1 + .../src/commands/create-lib/schema.json | 36 ++++ .../schematics/src/constants/symbols.ts | 1 + .../schematics/src/enums/exception.ts | 1 + .../src/models/generate-proxy-schema.ts | 10 ++ .../utils/angular-schematic/generate-lib.ts | 52 ++++++ .../src/utils/angular-schematic/index.ts | 1 + .../packages/schematics/src/utils/index.ts | 1 + .../packages/schematics/src/utils/lib.ts | 9 + .../packages/schematics/src/utils/source.ts | 2 +- .../schematics/src/utils/workspace.ts | 34 +++- npm/ng-packs/scripts/build-schematics.ts | 6 + 37 files changed, 627 insertions(+), 4 deletions(-) create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/.eslintrc.json.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/ng-package.json.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/__libraryName@kebab__-config.module.ts.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/enums/index.ts.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/enums/route-names.ts.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/providers/index.ts.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/providers/route.provider.ts.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/public-api.ts.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/karma.conf.js.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/ng-package.json.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/package.json.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/lib/__libraryName@kebab__-routing.module.ts.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/lib/__libraryName@kebab__.module.ts.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/public-api.ts.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/test.ts.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.lib.json.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.lib.prod.json.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.spec.json.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/ng-package.json.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/src/lib/__target@kebab__-__libraryName@kebab__.module.ts.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/src/lib/index.ts.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/src/public-api.ts.template create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/index.ts create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/models/generate-lib-schema.ts create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/models/index.ts create mode 100644 npm/ng-packs/packages/schematics/src/commands/create-lib/schema.json create mode 100644 npm/ng-packs/packages/schematics/src/constants/symbols.ts create mode 100644 npm/ng-packs/packages/schematics/src/utils/angular-schematic/generate-lib.ts create mode 100644 npm/ng-packs/packages/schematics/src/utils/angular-schematic/index.ts create mode 100644 npm/ng-packs/packages/schematics/src/utils/lib.ts diff --git a/npm/ng-packs/packages/schematics/src/collection.json b/npm/ng-packs/packages/schematics/src/collection.json index 0b1b738e08..8441a786b0 100644 --- a/npm/ng-packs/packages/schematics/src/collection.json +++ b/npm/ng-packs/packages/schematics/src/collection.json @@ -24,6 +24,11 @@ "description": "ABP API Generator Schematics", "factory": "./commands/api", "schema": "./commands/api/schema.json" + }, + "create-lib": { + "description": "ABP Create Library Schematics", + "factory": "./commands/create-lib", + "schema": "./commands/create-lib/schema.json" } } } diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/.eslintrc.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/.eslintrc.json.template new file mode 100644 index 0000000000..de84cad019 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/.eslintrc.json.template @@ -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": {} + } + ] +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/ng-package.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/ng-package.json.template new file mode 100644 index 0000000000..2e7019c90e --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/ng-package.json.template @@ -0,0 +1,7 @@ +{ + "$schema": "../../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/<%= kebab(libraryName) %>/config", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/__libraryName@kebab__-config.module.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/__libraryName@kebab__-config.module.ts.template new file mode 100644 index 0000000000..c39b4582b9 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/__libraryName@kebab__-config.module.ts.template @@ -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], + }; + } +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/enums/index.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/enums/index.ts.template new file mode 100644 index 0000000000..3bda94b078 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/enums/index.ts.template @@ -0,0 +1 @@ +export * from './route-names'; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/enums/route-names.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/enums/route-names.ts.template new file mode 100644 index 0000000000..3bbb75be52 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/enums/route-names.ts.template @@ -0,0 +1,3 @@ +export const enum e<%= pascal(libraryName) %>RouteNames { + <%= pascal(libraryName) %> = '<%= pascal(libraryName) %>', +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/providers/index.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/providers/index.ts.template new file mode 100644 index 0000000000..fe08efba8c --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/providers/index.ts.template @@ -0,0 +1 @@ +export * from './route.provider'; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/providers/route.provider.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/providers/route.provider.ts.template new file mode 100644 index 0000000000..5db50f7eaa --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/providers/route.provider.ts.template @@ -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, + }, + ]); + }; +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/public-api.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/public-api.ts.template new file mode 100644 index 0000000000..6a8953bca8 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/public-api.ts.template @@ -0,0 +1,3 @@ +export * from './enums'; +export * from './<%= kebab(libraryName) %>-config.module'; +export * from './providers'; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/karma.conf.js.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/karma.conf.js.template new file mode 100644 index 0000000000..a56984ac5b --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/karma.conf.js.template @@ -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 + }); +}; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/ng-package.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/ng-package.json.template new file mode 100644 index 0000000000..fd8e55d798 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/ng-package.json.template @@ -0,0 +1,7 @@ +{ + "$schema": "../../node_modules/ng-packagr/ng-package.schema.json", + "dest": "../../dist/<%= kebab(libraryName) %>", + "lib": { + "entryFile": "src/public-api.ts" + } +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/package.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/package.json.template new file mode 100644 index 0000000000..c4d36a8576 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/package.json.template @@ -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" + } +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/lib/__libraryName@kebab__-routing.module.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/lib/__libraryName@kebab__-routing.module.ts.template new file mode 100644 index 0000000000..4b9517b211 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/lib/__libraryName@kebab__-routing.module.ts.template @@ -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 {} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/lib/__libraryName@kebab__.module.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/lib/__libraryName@kebab__.module.ts.template new file mode 100644 index 0000000000..3d04aca7fb --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/lib/__libraryName@kebab__.module.ts.template @@ -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()); + } +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/public-api.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/public-api.ts.template new file mode 100644 index 0000000000..c09585ae58 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/public-api.ts.template @@ -0,0 +1,4 @@ +/* + * Public API Surface of my-project-name + */ +export * from './lib/<%= kebab(libraryName) %>.module'; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/test.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/test.ts.template new file mode 100644 index 0000000000..52e55168eb --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/test.ts.template @@ -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[]; + (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); diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.lib.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.lib.json.template new file mode 100644 index 0000000000..8da4414fb7 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.lib.json.template @@ -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" + ] +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.lib.prod.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.lib.prod.json.template new file mode 100644 index 0000000000..06de549e10 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.lib.prod.json.template @@ -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" + } +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.spec.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.spec.json.template new file mode 100644 index 0000000000..715dd0a5d2 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.spec.json.template @@ -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" + ] +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/ng-package.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/ng-package.json.template new file mode 100644 index 0000000000..4e2118d142 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/ng-package.json.template @@ -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" + } +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/src/lib/__target@kebab__-__libraryName@kebab__.module.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/src/lib/__target@kebab__-__libraryName@kebab__.module.ts.template new file mode 100644 index 0000000000..1165afb970 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/src/lib/__target@kebab__-__libraryName@kebab__.module.ts.template @@ -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: [] + } + } +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/src/lib/index.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/src/lib/index.ts.template new file mode 100644 index 0000000000..5631c60e6d --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/src/lib/index.ts.template @@ -0,0 +1 @@ +export * from './<%= kebab(target) %>-<%= kebab(libraryName) %>.module'; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/src/public-api.ts.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/src/public-api.ts.template new file mode 100644 index 0000000000..11aece60c4 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/src/public-api.ts.template @@ -0,0 +1 @@ +export * from './lib/index'; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/index.ts b/npm/ng-packs/packages/schematics/src/commands/create-lib/index.ts new file mode 100644 index 0000000000..1bc4c7a905 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/index.ts @@ -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), + ]), + ]); +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/models/generate-lib-schema.ts b/npm/ng-packs/packages/schematics/src/commands/create-lib/models/generate-lib-schema.ts new file mode 100644 index 0000000000..f3c6e5b97f --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/models/generate-lib-schema.ts @@ -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; +} diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/models/index.ts b/npm/ng-packs/packages/schematics/src/commands/create-lib/models/index.ts new file mode 100644 index 0000000000..e84ddcba49 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/models/index.ts @@ -0,0 +1 @@ +export * from './generate-lib-schema'; diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/schema.json b/npm/ng-packs/packages/schematics/src/commands/create-lib/schema.json new file mode 100644 index 0000000000..64d64c1d80 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/schema.json @@ -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": [] +} diff --git a/npm/ng-packs/packages/schematics/src/constants/symbols.ts b/npm/ng-packs/packages/schematics/src/constants/symbols.ts new file mode 100644 index 0000000000..f8280b84c4 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/constants/symbols.ts @@ -0,0 +1 @@ +export const NOT_FOUND_VALUE = Symbol('NOT_FOUND_VALUE'); diff --git a/npm/ng-packs/packages/schematics/src/enums/exception.ts b/npm/ng-packs/packages/schematics/src/enums/exception.ts index b7b206cbf6..88f5d1862b 100644 --- a/npm/ng-packs/packages/schematics/src/enums/exception.ts +++ b/npm/ng-packs/packages/schematics/src/enums/exception.ts @@ -13,4 +13,5 @@ export const enum Exception { NoEnvironment = '[Environment Not Found] An environment file cannot be located in "{0}" project.', NoApiUrl = '[API URL Not Found] Cannot resolve API URL for "{1}" remote service name from "{0}" project.', NoRootNamespace = '[Root Namespace Not Found] Cannot resolve root namespace for "{1}" api from "{0}" project.', + LibraryAlreadyExists = '[Library Already Exists] "{0}" already exists. Use --override to override.', } diff --git a/npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts b/npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts index 7b0c3701a2..b089305e17 100644 --- a/npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts +++ b/npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts @@ -23,4 +23,14 @@ export interface GenerateProxySchema { * Url to API definition */ url?: string; + + /** + * Create a lib for proxy generation + */ + createTarget?: boolean; + + /** + * Secondary entrypoint for proxy generation + */ + entryPoint?: string; } diff --git a/npm/ng-packs/packages/schematics/src/utils/angular-schematic/generate-lib.ts b/npm/ng-packs/packages/schematics/src/utils/angular-schematic/generate-lib.ts new file mode 100644 index 0000000000..f96b2b38ea --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/utils/angular-schematic/generate-lib.ts @@ -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`, + }, + }, + }, + }); + }); +} diff --git a/npm/ng-packs/packages/schematics/src/utils/angular-schematic/index.ts b/npm/ng-packs/packages/schematics/src/utils/angular-schematic/index.ts new file mode 100644 index 0000000000..0d2dd109ca --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/utils/angular-schematic/index.ts @@ -0,0 +1 @@ +export * from './generate-lib'; diff --git a/npm/ng-packs/packages/schematics/src/utils/index.ts b/npm/ng-packs/packages/schematics/src/utils/index.ts index eeb12e6d9e..53950e8a87 100644 --- a/npm/ng-packs/packages/schematics/src/utils/index.ts +++ b/npm/ng-packs/packages/schematics/src/utils/index.ts @@ -7,6 +7,7 @@ export * from './enum'; export * from './file'; export * from './generics'; export * from './import'; +export * from './lib'; export * from './methods'; export * from './model'; export * from './namespace'; diff --git a/npm/ng-packs/packages/schematics/src/utils/lib.ts b/npm/ng-packs/packages/schematics/src/utils/lib.ts new file mode 100644 index 0000000000..d083c6c206 --- /dev/null +++ b/npm/ng-packs/packages/schematics/src/utils/lib.ts @@ -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) { + + } +} diff --git a/npm/ng-packs/packages/schematics/src/utils/source.ts b/npm/ng-packs/packages/schematics/src/utils/source.ts index ad488b27be..be1156a775 100644 --- a/npm/ng-packs/packages/schematics/src/utils/source.ts +++ b/npm/ng-packs/packages/schematics/src/utils/source.ts @@ -23,7 +23,7 @@ export function createApiDefinitionGetter(params: GenerateProxySchema) { if (!sourceUrl) { sourceUrl = getSourceUrl(host, source, apiName); } - return await getApiDefinition(sourceUrl); + return await getApiDefinition(sourceUrl!); }; } diff --git a/npm/ng-packs/packages/schematics/src/utils/workspace.ts b/npm/ng-packs/packages/schematics/src/utils/workspace.ts index 81bfdc7307..54e8491d41 100644 --- a/npm/ng-packs/packages/schematics/src/utils/workspace.ts +++ b/npm/ng-packs/packages/schematics/src/utils/workspace.ts @@ -3,9 +3,11 @@ import { strings, workspaces } from '@angular-devkit/core'; import { SchematicsException, Tree } from '@angular-devkit/schematics'; import { Exception } from '../enums'; import { Project } from '../models'; -import { getWorkspace, getWorkspaceSchema, ProjectType, WorkspaceSchema } from './angular'; +import { getWorkspace, ProjectType, WorkspaceSchema } from './angular'; import { findEnvironmentExpression } from './ast'; import { readFileInTree } from './common'; +import { NOT_FOUND_VALUE } from '../constants/symbols'; +import { parseJson } from '@angular/cli/utilities/json-file'; export function isLibrary(project: workspaces.ProjectDefinition): boolean { return project.extensions['projectType'] === ProjectType.Library; @@ -33,8 +35,11 @@ export function readWorkspaceSchema(tree: Tree) { return workspaceSchema; } - -export async function resolveProject(tree: Tree, name: string): Promise { +export async function resolveProject( + tree: Tree, + name: string, + notFoundValue: T = NOT_FOUND_VALUE as unknown as any, +): Promise { name = name || readWorkspaceSchema(tree).defaultProject!; const workspace = await getWorkspace(tree); let definition: Project['definition'] | undefined; @@ -61,7 +66,30 @@ export async function resolveProject(tree: Tree, name: string): Promise definition = workspace.projects.get(name); } catch (_) {} + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (!definition && notFoundValue !== NOT_FOUND_VALUE) { + return notFoundValue; + } if (!definition) throw new SchematicsException(Exception.NoProject); return { name, definition }; } + +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) as Record as WorkspaceSchema; +} + +export function getWorkspacePath(host: Tree): string { + const possibleFiles = ['/angular.json', '/.angular.json']; + const path = possibleFiles.filter(path => host.exists(path))[0]; + + return path; +} diff --git a/npm/ng-packs/scripts/build-schematics.ts b/npm/ng-packs/scripts/build-schematics.ts index 64da4ac856..8884ca9571 100644 --- a/npm/ng-packs/scripts/build-schematics.ts +++ b/npm/ng-packs/scripts/build-schematics.ts @@ -20,6 +20,12 @@ class FileCopy { const PACKAGE_TO_BUILD = 'schematics'; const FILES_TO_COPY_AFTER_BUILD: (FileCopy | string)[] = [ + { src: 'src/commands/create-lib/schema.json', dest: 'commands/create-lib/schema.json' }, + { src: 'src/commands/create-lib/files-package', dest: 'commands/create-lib/files-package' }, + { + src: 'src/commands/create-lib/files-secondary-entrypoint', + dest: 'commands/create-lib/files-secondary-entrypoint', + }, { src: 'src/commands/proxy-add/schema.json', dest: 'commands/proxy-add/schema.json' }, { src: 'src/commands/proxy-index/schema.json', dest: 'commands/proxy-index/schema.json' }, { src: 'src/commands/proxy-refresh/schema.json', dest: 'commands/proxy-refresh/schema.json' }, From 31510763ae5b2483c803eac1cd9013fecb353509 Mon Sep 17 00:00:00 2001 From: muhammedaltug Date: Tue, 23 Aug 2022 08:41:42 +0300 Subject: [PATCH 3/3] modify tsconfig file and other commands --- .../schematics/src/commands/api/index.ts | 4 ++-- .../schematics/src/commands/api/schema.json | 9 +++++++++ .../tsconfig.lib.json.template | 2 +- .../schematics/src/commands/proxy-add/index.ts | 4 ++-- .../src/commands/proxy-add/schema.json | 9 +++++++++ .../src/commands/proxy-index/index.ts | 6 +++--- .../src/commands/proxy-index/schema.json | 9 +++++++++ .../src/commands/proxy-refresh/index.ts | 4 ++-- .../src/commands/proxy-refresh/schema.json | 10 ++++++++++ .../src/commands/proxy-remove/index.ts | 4 ++-- .../src/commands/proxy-remove/schema.json | 9 +++++++++ .../src/models/generate-proxy-schema.ts | 5 ----- .../packages/schematics/src/utils/workspace.ts | 17 ++++++++++++++++- 13 files changed, 74 insertions(+), 18 deletions(-) diff --git a/npm/ng-packs/packages/schematics/src/commands/api/index.ts b/npm/ng-packs/packages/schematics/src/commands/api/index.ts index 2f2f52a50c..bc97d8031b 100644 --- a/npm/ng-packs/packages/schematics/src/commands/api/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/api/index.ts @@ -13,7 +13,7 @@ import { Exception } from '../../enums'; import { GenerateProxySchema, ServiceGeneratorParams } from '../../models'; import { applyWithOverwrite, - buildDefaultPath, + buildTargetPath, createControllerToServiceMapper, createImportRefsToModelReducer, createImportRefToEnumMapper, @@ -42,7 +42,7 @@ export default function (schema: GenerateProxySchema) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const target = await resolveProject(tree, params.target!); - const targetPath = buildDefaultPath(target.definition); + const targetPath = buildTargetPath(target.definition, params.entryPoint); const readProxyConfig = createProxyConfigReader(targetPath); const createProxyConfigWriter = createProxyConfigWriterCreator(targetPath); const data = readProxyConfig(tree); diff --git a/npm/ng-packs/packages/schematics/src/commands/api/schema.json b/npm/ng-packs/packages/schematics/src/commands/api/schema.json index 11ca6845ae..ddaf06f001 100644 --- a/npm/ng-packs/packages/schematics/src/commands/api/schema.json +++ b/npm/ng-packs/packages/schematics/src/commands/api/schema.json @@ -39,6 +39,15 @@ "index": 3 }, "x-prompt": "Please enter target Angular project to place the generated code. (default: workspace \"defaultProject\")" + }, + "entryPoint": { + "description": "Target Angular project to place the generated code", + "type": "string", + "$default": { + "$source": "argv", + "index": 5 + }, + "x-prompt": "Please enter target Angular project to place the generated code. (default: workspace \"defaultProject\")" } }, "required": [] diff --git a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.lib.json.template b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.lib.json.template index 8da4414fb7..1407202dd0 100644 --- a/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.lib.json.template +++ b/npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.lib.json.template @@ -1,6 +1,6 @@ /* To learn more about this file see: https://angular.io/config/tsconfig. */ { - "extends": "../../tsconfig.prod.json", + "extends": "../../tsconfig.json", "compilerOptions": { "outDir": "../../out-tsc/lib", "target": "es2015", diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy-add/index.ts b/npm/ng-packs/packages/schematics/src/commands/proxy-add/index.ts index 4a6b4cf92e..9ea893e1f8 100644 --- a/npm/ng-packs/packages/schematics/src/commands/proxy-add/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/proxy-add/index.ts @@ -1,7 +1,7 @@ import { chain, SchematicContext, Tree } from '@angular-devkit/schematics'; import { GenerateProxySchema } from '../../models'; import { - buildDefaultPath, + buildTargetPath, createApiDefinitionGetter, createApisGenerator, createProxyClearer, @@ -21,7 +21,7 @@ export default function (schema: GenerateProxySchema) { return chain([ async (host: Tree, _context: SchematicContext) => { const target = await resolveProject(host, params.target!); - const targetPath = buildDefaultPath(target.definition); + const targetPath = buildTargetPath(target.definition, params.entryPoint); const readProxyConfig = createProxyConfigReader(targetPath); let generated: string[] = []; diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json b/npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json index 2c54f8f9fa..2cf27f9c33 100644 --- a/npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json +++ b/npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json @@ -50,5 +50,14 @@ "x-prompt": "Please enter URL for API definition (default: API Name's url in environment file)" } }, + "entryPoint": { + "description": "Target Angular project to place the generated code", + "type": "string", + "$default": { + "$source": "argv", + "index": 5 + }, + "x-prompt": "Please enter target Angular project to place the generated code. (default: workspace \"defaultProject\")" + }, "required": [] } diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy-index/index.ts b/npm/ng-packs/packages/schematics/src/commands/proxy-index/index.ts index 498428c154..473dbbe63e 100644 --- a/npm/ng-packs/packages/schematics/src/commands/proxy-index/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/proxy-index/index.ts @@ -1,18 +1,18 @@ import { SchematicContext, Tree } from '@angular-devkit/schematics'; import { - buildDefaultPath, + buildTargetPath, createProxyIndexGenerator, removeDefaultPlaceholders, resolveProject, } from '../../utils'; -export default function (schema: { target?: string }) { +export default function (schema: { target?: string; entryPoint?: string }) { const params = removeDefaultPlaceholders(schema); return async (host: Tree, _context: SchematicContext) => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const target = await resolveProject(host, params.target!); - const targetPath = buildDefaultPath(target.definition); + const targetPath = buildTargetPath(target.definition, params.entryPoint); const generateIndex = createProxyIndexGenerator(targetPath); diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy-index/schema.json b/npm/ng-packs/packages/schematics/src/commands/proxy-index/schema.json index a8173e63f4..545180954a 100644 --- a/npm/ng-packs/packages/schematics/src/commands/proxy-index/schema.json +++ b/npm/ng-packs/packages/schematics/src/commands/proxy-index/schema.json @@ -12,6 +12,15 @@ "index": 0 }, "x-prompt": "Please enter target Angular project to place the generated code. (default: workspace \"defaultProject\")" + }, + "entryPoint": { + "description": "Target Angular project to place the generated code", + "type": "string", + "$default": { + "$source": "argv", + "index": 5 + }, + "x-prompt": "Please enter target Angular project to place the generated code. (default: workspace \"defaultProject\")" } }, "required": [] diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy-refresh/index.ts b/npm/ng-packs/packages/schematics/src/commands/proxy-refresh/index.ts index 9428b9ae4d..cf905738b4 100644 --- a/npm/ng-packs/packages/schematics/src/commands/proxy-refresh/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/proxy-refresh/index.ts @@ -1,7 +1,7 @@ import { chain, SchematicContext, Tree } from '@angular-devkit/schematics'; import { GenerateProxySchema } from '../../models'; import { - buildDefaultPath, + buildTargetPath, createApiDefinitionGetter, createApisGenerator, createProxyClearer, @@ -18,7 +18,7 @@ export default function (schema: GenerateProxySchema) { return async (host: Tree, _context: SchematicContext) => { const target = await resolveProject(host, params.target!); - const targetPath = buildDefaultPath(target.definition); + const targetPath = buildTargetPath(target.definition, params.entryPoint); const readProxyConfig = createProxyConfigReader(targetPath); const { generated } = readProxyConfig(host); diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy-refresh/schema.json b/npm/ng-packs/packages/schematics/src/commands/proxy-refresh/schema.json index ad0ba1599a..c5fd51f9b4 100644 --- a/npm/ng-packs/packages/schematics/src/commands/proxy-refresh/schema.json +++ b/npm/ng-packs/packages/schematics/src/commands/proxy-refresh/schema.json @@ -48,7 +48,17 @@ "index": 4 }, "x-prompt": "Please enter URL for api definition (default: API Name's url in environment file)" + }, + "entryPoint": { + "description": "Target Angular project to place the generated code", + "type": "string", + "$default": { + "$source": "argv", + "index": 5 + }, + "x-prompt": "Please enter target Angular project to place the generated code. (default: workspace \"defaultProject\")" } }, + "required": [] } diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy-remove/index.ts b/npm/ng-packs/packages/schematics/src/commands/proxy-remove/index.ts index 900bd5f1a3..5e1980b3f7 100644 --- a/npm/ng-packs/packages/schematics/src/commands/proxy-remove/index.ts +++ b/npm/ng-packs/packages/schematics/src/commands/proxy-remove/index.ts @@ -1,7 +1,7 @@ import { chain, SchematicContext, Tree } from '@angular-devkit/schematics'; import { GenerateProxySchema } from '../../models'; import { - buildDefaultPath, + buildTargetPath, createApiDefinitionGetter, createApisGenerator, createProxyClearer, @@ -20,7 +20,7 @@ export default function (schema: GenerateProxySchema) { return async (host: Tree, _context: SchematicContext) => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const target = await resolveProject(host, params.target!); - const targetPath = buildDefaultPath(target.definition); + const targetPath = buildTargetPath(target.definition, params.entryPoint); const readProxyConfig = createProxyConfigReader(targetPath); const { generated } = readProxyConfig(host); diff --git a/npm/ng-packs/packages/schematics/src/commands/proxy-remove/schema.json b/npm/ng-packs/packages/schematics/src/commands/proxy-remove/schema.json index 2c54f8f9fa..253ac0b0c3 100644 --- a/npm/ng-packs/packages/schematics/src/commands/proxy-remove/schema.json +++ b/npm/ng-packs/packages/schematics/src/commands/proxy-remove/schema.json @@ -48,6 +48,15 @@ "index": 4 }, "x-prompt": "Please enter URL for API definition (default: API Name's url in environment file)" + }, + "entryPoint": { + "description": "Target Angular project to place the generated code", + "type": "string", + "$default": { + "$source": "argv", + "index": 5 + }, + "x-prompt": "Please enter target Angular project to place the generated code. (default: workspace \"defaultProject\")" } }, "required": [] diff --git a/npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts b/npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts index b089305e17..682cdda9fe 100644 --- a/npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts +++ b/npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts @@ -24,11 +24,6 @@ export interface GenerateProxySchema { */ url?: string; - /** - * Create a lib for proxy generation - */ - createTarget?: boolean; - /** * Secondary entrypoint for proxy generation */ diff --git a/npm/ng-packs/packages/schematics/src/utils/workspace.ts b/npm/ng-packs/packages/schematics/src/utils/workspace.ts index 54e8491d41..41bad90615 100644 --- a/npm/ng-packs/packages/schematics/src/utils/workspace.ts +++ b/npm/ng-packs/packages/schematics/src/utils/workspace.ts @@ -3,7 +3,7 @@ import { strings, workspaces } from '@angular-devkit/core'; import { SchematicsException, Tree } from '@angular-devkit/schematics'; import { Exception } from '../enums'; import { Project } from '../models'; -import { getWorkspace, ProjectType, WorkspaceSchema } from './angular'; +import { buildDefaultPath, getWorkspace, ProjectType, WorkspaceSchema } from './angular'; import { findEnvironmentExpression } from './ast'; import { readFileInTree } from './common'; import { NOT_FOUND_VALUE } from '../constants/symbols'; @@ -93,3 +93,18 @@ export function getWorkspacePath(host: Tree): string { return path; } + +/** + * Build a default project path for generating. + * @param project The project which will have its default path generated. + * @param entryPoint The secondary-entry-point. + */ +export function buildTargetPath( + project: workspaces.ProjectDefinition, + entryPoint?: string, +): string { + if (entryPoint) { + return `${project.root}/${entryPoint}/src`; + } + return buildDefaultPath(project); +}