Browse Source

Merge pull request #13716 from abpframework/feat/13293

Add create lib command
pull/14064/head
Mahmut Gundogdu 4 years ago
committed by GitHub
parent
commit
4c252b5ab2
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      npm/ng-packs/packages/schematics/package.json
  2. 5
      npm/ng-packs/packages/schematics/src/collection.json
  3. 4
      npm/ng-packs/packages/schematics/src/commands/api/index.ts
  4. 9
      npm/ng-packs/packages/schematics/src/commands/api/schema.json
  5. 44
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/.eslintrc.json.template
  6. 7
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/ng-package.json.template
  7. 12
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/__libraryName@kebab__-config.module.ts.template
  8. 1
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/enums/index.ts.template
  9. 3
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/enums/route-names.ts.template
  10. 1
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/providers/index.ts.template
  11. 26
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/providers/route.provider.ts.template
  12. 3
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/config/src/public-api.ts.template
  13. 44
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/karma.conf.js.template
  14. 7
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/ng-package.json.template
  15. 11
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/package.json.template
  16. 18
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/lib/__libraryName@kebab__-routing.module.ts.template
  17. 22
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/lib/__libraryName@kebab__.module.ts.template
  18. 4
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/public-api.ts.template
  19. 26
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/src/test.ts.template
  20. 20
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.lib.json.template
  21. 10
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.lib.prod.json.template
  22. 17
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-package/__libraryName@kebab__/tsconfig.spec.json.template
  23. 7
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/ng-package.json.template
  24. 11
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/src/lib/__target@kebab__-__libraryName@kebab__.module.ts.template
  25. 1
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/src/lib/index.ts.template
  26. 1
      npm/ng-packs/packages/schematics/src/commands/create-lib/files-secondary-entrypoint/__libraryName@kebab__/src/public-api.ts.template
  27. 159
      npm/ng-packs/packages/schematics/src/commands/create-lib/index.ts
  28. 17
      npm/ng-packs/packages/schematics/src/commands/create-lib/models/generate-lib-schema.ts
  29. 1
      npm/ng-packs/packages/schematics/src/commands/create-lib/models/index.ts
  30. 36
      npm/ng-packs/packages/schematics/src/commands/create-lib/schema.json
  31. 4
      npm/ng-packs/packages/schematics/src/commands/proxy-add/index.ts
  32. 9
      npm/ng-packs/packages/schematics/src/commands/proxy-add/schema.json
  33. 6
      npm/ng-packs/packages/schematics/src/commands/proxy-index/index.ts
  34. 9
      npm/ng-packs/packages/schematics/src/commands/proxy-index/schema.json
  35. 4
      npm/ng-packs/packages/schematics/src/commands/proxy-refresh/index.ts
  36. 10
      npm/ng-packs/packages/schematics/src/commands/proxy-refresh/schema.json
  37. 4
      npm/ng-packs/packages/schematics/src/commands/proxy-remove/index.ts
  38. 9
      npm/ng-packs/packages/schematics/src/commands/proxy-remove/schema.json
  39. 1
      npm/ng-packs/packages/schematics/src/constants/symbols.ts
  40. 1
      npm/ng-packs/packages/schematics/src/enums/exception.ts
  41. 5
      npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts
  42. 52
      npm/ng-packs/packages/schematics/src/utils/angular-schematic/generate-lib.ts
  43. 1
      npm/ng-packs/packages/schematics/src/utils/angular-schematic/index.ts
  44. 2
      npm/ng-packs/packages/schematics/src/utils/angular/README.md
  45. 193
      npm/ng-packs/packages/schematics/src/utils/angular/ast-utils.ts
  46. 32
      npm/ng-packs/packages/schematics/src/utils/angular/change.ts
  47. 541
      npm/ng-packs/packages/schematics/src/utils/angular/config.ts
  48. 12
      npm/ng-packs/packages/schematics/src/utils/angular/dependencies.ts
  49. 25
      npm/ng-packs/packages/schematics/src/utils/angular/find-module.ts
  50. 61
      npm/ng-packs/packages/schematics/src/utils/angular/generate-from-files.ts
  51. 6
      npm/ng-packs/packages/schematics/src/utils/angular/index.ts
  52. 43
      npm/ng-packs/packages/schematics/src/utils/angular/json-file.ts
  53. 221
      npm/ng-packs/packages/schematics/src/utils/angular/json-utils.ts
  54. 26
      npm/ng-packs/packages/schematics/src/utils/angular/latest-versions.ts
  55. 20
      npm/ng-packs/packages/schematics/src/utils/angular/latest-versions/package.json
  56. 54
      npm/ng-packs/packages/schematics/src/utils/angular/lint-fix.ts
  57. 9
      npm/ng-packs/packages/schematics/src/utils/angular/ng-ast-utils.ts
  58. 5
      npm/ng-packs/packages/schematics/src/utils/angular/parse-name.ts
  59. 2
      npm/ng-packs/packages/schematics/src/utils/angular/paths.ts
  60. 2
      npm/ng-packs/packages/schematics/src/utils/angular/project-targets.ts
  61. 76
      npm/ng-packs/packages/schematics/src/utils/angular/tsconfig.ts
  62. 60
      npm/ng-packs/packages/schematics/src/utils/angular/validation.ts
  63. 23
      npm/ng-packs/packages/schematics/src/utils/angular/workspace-models.ts
  64. 51
      npm/ng-packs/packages/schematics/src/utils/angular/workspace.ts
  65. 1
      npm/ng-packs/packages/schematics/src/utils/index.ts
  66. 9
      npm/ng-packs/packages/schematics/src/utils/lib.ts
  67. 2
      npm/ng-packs/packages/schematics/src/utils/source.ts
  68. 49
      npm/ng-packs/packages/schematics/src/utils/workspace.ts
  69. 6
      npm/ng-packs/scripts/build-schematics.ts

