diff --git a/packages/bazel/BUILD.bazel b/packages/bazel/BUILD.bazel index e8260eaa38..814e722639 100644 --- a/packages/bazel/BUILD.bazel +++ b/packages/bazel/BUILD.bazel @@ -7,6 +7,7 @@ npm_package( "package.json", "protractor-utils.js", "//packages/bazel/src:package_assets", + "//packages/bazel/src/builders:package_assets", "//packages/bazel/src/schematics:package_assets", ], packages = [ @@ -19,5 +20,6 @@ npm_package( "//packages/bazel/src/ngc-wrapped:ngc_lib", "//packages/bazel/src/protractor/utils", "//packages/bazel/src/schematics/bazel-workspace", + "//packages/bazel/src/schematics/ng-new", ], ) diff --git a/packages/bazel/src/builders/builders.json b/packages/bazel/src/builders/builders.json index 3999f35086..8f92ce5240 100644 --- a/packages/bazel/src/builders/builders.json +++ b/packages/bazel/src/builders/builders.json @@ -1,7 +1,7 @@ { "builders": { "build": { - "class": "./index#Builder", + "class": "./index", "schema": "./schema.json", "description": "Executes Bazel on a target." } diff --git a/packages/bazel/src/builders/index.ts b/packages/bazel/src/builders/index.ts index b2d88fe225..42785c8c34 100644 --- a/packages/bazel/src/builders/index.ts +++ b/packages/bazel/src/builders/index.ts @@ -5,10 +5,10 @@ * 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 * - * @fileoverview Bazel bundle builder + * @fileoverview Bazel builder */ -import {BuildEvent, Builder as BuilderInterface, BuilderConfiguration, BuilderContext} from '@angular-devkit/architect'; +import {BuildEvent, Builder, BuilderConfiguration, BuilderContext} from '@angular-devkit/architect'; import {getSystemPath, resolve} from '@angular-devkit/core'; import {Observable, of } from 'rxjs'; import {catchError, map, tap} from 'rxjs/operators'; @@ -16,7 +16,7 @@ import {catchError, map, tap} from 'rxjs/operators'; import {checkInstallation, runBazel} from './bazel'; import {Schema} from './schema'; -export class Builder implements BuilderInterface { +class BazelBuilder implements Builder { constructor(private context: BuilderContext) {} run(builderConfig: BuilderConfiguration>): Observable { @@ -38,3 +38,5 @@ export class Builder implements BuilderInterface { .pipe(map(() => ({success: true})), catchError(() => of ({success: false})), ); } } + +export default BazelBuilder; diff --git a/packages/bazel/src/schematics/BUILD.bazel b/packages/bazel/src/schematics/BUILD.bazel index 823048e672..5770199cc3 100644 --- a/packages/bazel/src/schematics/BUILD.bazel +++ b/packages/bazel/src/schematics/BUILD.bazel @@ -15,6 +15,7 @@ jasmine_node_test( bootstrap = ["angular/tools/testing/init_node_spec.js"], deps = [ "//packages/bazel/src/schematics/bazel-workspace:test", + "//packages/bazel/src/schematics/ng-new:test", "//tools/testing:node", ], ) diff --git a/packages/bazel/src/schematics/bazel-workspace/files/src/BUILD.bazel.template b/packages/bazel/src/schematics/bazel-workspace/files/src/BUILD.bazel.template index 3116a4fe6a..5ba1b71789 100644 --- a/packages/bazel/src/schematics/bazel-workspace/files/src/BUILD.bazel.template +++ b/packages/bazel/src/schematics/bazel-workspace/files/src/BUILD.bazel.template @@ -7,7 +7,15 @@ load("@build_bazel_rules_typescript//:defs.bzl", "ts_devserver") ng_module( name = "src", - srcs = glob(["**/*.ts"], exclude = ["**/*.spec.ts", "test.ts"]), + srcs = glob( + include = ["**/*.ts"], + exclude = [ + "**/*.spec.ts", + "main.ts", + "test.ts", + "initialize_testbed.ts", + ], + ), assets = glob([ "**/*.css", "**/*.html", @@ -21,7 +29,7 @@ ng_module( rollup_bundle( name = "bundle", - entry_point = "src/main", + entry_point = "src/main.prod", deps = ["//src"], ) @@ -50,7 +58,7 @@ ts_devserver( "npm/node_modules/zone.js/dist", "npm/node_modules/tslib", ], - entry_module = "<%= name %>/src/main", + entry_module = "<%= name %>/src/main.dev", serving_path = "/bundle.min.js", static_files = [ "@npm//node_modules/zone.js:dist/zone.min.js", @@ -67,14 +75,28 @@ ts_library( deps = [ ":src", "@angular//packages/core/testing", - "@angular//packages/platform-browser-dynamic/testing", "@npm//@types", ], ) +ts_library( + name = "initialize_testbed", + testonly = 1, + srcs = [ + "initialize_testbed.ts", + ], + deps = [ + "@angular//packages/core/testing", + "@angular//packages/platform-browser-dynamic/testing", + ], +) + ts_web_test_suite( name = "test", srcs = ["@npm//node_modules/tslib:tslib.js"], + runtime_deps = [ + ":initialize_testbed", + ], # do not sort bootstrap = [ "@npm//node_modules/zone.js:dist/zone-testing-bundle.js", diff --git a/packages/bazel/src/schematics/bazel-workspace/files/src/initialize_testbed.ts.template b/packages/bazel/src/schematics/bazel-workspace/files/src/initialize_testbed.ts.template new file mode 100644 index 0000000000..25cca1d783 --- /dev/null +++ b/packages/bazel/src/schematics/bazel-workspace/files/src/initialize_testbed.ts.template @@ -0,0 +1,9 @@ +/** + * @fileoverview Provides a script to initialize TestBed before tests are run. + * This file should be included in the "runtime_deps" of a "ts_web_test_suite" + * rule. + */ +import {TestBed} from '@angular/core/testing'; +import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing'; + +TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); diff --git a/packages/bazel/src/schematics/bazel-workspace/index.ts b/packages/bazel/src/schematics/bazel-workspace/index.ts index 6da5708f99..5c0ca019d6 100644 --- a/packages/bazel/src/schematics/bazel-workspace/index.ts +++ b/packages/bazel/src/schematics/bazel-workspace/index.ts @@ -30,7 +30,7 @@ export default function(options: BazelWorkspaceOptions): Rule { const appDir = `${newProjectRoot}/${options.name}`; const workspaceVersions = { - 'ANGULAR_VERSION': '7.0.2', + 'ANGULAR_VERSION': '7.1.0', 'RULES_SASS_VERSION': '1.14.1', 'RXJS_VERSION': '6.3.3', }; diff --git a/packages/bazel/src/schematics/collection.json b/packages/bazel/src/schematics/collection.json index e12d42033f..5e5d5eff46 100644 --- a/packages/bazel/src/schematics/collection.json +++ b/packages/bazel/src/schematics/collection.json @@ -2,6 +2,11 @@ "name": "@angular/bazel", "version": "0.1", "schematics": { + "ng-new": { + "factory": "./ng-new", + "schema": "./ng-new/schema.json", + "description": "Create an Angular project that builds with Bazel." + }, "bazel-workspace": { "factory": "./bazel-workspace", "schema": "./bazel-workspace/schema.json", diff --git a/packages/bazel/src/schematics/ng-new/BUILD.bazel b/packages/bazel/src/schematics/ng-new/BUILD.bazel new file mode 100644 index 0000000000..d70cab250f --- /dev/null +++ b/packages/bazel/src/schematics/ng-new/BUILD.bazel @@ -0,0 +1,36 @@ +package(default_visibility = ["//visibility:public"]) + +load("//tools:defaults.bzl", "ts_library") + +ts_library( + name = "ng-new", + srcs = [ + "index.ts", + "schema.d.ts", + ], + data = glob(["files/**/*"]) + [ + "schema.json", + ], + deps = [ + "//packages/bazel/src/schematics/bazel-workspace", + "@ngdeps//@angular-devkit/core", + "@ngdeps//@angular-devkit/schematics", + "@ngdeps//@schematics/angular", + "@ngdeps//typescript", + ], +) + +ts_library( + name = "test", + testonly = True, + srcs = [ + "index_spec.ts", + ], + data = [ + "//packages/bazel/src/schematics:package_assets", + ], + deps = [ + ":ng-new", + "@ngdeps//@angular-devkit/schematics", + ], +) diff --git a/packages/bazel/src/schematics/ng-new/files/index.html.template b/packages/bazel/src/schematics/ng-new/files/index.html.template new file mode 100644 index 0000000000..bfd3c9b8c9 --- /dev/null +++ b/packages/bazel/src/schematics/ng-new/files/index.html.template @@ -0,0 +1,16 @@ + + + + + <%= utils.classify(name) %> + + + + + + + + + + + diff --git a/packages/bazel/src/schematics/ng-new/files/main.dev.ts.template b/packages/bazel/src/schematics/ng-new/files/main.dev.ts.template new file mode 100644 index 0000000000..32bd701bdf --- /dev/null +++ b/packages/bazel/src/schematics/ng-new/files/main.dev.ts.template @@ -0,0 +1,4 @@ +import {platformBrowser} from '@angular/platform-browser'; +import {AppModuleNgFactory} from './app/app.module.ngfactory'; + +platformBrowser().bootstrapModuleFactory(AppModuleNgFactory); diff --git a/packages/bazel/src/schematics/ng-new/files/main.prod.ts.template b/packages/bazel/src/schematics/ng-new/files/main.prod.ts.template new file mode 100644 index 0000000000..70fd120dc5 --- /dev/null +++ b/packages/bazel/src/schematics/ng-new/files/main.prod.ts.template @@ -0,0 +1,6 @@ +import {enableProdMode} from '@angular/core'; +import {platformBrowser} from '@angular/platform-browser'; +import {AppModuleNgFactory} from './app/app.module.ngfactory'; + +enableProdMode(); +platformBrowser().bootstrapModuleFactory(AppModuleNgFactory); diff --git a/packages/bazel/src/schematics/ng-new/index.ts b/packages/bazel/src/schematics/ng-new/index.ts new file mode 100755 index 0000000000..0f16b6e7ac --- /dev/null +++ b/packages/bazel/src/schematics/ng-new/index.ts @@ -0,0 +1,180 @@ +/** + * @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 + * + * @fileoverview Schematics for ng-new project that builds with Bazel. + */ + +import {apply, applyTemplates, chain, externalSchematic, MergeStrategy, mergeWith, move, Rule, schematic, Tree, url, SchematicsException, UpdateRecorder,} from '@angular-devkit/schematics'; +import {parseJsonAst, JsonAstObject, strings, JsonValue} from '@angular-devkit/core'; +import {findPropertyInAstObject, insertPropertyInAstObjectInOrder} from '@schematics/angular/utility/json-utils'; +import {validateProjectName} from '@schematics/angular/utility/validation'; +import {getWorkspace} from '@schematics/angular/utility/config'; +import {Schema} from './schema'; + +function addDevDependenciesToPackageJson(options: Schema) { + return (host: Tree) => { + const packageJson = `${options.name}/package.json`; + + if (!host.exists(packageJson)) { + throw new Error(`Could not find ${packageJson}`); + } + const packageJsonContent = host.read(packageJson); + if (!packageJsonContent) { + throw new Error('Failed to read package.json content'); + } + const jsonAst = parseJsonAst(packageJsonContent.toString()) as JsonAstObject; + const deps = findPropertyInAstObject(jsonAst, 'dependencies') as JsonAstObject; + const devDeps = findPropertyInAstObject(jsonAst, 'devDependencies') as JsonAstObject; + + const angularCoreNode = findPropertyInAstObject(deps, '@angular/core'); + const angularCoreVersion = angularCoreNode !.value as string; + + const devDependencies: {[k: string]: string} = { + '@angular/bazel': angularCoreVersion, + '@bazel/karma': '0.21.0', + '@bazel/typescript': '0.21.0', + }; + + const recorder = host.beginUpdate(packageJson); + for (const packageName of Object.keys(devDependencies)) { + const version = devDependencies[packageName]; + const indent = 4; + insertPropertyInAstObjectInOrder(recorder, devDeps, packageName, version, indent); + } + host.commitUpdate(recorder); + return host; + }; +} + +function overwriteMainAndIndex(options: Schema) { + return (host: Tree) => { + let newProjectRoot = ''; + try { + const workspace = getWorkspace(host); + newProjectRoot = workspace.newProjectRoot || ''; + } catch { + } + const srcDir = `${newProjectRoot}/${options.name}/src`; + + return mergeWith( + apply( + url('./files'), + [ + applyTemplates({ + utils: strings, + ...options, + 'dot': '.', + }), + move(srcDir), + ]), + MergeStrategy.Overwrite); + }; +} + +function replacePropertyInAstObject( + recorder: UpdateRecorder, node: JsonAstObject, propertyName: string, value: JsonValue, + indent: number) { + const property = findPropertyInAstObject(node, propertyName); + if (property === null) { + throw new Error(`Property ${propertyName} does not exist in JSON object`); + } + const {start, text} = property; + recorder.remove(start.offset, text.length); + const indentStr = '\n' + + ' '.repeat(indent); + const content = JSON.stringify(value, null, ' ').replace(/\n/g, indentStr); + recorder.insertLeft(start.offset, content); +} + +function updateWorkspaceFileToUseBazelBuilder(options: Schema): Rule { + return (host: Tree, context: SchematicContext) => { + const {name} = options; + const workspacePath = `${name}/angular.json`; + if (!host.exists(workspacePath)) { + throw new SchematicsException(`Workspace file ${workspacePath} not found.`); + } + const workspaceBuffer = host.read(workspacePath) !; + const workspaceJsonAst = parseJsonAst(workspaceBuffer.toString()) as JsonAstObject; + const projects = findPropertyInAstObject(workspaceJsonAst, 'projects'); + if (!projects) { + throw new SchematicsException('Expect projects in angular.json to be an Object'); + } + const project = findPropertyInAstObject(projects as JsonAstObject, name); + if (!project) { + throw new SchematicsException(`Expected projects to contain ${name}`); + } + const recorder = host.beginUpdate(workspacePath); + const indent = 6; + replacePropertyInAstObject( + recorder, project as JsonAstObject, 'architect', { + 'build': { + 'builder': '@angular/bazel:build', + 'options': {'targetLabel': '//src:bundle.js', 'bazelCommand': 'build'}, + 'configurations': {'production': {'targetLabel': '//src:bundle'}} + }, + 'serve': { + 'builder': '@angular/bazel:build', + 'options': {'targetLabel': '//src:devserver', 'bazelCommand': 'run'}, + 'configurations': {'production': {'targetLabel': '//src:prodserver'}} + }, + 'extract-i18n': { + 'builder': '@angular-devkit/build-angular:extract-i18n', + 'options': {'browserTarget': `${name}:build`} + }, + 'test': { + 'builder': '@angular/bazel:build', + 'options': {'bazelCommand': 'test', 'targetLabel': '//src/...'} + }, + 'lint': { + 'builder': '@angular-devkit/build-angular:tslint', + 'options': { + 'tsConfig': ['src/tsconfig.app.json', 'src/tsconfig.spec.json'], + 'exclude': ['**/node_modules/**'] + } + } + }, + indent); + + const e2e = `${options.name}-e2e`; + const e2eNode = findPropertyInAstObject(projects as JsonAstObject, e2e); + if (e2eNode) { + replacePropertyInAstObject( + recorder, e2eNode as JsonAstObject, 'architect', { + 'e2e': { + 'builder': '@angular/bazel:build', + 'options': {'bazelCommand': 'test', 'targetLabel': '//e2e:devserver_test'}, + 'configurations': {'production': {'targetLabel': '//e2e:prodserver_test'}} + }, + 'lint': { + 'builder': '@angular-devkit/build-angular:tslint', + 'options': {'tsConfig': 'e2e/tsconfig.e2e.json', 'exclude': ['**/node_modules/**']} + } + }, + indent); + } + + host.commitUpdate(recorder); + return host; + }; +} + +export default function(options: Schema): Rule { + return (host: Tree) => { + validateProjectName(options.name); + + return chain([ + externalSchematic('@schematics/angular', 'ng-new', { + ...options, + skipInstall: true, + }), + addDevDependenciesToPackageJson(options), + schematic('bazel-workspace', options), + overwriteMainAndIndex(options), + updateWorkspaceFileToUseBazelBuilder(options), + ]); + }; +} diff --git a/packages/bazel/src/schematics/ng-new/index_spec.ts b/packages/bazel/src/schematics/ng-new/index_spec.ts new file mode 100644 index 0000000000..26403eff48 --- /dev/null +++ b/packages/bazel/src/schematics/ng-new/index_spec.ts @@ -0,0 +1,88 @@ +/** + * @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 {SchematicTestRunner} from '@angular-devkit/schematics/testing'; + +describe('Ng-new Schematic', () => { + const schematicRunner = + new SchematicTestRunner('@angular/bazel', require.resolve('../collection.json'), ); + const defaultOptions = { + name: 'demo', + version: '7.0.0', + }; + + it('should call external @schematics/angular', () => { + const options = {...defaultOptions}; + const host = schematicRunner.runSchematic('ng-new', options); + const {files} = host; + // External schematic should produce workspace file angular.json + expect(files).toContain('/demo/angular.json'); + }); + + it('should add @angular/bazel to package.json dependencies', () => { + const options = {...defaultOptions}; + const host = schematicRunner.runSchematic('ng-new', options); + const {files} = host; + expect(files).toContain('/demo/package.json'); + const content = host.readContent('/demo/package.json'); + expect(() => JSON.parse(content)).not.toThrow(); + const json = JSON.parse(content); + const core = '@angular/core'; + const bazel = '@angular/bazel'; + expect(Object.keys(json)).toContain('dependencies'); + expect(Object.keys(json)).toContain('devDependencies'); + expect(Object.keys(json.dependencies)).toContain(core); + expect(Object.keys(json.devDependencies)).toContain(bazel); + expect(json.dependencies[core]).toBe(json.devDependencies[bazel]); + }); + + it('should create Bazel workspace file', () => { + const options = {...defaultOptions}; + const host = schematicRunner.runSchematic('ng-new', options); + const {files} = host; + expect(files).toContain('/demo/WORKSPACE'); + expect(files).toContain('/demo/BUILD.bazel'); + }); + + it('should produce main.prod.ts for AOT', () => { + const options = {...defaultOptions}; + const host = schematicRunner.runSchematic('ng-new', options); + const {files} = host; + // main.prod.ts is used by Bazel for AOT + expect(files).toContain('/demo/src/main.prod.ts'); + // main.ts is produced by original ng-new schematics + // This file should be present for backwards compatibility. + expect(files).toContain('/demo/src/main.ts'); + }); + + it('should overwrite index.html with script tags', () => { + const options = {...defaultOptions}; + const host = schematicRunner.runSchematic('ng-new', options); + const {files} = host; + expect(files).toContain('/demo/src/index.html'); + const content = host.readContent('/demo/src/index.html'); + expect(content).toMatch(''); + expect(content).toMatch(''); + }); + + it('should update angular.json to use Bazel builder', () => { + const options = {...defaultOptions}; + const host = schematicRunner.runSchematic('ng-new', options); + const {files} = host; + expect(files).toContain('/demo/angular.json'); + const content = host.readContent('/demo/angular.json'); + expect(() => JSON.parse(content)).not.toThrow(); + const json = JSON.parse(content); + let {architect} = json.projects.demo; + expect(architect.build.builder).toBe('@angular/bazel:build'); + expect(architect.serve.builder).toBe('@angular/bazel:build'); + expect(architect.test.builder).toBe('@angular/bazel:build'); + architect = json.projects['demo-e2e'].architect; + expect(architect.e2e.builder).toBe('@angular/bazel:build'); + }); +}); diff --git a/packages/bazel/src/schematics/ng-new/schema.d.ts b/packages/bazel/src/schematics/ng-new/schema.d.ts new file mode 100644 index 0000000000..1f5d58270f --- /dev/null +++ b/packages/bazel/src/schematics/ng-new/schema.d.ts @@ -0,0 +1,103 @@ + +// THIS FILE IS AUTOMATICALLY GENERATED. TO UPDATE THIS FILE YOU NEED TO CHANGE THE +// CORRESPONDING JSON SCHEMA FILE, THEN RUN devkit-admin build (or bazel build ...). + +// tslint:disable:no-global-tslint-disable +// tslint:disable + +export interface Schema { + /** + * Initial repository commit information. + */ + commit?: CommitUnion; + /** + * Flag to toggle creation of an application in the new workspace. + */ + createApplication?: boolean; + /** + * The directory name to create the workspace in. + */ + directory?: string; + /** + * EXPERIMENTAL: Specifies whether to create a new application which uses the Ivy rendering + * engine. + */ + experimentalIvy?: 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; + /** + * Link CLI to global version (internal development only). + */ + linkCli?: boolean; + /** + * Create a barebones project without any testing frameworks + */ + minimal?: boolean; + /** + * The name of the workspace. + */ + name: string; + /** + * The path where new projects will be created. + */ + newProjectRoot?: string; + /** + * The prefix to apply to generated selectors. + */ + prefix?: string; + /** + * Generates a routing module. + */ + routing?: boolean; + /** + * Skip initializing a git repository. + */ + skipGit?: boolean; + /** + * Skip installing dependency packages. + */ + skipInstall?: boolean; + /** + * Skip creating spec files. + */ + skipTests?: boolean; + /** + * The file extension to be used for style files. + */ + style?: string; + /** + * The version of the Angular CLI to use. + */ + version: string; + /** + * Specifies the view encapsulation strategy. + */ + viewEncapsulation?: ViewEncapsulation; +} + +/** + * Initial repository commit information. + */ +export type CommitUnion = boolean | CommitObject; + +export interface CommitObject { + email: string; + message?: string; + name: string; +} + +/** + * Specifies the view encapsulation strategy. + */ +export enum ViewEncapsulation { + Emulated = 'Emulated', + Native = 'Native', + None = 'None', + ShadowDOM = 'ShadowDom', +} diff --git a/packages/bazel/src/schematics/ng-new/schema.json b/packages/bazel/src/schematics/ng-new/schema.json new file mode 100755 index 0000000000..e80bc82bc3 --- /dev/null +++ b/packages/bazel/src/schematics/ng-new/schema.json @@ -0,0 +1,150 @@ +{ + "$schema": "http://json-schema.org/schema", + "id": "SchematicsAngularNgNew", + "title": "Angular Ng New Options Schema", + "type": "object", + "properties": { + "directory": { + "type": "string", + "description": "The directory name to create the workspace in." + }, + "name": { + "description": "The name of the workspace.", + "type": "string", + "format": "html-selector", + "$default": { + "$source": "argv", + "index": 0 + }, + "x-prompt": "What name would you like to use for the project?" + }, + "experimentalIvy": { + "description": "EXPERIMENTAL: Specifies whether to create a new application which uses the Ivy rendering engine.", + "type": "boolean", + "default": false + }, + "skipInstall": { + "description": "Skip installing dependency packages.", + "type": "boolean", + "default": false + }, + "linkCli": { + "description": "Link CLI to global version (internal development only).", + "type": "boolean", + "default": false, + "visible": false + }, + "skipGit": { + "description": "Skip initializing a git repository.", + "type": "boolean", + "default": false, + "alias": "g" + }, + "commit": { + "description": "Initial repository commit information.", + "oneOf": [ + { "type": "boolean" }, + { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string", + "format": "email" + }, + "message": { + "type": "string" + } + }, + "required": [ + "name", + "email" + ] + } + ], + "default": true + }, + "newProjectRoot": { + "description": "The path where new projects will be created.", + "type": "string", + "default": "projects" + }, + "inlineStyle": { + "description": "Specifies if the style will be in the ts file.", + "type": "boolean", + "default": false, + "alias": "s" + }, + "inlineTemplate": { + "description": "Specifies if the template will be in the ts file.", + "type": "boolean", + "default": false, + "alias": "t" + }, + "viewEncapsulation": { + "description": "Specifies the view encapsulation strategy.", + "enum": ["Emulated", "Native", "None", "ShadowDom"], + "type": "string" + }, + "version": { + "type": "string", + "description": "The version of the Angular CLI to use.", + "visible": false, + "$default": { + "$source": "ng-cli-version" + } + }, + "routing": { + "type": "boolean", + "description": "Generates a routing module.", + "default": false, + "x-prompt": "Would you like to add Angular routing?" + }, + "prefix": { + "type": "string", + "format": "html-selector", + "description": "The prefix to apply to generated selectors.", + "minLength": 1, + "default": "app", + "alias": "p" + }, + "style": { + "description": "The file extension to be used for style files.", + "type": "string", + "default": "css", + "x-prompt": { + "message": "Which stylesheet format would you like to use?", + "type": "list", + "items": [ + { "value": "css", "label": "CSS" }, + { "value": "scss", "label": "SCSS [ http://sass-lang.com ]" }, + { "value": "sass", "label": "SASS [ http://sass-lang.com ]" }, + { "value": "less", "label": "LESS [ http://lesscss.org ]" }, + { "value": "styl", "label": "Stylus [ http://stylus-lang.com ]" } + ] + } + }, + "skipTests": { + "description": "Skip creating spec files.", + "type": "boolean", + "default": false, + "alias": "S" + }, + "createApplication": { + "description": "Flag to toggle creation of an application in the new workspace.", + "type": "boolean", + "default": true + }, + "minimal": { + "description": "Create a barebones project without any testing frameworks", + "type": "boolean", + "default": false + } + }, + "required": [ + "name", + "version" + ] +}