diff --git a/package.json b/package.json index edd851576b..1a6fe2f501 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "@types/diff": "^3.5.1", "@types/fs-extra": "4.0.2", "@types/hammerjs": "2.0.35", + "@types/inquirer": "^0.0.44", "@types/jasmine": "^2.8.8", "@types/jasminewd2": "^2.0.6", "@types/minimist": "^1.2.0", diff --git a/packages/core/schematics/migrations/static-queries/BUILD.bazel b/packages/core/schematics/migrations/static-queries/BUILD.bazel index 00da5b6919..f8fa0a26ba 100644 --- a/packages/core/schematics/migrations/static-queries/BUILD.bazel +++ b/packages/core/schematics/migrations/static-queries/BUILD.bazel @@ -15,6 +15,7 @@ ts_library( "//packages/core/schematics/utils", "@npm//@angular-devkit/schematics", "@npm//@types/node", + "@npm//rxjs", "@npm//typescript", ], ) diff --git a/packages/core/schematics/migrations/static-queries/index.ts b/packages/core/schematics/migrations/static-queries/index.ts index cabccfa242..12aea9c7e1 100644 --- a/packages/core/schematics/migrations/static-queries/index.ts +++ b/packages/core/schematics/migrations/static-queries/index.ts @@ -6,13 +6,14 @@ * found in the LICENSE file at https://angular.io/license */ -import {logging} from '@angular-devkit/core'; import {Rule, SchematicContext, SchematicsException, Tree} from '@angular-devkit/schematics'; import {dirname, relative} from 'path'; +import {from} from 'rxjs'; import * as ts from 'typescript'; import {NgComponentTemplateVisitor} from '../../utils/ng_component_template'; import {getProjectTsConfigPaths} from '../../utils/project_tsconfig_paths'; +import {getInquirer, supportsPrompt} from '../../utils/schematics_prompt'; import {parseTsconfigFile} from '../../utils/typescript/parse_tsconfig'; import {TypeScriptVisitor, visitAllNodes} from '../../utils/typescript/visit_nodes'; @@ -22,34 +23,83 @@ import {TimingStrategy} from './strategies/timing-strategy'; import {QueryUsageStrategy} from './strategies/usage_strategy/usage_strategy'; import {getTransformedQueryCallExpr} from './transform'; -type Logger = logging.LoggerApi; - /** Entry point for the V8 static-query migration. */ export default function(): Rule { return (tree: Tree, context: SchematicContext) => { - const projectTsConfigPaths = getProjectTsConfigPaths(tree); - const basePath = process.cwd(); - - if (!projectTsConfigPaths.length) { - throw new SchematicsException( - 'Could not find any tsconfig file. Cannot migrate queries ' + - 'to explicit timing.'); - } - - for (const tsconfigPath of projectTsConfigPaths) { - runStaticQueryMigration(tree, tsconfigPath, basePath, context.logger); - } + // We need to cast the returned "Observable" to "any" as there is a + // RxJS version mismatch that breaks the TS compilation. + return from(runMigration(tree, context).then(() => tree)) as any; }; } +/** Runs the V8 migration static-query migration for all determined TypeScript projects. */ +async function runMigration(tree: Tree, context: SchematicContext) { + const projectTsConfigPaths = getProjectTsConfigPaths(tree); + const basePath = process.cwd(); + const logger = context.logger; + + logger.info('------ Static Query migration ------'); + logger.info('In preparation for Ivy, developers can now explicitly specify the'); + logger.info('timing of their queries. Read more about this here:'); + logger.info('https://github.com/angular/angular/pull/28810'); + logger.info(''); + + if (!projectTsConfigPaths.length) { + throw new SchematicsException( + 'Could not find any tsconfig file. Cannot migrate queries ' + + 'to explicit timing.'); + } + + // In case prompts are supported, determine the desired migration strategy + // by creating a choice prompt. By default the template strategy is used. + let isUsageStrategy = false; + if (supportsPrompt()) { + logger.info('There are two available migration strategies that can be selected:'); + logger.info(' • Template strategy - migration tool (short-term gains, rare corrections)'); + logger.info(' • Usage strategy - best practices (long-term gains, manual corrections)'); + logger.info('For an easy migration, the template strategy is recommended. The usage'); + logger.info('strategy can be used for best practices and a code base that will be more'); + logger.info('flexible to changes going forward.'); + const {strategyName} = await getInquirer().prompt<{strategyName: string}>({ + type: 'list', + name: 'strategyName', + message: 'What migration strategy do you want to use?', + choices: [ + {name: 'Template strategy', value: 'template'}, {name: 'Usage strategy', value: 'usage'} + ], + default: 'template', + }); + logger.info(''); + isUsageStrategy = strategyName === 'usage'; + } else { + // In case prompts are not supported, we still want to allow developers to opt + // into the usage strategy by specifying an environment variable. The tests also + // use the environment variable as there is no headless way to select via prompt. + isUsageStrategy = !!process.env['NG_STATIC_QUERY_USAGE_STRATEGY']; + } + + const failures = []; + for (const tsconfigPath of projectTsConfigPaths) { + failures.push(...await runStaticQueryMigration(tree, tsconfigPath, basePath, isUsageStrategy)); + } + + if (failures.length) { + logger.info('Some queries cannot be migrated automatically. Please go through'); + logger.info('those manually and apply the appropriate timing:'); + failures.forEach(failure => logger.warn(`⮑ ${failure}`)); + } + + logger.info('------------------------------------------------'); +} + /** * Runs the static query migration for the given TypeScript project. The schematic * analyzes all queries within the project and sets up the query timing based on * the current usage of the query property. e.g. a view query that is not used in any * lifecycle hook does not need to be static and can be set up with "static: false". */ -function runStaticQueryMigration( - tree: Tree, tsconfigPath: string, basePath: string, logger: Logger) { +async function runStaticQueryMigration( + tree: Tree, tsconfigPath: string, basePath: string, isUsageStrategy: boolean) { const parsed = parseTsconfigFile(tsconfigPath, dirname(tsconfigPath)); const host = ts.createCompilerHost(parsed.options, true); @@ -62,7 +112,6 @@ function runStaticQueryMigration( return buffer ? buffer.toString() : undefined; }; - const isUsageStrategy = !!process.env['NG_STATIC_QUERY_USAGE_STRATEGY']; const program = ts.createProgram(parsed.fileNames, parsed.options, host); const typeChecker = program.getTypeChecker(); const queryVisitor = new NgQueryResolveVisitor(typeChecker); @@ -100,13 +149,13 @@ function runStaticQueryMigration( const strategy: TimingStrategy = isUsageStrategy ? new QueryUsageStrategy(classMetadata, typeChecker) : new QueryTemplateStrategy(tsconfigPath, classMetadata, host); - const detectionMessages: string[] = []; + const failureMessages: string[] = []; // In case the strategy could not be set up properly, we just exit the // migration. We don't want to throw an exception as this could mean // that other migrations are interrupted. if (!strategy.setup()) { - return; + return []; } // Walk through all source files that contain resolved queries and update @@ -135,23 +184,15 @@ function runStaticQueryMigration( update.remove(queryExpr.getStart(), queryExpr.getWidth()); update.insertRight(queryExpr.getStart(), newText); - const {line, character} = - ts.getLineAndCharacterOfPosition(sourceFile, q.decorator.node.getStart()); - detectionMessages.push(`${relativePath}@${line + 1}:${character + 1}: ${message}`); + if (message) { + const {line, character} = + ts.getLineAndCharacterOfPosition(sourceFile, q.decorator.node.getStart()); + failureMessages.push(`${relativePath}@${line + 1}:${character + 1}: ${message}`); + } }); tree.commitUpdate(update); }); - if (detectionMessages.length) { - logger.info('------ Static Query migration ------'); - logger.info('In preparation for Ivy, developers can now explicitly specify the'); - logger.info('timing of their queries. Read more about this here:'); - logger.info('https://github.com/angular/angular/pull/28810'); - logger.info(''); - logger.info('Some queries cannot be migrated automatically. Please go through'); - logger.info('those manually and apply the appropriate timing:'); - detectionMessages.forEach(failure => logger.warn(`⮑ ${failure}`)); - logger.info('------------------------------------------------'); - } + return failureMessages; } diff --git a/packages/core/schematics/test/static_queries_migration_template_spec.ts b/packages/core/schematics/test/static_queries_migration_template_spec.ts index 5345f3c1f7..b2ce90c8cb 100644 --- a/packages/core/schematics/test/static_queries_migration_template_spec.ts +++ b/packages/core/schematics/test/static_queries_migration_template_spec.ts @@ -93,11 +93,13 @@ describe('static-queries migration with template strategy', () => { host.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(contents)); } - function runMigration() { runner.runSchematic('migration-v8-static-queries', {}, tree); } + async function runMigration() { + await runner.runSchematicAsync('migration-v8-static-queries', {}, tree).toPromise(); + } describe('ViewChild', () => { - it('should detect queries selecting elements through template reference', () => { + it('should detect queries selecting elements through template reference', async() => { writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; @@ -118,7 +120,7 @@ describe('static-queries migration with template strategy', () => { export class MyModule {} `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@ViewChild('myButton', { static: false }) query: any;`); @@ -126,7 +128,7 @@ describe('static-queries migration with template strategy', () => { .toContain(`@ViewChild('myStaticButton', { static: true }) query2: any;`); }); - it('should detect queries selecting ng-template as static', () => { + it('should detect queries selecting ng-template as static', async() => { writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; @@ -143,13 +145,13 @@ describe('static-queries migration with template strategy', () => { export class MyModule {} `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@ViewChild('myTmpl', { static: true }) query: any;`); }); - it('should detect queries selecting component view providers through string token', () => { + it('should detect queries selecting component view providers through string token', async() => { writeFile('/index.ts', ` import {Component, Directive, NgModule, ViewChild} from '@angular/core'; @@ -186,7 +188,7 @@ describe('static-queries migration with template strategy', () => { `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@ViewChild('my-token', { static: true }) query: any;`); @@ -194,7 +196,7 @@ describe('static-queries migration with template strategy', () => { .toContain(`@ViewChild('my-token-2', { static: false }) query2: any;`); }); - it('should detect queries selecting component view providers using class token', () => { + it('should detect queries selecting component view providers using class token', async() => { writeFile('/index.ts', ` import {Component, Directive, NgModule, ViewChild} from '@angular/core'; @@ -230,7 +232,7 @@ describe('static-queries migration with template strategy', () => { `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@ViewChild(MyService, { static: true }) query: any;`); @@ -238,7 +240,7 @@ describe('static-queries migration with template strategy', () => { .toContain(`@ViewChild(MyService2, { static: false }) query2: any;`); }); - it('should detect queries selecting component', () => { + it('should detect queries selecting component', async() => { writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; import {HomeComponent, HomeComponent2} from './home-comp'; @@ -276,7 +278,7 @@ describe('static-queries migration with template strategy', () => { export class HomeComponent2 {} `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@ViewChild(HomeComponent, { static: true }) query: any;`); @@ -284,7 +286,7 @@ describe('static-queries migration with template strategy', () => { .toContain(`@ViewChild(HomeComponent2, { static: false }) query2: any;`); }); - it('should detect queries selecting third-party component', () => { + it('should detect queries selecting third-party component', async() => { writeFakeLibrary(); writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; @@ -303,13 +305,13 @@ describe('static-queries migration with template strategy', () => { My projected content `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@ViewChild(MyLibComponent, { static: true }) query: any;`); }); - it('should detect queries selecting third-party component with multiple selectors', () => { + it('should detect queries selecting third-party component with multiple selectors', async() => { writeFakeLibrary('a-selector, test-selector'); writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; @@ -331,13 +333,13 @@ describe('static-queries migration with template strategy', () => { `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@ViewChild(MyLibComponent, { static: false }) query: any;`); }); - it('should detect queries within structural directive', () => { + it('should detect queries within structural directive', async() => { writeFile('/index.ts', ` import {Component, Directive, NgModule, ViewChild} from '@angular/core'; @@ -359,7 +361,7 @@ describe('static-queries migration with template strategy', () => { With asterisk `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@ViewChild('myRef', { static: true }) query: any;`); @@ -367,7 +369,7 @@ describe('static-queries migration with template strategy', () => { .toContain(`@ViewChild('myRef2', { static: false }) query2: any;`); }); - it('should detect inherited queries', () => { + it('should detect inherited queries', async() => { writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; @@ -386,13 +388,13 @@ describe('static-queries migration with template strategy', () => { My Ref `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@ViewChild('myRef', { static: true }) query: any;`); }); - it('should add a todo if a query is not declared in any component', () => { + it('should add a todo if a query is not declared in any component', async() => { writeFile('/index.ts', ` import {Component, NgModule, ViewChild, SomeToken} from '@angular/core'; @@ -401,7 +403,7 @@ describe('static-queries migration with template strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain( @@ -412,7 +414,7 @@ describe('static-queries migration with template strategy', () => { /^⮑ {3}index.ts@5:11:.+could not be determined.+not declared in any component/); }); - it('should add a todo if a query is used multiple times with different timing', () => { + it('should add a todo if a query is used multiple times with different timing', async() => { writeFile('/index.ts', ` import {Component, NgModule, ViewChild} from '@angular/core'; @@ -430,7 +432,7 @@ describe('static-queries migration with template strategy', () => { export class MyModule {} `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@ViewChild('myRef', /* TODO: add static flag */ {}) query: any;`); @@ -440,7 +442,7 @@ describe('static-queries migration with template strategy', () => { /^⮑ {3}index.ts@5:11: Multiple components use the query with different timings./); }); - it('should gracefully exit migration if queries could not be analyzed', () => { + it('should gracefully exit migration if queries could not be analyzed', async() => { writeFile('/index.ts', ` import {Component, ViewChild} from '@angular/core'; @@ -456,7 +458,7 @@ describe('static-queries migration with template strategy', () => { // We don't expect an error to be thrown as this could interrupt other // migrations which are scheduled with "ng update" in the CLI. - expect(() => runMigration()).not.toThrow(); + await runMigration(); expect(console.error) .toHaveBeenCalledWith('Could not create Angular AOT compiler to determine query timing.'); @@ -465,7 +467,7 @@ describe('static-queries migration with template strategy', () => { jasmine.stringMatching(/Cannot determine the module for class MyComp/)); }); - it('should add a todo for content queries which are not detectable', () => { + it('should add a todo for content queries which are not detectable', async() => { writeFile('/index.ts', ` import {Component, NgModule, ContentChild} from '@angular/core'; @@ -478,7 +480,7 @@ describe('static-queries migration with template strategy', () => { export class MyModule {} `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@ContentChild('myRef', /* TODO: add static flag */ {}) query: any;`); diff --git a/packages/core/schematics/test/static_queries_migration_usage_spec.ts b/packages/core/schematics/test/static_queries_migration_usage_spec.ts index 025618e949..7b33fc99f8 100644 --- a/packages/core/schematics/test/static_queries_migration_usage_spec.ts +++ b/packages/core/schematics/test/static_queries_migration_usage_spec.ts @@ -52,7 +52,7 @@ describe('static-queries migration with usage strategy', () => { describe('ViewChild', () => { createQueryTests('ViewChild'); - it('should mark view queries used in "ngAfterContentInit" as static', () => { + it('should mark view queries used in "ngAfterContentInit" as static', async() => { writeFile('/index.ts', ` import {Component, ViewChild} from '@angular/core'; @@ -66,13 +66,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@ViewChild('test', { static: true }) query: any;`); }); - it('should mark view queries used in "ngAfterContentChecked" as static', () => { + it('should mark view queries used in "ngAfterContentChecked" as static', async() => { writeFile('/index.ts', ` import {Component, ViewChild} from '@angular/core'; @@ -86,7 +86,7 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@ViewChild('test', { static: true }) query: any;`); @@ -96,7 +96,7 @@ describe('static-queries migration with usage strategy', () => { describe('ContentChild', () => { createQueryTests('ContentChild'); - it('should not mark content queries used in "ngAfterContentInit" as static', () => { + it('should not mark content queries used in "ngAfterContentInit" as static', async() => { writeFile('/index.ts', ` import {Component, ContentChild} from '@angular/core'; @@ -110,13 +110,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@ContentChild('test', { static: false }) query: any;`); }); - it('should not mark content queries used in "ngAfterContentChecked" as static', () => { + it('should not mark content queries used in "ngAfterContentChecked" as static', async() => { writeFile('/index.ts', ` import {Component, ContentChild} from '@angular/core'; @@ -130,7 +130,7 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@ContentChild('test', { static: false }) query: any;`); @@ -141,10 +141,12 @@ describe('static-queries migration with usage strategy', () => { host.sync.write(normalize(filePath), virtualFs.stringToFileBuffer(contents)); } - function runMigration() { runner.runSchematic('migration-v8-static-queries', {}, tree); } + async function runMigration() { + await runner.runSchematicAsync('migration-v8-static-queries', {}, tree).toPromise(); + } function createQueryTests(queryType: 'ViewChild' | 'ContentChild') { - it('should mark queries as dynamic', () => { + it('should mark queries as dynamic', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -159,7 +161,7 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: false }) unused: any;`); @@ -167,7 +169,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('dynamic', { static: false }) dynamic: any`); }); - it('should mark queries used in "ngOnChanges" as static', () => { + it('should mark queries used in "ngOnChanges" as static', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -181,13 +183,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should mark queries used in "ngOnInit" as static', () => { + it('should mark queries used in "ngOnInit" as static', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -201,13 +203,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should mark queries used in "ngDoCheck" as static', () => { + it('should mark queries used in "ngDoCheck" as static', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -221,13 +223,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should keep existing query options when updating timing', () => { + it('should keep existing query options when updating timing', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -241,13 +243,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { /* test */ read: null, static: true }) query: any;`); }); - it('should not overwrite existing explicit query timing', () => { + it('should not overwrite existing explicit query timing', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -257,13 +259,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', {static: /* untouched */ someVal}) query: any;`); }); - it('should detect queries used in deep method chain', () => { + it('should detect queries used in deep method chain', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -292,13 +294,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should properly exit if recursive function is analyzed', () => { + it('should properly exit if recursive function is analyzed', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -316,13 +318,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: false }) query: any;`); }); - it('should detect queries used in newly instantiated classes', () => { + it('should detect queries used in newly instantiated classes', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -353,7 +355,7 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); @@ -361,7 +363,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query2: any;`); }); - it('should detect queries used in parenthesized new expressions', () => { + it('should detect queries used in parenthesized new expressions', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -381,13 +383,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect queries in lifecycle hook with string literal name', () => { + it('should detect queries in lifecycle hook with string literal name', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -401,13 +403,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect static queries within nested inheritance', () => { + it('should detect static queries within nested inheritance', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -426,13 +428,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect static queries used within input setters', () => { + it('should detect static queries used within input setters', async() => { writeFile('/index.ts', ` import {Component, Input, ${queryType}} from '@angular/core'; @@ -448,13 +450,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect inputs defined in metadata', () => { + it('should detect inputs defined in metadata', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -474,13 +476,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect aliased inputs declared in metadata', () => { + it('should detect aliased inputs declared in metadata', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -497,13 +499,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should not mark query as static if query is used in non-input setter', () => { + it('should not mark query as static if query is used in non-input setter', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -517,13 +519,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: false }) query: any;`); }); - it('should detect input decorator on setter', () => { + it('should detect input decorator on setter', async() => { writeFile('/index.ts', ` import {Input, Component, ${queryType}} from '@angular/core'; @@ -542,13 +544,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect setter inputs in derived classes', () => { + it('should detect setter inputs in derived classes', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -567,13 +569,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should properly detect static query in external derived class', () => { + it('should properly detect static query in external derived class', async() => { writeFile('/src/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -597,13 +599,13 @@ describe('static-queries migration with usage strategy', () => { // recorded for TypeScript projects not at the schematic tree root. host.sync.rename(normalize('/tsconfig.json'), normalize('/src/tsconfig.json')); - runMigration(); + await runMigration(); expect(tree.readContent('/src/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should not mark queries used in promises as static', () => { + it('should not mark queries used in promises as static', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -637,7 +639,7 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: false }) query: any;`); @@ -645,7 +647,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query2: any;`); }); - it('should handle function callbacks which statically access queries', () => { + it('should handle function callbacks which statically access queries', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -667,14 +669,15 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should handle class instantiations with specified callbacks that access queries', () => { - writeFile('/index.ts', ` + it('should handle class instantiations with specified callbacks that access queries', + async() => { + writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; import {External} from './external'; @@ -688,7 +691,7 @@ describe('static-queries migration with usage strategy', () => { } `); - writeFile('/external.ts', ` + writeFile('/external.ts', ` export class External { constructor(cb: () => void) { // Add extra parentheses to ensure that expression is unwrapped. @@ -697,13 +700,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); - expect(tree.readContent('/index.ts')) - .toContain(`@${queryType}('test', { static: true }) query: any;`); - }); + expect(tree.readContent('/index.ts')) + .toContain(`@${queryType}('test', { static: true }) query: any;`); + }); - it('should handle nested functions with arguments from parent closure', () => { + it('should handle nested functions with arguments from parent closure', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -726,13 +729,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should not mark queries used in setTimeout as static', () => { + it('should not mark queries used in setTimeout as static', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -757,7 +760,7 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: false }) query: any;`); @@ -767,7 +770,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: false }) query3: any;`); }); - it('should not mark queries used in "addEventListener" as static', () => { + it('should not mark queries used in "addEventListener" as static', async() => { writeFile('/index.ts', ` import {Component, ElementRef, ${queryType}} from '@angular/core'; @@ -785,13 +788,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: false }) query: any;`); }); - it('should not mark queries used in "requestAnimationFrame" as static', () => { + it('should not mark queries used in "requestAnimationFrame" as static', async() => { writeFile('/index.ts', ` import {Component, ElementRef, ${queryType}} from '@angular/core'; @@ -809,13 +812,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: false }) query: any;`); }); - it('should mark queries used in immediately-invoked function expression as static', () => { + it('should mark queries used in immediately-invoked function expression as static', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -836,7 +839,7 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); @@ -844,7 +847,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query2: any;`); }); - it('should detect static queries used in external function-like declaration', () => { + it('should detect static queries used in external function-like declaration', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; import {externalFn} from './external'; @@ -867,13 +870,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect static queries used through getter property access', () => { + it('should detect static queries used through getter property access', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -891,13 +894,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect static queries used through external getter access', () => { + it('should detect static queries used through external getter access', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; import {External} from './external'; @@ -929,13 +932,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should not mark queries as static if a value is assigned to accessor property', () => { + it('should not mark queries as static if a value is assigned to accessor property', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -954,13 +957,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: false }) query: any;`); }); - it('should mark queries as static if non-input setter uses query', () => { + it('should mark queries as static if non-input setter uses query', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -979,13 +982,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should check setter and getter when using compound assignment', () => { + it('should check setter and getter when using compound assignment', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1005,7 +1008,7 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); @@ -1013,7 +1016,7 @@ describe('static-queries migration with usage strategy', () => { .toContain(`@${queryType}('test', { static: true }) query2: any;`); }); - it('should check getters when using comparison operator in binary expression', () => { + it('should check getters when using comparison operator in binary expression', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1032,13 +1035,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should check derived abstract class methods', () => { + it('should check derived abstract class methods', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1068,13 +1071,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect queries accessed through deep abstract class method', () => { + it('should detect queries accessed through deep abstract class method', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1100,13 +1103,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect queries accessed through abstract property getter', () => { + it('should detect queries accessed through abstract property getter', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1126,13 +1129,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect queries accessed through abstract property setter', () => { + it('should detect queries accessed through abstract property setter', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1153,13 +1156,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect query usage in abstract class methods accessing inherited query', () => { + it('should detect query usage in abstract class methods accessing inherited query', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1189,13 +1192,13 @@ describe('static-queries migration with usage strategy', () => { } `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect query usage within component template', () => { + it('should detect query usage within component template', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1210,13 +1213,13 @@ describe('static-queries migration with usage strategy', () => { `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should detect query usage with nested property read within component template', () => { + it('should detect query usage with nested property read within component template', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1231,14 +1234,15 @@ describe('static-queries migration with usage strategy', () => { `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should not mark query as static if template has template reference with same name', () => { - writeFile('/index.ts', ` + it('should not mark query as static if template has template reference with same name', + async() => { + writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @Component({templateUrl: 'my-template.html'}) @@ -1247,21 +1251,21 @@ describe('static-queries migration with usage strategy', () => { } `); - writeFile(`/my-template.html`, ` + writeFile(`/my-template.html`, ` `); - runMigration(); + await runMigration(); - expect(tree.readContent('/index.ts')) - .toContain(`@${queryType}('test', { static: false }) query: any;`); - }); + expect(tree.readContent('/index.ts')) + .toContain(`@${queryType}('test', { static: false }) query: any;`); + }); it('should not mark query as static if template has property read with query name but different receiver', - () => { + async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1280,13 +1284,13 @@ describe('static-queries migration with usage strategy', () => { `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: false }) someProp: any;`); }); - it('should ignore queries accessed within element', () => { + it('should ignore queries accessed within element', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1304,13 +1308,13 @@ describe('static-queries migration with usage strategy', () => { `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: false }) query: any;`); }); - it('should detect inherited queries used in templates', () => { + it('should detect inherited queries used in templates', async() => { writeFile('/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1327,13 +1331,13 @@ describe('static-queries migration with usage strategy', () => { `); - runMigration(); + await runMigration(); expect(tree.readContent('/index.ts')) .toContain(`@${queryType}('test', { static: true }) query: any;`); }); - it('should properly handle multiple tsconfig files', () => { + it('should properly handle multiple tsconfig files', async() => { writeFile('/src/index.ts', ` import {Component, ${queryType}} from '@angular/core'; @@ -1352,7 +1356,7 @@ describe('static-queries migration with usage strategy', () => { // The migration runs for "/tsconfig.json" and "/src/tsconfig.json" which both // contain the "src/index.ts" file. This test ensures that we don't incorrectly // apply the code transformation multiple times with outdated offsets. - runMigration(); + await runMigration(); expect(tree.readContent('/src/index.ts')) .toContain(`@${queryType}('test', { static: false }) query: any;`); diff --git a/packages/core/schematics/utils/schematics_prompt.ts b/packages/core/schematics/utils/schematics_prompt.ts new file mode 100644 index 0000000000..9fcfc92f48 --- /dev/null +++ b/packages/core/schematics/utils/schematics_prompt.ts @@ -0,0 +1,34 @@ +/** + * @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 + */ + +type Inquirer = typeof import('inquirer'); + +let resolvedInquirerModule: Inquirer|null; + +try { + // "inquirer" is the prompt module also used by the devkit schematics CLI + // in order to show prompts for schematics. We transitively depend on this + // module, but don't want to throw an exception if the module is not + // installed for some reason. In that case prompts are just not supported. + resolvedInquirerModule = require('inquirer'); +} catch (e) { + resolvedInquirerModule = null; +} + +/** Whether prompts are currently supported. */ +export function supportsPrompt(): boolean { + return !!resolvedInquirerModule && !!process.stdin.isTTY; +} + +/** + * Gets the resolved instance of "inquirer" which can be used to programmatically + * create prompts. + */ +export function getInquirer(): Inquirer { + return resolvedInquirerModule !; +} diff --git a/yarn.lock b/yarn.lock index f036c51758..dddf995843 100644 --- a/yarn.lock +++ b/yarn.lock @@ -455,6 +455,14 @@ resolved "https://registry.yarnpkg.com/@types/hammerjs/-/hammerjs-2.0.35.tgz#7b7c950c7d54593e23bffc8d2b4feba9866a7277" integrity sha512-4mUIMSZ2U4UOWq1b+iV7XUTE4w+Kr3x+Zb/Qz5ROO6BTZLw2c8/ftjq0aRgluguLs4KRuBnrOy/s389HVn1/zA== +"@types/inquirer@^0.0.44": + version "0.0.44" + resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-0.0.44.tgz#60ce954581cfdf44ad3899ec4cdc5fbe3fef1694" + integrity sha512-ugbhy1yBtCz5iTWYF+AGRS/UcMcWicdyHhxl9VaeFYc3ueg0CCssthQLB3rIcIOeGtfG6WPEvHdLu/IjKYfefg== + dependencies: + "@types/rx" "*" + "@types/through" "*" + "@types/jasmine@*": version "3.3.5" resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.3.5.tgz#3738ffbf34dffae9ecaac4503d7d969744f0e1d7" @@ -524,6 +532,107 @@ resolved "https://registry.yarnpkg.com/@types/q/-/q-0.0.32.tgz#bd284e57c84f1325da702babfc82a5328190c0c5" integrity sha1-vShOV8hPEyXacCur/IKlMoGQwMU= +"@types/rx-core-binding@*": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/rx-core-binding/-/rx-core-binding-4.0.4.tgz#d969d32f15a62b89e2862c17b3ee78fe329818d3" + integrity sha512-5pkfxnC4w810LqBPUwP5bg7SFR/USwhMSaAeZQQbEHeBp57pjKXRlXmqpMrLJB4y1oglR/c2502853uN0I+DAQ== + dependencies: + "@types/rx-core" "*" + +"@types/rx-core@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-core/-/rx-core-4.0.3.tgz#0b3354b1238cedbe2b74f6326f139dbc7a591d60" + integrity sha1-CzNUsSOM7b4rdPYybxOdvHpZHWA= + +"@types/rx-lite-aggregates@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-lite-aggregates/-/rx-lite-aggregates-4.0.3.tgz#6efb2b7f3d5f07183a1cb2bd4b1371d7073384c2" + integrity sha512-MAGDAHy8cRatm94FDduhJF+iNS5//jrZ/PIfm+QYw9OCeDgbymFHChM8YVIvN2zArwsRftKgE33QfRWvQk4DPg== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-async@*": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@types/rx-lite-async/-/rx-lite-async-4.0.2.tgz#27fbf0caeff029f41e2d2aae638b05e91ceb600c" + integrity sha512-vTEv5o8l6702ZwfAM5aOeVDfUwBSDOs+ARoGmWAKQ6LOInQ8J4/zjM7ov12fuTpktUKdMQjkeCp07Vd73mPkxw== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-backpressure@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-lite-backpressure/-/rx-lite-backpressure-4.0.3.tgz#05abb19bdf87cc740196c355e5d0b37bb50b5d56" + integrity sha512-Y6aIeQCtNban5XSAF4B8dffhIKu6aAy/TXFlScHzSxh6ivfQBQw6UjxyEJxIOt3IT49YkS+siuayM2H/Q0cmgA== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-coincidence@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-lite-coincidence/-/rx-lite-coincidence-4.0.3.tgz#80bd69acc4054a15cdc1638e2dc8843498cd85c0" + integrity sha512-1VNJqzE9gALUyMGypDXZZXzR0Tt7LC9DdAZQ3Ou/Q0MubNU35agVUNXKGHKpNTba+fr8GdIdkC26bRDqtCQBeQ== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-experimental@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/rx-lite-experimental/-/rx-lite-experimental-4.0.1.tgz#c532f5cbdf3f2c15da16ded8930d1b2984023cbd" + integrity sha1-xTL1y98/LBXaFt7Ykw0bKYQCPL0= + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-joinpatterns@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/rx-lite-joinpatterns/-/rx-lite-joinpatterns-4.0.1.tgz#f70fe370518a8432f29158cc92ffb56b4e4afc3e" + integrity sha1-9w/jcFGKhDLykVjMkv+1a05K/D4= + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-testing@*": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/rx-lite-testing/-/rx-lite-testing-4.0.1.tgz#21b19d11f4dfd6ffef5a9d1648e9c8879bfe21e9" + integrity sha1-IbGdEfTf1v/vWp0WSOnIh5v+Iek= + dependencies: + "@types/rx-lite-virtualtime" "*" + +"@types/rx-lite-time@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-lite-time/-/rx-lite-time-4.0.3.tgz#0eda65474570237598f3448b845d2696f2dbb1c4" + integrity sha512-ukO5sPKDRwCGWRZRqPlaAU0SKVxmWwSjiOrLhoQDoWxZWg6vyB9XLEZViKOzIO6LnTIQBlk4UylYV0rnhJLxQw== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite-virtualtime@*": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/rx-lite-virtualtime/-/rx-lite-virtualtime-4.0.3.tgz#4b30cacd0fe2e53af29f04f7438584c7d3959537" + integrity sha512-3uC6sGmjpOKatZSVHI2xB1+dedgml669ZRvqxy+WqmGJDVusOdyxcKfyzjW0P3/GrCiN4nmRkLVMhPwHCc5QLg== + dependencies: + "@types/rx-lite" "*" + +"@types/rx-lite@*": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/rx-lite/-/rx-lite-4.0.6.tgz#3c02921c4244074234f26b772241bcc20c18c253" + integrity sha512-oYiDrFIcor9zDm0VDUca1UbROiMYBxMLMaM6qzz4ADAfOmA9r1dYEcAFH+2fsPI5BCCjPvV9pWC3X3flbrvs7w== + dependencies: + "@types/rx-core" "*" + "@types/rx-core-binding" "*" + +"@types/rx@*": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@types/rx/-/rx-4.1.1.tgz#598fc94a56baed975f194574e0f572fd8e627a48" + integrity sha1-WY/JSla67ZdfGUV04PVy/Y5iekg= + dependencies: + "@types/rx-core" "*" + "@types/rx-core-binding" "*" + "@types/rx-lite" "*" + "@types/rx-lite-aggregates" "*" + "@types/rx-lite-async" "*" + "@types/rx-lite-backpressure" "*" + "@types/rx-lite-coincidence" "*" + "@types/rx-lite-experimental" "*" + "@types/rx-lite-joinpatterns" "*" + "@types/rx-lite-testing" "*" + "@types/rx-lite-time" "*" + "@types/rx-lite-virtualtime" "*" + "@types/selenium-webdriver@3.0.7": version "3.0.7" resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.7.tgz#5d3613d1ab3ca08b74d19683a3a7c573129ab18f" @@ -554,6 +663,13 @@ resolved "https://registry.yarnpkg.com/@types/systemjs/-/systemjs-0.19.32.tgz#e9204c4cdbc8e275d645c00e6150e68fc5615a24" integrity sha1-6SBMTNvI4nXWRcAOYVDmj8VhWiQ= +"@types/through@*": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/through/-/through-0.0.29.tgz#72943aac922e179339c651fa34a4428a4d722f93" + integrity sha512-9a7C5VHh+1BKblaYiq+7Tfc+EOmjMdZaD1MYtkQjSoxgB69tBjW98ry6SKsi4zEIWztLOMRuL87A3bdT/Fc/4w== + dependencies: + "@types/node" "*" + "@types/yargs@^11.1.1": version "11.1.1" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-11.1.1.tgz#2e724257167fd6b615dbe4e54301e65fe597433f"