6
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",

5
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"
}
}
}

4
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);

9
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": []

44
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": {}
}
]
}

7
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"
}
}

12
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],
};
}
}

1
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';

3
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) %>',
}

1
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';

26
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,
},
]);
};
}

3
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';

44
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
});
};

7
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"
}
}

11
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"
}
}

18
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 {}

22
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());
}
}

4
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';

26
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[];
<T>(id: string): T;
};
};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);

20
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.json",
"compilerOptions": {
"outDir": "../../out-tsc/lib",
"target": "es2015",
"declaration": true,
"declarationMap": true,
"inlineSources": true,
"types": [],
"lib": [
"dom",
"es2018"
]
},
"exclude": [
"src/test.ts",
"**/*.spec.ts"
]
}

10
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"
}
}

17
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"
]
}

7
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"
}
}

11
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: []
}
}
}

1
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';

1
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';

159
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),
]),
]);
}

17
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;
}

1
npm/ng-packs/packages/schematics/src/commands/create-lib/models/index.ts

@ -0,0 +1 @@
export * from './generate-lib-schema';

36
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": []
}

4
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[] = [];

9
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": []
}

6
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);

9
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": []

4
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);

10
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": []
}

4
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);

9
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": []

1
npm/ng-packs/packages/schematics/src/constants/symbols.ts

@ -0,0 +1 @@
export const NOT_FOUND_VALUE = Symbol('NOT_FOUND_VALUE');

1
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.',
}

5
npm/ng-packs/packages/schematics/src/models/generate-proxy-schema.ts

@ -23,4 +23,9 @@ export interface GenerateProxySchema {
* Url to API definition
*/
url?: string;
/**
* Secondary entrypoint for proxy generation
*/
entryPoint?: string;
}

52
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`,
},
},
},
});
});
}

1
npm/ng-packs/packages/schematics/src/utils/angular-schematic/index.ts

@ -0,0 +1 @@
export * from './generate-lib';

2
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.

193
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<T extends ts.Node>(
}
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<ts.Node>;
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) {

32
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<void>;
read(path: string): Promise<string>;
@ -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<void> {
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<void> {
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.');
}
}
}

541
npm/ng-packs/packages/schematics/src/utils/angular/config.ts

@ -1,541 +0,0 @@
/* eslint-disable @typescript-eslint/ban-types */
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { JsonParseMode, parseJson } from '@angular-devkit/core';
import { Rule, SchematicContext, SchematicsException, Tree } from '@angular-devkit/schematics';
import { ProjectType, WorkspaceProject, WorkspaceSchema } from './workspace-models';
// The interfaces below are generated from the Angular CLI configuration schema
// https://github.com/angular/angular-cli/blob/master/packages/@angular/cli/lib/config/schema.json
export interface AppConfig {
/**
* Name of the app.
*/
name?: string;
/**
* Directory where app files are placed.
*/
appRoot?: string;
/**
* The root directory of the app.
*/
root?: string;
/**
* The output directory for build results.
*/
outDir?: string;
/**
* List of application assets.
*/
assets?: (
| string
| {
/**
* The pattern to match.
*/
glob?: string;
/**
* The dir to search within.
*/
input?: string;
/**
* The output path (relative to the outDir).
*/
output?: string;
}
)[];
/**
* URL where files will be deployed.
*/
deployUrl?: string;
/**
* Base url for the application being built.
*/
baseHref?: string;
/**
* The runtime platform of the app.
*/
platform?: 'browser' | 'server';
/**
* The name of the start HTML file.
*/
index?: string;
/**
* The name of the main entry-point file.
*/
main?: string;
/**
* The name of the polyfills file.
*/
polyfills?: string;
/**
* The name of the test entry-point file.
*/
test?: string;
/**
* The name of the TypeScript configuration file.
*/
tsconfig?: string;
/**
* The name of the TypeScript configuration file for unit tests.
*/
testTsconfig?: string;
/**
* The prefix to apply to generated selectors.
*/
prefix?: string;
/**
* Experimental support for a service worker from @angular/service-worker.
*/
serviceWorker?: boolean;
/**
* Global styles to be included in the build.
*/
styles?: (
| string
| {
input?: string;
[name: string]: any;
}
)[];
/**
* Options to pass to style preprocessors
*/
stylePreprocessorOptions?: {
/**
* Paths to include. Paths will be resolved to project root.
*/
includePaths?: string[];
};
/**
* Global scripts to be included in the build.
*/
scripts?: (
| string
| {
input: string;
[name: string]: any;
}
)[];
/**
* Source file for environment config.
*/
environmentSource?: string;
/**
* Name and corresponding file for environment config.
*/
environments?: {
[name: string]: any;
};
appShell?: {
app: string;
route: string;
};
budgets?: {
/**
* The type of budget
*/
type?: 'bundle' | 'initial' | 'allScript' | 'all' | 'anyScript' | 'any' | 'anyComponentStyle';
/**
* The name of the bundle
*/
name?: string;
/**
* The baseline size for comparison.
*/
baseline?: string;
/**
* The maximum threshold for warning relative to the baseline.
*/
maximumWarning?: string;
/**
* The maximum threshold for error relative to the baseline.
*/
maximumError?: string;
/**
* The minimum threshold for warning relative to the baseline.
*/
minimumWarning?: string;
/**
* The minimum threshold for error relative to the baseline.
*/
minimumError?: string;
/**
* The threshold for warning relative to the baseline (min & max).
*/
warning?: string;
/**
* The threshold for error relative to the baseline (min & max).
*/
error?: string;
}[];
}
export interface CliConfig {
$schema?: string;
/**
* The global configuration of the project.
*/
project?: {
/**
* The name of the project.
*/
name?: string;
/**
* Whether or not this project was ejected.
*/
ejected?: boolean;
};
/**
* Properties of the different applications in this project.
*/
apps?: AppConfig[];
/**
* Configuration for end-to-end tests.
*/
e2e?: {
protractor?: {
/**
* Path to the config file.
*/
config?: string;
};
};
/**
* Properties to be passed to TSLint.
*/
lint?: {
/**
* File glob(s) to lint.
*/
files?: string | string[];
/**
* Location of the tsconfig.json project file.
* Will also use as files to lint if 'files' property not present.
*/
project: string;
/**
* Location of the tslint.json configuration.
*/
tslintConfig?: string;
/**
* File glob(s) to ignore.
*/
exclude?: string | string[];
}[];
/**
* Configuration for unit tests.
*/
test?: {
karma?: {
/**
* Path to the karma config file.
*/
config?: string;
};
codeCoverage?: {
/**
* Globs to exclude from code coverage.
*/
exclude?: string[];
};
};
/**
* Specify the default values for generating.
*/
defaults?: {
/**
* The file extension to be used for style files.
*/
styleExt?: string;
/**
* How often to check for file updates.
*/
poll?: number;
/**
* Use lint to fix files after generation
*/
lintFix?: boolean;
/**
* Options for generating a class.
*/
class?: {
/**
* Specifies if a spec file is generated.
*/
spec?: boolean;
};
/**
* Options for generating a component.
*/
component?: {
/**
* Flag to indicate if a directory is created.
*/
flat?: boolean;
/**
* Specifies if a spec file is generated.
*/
spec?: boolean;
/**
* Specifies if the style will be in the ts file.
*/
inlineStyle?: boolean;
/**
* Specifies if the template will be in the ts file.
*/
inlineTemplate?: boolean;
/**
* Specifies the view encapsulation strategy.
*/
viewEncapsulation?: 'Emulated' | 'Native' | 'None';
/**
* Specifies the change detection strategy.
*/
changeDetection?: 'Default' | 'OnPush';
};
/**
* Options for generating a directive.
*/
directive?: {
/**
* Flag to indicate if a directory is created.
*/
flat?: boolean;
/**
* Specifies if a spec file is generated.
*/
spec?: boolean;
};
/**
* Options for generating a guard.
*/
guard?: {
/**
* Flag to indicate if a directory is created.
*/
flat?: boolean;
/**
* Specifies if a spec file is generated.
*/
spec?: boolean;
};
/**
* Options for generating an interface.
*/
interface?: {
/**
* Prefix to apply to interface names. (i.e. I)
*/
prefix?: string;
};
/**
* Options for generating a module.
*/
module?: {
/**
* Flag to indicate if a directory is created.
*/
flat?: boolean;
/**
* Specifies if a spec file is generated.
*/
spec?: boolean;
};
/**
* Options for generating a pipe.
*/
pipe?: {
/**
* Flag to indicate if a directory is created.
*/
flat?: boolean;
/**
* Specifies if a spec file is generated.
*/
spec?: boolean;
};
/**
* Options for generating a service.
*/
service?: {
/**
* Flag to indicate if a directory is created.
*/
flat?: boolean;
/**
* Specifies if a spec file is generated.
*/
spec?: boolean;
};
/**
* Properties to be passed to the build command.
*/
build?: {
/**
* Output sourcemaps.
*/
sourcemaps?: boolean;
/**
* Base url for the application being built.
*/
baseHref?: string;
/**
* The ssl key used by the server.
*/
progress?: boolean;
/**
* Enable and define the file watching poll time period (milliseconds).
*/
poll?: number;
/**
* Delete output path before build.
*/
deleteOutputPath?: boolean;
/**
* Do not use the real path when resolving modules.
*/
preserveSymlinks?: boolean;
/**
* Show circular dependency warnings on builds.
*/
showCircularDependencies?: boolean;
/**
* Use a separate bundle containing code used across multiple bundles.
*/
commonChunk?: boolean;
/**
* Use file name for lazy loaded chunks.
*/
namedChunks?: boolean;
};
/**
* Properties to be passed to the serve command.
*/
serve?: {
/**
* The port the application will be served on.
*/
port?: number;
/**
* The host the application will be served on.
*/
host?: string;
/**
* Enables ssl for the application.
*/
ssl?: boolean;
/**
* The ssl key used by the server.
*/
sslKey?: string;
/**
* The ssl certificate used by the server.
*/
sslCert?: string;
/**
* Proxy configuration file.
*/
proxyConfig?: string;
};
/**
* Properties about schematics.
*/
schematics?: {
/**
* The schematics collection to use.
*/
collection?: string;
/**
* The new app schematic.
*/
newApp?: string;
};
};
/**
* Specify which package manager tool to use.
*/
packageManager?: 'npm' | 'cnpm' | 'yarn' | 'default';
/**
* Allow people to disable console warnings.
*/
warnings?: {
versionMismatch?: boolean;
};
}
export function getWorkspacePath(host: Tree): string {
const possibleFiles = ['/angular.json', '/.angular.json'];
const path = possibleFiles.filter(path => host.exists(path))[0];
return path;
}
export function getWorkspaceSchema(host: Tree): WorkspaceSchema {
const path = getWorkspacePath(host);
const configBuffer = host.read(path);
if (configBuffer === null) {
throw new SchematicsException(`Could not find (${path})`);
}
const content = configBuffer.toString();
return parseJson(content, JsonParseMode.Loose) as {} as WorkspaceSchema;
}
export function addProjectToWorkspace<TProjectType extends ProjectType = ProjectType.Application>(
workspace: WorkspaceSchema,
name: string,
project: WorkspaceProject<TProjectType>,
): Rule {
return (_host: Tree, _context: SchematicContext) => {
if (workspace.projects[name]) {
throw new Error(`Project '${name}' already exists in workspace.`);
}
// Add project to workspace.
workspace.projects[name] = project;
if (!workspace.defaultProject && Object.keys(workspace.projects).length === 1) {
// Make the new project the default one.
workspace.defaultProject = name;
}
return updateWorkspaceSchema(workspace);
};
}
export function updateWorkspaceSchema(workspace: WorkspaceSchema): Rule {
return (host: Tree, _context: SchematicContext) => {
host.overwrite(getWorkspacePath(host), JSON.stringify(workspace, null, 2));
};
}
export const configPath = '/.angular-cli.json';
export function getConfig(host: Tree): CliConfig {
const configBuffer = host.read(configPath);
if (configBuffer === null) {
throw new SchematicsException('Could not find .angular-cli.json');
}
const config = parseJson(configBuffer.toString(), JsonParseMode.Loose) as {} as CliConfig;
return config;
}
export function getAppFromConfig(config: CliConfig, appIndexOrName: string): AppConfig | null {
if (!config.apps) {
return null;
}
if (parseInt(appIndexOrName) >= 0) {
return config.apps[parseInt(appIndexOrName)];
}
return config.apps.filter(app => app.name === appIndexOrName)[0];
}

12
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]);

25
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);
}

61
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, string | ((v: string) => string)> = {},
): Rule {
return async (host: Tree) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
options.path ??= await createDefaultPath(host, options.project as string);
options.prefix ??= '';
options.flat ??= true;
const parsedPath = parseName(options.path!, options.name);
options.name = parsedPath.name;
options.path = parsedPath.path;
const templateSource = apply(url('./files'), [
options.skipTests ? filter(path => !path.endsWith('.spec.ts.template')) : noop(),
applyTemplates({
...strings,
...options,
...extraTemplateValues,
}),
move(parsedPath.path + (options.flat ? '' : '/' + strings.dasherize(options.name))),
]);
return chain([mergeWith(templateSource)]);
};
}

6
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';

43
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, {

221
npm/ng-packs/packages/schematics/src/utils/angular/json-utils.ts

@ -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);
}

26
npm/ng-packs/packages/schematics/src/utils/angular/latest-versions.ts

@ -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',
};

20
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"
}
}

54
npm/ng-packs/packages/schematics/src/utils/angular/lint-fix.ts

@ -1,54 +0,0 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {
DirEntry,
Rule,
SchematicContext,
SchematicsException,
Tree,
} from '@angular-devkit/schematics';
import { TslintFixTask } from '@angular-devkit/schematics/tasks';
export function applyLintFix(path = '/'): Rule {
return (tree: Tree, context: SchematicContext) => {
// Find the closest tslint.json or tslint.yaml
let dir: DirEntry | null = tree.getDir(path.substr(0, path.lastIndexOf('/')));
do {
if ((dir.subfiles as string[]).some(f => f === 'tslint.json' || f === 'tslint.yaml')) {
break;
}
dir = dir.parent;
} while (dir !== null);
if (dir === null) {
throw new SchematicsException(
'Asked to run lint fixes, but could not find a tslint.json or tslint.yaml config file.',
);
}
// Only include files that have been touched.
const files = tree.actions.reduce((acc: Set<string>, action) => {
const path = action.path.substr(1); // Remove the starting '/'.
if (path.endsWith('.ts') && dir && action.path.startsWith(dir.path)) {
acc.add(path);
}
return acc;
}, new Set<string>());
context.addTask(
new TslintFixTask({
ignoreErrors: true,
tsConfigPath: 'tsconfig.json',
files: [...files],
}),
);
};
}

9
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;

5
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,

2
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

2
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

76
npm/ng-packs/packages/schematics/src/utils/angular/tsconfig.ts

@ -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'.`,
);
}

60
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;
}

23
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<BrowserBuilderBaseOptions> {
karmaConfig: string;
}
export interface LintBuilderOptions {
tsConfig: string[] | string;
exclude?: string[];
}
export interface ExtractI18nOptions {
browserTarget: string;
}
@ -128,7 +124,6 @@ export type LibraryBuilderTarget = BuilderTarget<Builders.NgPackagr, LibraryBuil
export type BrowserBuilderTarget = BuilderTarget<Builders.Browser, BrowserBuilderOptions>;
export type ServerBuilderTarget = BuilderTarget<Builders.Server, ServerBuilderOptions>;
export type AppShellBuilderTarget = BuilderTarget<Builders.AppShell, AppShellBuilderOptions>;
export type LintBuilderTarget = BuilderTarget<Builders.TsLint, LintBuilderOptions>;
export type TestBuilderTarget = BuilderTarget<Builders.Karma, TestBuilderOptions>;
export type ServeBuilderTarget = BuilderTarget<Builders.DevServer, ServeBuilderOptions>;
export type ExtractI18nBuilderTarget = BuilderTarget<Builders.ExtractI18n, ExtractI18nOptions>;
@ -168,12 +163,12 @@ export interface WorkspaceProject<TProjectType extends ProjectType = ProjectType
export interface WorkspaceTargets<TProjectType extends ProjectType = ProjectType.Application> {
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;
}

51
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<void>,
updater: (workspace: workspaces.WorkspaceDefinition) => void | Rule | PromiseLike<void | Rule>,
): Rule;
export function updateWorkspace(workspace: workspaces.WorkspaceDefinition): Rule;
export function updateWorkspace(
updaterOrWorkspace:
| workspaces.WorkspaceDefinition
| ((workspace: workspaces.WorkspaceDefinition) => void | PromiseLike<void>),
| ((workspace: workspaces.WorkspaceDefinition) => void | Rule | PromiseLike<void | Rule>),
): 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<string, json.JsonValue | undefined>]> {
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];
}
}
}

1
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';

9
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) {
}
}

2
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!);
};
}

49
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 { buildDefaultPath, 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<Project> {
export async function resolveProject<T = any>(
tree: Tree,
name: string,
notFoundValue: T = NOT_FOUND_VALUE as unknown as any,
): Promise<Project | T> {
name = name || readWorkspaceSchema(tree).defaultProject!;
const workspace = await getWorkspace(tree);
let definition: Project['definition'] | undefined;
@ -61,7 +66,45 @@ export async function resolveProject(tree: Tree, name: string): Promise<Project>
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<string, any> 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;
}
/**
* 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);
}

6
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' },

Loading…
Cancel
Save